mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 12:21:54 +00:00
Canvas: a 3D effect on a fill that builds nothing draws without it
An object with an effect of its own (Drop Shadow, Roughen) and a 3D effect on its fill or stroke that builds nothing (Revolve after Roughen, a solid too complex) overflowed the stack when drawn, on the canvas and in Object › Rasterize: the art made for the object kept the item's 3D effect, so drawing it drew it as an object with effects again, and that art again, without end. That art now leaves out the 3D effects on its fills and strokes when none built anything, as a group's or layer's own are left to the art of each fill or stroke. The fill draws as without the effect; with a raster effect the object draws from that art, so its edges differ a little from the same object without the 3D effect, drawn as a path.
This commit is contained in:
@@ -600,7 +600,16 @@ impl Renderer {
|
||||
None if matches!(a.kind, NodeKind::Text(_) | NodeKind::Image(_) | NodeKind::Path { .. } | NodeKind::Compound { .. }) => (**a).clone(),
|
||||
None => effects::outline_art(Some(doc), a, symbol.as_ref()).unwrap_or_else(|| Node::group(a.id, vec![])),
|
||||
};
|
||||
let art = if effects::has_revolve(a) || effects::has_solid(a) { effects::evaluate_container(Some(doc), &art).unwrap_or(art) } else { art };
|
||||
let has_three_d = effects::has_revolve(a) || effects::has_solid(a);
|
||||
let mut art = if has_three_d { effects::evaluate_container(Some(doc), &art).unwrap_or(art) } else { art };
|
||||
// A 3D effect on a fill or stroke that built nothing (Revolve after Roughen, a solid too
|
||||
// complex) draws the item as without it: left in the art, it would draw the art as an
|
||||
// object with effects again, and that art again, without end.
|
||||
if has_three_d && !three_d && !container {
|
||||
for item in &mut art.appearance.items {
|
||||
item.effects_mut().retain(|e| !effects::is_three_d(&e.id));
|
||||
}
|
||||
}
|
||||
// Vector Halftone samples the object with its colour adjustments: its dots keep their inks.
|
||||
let outer = if effects::addons::halftone::has_halftone(a) { None } else { effects::color_map(&a.appearance.effects) };
|
||||
let mut art = self.adjusted_art(doc, art, outer.as_ref());
|
||||
|
||||
@@ -1700,3 +1700,42 @@ fn an_image_inner_glow_follows_its_visible_pixels() {
|
||||
assert!(lum(img.pixel(60, 50)) < lum(img.pixel(51, 50)), "threads {threads}: fading inward");
|
||||
}
|
||||
}
|
||||
|
||||
/// A 3D effect on a path's fill that builds nothing (Revolve after Roughen) draws the fill as
|
||||
/// without it, under the object's own effects too: the art made for the object is drawn without
|
||||
/// it, not as an object with effects again, and that art again, without end. With a raster effect
|
||||
/// the object draws from that art, where without the 3D effect it draws as a path: their edges
|
||||
/// differ a little. An Extrude may build.
|
||||
#[test]
|
||||
fn a_fills_3d_effect_that_builds_nothing_draws_the_fill_as_without_it() {
|
||||
let rough = |detail: i64| Effect {
|
||||
id: "distort.roughen".into(),
|
||||
params: json!({"size": 10, "relative": false, "detail": detail, "points": "smooth"}),
|
||||
visible: true,
|
||||
};
|
||||
let shadow = Effect { id: "stylize.dropShadow".into(), params: json!({}), visible: true };
|
||||
let revolve = Effect { id: "threeD.revolve".into(), params: json!({}), visible: true };
|
||||
let extrude = Effect { id: "threeD.extrude".into(), params: json!({"depth": 20.0}), visible: true };
|
||||
for (object, fill, three) in [
|
||||
(vec![shadow.clone()], rough(10), revolve.clone()),
|
||||
(vec![], rough(10), revolve),
|
||||
(vec![shadow], rough(60), extrude.clone()),
|
||||
(vec![rough(60)], rough(60), extrude),
|
||||
] {
|
||||
let draw = |three: Option<&Effect>| {
|
||||
let mut d = doc_with(Rect::new(30.0, 30.0, 60.0, 60.0), Color::BLACK, vec![]);
|
||||
let id = d.layers[0].children().unwrap()[0].id;
|
||||
let n = d.node_mut(id).unwrap();
|
||||
n.appearance.effects = object.clone();
|
||||
n.appearance.fill_mut().unwrap().effects = [Some(&fill), three].into_iter().flatten().cloned().collect();
|
||||
Renderer::new().render(&d, 100, 100, Affine::IDENTITY, &RenderOptions::default()).pixels
|
||||
};
|
||||
let with = draw(Some(&three));
|
||||
if three.id == "threeD.revolve" {
|
||||
let without = draw(None);
|
||||
let differ = with.iter().zip(&without).filter(|(a, b)| a != b).count();
|
||||
let most = with.iter().zip(&without).map(|(a, b)| a.abs_diff(*b)).max().unwrap_or(0);
|
||||
assert!(differ * 100 < with.len() && most <= 32, "{object:?}: {differ} channels differ, by up to {most}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user