From 991c36458a258de43f406256f8624feec3196fd7 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 20:03:35 -0800 Subject: [PATCH] .ai import: an Art brushed path opens live only when its own settings would A path's Art brush settings matched the brush's within a small tolerance, and only the brush's were checked. A path with a Spacing of 0.0005, or guides that leave no art between them, opened live with the brush's settings. The path's settings now pass the same checks as the brush's, and such a path keeps its drawn look. --- crates/eps/src/import/ai/brush.rs | 7 +++++-- crates/eps/src/import/ai/tests.rs | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/eps/src/import/ai/brush.rs b/crates/eps/src/import/ai/brush.rs index b995b1bda0..58dac58c92 100644 --- a/crates/eps/src/import/ai/brush.rs +++ b/crates/eps/src/import/ai/brush.rs @@ -277,7 +277,8 @@ pub(super) fn bending_art(drafts: &[BrushDraft], art: &HashMap) -> /// Does the brush `draft` draw as `settings`, the settings a path's art style gives it? A path whose /// settings differ (Options of Selected Object) keeps its drawn look: a stroke names a brush, and -/// has no settings of its own. An Art brush's settings that don't draw ([`drawn`]) may differ. +/// has no settings of its own. An Art brush's settings that don't draw ([`drawn`]) may differ, and +/// the path's own settings must be ones the brush would open with ([`art_options`], [`opens_live`]). /// `bending`: [`bending_art`]. pub(super) fn draws_as(draft: &BrushDraft, settings: &PathBrush, bending: &HashMap) -> bool { let close = |a: f64, b: f64| (a - b).abs() <= 1e-3 * a.abs().max(b.abs()).max(1.0); @@ -290,7 +291,9 @@ pub(super) fn draws_as(draft: &BrushDraft, settings: &PathBrush, bending: &HashM && d.modes() == c.modes() } (DraftKind::Art { settings: Some(d), art: name, .. }, PathBrush::Art(s)) => { - drawn(d).iter().zip(drawn(s)).all(|(a, b)| close(*a, b)) && bending.get(name).is_some_and(|length| opens_live(d, *length)) + drawn(d).iter().zip(drawn(s)).all(|(a, b)| close(*a, b)) + && art_options(s).is_some() + && bending.get(name).is_some_and(|length| opens_live(d, *length) && opens_live(s, *length)) } _ => false, } diff --git a/crates/eps/src/import/ai/tests.rs b/crates/eps/src/import/ai/tests.rs index feaa4201f6..2701cb6fec 100644 --- a/crates/eps/src/import/ai/tests.rs +++ b/crates/eps/src/import/ai/tests.rs @@ -10601,7 +10601,8 @@ fn an_art_brushed_path_the_brush_cant_draw_keeps_its_drawn_look() { ("its own width", good.clone(), style_with("Scaling", "2")), ("a width on the pen", good.clone(), style_with("ScaleYDependsOn", "2")), ("spacing", good.clone(), style_with("Spacing", "1")), - ("flipped", good.clone(), style_with("UpsideDown", "1")), + ("a spacing close to the brush's 0", good.clone(), style_with("Spacing", "0.0005")), + ("flipped",good.clone(), style_with("UpsideDown", "1")), ("flipped the other way", good.clone(), style_with("Counterclockwise", "1")), ("another adjustment", good.clone(), style_with("Adjustment", "2")), ("no fold adjustment", good.clone(), style_with("FoldAdjustment", "0")), @@ -10639,13 +10640,16 @@ fn an_art_brushed_path_the_brush_cant_draw_keeps_its_drawn_look() { ("both: guides that leave no art between them", &[(10, "1"), (11, "30"), (12, "10")]), ("both: a guide past the start", &[(10, "1"), (11, "-5"), (12, "10")]), ]; - let cases: Vec<(&str, String, String)> = cases + let mut cases: Vec<(&str, String, String)> = cases .into_iter() .chain(own.iter().map(|(why, changes)| { let (def, style) = ribbon_with(changes); (*why, def, style) })) .collect(); + // The brush's guides leave art between them, and the path's, close to them, leave none. + let (def, style) = ribbon_with(&[(10, "1"), (11, "20"), (12, "19.99")]); + cases.push(("path guides close to the brush's that leave no art between them", def, style.replace("19.99 /", "20 /"))); let mut wrong = vec![]; for (why, def, style) in cases { let r = read_ok(&format!("{def}{style}{}", layer(&brushed_path())));