From efc1b87ab0d054de754ad3dc27c6d361a6f1c22b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 11 Nov 2022 10:37:09 +0100 Subject: [PATCH] :sparkles: Performance improvements --- common/src/app/common/geom/matrix.cljc | 7 + common/src/app/common/geom/shapes.cljc | 1 - common/src/app/common/geom/shapes/common.cljc | 24 ++- .../src/app/common/geom/shapes/modifiers.cljc | 54 +++--- .../common/geom/shapes/pixel_precision.cljc | 36 +++- common/src/app/common/geom/shapes/rect.cljc | 7 + .../app/common/geom/shapes/transforms.cljc | 159 +++++++++++------- common/src/app/common/math.cljc | 2 +- common/src/app/common/types/modifiers.cljc | 126 ++++++++++---- .../shapes/frame/dynamic_modifiers.cljs | 5 +- 10 files changed, 292 insertions(+), 129 deletions(-) diff --git a/common/src/app/common/geom/matrix.cljc b/common/src/app/common/geom/matrix.cljc index 53f3d17489..b8145090f6 100644 --- a/common/src/app/common/geom/matrix.cljc +++ b/common/src/app/common/geom/matrix.cljc @@ -263,3 +263,10 @@ matrix (translate-matrix (gpt/negate center)))) point)) + +(defn move? + [{:keys [a b c d _ _]}] + (and (mth/almost-zero? (- a 1)) + (mth/almost-zero? b) + (mth/almost-zero? c) + (mth/almost-zero? (- d 1)))) diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 163a29b1cd..8d56036a0b 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -175,7 +175,6 @@ (dm/export gtr/transform-selrect-matrix) (dm/export gtr/transform-bounds) (dm/export gtr/move-position-data) -(dm/export gtr/apply-transform) (dm/export gtr/apply-objects-modifiers) (dm/export gtr/parent-coords-rect) (dm/export gtr/parent-coords-points) diff --git a/common/src/app/common/geom/shapes/common.cljc b/common/src/app/common/geom/shapes/common.cljc index 8cb899f045..710c15e782 100644 --- a/common/src/app/common/geom/shapes/common.cljc +++ b/common/src/app/common/geom/shapes/common.cljc @@ -8,7 +8,8 @@ (:require [app.common.data :as d] [app.common.geom.matrix :as gmt] - [app.common.geom.point :as gpt])) + [app.common.geom.point :as gpt] + [app.common.geom.shapes.rect :as gpr])) (defn center-rect [{:keys [x y width height]}] @@ -31,6 +32,22 @@ (gpt/point (/ (+ minx maxx) 2.0) (/ (+ miny maxy) 2.0)))) +(defn center-bounds [[a b c d]] + (let [xa (:x a) + ya (:y a) + xb (:x b) + yb (:y b) + xc (:x c) + yc (:y c) + xd (:x d) + yd (:y d) + minx (min xa xb xc xd) + miny (min ya yb yc yd) + maxx (max xa xb xc xd) + maxy (max ya yb yc yd)] + (gpt/point (/ (+ minx maxx) 2.0) + (/ (+ miny maxy) 2.0)))) + (defn center-shape "Calculate the center of the shape." [shape] @@ -49,3 +66,8 @@ (gpt/transform point (gmt/multiply prev matrix post)))] (mapv tr-point points)) points))) + +(defn transform-selrect + [{:keys [x1 y1 x2 y2] :as sr} matrix] + (let [[c1 c2] (transform-points [(gpt/point x1 y1) (gpt/point x2 y2)] matrix)] + (gpr/corners->selrect c1 c2))) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 5386c05c51..4a187191b8 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -91,20 +91,30 @@ (defn- set-children-modifiers "Propagates the modifiers from a parent too its children applying constraints if necesary" - [modif-tree objects parent transformed-parent ignore-constraints snap-pixel?] - (let [children (map (d/getf objects) (:shapes parent)) - modifiers (dm/get-in modif-tree [(:id parent) :modifiers]) - parent (gtr/transform-shape parent (ctm/select-parent-modifiers modifiers)) + [modif-tree objects parent transformed-parent ignore-constraints] + (let [children (:shapes parent) + modifiers (dm/get-in modif-tree [(:id parent) :modifiers])] - set-child - (fn [modif-tree child] - (let [child-modifiers (gct/calc-child-modifiers parent child modifiers ignore-constraints transformed-parent) - child-modifiers (cond-> child-modifiers snap-pixel? (gpp/set-pixel-precision child))] - (cond-> modif-tree - (not (ctm/empty? child-modifiers)) - (update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers))))] + (if (ctm/only-move? modifiers) + ;; Move modifiers don't need to calculate constraints + (loop [modif-tree modif-tree + children (seq children)] + (if-let [current (first children)] + (recur (update-in modif-tree [current :modifiers] ctm/add-modifiers modifiers) + (rest children)) + modif-tree)) - (reduce set-child modif-tree children))) + ;; Check the constraints, then resize + (let [parent (gtr/transform-shape parent (ctm/select-parent-modifiers modifiers))] + (loop [modif-tree modif-tree + children (seq children)] + (if-let [current (first children)] + (let [child-modifiers (gct/calc-child-modifiers parent (get objects current) modifiers ignore-constraints transformed-parent)] + (recur (cond-> modif-tree + (not (ctm/empty? child-modifiers)) + (update-in [current :modifiers] ctm/add-modifiers child-modifiers)) + (rest children))) + modif-tree)))))) (defn- process-layout-children [modif-tree objects parent transformed-parent] @@ -210,17 +220,11 @@ (assoc-in modif-tree [(:id parent) :modifiers] modifiers)))) (defn- propagate-modifiers - [objects snap-pixel? ignore-constraints [modif-tree recalculate] parent] + "Propagate modifiers to its children" + [objects ignore-constraints [modif-tree recalculate] parent] (let [parent-id (:id parent) root? (= uuid/zero parent-id) modifiers (dm/get-in modif-tree [parent-id :modifiers]) - - modifiers (cond-> modifiers - (and (not root?) (ctm/has-geometry? modifiers) snap-pixel?) - (gpp/set-pixel-precision parent)) - - modif-tree (-> modif-tree (assoc-in [parent-id :modifiers] modifiers)) - transformed-parent (gtr/transform-shape parent modifiers) has-modifiers? (ctm/child-modifiers? modifiers) @@ -233,7 +237,7 @@ [(cond-> modif-tree (and (not is-layout?) has-modifiers? is-parent? (not root?)) - (set-children-modifiers objects parent transformed-parent (or ignore-constraints is-inside-layout?) snap-pixel?) + (set-children-modifiers objects parent transformed-parent (or ignore-constraints is-inside-layout?)) is-layout? (-> (process-layout-children objects parent transformed-parent) @@ -266,7 +270,7 @@ (let [shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects) [modif-tree recalculate] - (reduce (partial propagate-modifiers objects snap-pixel? ignore-constraints) [modif-tree #{}] shapes-tree) + (reduce (partial propagate-modifiers objects ignore-constraints) [modif-tree #{}] shapes-tree) shapes-tree (resolve-tree-sequence recalculate objects) @@ -281,7 +285,11 @@ modif-tree (->> shapes-tree (filter ctl/layout?) - (reduce (partial calculate-reflow-layout objects) modif-tree ))] + (reduce (partial calculate-reflow-layout objects) modif-tree)) + + modif-tree + (cond-> modif-tree + snap-pixel? (gpp/adjust-pixel-precision objects))] ;;#?(:cljs ;; (.log js/console ">result" (modif->js modif-tree objects))) diff --git a/common/src/app/common/geom/shapes/pixel_precision.cljc b/common/src/app/common/geom/shapes/pixel_precision.cljc index 297abf99a6..93dd26dcd8 100644 --- a/common/src/app/common/geom/shapes/pixel_precision.cljc +++ b/common/src/app/common/geom/shapes/pixel_precision.cljc @@ -6,6 +6,8 @@ (ns app.common.geom.shapes.pixel-precision (:require + [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.geom.shapes.points :as gpo] [app.common.geom.shapes.rect :as gpr] @@ -15,9 +17,8 @@ [app.common.types.modifiers :as ctm])) (defn size-pixel-precision - [modifiers shape] - (let [{:keys [points transform transform-inverse] :as shape} (gtr/transform-shape shape modifiers) - origin (gpo/origin points) + [modifiers {:keys [points transform transform-inverse] :as shape}] + (let [origin (gpo/origin points) curr-width (gpo/width-points points) curr-height (gpo/height-points points) @@ -35,9 +36,8 @@ (ctm/resize scalev origin transform transform-inverse)))) (defn position-pixel-precision - [modifiers shape] - (let [{:keys [points]} (gtr/transform-shape shape modifiers) - bounds (gpr/points->rect points) + [modifiers {:keys [points]}] + (let [bounds (gpr/points->rect points) corner (gpt/point bounds) target-corner (gpt/round corner) deltav (gpt/to-vec corner target-corner)] @@ -47,7 +47,25 @@ (defn set-pixel-precision "Adjust modifiers so they adjust to the pixel grid" [modifiers shape] - - (-> modifiers + (let [move? (ctm/only-move? modifiers)] + (cond-> modifiers + (not move?) (size-pixel-precision shape) - (position-pixel-precision shape))) + + :always + (position-pixel-precision shape)))) + +(defn adjust-pixel-precision + [modif-tree objects] + (let [update-modifiers + (fn [modif-tree shape] + (let [modifiers (dm/get-in modif-tree [(:id shape) :modifiers])] + (if-not (ctm/has-geometry? modifiers) + modif-tree + (let [shape (gtr/transform-shape shape modifiers)] + (-> modif-tree + (update-in [(:id shape) :modifiers] set-pixel-precision shape))))))] + + (->> (keys modif-tree) + (map (d/getf objects)) + (reduce update-modifiers modif-tree)))) diff --git a/common/src/app/common/geom/shapes/rect.cljc b/common/src/app/common/geom/shapes/rect.cljc index ad0ff5659d..be8d5a5b5e 100644 --- a/common/src/app/common/geom/shapes/rect.cljc +++ b/common/src/app/common/geom/shapes/rect.cljc @@ -192,3 +192,10 @@ (>= (:y1 sr2) (:y1 sr1)) (<= (:y2 sr2) (:y2 sr1)))) +(defn corners->selrect + [p1 p2] + (let [xp1 (:x p1) + xp2 (:x p2) + yp1 (:y p1) + yp2 (:y p2)] + (make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2))))) diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 9928ffee15..04ac8fc276 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -188,55 +188,52 @@ "Calculates a matrix that is a series of transformations we have to do to the transformed rectangle so that after applying them the end result is the `shape-path-temp`. This is compose of three transformations: skew, resize and rotation" - ([points-temp points-rec] - (calculate-adjust-matrix points-temp points-rec false false)) + [points-temp points-rec flip-x flip-y] + (let [center (gco/center-bounds points-temp) - ([points-temp points-rec flip-x flip-y] - (let [center (gco/center-points points-temp) + stretch-matrix (gmt/matrix) - stretch-matrix (gmt/matrix) + skew-angle (calculate-skew-angle points-temp) - skew-angle (calculate-skew-angle points-temp) + ;; When one of the axis is flipped we have to reverse the skew + ;; skew-angle (if (neg? (* (:x resize-vector) (:y resize-vector))) (- skew-angle) skew-angle ) + skew-angle (if (and (or flip-x flip-y) + (not (and flip-x flip-y))) (- skew-angle) skew-angle ) + skew-angle (if (mth/nan? skew-angle) 0 skew-angle) - ;; When one of the axis is flipped we have to reverse the skew - ;; skew-angle (if (neg? (* (:x resize-vector) (:y resize-vector))) (- skew-angle) skew-angle ) - skew-angle (if (and (or flip-x flip-y) - (not (and flip-x flip-y))) (- skew-angle) skew-angle ) - skew-angle (if (mth/nan? skew-angle) 0 skew-angle) + stretch-matrix (gmt/multiply stretch-matrix (gmt/skew-matrix skew-angle 0)) - stretch-matrix (gmt/multiply stretch-matrix (gmt/skew-matrix skew-angle 0)) + h1 (max 1 (calculate-height points-temp)) + h2 (max 1 (calculate-height (gco/transform-points points-rec center stretch-matrix))) + h3 (if-not (mth/almost-zero? h2) (/ h1 h2) 1) + h3 (if (mth/nan? h3) 1 h3) - h1 (max 1 (calculate-height points-temp)) - h2 (max 1 (calculate-height (gco/transform-points points-rec center stretch-matrix))) - h3 (if-not (mth/almost-zero? h2) (/ h1 h2) 1) - h3 (if (mth/nan? h3) 1 h3) + w1 (max 1 (calculate-width points-temp)) + w2 (max 1 (calculate-width (gco/transform-points points-rec center stretch-matrix))) + w3 (if-not (mth/almost-zero? w2) (/ w1 w2) 1) + w3 (if (mth/nan? w3) 1 w3) - w1 (max 1 (calculate-width points-temp)) - w2 (max 1 (calculate-width (gco/transform-points points-rec center stretch-matrix))) - w3 (if-not (mth/almost-zero? w2) (/ w1 w2) 1) - w3 (if (mth/nan? w3) 1 w3) + stretch-matrix (gmt/multiply stretch-matrix (gmt/scale-matrix (gpt/point w3 h3))) - stretch-matrix (gmt/multiply stretch-matrix (gmt/scale-matrix (gpt/point w3 h3))) + rotation-angle (calculate-rotation + center + (gco/transform-points points-rec (gco/center-points points-rec) stretch-matrix) + points-temp + flip-x + flip-y) - rotation-angle (calculate-rotation - center - (gco/transform-points points-rec (gco/center-points points-rec) stretch-matrix) - points-temp - flip-x - flip-y) + stretch-matrix (gmt/multiply (gmt/rotate-matrix rotation-angle) stretch-matrix) - stretch-matrix (gmt/multiply (gmt/rotate-matrix rotation-angle) stretch-matrix) - - ;; This is the inverse to be able to remove the transformation - stretch-matrix-inverse - (gmt/multiply (gmt/scale-matrix (gpt/point (/ 1 w3) (/ 1 h3))) - (gmt/skew-matrix (- skew-angle) 0) - (gmt/rotate-matrix (- rotation-angle)))] - [stretch-matrix stretch-matrix-inverse rotation-angle]))) + ;; This is the inverse to be able to remove the transformation + stretch-matrix-inverse + (gmt/multiply (gmt/scale-matrix (gpt/point (/ 1 w3) (/ 1 h3))) + (gmt/skew-matrix (- skew-angle) 0) + (gmt/rotate-matrix (- rotation-angle)))] + [stretch-matrix stretch-matrix-inverse rotation-angle])) (defn- adjust-rotated-transform [{:keys [transform transform-inverse flip-x flip-y]} points] - (let [center (gco/center-points points) + (let [center (gco/center-bounds points) points-temp (cond-> points (some? transform-inverse) @@ -280,7 +277,28 @@ (-> (update :flip-y not) (update :rotation -))))) -(defn apply-transform +(defn- apply-transform-move + "Given a new set of points transformed, set up the rectangle so it keeps + its properties. We adjust de x,y,width,height and create a custom transform" + [shape transform-mtx] + (let [bool? (= (:type shape) :bool) + path? (= (:type shape) :path) + points (gco/transform-points (:points shape) transform-mtx) + selrect (gco/transform-selrect (:selrect shape) transform-mtx)] + (-> shape + (cond-> bool? + (update :bool-content gpa/transform-content transform-mtx)) + (cond-> path? + (update :content gpa/transform-content transform-mtx)) + (cond-> (not path?) + (assoc :x (:x selrect) + :y (:y selrect) + :width (:width selrect) + :height (:height selrect))) + (assoc :selrect selrect) + (assoc :points points)))) + +(defn- apply-transform-generic "Given a new set of points transformed, set up the rectangle so it keeps its properties. We adjust de x,y,width,height and create a custom transform" [shape transform-mtx] @@ -303,7 +321,10 @@ (cond-> path? (update :content gpa/transform-content transform-mtx)) (cond-> (not path?) - (-> (merge (select-keys selrect [:x :y :width :height])))) + (assoc :x (:x selrect) + :y (:y selrect) + :width (:width selrect) + :height (:height selrect))) (cond-> transform (-> (assoc :transform transform) (assoc :transform-inverse transform-inverse))) @@ -316,6 +337,14 @@ (assoc :points points)) (assoc :rotation rotation)))) +(defn- apply-transform + "Given a new set of points transformed, set up the rectangle so it keeps + its properties. We adjust de x,y,width,height and create a custom transform" + [shape transform-mtx] + (if (gmt/move? transform-mtx) + (apply-transform-move shape transform-mtx) + (apply-transform-generic shape transform-mtx))) + (defn- update-group-viewbox "Updates the viewbox for groups imported from SVG's" [{:keys [selrect svg-viewbox] :as group} new-selrect] @@ -376,24 +405,6 @@ (assoc :flip-x (-> mask :flip-x)) (assoc :flip-y (-> mask :flip-y))))) -(defn apply-modifiers - [shape modifiers] - (let [transform (ctm/modifiers->transform modifiers)] - (cond-> shape - (and (some? transform) - ;; Never transform the root frame - (not= uuid/zero (:id shape))) - (apply-transform transform) - - :always - (ctm/apply-structure-modifiers modifiers)))) - -(defn apply-objects-modifiers - [objects modifiers] - (letfn [(process-shape [objects [id modifier]] - (d/update-when objects id apply-modifiers (:modifiers modifier)))] - (reduce process-shape objects modifiers))) - (defn transform-shape ([shape] (let [modifiers (:modifiers shape)] @@ -402,9 +413,35 @@ (transform-shape modifiers)))) ([shape modifiers] - (cond-> shape - (and (some? modifiers) (not (ctm/empty? modifiers))) - (apply-modifiers modifiers)))) + (letfn [(apply-modifiers + [shape modifiers] + (if (ctm/empty? modifiers) + shape + (let [transform (ctm/modifiers->transform modifiers)] + (cond-> shape + (and (some? transform) (not= uuid/zero (:id shape))) ;; Never transform the root frame + (apply-transform transform) + + (ctm/has-structure? modifiers) + (ctm/apply-structure-modifiers modifiers)))))] + + (cond-> shape + (and (some? modifiers) (not (ctm/empty? modifiers))) + (apply-modifiers modifiers))))) + +(defn apply-objects-modifiers + [objects modifiers] + + (loop [objects objects + entry (first modifiers) + modifiers (rest modifiers)] + + (if (nil? entry) + objects + (let [[id modifier] entry] + (recur (d/update-when objects id transform-shape (:modifiers modifier)) + (first modifiers) + (rest modifiers)))))) (defn transform-bounds ([points modifiers] @@ -412,7 +449,9 @@ ([points center modifiers] (let [transform (ctm/modifiers->transform modifiers)] - (gco/transform-points points center transform)))) + (cond-> points + (some? transform) + (gco/transform-points center transform))))) (defn transform-selrect [selrect modifiers] diff --git a/common/src/app/common/math.cljc b/common/src/app/common/math.cljc index fc35d15f34..e5483b1189 100644 --- a/common/src/app/common/math.cljc +++ b/common/src/app/common/math.cljc @@ -156,7 +156,7 @@ (if (> num to) to num))) (defn almost-zero? [num] - (< (abs (double num)) 1e-5)) + (< (abs (double num)) 1e-4)) (defonce float-equal-precision 0.001) diff --git a/common/src/app/common/types/modifiers.cljc b/common/src/app/common/types/modifiers.cljc index 2b994d6f69..61af67564e 100644 --- a/common/src/app/common/types/modifiers.cljc +++ b/common/src/app/common/types/modifiers.cljc @@ -38,21 +38,79 @@ ;; * rotation ;; * change-properties +;; Private aux functions + (def conjv (fnil conj [])) +(defn- move-vec? [vector] + (or (not (mth/almost-zero? (:x vector))) + (not (mth/almost-zero? (:y vector))))) + +(defn- resize-vec? [vector] + (or (not (mth/almost-zero? (- (:x vector) 1))) + (not (mth/almost-zero? (- (:y vector) 1))))) + + +(defn- mergeable-move? + [op1 op2] + (and (= :move (:type op1)) + (= :move (:type op2)))) + +(defn- mergeable-resize? + [op1 op2] + (and (= :resize (:type op1)) + (= :resize (:type op2)) + + ;; Same transforms + (gmt/close? (or (:transform op1) (gmt/matrix)) (or (:transform op2) (gmt/matrix))) + (gmt/close? (or (:transform-inverse op1) (gmt/matrix)) (or (:transform-inverse op2) (gmt/matrix))) + + ;; Same origin + (gpt/close? (:origin op1) (:origin op2)))) + +(defn- merge-move + [op1 op2] + {:type :move + :vector (gpt/add (:vector op1) (:vector op2))}) + +(defn- merge-resize + [op1 op2] + (let [vector (gpt/point (* (-> op1 :vector :x) (-> op2 :vector :x)) + (* (-> op1 :vector :y) (-> op2 :vector :y)))] + (assoc op1 :vector vector))) + +(defn- maybe-add-move + "Check the last operation to check if we can stack it over the last one" + [operations op] + (if (c/empty? operations) + [op] + (let [head (peek operations)] + (if (mergeable-move? head op) + (let [item (merge-move head op)] + (cond-> (pop operations) + (move-vec? (:vector item)) + (conj item))) + (conj operations op))))) + +(defn- maybe-add-resize + "Check the last operation to check if we can stack it over the last one" + [operations op] + + (if (c/empty? operations) + [op] + (let [head (peek operations)] + (if (mergeable-resize? head op) + (let [item (merge-resize head op)] + (cond-> (pop operations) + (resize-vec? (:vector item)) + (conj item))) + (conj operations op))))) + ;; Public builder API (defn empty [] {}) -(defn move-vec? [vector] - (or (not (mth/almost-zero? (:x vector))) - (not (mth/almost-zero? (:y vector))))) - -(defn resize-vec? [vector] - (or (not (mth/almost-zero? (- (:x vector) 1))) - (not (mth/almost-zero? (- (:y vector) 1))))) - (defn move-parent ([modifiers x y] (move-parent modifiers (gpt/point x y))) @@ -66,18 +124,18 @@ ([modifiers vector origin] (cond-> modifiers (resize-vec? vector) - (update :geometry-parent conjv {:type :resize - :vector vector - :origin origin}))) + (update :geometry-parent maybe-add-resize {:type :resize + :vector vector + :origin origin}))) ([modifiers vector origin transform transform-inverse] (cond-> modifiers (resize-vec? vector) - (update :geometry-parent conjv {:type :resize - :vector vector - :origin origin - :transform transform - :transform-inverse transform-inverse})))) + (update :geometry-parent maybe-add-resize {:type :resize + :vector vector + :origin origin + :transform transform + :transform-inverse transform-inverse})))) (defn move ([modifiers x y] (move modifiers (gpt/point x y))) @@ -85,24 +143,24 @@ ([modifiers vector] (cond-> modifiers (move-vec? vector) - (update :geometry-child conjv {:type :move :vector vector})))) + (update :geometry-child maybe-add-move {:type :move :vector vector})))) (defn resize ([modifiers vector origin] (cond-> modifiers (resize-vec? vector) - (update :geometry-child conjv {:type :resize - :vector vector - :origin origin}))) + (update :geometry-child maybe-add-resize {:type :resize + :vector vector + :origin origin}))) ([modifiers vector origin transform transform-inverse] (cond-> modifiers (resize-vec? vector) - (update :geometry-child conjv {:type :resize - :vector vector - :origin origin - :transform transform - :transform-inverse transform-inverse})))) + (update :geometry-child maybe-add-resize {:type :resize + :vector vector + :origin origin + :transform transform + :transform-inverse transform-inverse})))) (defn rotation [modifiers center angle] @@ -296,17 +354,20 @@ (defn only-move? "Returns true if there are only move operations" - [modifier] - (or (and (= 1 (-> modifier :geometry-child count)) - (= :move (-> modifier :geometry-child first :type))) - (and (= 1 (-> modifier :geometry-parent count)) - (= :move (-> modifier :geometry-parent first :type))))) + [{:keys [geometry-child geometry-parent]}] + (and (every? #(= :move (:type %)) geometry-child) + (every? #(= :move (:type %)) geometry-parent))) (defn has-geometry? [{:keys [geometry-parent geometry-child]}] (or (d/not-empty? geometry-parent) (d/not-empty? geometry-child))) +(defn has-structure? + [{:keys [structure-parent structure-child]}] + (or (d/not-empty? structure-parent) + (d/not-empty? structure-child))) + ;; Extract subsets of modifiers (defn select-child-modifiers @@ -374,8 +435,9 @@ (let [modifiers (if (d/not-empty? (:geometry-parent modifiers)) (d/concat-vec (:geometry-parent modifiers) (:geometry-child modifiers)) (:geometry-child modifiers))] - (->> modifiers - (reduce apply-modifier (gmt/matrix)))))) + (when (d/not-empty? modifiers) + (->> modifiers + (reduce apply-modifier (gmt/matrix))))))) (defn apply-structure-modifiers "Apply structure changes to a shape" diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index 5b2a0a8008..c4df18c29f 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -200,8 +200,8 @@ (when (some? modifiers) (d/mapm (fn [id {modifiers :modifiers}] (let [shape (get objects id) - text? (= :text (:type shape)) - modifiers (cond-> modifiers text? (adapt-text-modifiers shape))] + adapt-text? (and (= :text (:type shape)) (not (ctm/only-move? modifiers))) + modifiers (cond-> modifiers adapt-text? (adapt-text-modifiers shape))] (ctm/modifiers->transform modifiers))) modifiers)))) @@ -214,6 +214,7 @@ (mf/deps transforms) (fn [] (->> (keys transforms) + (filter #(some? (get transforms %))) (mapv (d/getf objects))))) prev-shapes (mf/use-var nil)