diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 01dd329dd..379a016c8 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -346,29 +346,32 @@ center (gco/points->center points) selrect (calculate-selrect points center) - transform (calculate-transform points center selrect) - inverse (when (some? transform) (gmt/inverse transform))] - (if-not (and (some? inverse) (some? transform)) - shape - (let [type (dm/get-prop shape :type) - rotation (mod (+ (d/nilv (:rotation shape) 0) - (d/nilv (dm/get-in shape [:modifiers :rotation]) 0)) - 360) + [transform inverse] + (let [transform (calculate-transform points center selrect) + inverse (when (some? transform) (gmt/inverse transform))] + (if (and (some? transform) (some? inverse)) + [transform inverse] + [(:transform shape (gmt/matrix)) (:transform-inverse shape (gmt/matrix))])) - shape (if (or (= type :path) (= type :bool)) - (update shape :content path/transform-content transform-mtx) - (assoc shape - :x (dm/get-prop selrect :x) - :y (dm/get-prop selrect :y) - :width (dm/get-prop selrect :width) - :height (dm/get-prop selrect :height)))] - (-> shape - (assoc :transform transform) - (assoc :transform-inverse inverse) - (assoc :selrect selrect) - (assoc :points points) - (assoc :rotation rotation)))))) + type (dm/get-prop shape :type) + rotation (mod (+ (d/nilv (:rotation shape) 0) + (d/nilv (dm/get-in shape [:modifiers :rotation]) 0)) + 360) + + shape (if (or (= type :path) (= type :bool)) + (update shape :content path/transform-content transform-mtx) + (assoc shape + :x (dm/get-prop selrect :x) + :y (dm/get-prop selrect :y) + :width (dm/get-prop selrect :width) + :height (dm/get-prop selrect :height)))] + (-> shape + (assoc :transform transform) + (assoc :transform-inverse inverse) + (assoc :selrect selrect) + (assoc :points points) + (assoc :rotation rotation)))) (defn apply-transform "Given a new set of points transformed, set up the rectangle so it keeps diff --git a/common/test/common_tests/geom_shapes_test.cljc b/common/test/common_tests/geom_shapes_test.cljc index 5656b42f4..3164e37d4 100644 --- a/common/test/common_tests/geom_shapes_test.cljc +++ b/common/test/common_tests/geom_shapes_test.cljc @@ -114,11 +114,8 @@ (let [modifiers (ctm/resize-modifiers (gpt/point 0 0) (gpt/point 0 0)) shape-before (create-test-shape :rect {:modifiers modifiers}) shape-after (gsh/transform-shape shape-before)] - - (t/is (close? (get-in shape-before [:selrect :width]) - (get-in shape-after [:selrect :width]))) - (t/is (close? (get-in shape-before [:selrect :height]) - (get-in shape-after [:selrect :height]))))) + (t/is (close? 0.01 (get-in shape-after [:selrect :width]))) + (t/is (close? 0.01 (get-in shape-after [:selrect :height]))))) (t/testing "Transform shape with rotation modifiers" (t/are [type] diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index f67f3f35c..a5fb08e92 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -486,11 +486,16 @@ [] (keep (fn [[id data]] - (if (ctm/has-geometry? (:modifiers data)) + (cond + (= id uuid/zero) + nil + + (ctm/has-geometry? (:modifiers data)) {:id id :transform (ctm/modifiers->transform (:modifiers data))} ;; Unit matrix is used for reflowing + :else {:id id :transform (gmt/matrix)}))) modif-tree)) @@ -511,6 +516,13 @@ (update [_ state] (assoc state :workspace-selrect selrect)))) +(defn set-temporary-modifiers + [modifiers] + (ptk/reify ::set-temporary-modifiers + ptk/UpdateEvent + (update [_ state] + (assoc state :workspace-wasm-modifiers modifiers)))) + #_:clj-kondo/ignore (defn set-wasm-modifiers [modif-tree & {:keys [ignore-constraints ignore-snap-pixel] @@ -521,7 +533,6 @@ (update [_ state] (let [property-changes (extract-property-changes modif-tree)] - (-> state (assoc :prev-wasm-props (:wasm-props state)) (assoc :wasm-props property-changes)))) @@ -537,8 +548,11 @@ (let [structure-entries (parse-structure-modifiers modif-tree)] (wasm.api/set-structure-modifiers structure-entries) (let [geometry-entries (parse-geometry-modifiers modif-tree) - selrect (wasm.api/propagate-apply geometry-entries pixel-precision)] - (rx/of (set-temporary-selrect selrect)))))))) + modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)] + (wasm.api/set-modifiers modifiers) + (let [selrect (wasm.api/get-selection-rect (->> geometry-entries (map :id)))] + (rx/of (set-temporary-selrect selrect) + (set-temporary-modifiers modifiers))))))))) #_:clj-kondo/ignore (defn apply-wasm-modifiers diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index eb6804688..a68e484c9 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -136,7 +136,7 @@ (update [_ state] (-> state (update :workspace-local dissoc :transform :duplicate-move-started?) - (dissoc :workspace-selrect))))) + (dissoc :workspace-selrect :workspace-wasm-modifiers))))) ;; -- Resize -------------------------------------------------------- diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 002dfcc16..d874c9d70 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -15,7 +15,6 @@ [app.common.types.shape :as cts] [app.common.types.shape-tree :as ctt] [app.common.types.shape.layout :as ctl] - [app.main.data.workspace.modifiers :as dwm] [app.main.data.workspace.transforms :as dwt] [app.main.features :as features] [app.main.refs :as refs] @@ -54,27 +53,23 @@ [app.util.debug :as dbg] [app.util.text-editor :as ted] [beicon.v2.core :as rx] + [okulary.core :as l] [promesa.core :as p] [rumext.v2 :as mf])) ;; --- Viewport +(def workspace-wasm-modifiers + (l/derived :workspace-wasm-modifiers st/state)) + (defn apply-modifiers-to-selected - [selected objects text-modifiers modifiers] - (reduce - (fn [objects id] - (update - objects id - (fn [shape] - (cond-> shape - (and (cfh/text-shape? shape) (contains? text-modifiers id)) - (dwm/apply-text-modifier (get text-modifiers id)) - - (contains? modifiers id) - (gsh/transform-shape (dm/get-in modifiers [id :modifiers])))))) - - objects - selected)) + [selected objects modifiers] + (->> modifiers + (filter #(contains? selected (:id %))) + (reduce + (fn [objects {:keys [id transform]}] + (update objects id gsh/apply-transform transform)) + objects))) (mf/defc viewport* [{:keys [selected wglobal wlocal layout file page palete-size]}] @@ -113,12 +108,13 @@ base-objects (ui-hooks/with-focus-objects objects focus) - modifiers (mf/deref refs/workspace-modifiers) - text-modifiers (mf/deref refs/workspace-text-modifier) + wasm-modifiers (mf/deref workspace-wasm-modifiers) - objects-modified (mf/with-memo [base-objects text-modifiers modifiers] - (binding [cts/*wasm-sync* false] - (apply-modifiers-to-selected selected base-objects text-modifiers modifiers))) + objects-modified + (mf/with-memo + [base-objects wasm-modifiers] + (binding [cts/*wasm-sync* false] + (apply-modifiers-to-selected selected base-objects wasm-modifiers))) selected-shapes (->> selected (into [] (keep (d/getf objects-modified))) @@ -409,11 +405,9 @@ (when show-text-editor? (if (features/active-feature? @st/state "text-editor/v2") [:& editor-v2/text-editor {:shape editing-shape - :ref text-editor-ref - :modifiers modifiers}] + :ref text-editor-ref}] [:& editor-v1/text-editor-svg {:shape editing-shape - :ref text-editor-ref - :modifiers modifiers}])) + :ref text-editor-ref}])) (when show-frame-outline? (let [outlined-frame-id @@ -426,8 +420,7 @@ [:& outline/shape-outlines {:objects base-objects :hover #{outlined-frame-id} - :zoom zoom - :modifiers modifiers}] + :zoom zoom}] (when (ctl/any-layout? outlined-frame) [:g.ghost-outline @@ -443,8 +436,7 @@ :hover #{(:id @hover) @frame-hover} :highlighted highlighted :edition edition - :zoom zoom - :modifiers modifiers}]) + :zoom zoom}]) (when (and show-selection-handlers? selected-shapes) @@ -459,8 +451,7 @@ (when show-text-editor? [:& text-edition-outline {:shape (get base-objects edition) - :zoom zoom - :modifiers modifiers}]) + :zoom zoom}]) (when show-measures? [:& msr/measurement @@ -586,8 +577,7 @@ :vbox vbox :guides (:guides page) :hover-frame guide-frame - :disabled-guides disabled-guides? - :modifiers modifiers}]) + :disabled-guides disabled-guides?}]) ;; DEBUG LAYOUT DROP-ZONES (when (dbg/enabled? :layout-drop-zones) @@ -651,8 +641,7 @@ (when (or show-grid-editor? hover-grid?) [:& grid-layout/editor {:zoom zoom - :objects base-objects - :modifiers modifiers + :objects objects-modified :shape (or (get base-objects edition) (get base-objects @hover-top-frame-id)) :view-only (not show-grid-editor?)}]) @@ -665,8 +654,7 @@ [:& grid-layout/editor {:zoom zoom :key (dm/str (:id frame)) - :objects base-objects - :modifiers modifiers + :objects objects-modified :shape frame :view-only true}]))] [:g.scrollbar-wrapper {:clipPath "url(#clip-handlers)"} diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index ece21a6d1..8a28f1913 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -870,6 +870,38 @@ :center (gpt/point cx cy) :transform transform})))) +(defn get-selection-rect + [entries] + (when (d/not-empty? entries) + (let [offset (mem/alloc-bytes-32 (* (count entries) 16)) + heapu32 (mem/get-heap-u32)] + + (loop [entries (seq entries) + current-offset offset] + (when-not (empty? entries) + (let [id (first entries)] + (sr/heapu32-set-uuid id heapu32 current-offset) + (recur (rest entries) (+ current-offset (mem/ptr8->ptr32 16)))))) + + (let [offset (h/call wasm/internal-module "_get_selection_rect") + heapf32 (mem/get-heap-f32) + width (aget heapf32 (mem/ptr8->ptr32 (+ offset 0))) + height (aget heapf32 (mem/ptr8->ptr32 (+ offset 4))) + cx (aget heapf32 (mem/ptr8->ptr32 (+ offset 8))) + cy (aget heapf32 (mem/ptr8->ptr32 (+ offset 12))) + a (aget heapf32 (mem/ptr8->ptr32 (+ offset 16))) + b (aget heapf32 (mem/ptr8->ptr32 (+ offset 20))) + c (aget heapf32 (mem/ptr8->ptr32 (+ offset 24))) + d (aget heapf32 (mem/ptr8->ptr32 (+ offset 28))) + e (aget heapf32 (mem/ptr8->ptr32 (+ offset 32))) + f (aget heapf32 (mem/ptr8->ptr32 (+ offset 36))) + transform (gmt/matrix a b c d e f)] + (h/call wasm/internal-module "_free_bytes") + {:width width + :height height + :center (gpt/point cx cy) + :transform transform})))) + (defn set-canvas-background [background] (let [rgba (sr-clr/hex->u32argb background 1)] diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 5b04e616d..6eeac81e5 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -362,36 +362,41 @@ pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> *mut u8 { .collect(); with_state!(state, { - let (result, _) = shapes::propagate_modifiers(state, &entries, pixel_precision); + let result = shapes::propagate_modifiers(state, &entries, pixel_precision); mem::write_vec(result) }) } #[no_mangle] -pub extern "C" fn propagate_apply(pixel_precision: bool) -> *mut u8 { +pub extern "C" fn get_selection_rect() -> *mut u8 { let bytes = mem::bytes(); - let entries: Vec<_> = bytes - .chunks(size_of::<::BytesType>()) - .map(|data| TransformEntry::from_bytes(data.try_into().unwrap())) + let entries: Vec = bytes + .chunks(16) + .map(|bytes| { + uuid_from_u32_quartet( + u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]), + u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]), + u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]), + u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]), + ) + }) .collect(); with_state!(state, { - let (result, bounds) = shapes::propagate_modifiers(state, &entries, pixel_precision); - - for entry in result { - state.modifiers.insert(entry.id, entry.transform); - } - state.rebuild_modifier_tiles(); - - mem::free_bytes(); - - let bbs: Vec<_> = entries.iter().flat_map(|e| bounds.get(&e.id)).collect(); + let bbs: Vec<_> = entries + .iter() + .flat_map(|id| { + let default = Matrix::default(); + let modifier = state.modifiers.get(id).unwrap_or(&default); + state.shapes.get(id).map(|b| b.bounds().transform(modifier)) + }) + .collect(); let result_bound = if bbs.len() == 1 { bbs[0] } else { - &Bounds::join_bounds(&bbs) + Bounds::join_bounds(&bbs) }; let width = result_bound.width(); @@ -410,7 +415,6 @@ pub extern "C" fn propagate_apply(pixel_precision: bool) -> *mut u8 { bytes[28..32].clone_from_slice(&transform[4].to_le_bytes()); bytes[32..36].clone_from_slice(&transform[2].to_le_bytes()); bytes[36..40].clone_from_slice(&transform[5].to_le_bytes()); - mem::write_bytes(bytes) }) } diff --git a/render-wasm/src/math.rs b/render-wasm/src/math.rs index 298bc895a..25e698918 100644 --- a/render-wasm/src/math.rs +++ b/render-wasm/src/math.rs @@ -64,7 +64,7 @@ impl Bounds { Self { nw, ne, se, sw } } - pub fn join_bounds(bounds: &[&Bounds]) -> Self { + pub fn join_bounds(bounds: &[Bounds]) -> Self { let (min_x, min_y, max_x, max_y) = bounds .iter() diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index 6b33dd3f6..4039c8581 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -132,7 +132,7 @@ pub fn propagate_modifiers( state: &State, modifiers: &[TransformEntry], pixel_precision: bool, -) -> (Vec, HashMap) { +) -> Vec { let shapes = &state.shapes; let font_col = state.render_state.fonts.font_collection(); @@ -341,13 +341,10 @@ pub fn propagate_modifiers( layout_reflows = Vec::new(); } - ( - modifiers - .iter() - .map(|(key, val)| TransformEntry::new(*key, *val)) - .collect(), - bounds, - ) + modifiers + .iter() + .map(|(key, val)| TransformEntry::new(*key, *val)) + .collect() } #[cfg(test)] diff --git a/render-wasm/src/shapes/modifiers/grid_layout.rs b/render-wasm/src/shapes/modifiers/grid_layout.rs index 92aeda55a..04fd59baf 100644 --- a/render-wasm/src/shapes/modifiers/grid_layout.rs +++ b/render-wasm/src/shapes/modifiers/grid_layout.rs @@ -64,7 +64,6 @@ fn calculate_tracks( set_fr_value(is_column, shape, layout_data, &mut tracks, layout_size); stretch_tracks(is_column, shape, layout_data, &mut tracks, layout_size); assign_anchors(is_column, layout_data, layout_bounds, &mut tracks); - tracks } @@ -386,9 +385,10 @@ fn stretch_tracks( tracks: &mut [TrackData], layout_size: f32, ) { - if (column - && (layout_data.justify_content != JustifyContent::Stretch - || shape.is_layout_horizontal_auto())) + if (tracks.is_empty() + || column + && (layout_data.justify_content != JustifyContent::Stretch + || shape.is_layout_horizontal_auto())) || (!column && (layout_data.align_content != AlignContent::Stretch || shape.is_layout_vertical_auto())) @@ -462,7 +462,11 @@ fn assign_anchors( ) }; - let tot_gap = gap * (tracks.len() - 1) as f32; + let tot_gap = if tracks.is_empty() { + 0.0 + } else { + gap * (tracks.len() - 1) as f32 + }; let tot_size = tot_track_length + tot_gap; let padding = padding_start + padding_end; let pad_size = size - padding;