diff --git a/crates/effects/src/raster.rs b/crates/effects/src/raster.rs index ecd7d9a9bb..b59d61beb2 100644 --- a/crates/effects/src/raster.rs +++ b/crates/effects/src/raster.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use serde_json::{Map, Value, json}; use vectorcraft_doc::color::{BlendMode, Color}; use vectorcraft_doc::rastersettings::MAX_ADD_AROUND; -use vectorcraft_doc::{Effect, Node, RasterColorModel, RasterEffectsSettings}; +use vectorcraft_doc::{AppearanceItem, Effect, Node, RasterColorModel, RasterEffectsSettings}; use vectorcraft_geom::{Affine, Point, Rect, Vec2}; @@ -553,6 +553,16 @@ fn follow_items_by(n: &mut Node, a: Affine) -> bool { changed } +/// Make the raster effects of `items`, the fills and strokes of an object whose own effects are +/// `owner`, follow the Transform effects of `owner` ([`follow_contents`]): for art that applies +/// those effects to the object's path and draws each fill and stroke on it as a node of its own. +pub(crate) fn follow_paints(items: &mut [AppearanceItem], owner: &[Effect]) { + let Some(a) = contents_step(owner) else { return }; + for item in items { + follow_contents(item.effects_mut(), a, Affine::IDENTITY); + } +} + /// `effects`, those of a fill or stroke of an object whose own effects are `owner`, with each /// raster effect following the Transform effects of `owner` as well ([`follow_contents`]): the /// list to draw that fill's or stroke's raster effects from ([`raster_effects`]). Borrowed when diff --git a/crates/effects/src/revolve.rs b/crates/effects/src/revolve.rs index 20a66e9a83..36eb9c2b84 100644 --- a/crates/effects/src/revolve.rs +++ b/crates/effects/src/revolve.rs @@ -565,9 +565,13 @@ fn art(doc: Option<&Document>, n: &Node, expand: bool) -> Option { } else { let path = geometry(&path, &n.appearance.effects, &ctx); remaining = crate::without_geometry(&remaining); + // The object's Transform effects, applied to the path above, transform the items' raster + // effects too. + let mut items: Vec = n.appearance.items.iter().filter(|i| crate::paints(i)).cloned().collect(); + crate::raster::follow_paints(&mut items, &n.appearance.effects); // Every item's shadow goes behind every item's art. let mut shadows = vec![]; - for item in n.appearance.items.iter().filter(|i| crate::paints(i)) { + for item in &items { if let Some(index) = item.effects().iter().position(|e| e.visible && is_revolve(&e.id)) { let fx = item.effects().get(index)?; let p = geometry(&path, item.effects().get(..index)?, &ctx.item(item)); diff --git a/crates/effects/src/solids.rs b/crates/effects/src/solids.rs index a49829f50c..2e96d4dcdb 100644 --- a/crates/effects/src/solids.rs +++ b/crates/effects/src/solids.rs @@ -982,6 +982,10 @@ pub fn solid_art_in(doc: Option<&Document>, n: &Node) -> Option { } else { let path = geometry(&path, &n.appearance.effects, &ctx); remaining = crate::without_geometry(&n.appearance.effects); + // The object's Transform effects, applied to the path above, transform the items' raster + // effects too. + let mut paints = paints; + crate::raster::follow_paints(&mut paints, &n.appearance.effects); // Every item's shadow goes behind every item's art. let mut shadows = vec![]; for item in &paints { diff --git a/crates/effects/src/tests/rasterfollow.rs b/crates/effects/src/tests/rasterfollow.rs index 93b474f1f5..f8e087b368 100644 --- a/crates/effects/src/tests/rasterfollow.rs +++ b/crates/effects/src/tests/rasterfollow.rs @@ -241,6 +241,23 @@ fn a_shadow_before_a_transform_follows_it_into_3d_art() { } } +/// An object's Transform effect transforms the raster effects of its fills when only a fill has a +/// 3D effect: the shadow after that fill's 3D effect and another fill's shadow, as it does without +/// the 3D effect. +#[test] +fn an_objects_transform_moves_its_fills_shadows_beside_a_fills_3d_effect() { + for three_d in [crate::EXTRUDE, crate::REVOLVE] { + let art = |n: &Node| if three_d == crate::EXTRUDE { crate::solid_art(n) } else { crate::revolve_art(n) }; + let mut n = fill_square(18, vec![fx(three_d, json!({})), shadow(10.0, 10.0, 0.0)]); + let mut other = n.appearance.items[0].clone(); + *other.effects_mut() = vec![shadow(10.0, 10.0, 0.0)]; + n.appearance.items.push(other); + n.appearance.effects = vec![transform(json!({"scaleH": 200, "scaleV": 100}))]; + let drawn = shadows_in(&art(&n).unwrap()); + assert!(drawn.len() > 1 && near(&drawn, &vec![(20.0, 10.0); drawn.len()]), "{three_d}: {drawn:?}"); + } +} + #[test] fn glows_blurs_and_feathers_scale_with_the_transform_after_them() { let effects = [