🐛 Fix some problems with modifiers

This commit is contained in:
alonso.torres 2025-05-26 15:41:12 +02:00
parent 1a705cee24
commit a85a42d367
10 changed files with 138 additions and 99 deletions

View file

@ -346,29 +346,32 @@
center (gco/points->center points) center (gco/points->center points)
selrect (calculate-selrect points center) 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)) [transform inverse]
shape (let [transform (calculate-transform points center selrect)
(let [type (dm/get-prop shape :type) inverse (when (some? transform) (gmt/inverse transform))]
rotation (mod (+ (d/nilv (:rotation shape) 0) (if (and (some? transform) (some? inverse))
(d/nilv (dm/get-in shape [:modifiers :rotation]) 0)) [transform inverse]
360) [(:transform shape (gmt/matrix)) (:transform-inverse shape (gmt/matrix))]))
shape (if (or (= type :path) (= type :bool)) type (dm/get-prop shape :type)
(update shape :content path/transform-content transform-mtx) rotation (mod (+ (d/nilv (:rotation shape) 0)
(assoc shape (d/nilv (dm/get-in shape [:modifiers :rotation]) 0))
:x (dm/get-prop selrect :x) 360)
:y (dm/get-prop selrect :y)
:width (dm/get-prop selrect :width) shape (if (or (= type :path) (= type :bool))
:height (dm/get-prop selrect :height)))] (update shape :content path/transform-content transform-mtx)
(-> shape (assoc shape
(assoc :transform transform) :x (dm/get-prop selrect :x)
(assoc :transform-inverse inverse) :y (dm/get-prop selrect :y)
(assoc :selrect selrect) :width (dm/get-prop selrect :width)
(assoc :points points) :height (dm/get-prop selrect :height)))]
(assoc :rotation rotation)))))) (-> shape
(assoc :transform transform)
(assoc :transform-inverse inverse)
(assoc :selrect selrect)
(assoc :points points)
(assoc :rotation rotation))))
(defn apply-transform (defn apply-transform
"Given a new set of points transformed, set up the rectangle so it keeps "Given a new set of points transformed, set up the rectangle so it keeps

View file

@ -114,11 +114,8 @@
(let [modifiers (ctm/resize-modifiers (gpt/point 0 0) (gpt/point 0 0)) (let [modifiers (ctm/resize-modifiers (gpt/point 0 0) (gpt/point 0 0))
shape-before (create-test-shape :rect {:modifiers modifiers}) shape-before (create-test-shape :rect {:modifiers modifiers})
shape-after (gsh/transform-shape shape-before)] shape-after (gsh/transform-shape shape-before)]
(t/is (close? 0.01 (get-in shape-after [:selrect :width])))
(t/is (close? (get-in shape-before [:selrect :width]) (t/is (close? 0.01 (get-in shape-after [:selrect :height])))))
(get-in shape-after [:selrect :width])))
(t/is (close? (get-in shape-before [:selrect :height])
(get-in shape-after [:selrect :height])))))
(t/testing "Transform shape with rotation modifiers" (t/testing "Transform shape with rotation modifiers"
(t/are [type] (t/are [type]

View file

@ -486,11 +486,16 @@
[] []
(keep (keep
(fn [[id data]] (fn [[id data]]
(if (ctm/has-geometry? (:modifiers data)) (cond
(= id uuid/zero)
nil
(ctm/has-geometry? (:modifiers data))
{:id id {:id id
:transform (ctm/modifiers->transform (:modifiers data))} :transform (ctm/modifiers->transform (:modifiers data))}
;; Unit matrix is used for reflowing ;; Unit matrix is used for reflowing
:else
{:id id {:id id
:transform (gmt/matrix)}))) :transform (gmt/matrix)})))
modif-tree)) modif-tree))
@ -511,6 +516,13 @@
(update [_ state] (update [_ state]
(assoc state :workspace-selrect selrect)))) (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 #_:clj-kondo/ignore
(defn set-wasm-modifiers (defn set-wasm-modifiers
[modif-tree & {:keys [ignore-constraints ignore-snap-pixel] [modif-tree & {:keys [ignore-constraints ignore-snap-pixel]
@ -521,7 +533,6 @@
(update [_ state] (update [_ state]
(let [property-changes (let [property-changes
(extract-property-changes modif-tree)] (extract-property-changes modif-tree)]
(-> state (-> state
(assoc :prev-wasm-props (:wasm-props state)) (assoc :prev-wasm-props (:wasm-props state))
(assoc :wasm-props property-changes)))) (assoc :wasm-props property-changes))))
@ -537,8 +548,11 @@
(let [structure-entries (parse-structure-modifiers modif-tree)] (let [structure-entries (parse-structure-modifiers modif-tree)]
(wasm.api/set-structure-modifiers structure-entries) (wasm.api/set-structure-modifiers structure-entries)
(let [geometry-entries (parse-geometry-modifiers modif-tree) (let [geometry-entries (parse-geometry-modifiers modif-tree)
selrect (wasm.api/propagate-apply geometry-entries pixel-precision)] modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)]
(rx/of (set-temporary-selrect selrect)))))))) (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 #_:clj-kondo/ignore
(defn apply-wasm-modifiers (defn apply-wasm-modifiers

View file

@ -136,7 +136,7 @@
(update [_ state] (update [_ state]
(-> state (-> state
(update :workspace-local dissoc :transform :duplicate-move-started?) (update :workspace-local dissoc :transform :duplicate-move-started?)
(dissoc :workspace-selrect))))) (dissoc :workspace-selrect :workspace-wasm-modifiers)))))
;; -- Resize -------------------------------------------------------- ;; -- Resize --------------------------------------------------------

View file

@ -15,7 +15,6 @@
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.transforms :as dwt]
[app.main.features :as features] [app.main.features :as features]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -54,27 +53,23 @@
[app.util.debug :as dbg] [app.util.debug :as dbg]
[app.util.text-editor :as ted] [app.util.text-editor :as ted]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[okulary.core :as l]
[promesa.core :as p] [promesa.core :as p]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
;; --- Viewport ;; --- Viewport
(def workspace-wasm-modifiers
(l/derived :workspace-wasm-modifiers st/state))
(defn apply-modifiers-to-selected (defn apply-modifiers-to-selected
[selected objects text-modifiers modifiers] [selected objects modifiers]
(reduce (->> modifiers
(fn [objects id] (filter #(contains? selected (:id %)))
(update (reduce
objects id (fn [objects {:keys [id transform]}]
(fn [shape] (update objects id gsh/apply-transform transform))
(cond-> shape objects)))
(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))
(mf/defc viewport* (mf/defc viewport*
[{:keys [selected wglobal wlocal layout file page palete-size]}] [{:keys [selected wglobal wlocal layout file page palete-size]}]
@ -113,12 +108,13 @@
base-objects (ui-hooks/with-focus-objects objects focus) base-objects (ui-hooks/with-focus-objects objects focus)
modifiers (mf/deref refs/workspace-modifiers) wasm-modifiers (mf/deref workspace-wasm-modifiers)
text-modifiers (mf/deref refs/workspace-text-modifier)
objects-modified (mf/with-memo [base-objects text-modifiers modifiers] objects-modified
(binding [cts/*wasm-sync* false] (mf/with-memo
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers))) [base-objects wasm-modifiers]
(binding [cts/*wasm-sync* false]
(apply-modifiers-to-selected selected base-objects wasm-modifiers)))
selected-shapes (->> selected selected-shapes (->> selected
(into [] (keep (d/getf objects-modified))) (into [] (keep (d/getf objects-modified)))
@ -409,11 +405,9 @@
(when show-text-editor? (when show-text-editor?
(if (features/active-feature? @st/state "text-editor/v2") (if (features/active-feature? @st/state "text-editor/v2")
[:& editor-v2/text-editor {:shape editing-shape [:& editor-v2/text-editor {:shape editing-shape
:ref text-editor-ref :ref text-editor-ref}]
:modifiers modifiers}]
[:& editor-v1/text-editor-svg {:shape editing-shape [:& editor-v1/text-editor-svg {:shape editing-shape
:ref text-editor-ref :ref text-editor-ref}]))
:modifiers modifiers}]))
(when show-frame-outline? (when show-frame-outline?
(let [outlined-frame-id (let [outlined-frame-id
@ -426,8 +420,7 @@
[:& outline/shape-outlines [:& outline/shape-outlines
{:objects base-objects {:objects base-objects
:hover #{outlined-frame-id} :hover #{outlined-frame-id}
:zoom zoom :zoom zoom}]
:modifiers modifiers}]
(when (ctl/any-layout? outlined-frame) (when (ctl/any-layout? outlined-frame)
[:g.ghost-outline [:g.ghost-outline
@ -443,8 +436,7 @@
:hover #{(:id @hover) @frame-hover} :hover #{(:id @hover) @frame-hover}
:highlighted highlighted :highlighted highlighted
:edition edition :edition edition
:zoom zoom :zoom zoom}])
:modifiers modifiers}])
(when (and show-selection-handlers? (when (and show-selection-handlers?
selected-shapes) selected-shapes)
@ -459,8 +451,7 @@
(when show-text-editor? (when show-text-editor?
[:& text-edition-outline [:& text-edition-outline
{:shape (get base-objects edition) {:shape (get base-objects edition)
:zoom zoom :zoom zoom}])
:modifiers modifiers}])
(when show-measures? (when show-measures?
[:& msr/measurement [:& msr/measurement
@ -586,8 +577,7 @@
:vbox vbox :vbox vbox
:guides (:guides page) :guides (:guides page)
:hover-frame guide-frame :hover-frame guide-frame
:disabled-guides disabled-guides? :disabled-guides disabled-guides?}])
:modifiers modifiers}])
;; DEBUG LAYOUT DROP-ZONES ;; DEBUG LAYOUT DROP-ZONES
(when (dbg/enabled? :layout-drop-zones) (when (dbg/enabled? :layout-drop-zones)
@ -651,8 +641,7 @@
(when (or show-grid-editor? hover-grid?) (when (or show-grid-editor? hover-grid?)
[:& grid-layout/editor [:& grid-layout/editor
{:zoom zoom {:zoom zoom
:objects base-objects :objects objects-modified
:modifiers modifiers
:shape (or (get base-objects edition) :shape (or (get base-objects edition)
(get base-objects @hover-top-frame-id)) (get base-objects @hover-top-frame-id))
:view-only (not show-grid-editor?)}]) :view-only (not show-grid-editor?)}])
@ -665,8 +654,7 @@
[:& grid-layout/editor [:& grid-layout/editor
{:zoom zoom {:zoom zoom
:key (dm/str (:id frame)) :key (dm/str (:id frame))
:objects base-objects :objects objects-modified
:modifiers modifiers
:shape frame :shape frame
:view-only true}]))] :view-only true}]))]
[:g.scrollbar-wrapper {:clipPath "url(#clip-handlers)"} [:g.scrollbar-wrapper {:clipPath "url(#clip-handlers)"}

View file

@ -870,6 +870,38 @@
:center (gpt/point cx cy) :center (gpt/point cx cy)
:transform transform})))) :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 (defn set-canvas-background
[background] [background]
(let [rgba (sr-clr/hex->u32argb background 1)] (let [rgba (sr-clr/hex->u32argb background 1)]

View file

@ -362,36 +362,41 @@ pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> *mut u8 {
.collect(); .collect();
with_state!(state, { 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) mem::write_vec(result)
}) })
} }
#[no_mangle] #[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 bytes = mem::bytes();
let entries: Vec<_> = bytes let entries: Vec<Uuid> = bytes
.chunks(size_of::<<TransformEntry as SerializableResult>::BytesType>()) .chunks(16)
.map(|data| TransformEntry::from_bytes(data.try_into().unwrap())) .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(); .collect();
with_state!(state, { with_state!(state, {
let (result, bounds) = shapes::propagate_modifiers(state, &entries, pixel_precision); let bbs: Vec<_> = entries
.iter()
for entry in result { .flat_map(|id| {
state.modifiers.insert(entry.id, entry.transform); let default = Matrix::default();
} let modifier = state.modifiers.get(id).unwrap_or(&default);
state.rebuild_modifier_tiles(); state.shapes.get(id).map(|b| b.bounds().transform(modifier))
})
mem::free_bytes(); .collect();
let bbs: Vec<_> = entries.iter().flat_map(|e| bounds.get(&e.id)).collect();
let result_bound = if bbs.len() == 1 { let result_bound = if bbs.len() == 1 {
bbs[0] bbs[0]
} else { } else {
&Bounds::join_bounds(&bbs) Bounds::join_bounds(&bbs)
}; };
let width = result_bound.width(); 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[28..32].clone_from_slice(&transform[4].to_le_bytes());
bytes[32..36].clone_from_slice(&transform[2].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()); bytes[36..40].clone_from_slice(&transform[5].to_le_bytes());
mem::write_bytes(bytes) mem::write_bytes(bytes)
}) })
} }

View file

@ -64,7 +64,7 @@ impl Bounds {
Self { nw, ne, se, sw } 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) = let (min_x, min_y, max_x, max_y) =
bounds bounds
.iter() .iter()

View file

@ -132,7 +132,7 @@ pub fn propagate_modifiers(
state: &State, state: &State,
modifiers: &[TransformEntry], modifiers: &[TransformEntry],
pixel_precision: bool, pixel_precision: bool,
) -> (Vec<TransformEntry>, HashMap<Uuid, Bounds>) { ) -> Vec<TransformEntry> {
let shapes = &state.shapes; let shapes = &state.shapes;
let font_col = state.render_state.fonts.font_collection(); let font_col = state.render_state.fonts.font_collection();
@ -341,13 +341,10 @@ pub fn propagate_modifiers(
layout_reflows = Vec::new(); layout_reflows = Vec::new();
} }
( modifiers
modifiers .iter()
.iter() .map(|(key, val)| TransformEntry::new(*key, *val))
.map(|(key, val)| TransformEntry::new(*key, *val)) .collect()
.collect(),
bounds,
)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -64,7 +64,6 @@ fn calculate_tracks(
set_fr_value(is_column, shape, layout_data, &mut tracks, layout_size); set_fr_value(is_column, shape, layout_data, &mut tracks, layout_size);
stretch_tracks(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); assign_anchors(is_column, layout_data, layout_bounds, &mut tracks);
tracks tracks
} }
@ -386,9 +385,10 @@ fn stretch_tracks(
tracks: &mut [TrackData], tracks: &mut [TrackData],
layout_size: f32, layout_size: f32,
) { ) {
if (column if (tracks.is_empty()
&& (layout_data.justify_content != JustifyContent::Stretch || column
|| shape.is_layout_horizontal_auto())) && (layout_data.justify_content != JustifyContent::Stretch
|| shape.is_layout_horizontal_auto()))
|| (!column || (!column
&& (layout_data.align_content != AlignContent::Stretch && (layout_data.align_content != AlignContent::Stretch
|| shape.is_layout_vertical_auto())) || 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 tot_size = tot_track_length + tot_gap;
let padding = padding_start + padding_end; let padding = padding_start + padding_end;
let pad_size = size - padding; let pad_size = size - padding;