diff --git a/crates/eps/src/ai_editing.rs b/crates/eps/src/ai_editing.rs index 552a99fe0..d87a00396 100644 --- a/crates/eps/src/ai_editing.rs +++ b/crates/eps/src/ai_editing.rs @@ -156,26 +156,35 @@ fn crlf(s: &str) -> Vec { s.replace('\n', "\r\n").into_bytes() } -/// `doc` with its geometry effects baked ([`vectorcraft_effects::bake_document`]), in its symbols' -/// art too (which instances are written as); `None` when nothing changes. +/// `doc` with its geometry effects and group paint baked ([`vectorcraft_effects::bake_document`]), +/// in its symbols' art and its patterns' tile art too (which instances and pattern fills are +/// written as); `None` when nothing changes. fn baked(doc: &Document) -> Option { let baked = vectorcraft_effects::bake_document(doc); let d = baked.as_ref().unwrap_or(doc); - if !d.symbols.iter().any(|s| vectorcraft_effects::needs_bake(&s.art)) { + let needs = vectorcraft_effects::needs_bake; + if !d.symbols.iter().any(|s| needs(&s.art)) && !d.patterns.iter().any(|p| p.art.iter().any(|a| needs(a))) { return baked; } - // The symbols' art baked as the layers of a copy of the document. + // The symbols' art, then each pattern's art, baked as the layers of a copy of the document. let mut art = d.clone(); - art.layers = d.symbols.iter().map(|s| Arc::new(Node::group(s.art.id, vec![s.art.clone()]))).collect(); + let symbols = d.symbols.iter().map(|s| Arc::new(Node::group(s.art.id, vec![s.art.clone()]))); + let patterns = d.patterns.iter().map(|p| Arc::new(Node::group(vectorcraft_doc::NodeId(0), p.art.clone()))); + art.layers = symbols.chain(patterns).collect(); // The copy keeps what baking adds to the document (the images color adjustments make). let Some(mut out) = vectorcraft_effects::bake_document(&art) else { return baked }; - let arts: Vec>> = out.layers.iter().map(|l| l.children().and_then(|c| c.first()).cloned()).collect(); - out.layers = d.layers.clone(); - for (s, a) in out.symbols.iter_mut().zip(arts) { - if let Some(a) = a { + let layers = std::mem::replace(&mut out.layers, d.layers.clone()); + let mut members = layers.iter().map(|l| l.children().cloned().unwrap_or_default()); + for s in out.symbols.iter_mut() { + if let Some(a) = members.next().and_then(|m| m.first().cloned()) { s.art = a; } } + for p in out.patterns.iter_mut() { + if let Some(m) = members.next() { + p.art = m; + } + } Some(out) } diff --git a/crates/eps/src/ai_editing/tests.rs b/crates/eps/src/ai_editing/tests.rs index e7f9c9d39..696014fb9 100644 --- a/crates/eps/src/ai_editing/tests.rs +++ b/crates/eps/src/ai_editing/tests.rs @@ -718,6 +718,27 @@ fn an_adjusted_image_in_a_symbol_is_written_in_its_adjusted_color() { assert_ne!(fill, paint(&solid(Color::rgb(0.0, 0.0, 0.0))), "the image is adjusted"); } +#[test] +fn a_group_fill_in_a_pattern_tile_is_written() { + let mut doc = Document::new(200.0, 100.0); + let layer = doc.layers[0].id; + let dot = Node::path(doc.alloc_id(), shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0)), Appearance::default()); + let mut group = Node::group(doc.alloc_id(), vec![Arc::new(dot)]); + group.appearance = Appearance::basic(solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0).into(); + doc.patterns.push(vectorcraft_doc::PatternDef::new("Dots", vec![Arc::new(group)])); + let filled = Node::path( + doc.alloc_id(), + shapes::rectangle(Rect::new(20.0, 20.0, 40.0, 40.0)), + Appearance::basic(Paint::Pattern { pattern: "Dots".into(), xf: Affine::IDENTITY }, Paint::None, 0.0), + ); + doc.insert(Some(layer), usize::MAX, filled).unwrap(); + let back = read_back(&doc); + let mut all = vec![]; + paths(back.layers[0].children().unwrap(), &mut all); + let red = paint(&solid(Color::rgb(1.0, 0.0, 0.0))); + assert!(all.iter().any(|p| p.appearance.items.iter().any(|i| paint(i.paint()) == red)), "the tiles keep their group's fill"); +} + #[test] fn effects_on_a_fill_are_reported_left_out() { let mut doc = Document::new(200.0, 100.0);