mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 03:05:44 +00:00
.ai import: a variable-width stroke whose look draws nothing comes in as itself
An object with a variable-width stroke whose drawn look holds nothing that draws, apart from copies read from %_ lines, has no outline to keep. It now comes in as the object with its width profile instead of being dropped.
This commit is contained in:
@@ -531,6 +531,24 @@ impl<'a> Reader<'a> {
|
||||
{
|
||||
return;
|
||||
}
|
||||
// A variable-width stroke's look that draws nothing but copies read from `%_` lines: an
|
||||
// empty group, which [`styled`] takes the stroke for.
|
||||
let drawn;
|
||||
let look = if !hidden
|
||||
&& slot_of(object.name.as_deref()).is_none()
|
||||
&& matches!(look.kind, NodeKind::Group { .. })
|
||||
&& self.styles.get(&style).is_some_and(|st| st.uneven)
|
||||
&& !draws(look, &self.hidden_ids)
|
||||
{
|
||||
let mut copy = look.clone();
|
||||
if let Some(children) = copy.children_mut() {
|
||||
children.clear();
|
||||
}
|
||||
drawn = copy;
|
||||
&drawn
|
||||
} else {
|
||||
look
|
||||
};
|
||||
// A style read here: the object, painted as it says.
|
||||
if let Some(appearance) = self
|
||||
.styles
|
||||
@@ -658,7 +676,8 @@ fn styled(object: &Node, look: &Node, st: &crate::import::ai::style::ArtStyle, p
|
||||
// A text object's slot (an empty group standing for it) takes the style as the type will.
|
||||
let slot = slot_of(object.name.as_deref()).is_some();
|
||||
if !st.plain
|
||||
|| (!slot && !st.simple)
|
||||
// A look with no outline in it to keep takes a variable-width stroke as the object's own.
|
||||
|| (!slot && !st.simple && (!st.uneven || draws(look, &HashSet::new())))
|
||||
|| (object.is_container() && !slot && !matches!(object.kind, NodeKind::Compound { .. }) && !st.items.is_empty())
|
||||
{
|
||||
return None;
|
||||
@@ -733,3 +752,15 @@ fn strip(n: &mut Node, hidden: &HashSet<NodeId>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `n` holds anything that draws, apart from the children read from `%_` lines (`hidden`):
|
||||
/// a node other than a group or a compound path.
|
||||
fn draws(n: &Node, hidden: &HashSet<NodeId>) -> bool {
|
||||
if hidden.contains(&n.id) {
|
||||
return false;
|
||||
}
|
||||
match n.children() {
|
||||
Some(children) if matches!(n.kind, NodeKind::Group { .. } | NodeKind::Compound { .. }) => children.iter().any(|c| draws(c, hidden)),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ pub(super) struct ArtStyle {
|
||||
/// Shadow on the object: what any object is read with. Type takes the other plain styles too (its drawn look
|
||||
/// can't be retyped); other objects keep their drawn look, which is the app's own rendering.
|
||||
pub simple: bool,
|
||||
/// It would be [`Self::simple`] but for a variable-width stroke: an object whose drawn look
|
||||
/// is empty takes it with its profile.
|
||||
pub uneven: bool,
|
||||
/// Its fills and strokes, bottom first.
|
||||
pub items: Vec<StyleItem>,
|
||||
/// Each item's own visibility, transparency and effects, in the order of [`Self::items`].
|
||||
@@ -652,14 +655,16 @@ fn style(filters: &[Filter]) -> ArtStyle {
|
||||
}
|
||||
st.plain = b.plain;
|
||||
let count = |stroke: bool| st.items.iter().filter(|i| matches!(i, StyleItem::Stroke { .. }) == stroke).count();
|
||||
st.simple = st.plain
|
||||
let simple = st.plain
|
||||
&& count(true) <= 1
|
||||
&& count(false) <= 1
|
||||
// A variable-width stroke curves between its width points, and `WidthProfile` is straight
|
||||
// between them: the drawn look holds the outline as drawn.
|
||||
&& st.items.iter().all(|i| matches!(i, StyleItem::Fill(_) | StyleItem::Stroke { profile: None, .. } | StyleItem::Gradient { .. }))
|
||||
&& st.items.iter().all(|i| matches!(i, StyleItem::Fill(_) | StyleItem::Stroke { .. } | StyleItem::Gradient { .. }))
|
||||
&& st.looks.iter().all(|l| l.visible && l.opacity >= 1.0 && l.blend == BlendMode::Normal && l.effects.is_empty())
|
||||
&& st.effects.iter().all(|e| matches!(e.id.as_str(), "stylize.outerGlow" | "stylize.feather" | "stylize.dropShadow"));
|
||||
// A variable-width stroke curves between its width points, and `WidthProfile` is straight
|
||||
// between them: the drawn look holds the outline as drawn.
|
||||
let even = st.items.iter().all(|i| !matches!(i, StyleItem::Stroke { profile: Some(_), .. }));
|
||||
(st.simple, st.uneven) = (simple && even, simple && !even);
|
||||
st
|
||||
}
|
||||
|
||||
|
||||
@@ -1916,6 +1916,24 @@ fn a_variable_width_stroke_comes_in_as_its_drawn_outline() {
|
||||
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{:?}", r.warnings);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_variable_width_stroke_with_an_empty_look_comes_in_as_itself() {
|
||||
// GUARDS: with no outline in its look to keep, the object was dropped.
|
||||
let hidden_line = "0 5 m\n10 5 L\n".replace('\n', "\n%_");
|
||||
let hidden_fill = format!("{}f\n", rect_path(0.0, 3.0, 10.0, 7.0)).replace('\n', "\n%_");
|
||||
let lens = width_profile_style(&[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)]);
|
||||
// An empty look, and ones whose group or compound path holds only a copy read from `%_` lines.
|
||||
for look in ["u\nU\n".to_string(), format!("u\nu\n%_{hidden_fill}\nU\nU\n"), format!("u\n*u\n%_{hidden_fill}\n*U\nU\n")] {
|
||||
let art_ = format!("{look}%_{hidden_line}S\n1 (Tapered) XW\n");
|
||||
let r = read_ok(&format!("{lens}{}", layer(&art_)));
|
||||
let a = art(&r.doc);
|
||||
let [object] = a.as_slice() else { panic!("{look:?}: {a:#?}") };
|
||||
assert!(matches!(object.kind, NodeKind::Path { .. }), "{look:?}: {object:#?}");
|
||||
assert_eq!(object.appearance.stroke().map(|s| (s.width, s.profile.is_some())), Some((4.0, true)), "{look:?}: {object:#?}");
|
||||
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{look:?}: {:?}", r.warnings);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bare_comment_line_in_a_prolog_leaves_its_end_alone() {
|
||||
// GUARDS: a bare `%` line's empty marker matched the empty begin marker of a section that has none.
|
||||
|
||||
Reference in New Issue
Block a user