diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index b589327fc9..47f49be841 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -252,6 +252,11 @@ #?(:clj (Object.) :cljs (js/Object.))) +(defn getf + "Returns a function to access a map" + [coll] + (partial get coll)) + (defn update-in-when [m key-seq f & args] (let [found (get-in m key-seq sentinel)] diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 3b2110cfcb..d84f7aec43 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -220,6 +220,7 @@ "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 round-coords?] + ;; FIXME: Improve performance (let [points (-> shape :points (gco/transform-points transform)) center (gco/center-points points) @@ -491,17 +492,20 @@ ([shape {:keys [round-coords?] :or {round-coords? true}}] - (let [shape (apply-displacement shape) - center (gco/center-shape shape) - modifiers (:modifiers shape)] - (if (and modifiers center) - (let [transform (modifiers->transform center modifiers)] - (-> shape - (set-flip modifiers) - (apply-transform transform round-coords?) - (apply-text-resize modifiers) - (dissoc :modifiers))) - shape)))) + + (if (and (contains? shape :modifiers) (empty? (:modifiers shape))) + (dissoc shape :modifiers) + (let [shape (apply-displacement shape) + center (gco/center-shape shape) + modifiers (:modifiers shape)] + (if (and modifiers center) + (let [transform (modifiers->transform center modifiers)] + (-> shape + (set-flip modifiers) + (apply-transform transform round-coords?) + (apply-text-resize modifiers) + (dissoc :modifiers))) + shape))))) (defn calc-transformed-parent-rect [parent parent-modifiers] diff --git a/common/src/app/common/pages.cljc b/common/src/app/common/pages.cljc index 00725454ae..3df51409d3 100644 --- a/common/src/app/common/pages.cljc +++ b/common/src/app/common/pages.cljc @@ -69,6 +69,7 @@ (d/export helpers/compact-path) (d/export helpers/compact-name) (d/export helpers/unframed-shape?) +(d/export helpers/children-seq) ;; Indices (d/export indices/calculate-z-index) diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 6fd996c492..e27c8ac5bb 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -99,7 +99,7 @@ (get-in component [:objects (:id component)])) ;; Implemented with transient for performance -(defn get-children +(defn get-children* "Retrieve all children ids recursively for a given object. The children's order will be breadth first." [id objects] @@ -128,6 +128,8 @@ (recur result (pop! pending) next)) (persistent! result))))) +(def get-children (memoize get-children*)) + (defn get-children-objects "Retrieve all children objects recursively for a given object" [id objects] @@ -172,9 +174,10 @@ shape (get objects (:frame-id shape)))) -(defn clean-loops +(defn clean-loops* "Clean a list of ids from circular references." [objects ids] + (let [parent-selected? (fn [id] (let [parents (get-parents id objects)] @@ -188,6 +191,8 @@ (reduce add-element (d/ordered-set) ids))) +(def clean-loops (memoize clean-loops*)) + (defn calculate-invalid-targets [shape-id objects] (let [result #{shape-id} @@ -494,3 +499,9 @@ (and (not= (:type shape) :frame) (= (:frame-id shape) uuid/zero))) +(defn children-seq + "Creates a sequence of shapes through the objects tree" + [shape objects] + (tree-seq #(d/not-empty? (get shape :shapes)) + #(->> (get % :shapes) (map (partial get objects))) + shape)) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 724d66ade6..75172fbfef 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -107,7 +107,6 @@ ;; geometric attributes of the shapes. (declare set-modifiers-recursive) -(declare check-delta) (declare set-local-displacement) (declare clear-local-transform) @@ -195,39 +194,6 @@ (clear-local-transform) (dwu/commit-undo-transaction)))))) -(defn- set-modifiers-recursive - [modif-tree objects shape modifiers root transformed-root ignore-constraints] - (let [children (->> (get shape :shapes []) - (map #(get objects %))) - - transformed-shape (gsh/transform-shape (assoc shape :modifiers modifiers)) - - [root transformed-root ignore-geometry?] - (check-delta shape root transformed-shape transformed-root objects) - - modifiers (assoc modifiers :ignore-geometry? ignore-geometry?) - - transformed-rect (gsh/calc-transformed-parent-rect shape modifiers) - - set-child - (fn [modif-tree child] - (let [child-modifiers - (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)] - - (set-modifiers-recursive modif-tree - objects - child - child-modifiers - root - transformed-root - ignore-constraints))) - - modif-tree - (-> modif-tree - (assoc-in [(:id shape) :modifiers] modifiers))] - - (reduce set-child modif-tree children))) - (defn- check-delta "If the shape is a component instance, check its relative position respect the root of the component, and see if it changes after applying a transformation." @@ -262,6 +228,35 @@ [root transformed-root ignore-geometry?])) +(defn- set-modifiers-recursive + [modif-tree objects shape modifiers root transformed-root ignore-constraints] + (let [children (map (d/getf objects) (:shapes shape)) + + transformed-shape (gsh/transform-shape (assoc shape :modifiers modifiers)) + + [root transformed-root ignore-geometry?] + (check-delta shape root transformed-shape transformed-root objects) + + modifiers (assoc modifiers :ignore-geometry? ignore-geometry?) + + transformed-rect (gsh/calc-transformed-parent-rect shape modifiers) + + set-child + (fn [modif-tree child] + (let [child-modifiers + (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)] + + (cond-> modif-tree + (not (empty? (d/without-keys child-modifiers [:ignore-geometry?]))) + (set-modifiers-new* + objects child child-modifiers root transformed-root ignore-constraints)))) + + modif-tree + (-> modif-tree + (assoc-in [(:id shape) :modifiers] modifiers))] + + (reduce set-child modif-tree children))) + (defn- set-local-displacement [point] (ptk/reify ::start-local-displacement ptk/UpdateEvent diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index c174f35050..7da923afed 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -245,7 +245,7 @@ objects (cond-> objects with-modifiers? (gsh/merge-modifiers modifiers)) - xform (comp (map #(get objects %)) + xform (comp (map (d/getf objects)) (remove nil?))] (into [] xform ids)))] (l/derived selector st/state =)))) @@ -300,19 +300,10 @@ (def selected-shapes-with-children (letfn [(selector [{:keys [selected objects]}] - (let [children (->> selected - (mapcat #(cp/get-children % objects)) - (filterv (comp not nil?)))] - (into selected children)))] - (l/derived selector selected-data =))) - -(def selected-objects-with-children - (letfn [(selector [{:keys [selected objects]}] - (let [children (->> selected - (mapcat #(cp/get-children % objects)) - (filterv (comp not nil?))) - shapes (into selected children)] - (mapv #(get objects %) shapes)))] + (let [xform (comp (remove nil?) + (mapcat #(cp/get-children % objects))) + shapes (into selected xform selected)] + (mapv (d/getf objects) shapes)))] (l/derived selector selected-data =))) ;; ---- Viewer refs diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index e0a62cbff6..9e55233453 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -92,7 +92,7 @@ page-id (mf/use-ctx ctx/current-page-id) file-id (mf/use-ctx ctx/current-file-id) shapes (mf/deref refs/selected-objects) - shapes-with-children (mf/deref refs/selected-objects-with-children)] + shapes-with-children (mf/deref refs/selected-shapes-with-children)] [:& options-content {:shapes shapes :selected selected :shapes-with-children shapes-with-children diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs index 8476844471..187c884fd8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/booleans.cljs @@ -18,7 +18,7 @@ (mf/defc booleans-options [] (let [selected (mf/deref refs/selected-objects) - selected-with-children (mf/deref refs/selected-objects-with-children) + selected-with-children (mf/deref refs/selected-shapes-with-children) has-invalid-shapes? (->> selected-with-children (some (comp #{:frame :text} :type)))