diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index c438cf8815..e56e0c09d1 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -527,6 +527,16 @@ :else (d/vec2 id default-transform)))))) +(defn- root-only-modif-tree + "Restrict modif-tree to shapes whose parent is not in modif-tree (so WASM + propagates once; including children would cause double application)." + [modif-tree objects] + (let [ids-in-tree (set (keys modif-tree)) + root? (fn [id] + (let [parent-id (get-in objects [id :parent-id])] + (or (nil? parent-id) (not (ids-in-tree parent-id)))))] + (select-keys modif-tree (filter root? (keys modif-tree))))) + (defn- parse-geometry-modifiers [modif-tree] (into [] xf:parse-geometry-modifier modif-tree)) @@ -580,7 +590,7 @@ (set-wasm-props! objects prev-wasm-props wasm-props) (let [structure-entries (parse-structure-modifiers modif-tree)] (wasm.api/set-structure-modifiers structure-entries) - (let [geometry-entries (parse-geometry-modifiers modif-tree) + (let [geometry-entries (parse-geometry-modifiers (root-only-modif-tree modif-tree objects)) modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)] (wasm.api/set-modifiers modifiers) (let [ids (into [] xf:map-key geometry-entries) @@ -637,7 +647,7 @@ (assoc :attrs transform-attrs)) geometry-entries - (parse-geometry-modifiers modif-tree) + (parse-geometry-modifiers (root-only-modif-tree modif-tree objects)) snap-pixel? (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index 123bbd9336..60264bee8d 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -379,22 +379,10 @@ pub fn propagate_modifiers( modifiers: &[TransformEntry], pixel_precision: bool, ) -> Vec { - // Ids of all shapes that have an entry in the incoming modifiers list. - let modifier_ids: HashSet = modifiers.iter().map(|e| e.id).collect(); + // Frontend sends only "root" entries (no child when parent is in the list), + // so we don't need to filter here. let mut entries: VecDeque<_> = modifiers .iter() - .filter(|entry| { - // Skip child shapes whose parent is also in the modifiers list. - // Those children will get their transform when the parent's transform - // is propagated via propagate_children; including them here would - // cause the transform to be applied twice (once here, once from parent). - let should_skip = state - .shapes - .get(&entry.id) - .and_then(|s| s.parent_id.map(|pid| modifier_ids.contains(&pid))) - .unwrap_or(false); - !should_skip - }) .map(|entry| { // If we receibe a identity matrix we force a reflow if math::identitish(&entry.transform) {