diff --git a/CHANGES.md b/CHANGES.md index c32c0065d..ae7fa2d07 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # CHANGELOG +## 1.19.5 + +### :bug: New features + +- Fix problem with alignment performance + ## 1.19.4 ### :sparkles: New features diff --git a/common/src/app/common/geom/align.cljc b/common/src/app/common/geom/align.cljc index 7c2091cb9..cc1700340 100644 --- a/common/src/app/common/geom/align.cljc +++ b/common/src/app/common/geom/align.cljc @@ -6,8 +6,7 @@ (ns app.common.geom.align (:require - [app.common.geom.shapes :as gsh] - [app.common.pages.helpers :refer [get-children]])) + [app.common.geom.shapes :as gsh])) ;; --- Alignment @@ -16,25 +15,18 @@ (declare calc-align-pos) -(defn- recursive-move - "Move the shape and all its recursive children." - [shape dpoint objects] - (->> (get-children objects (:id shape)) - (cons shape) - (map #(gsh/move % dpoint)))) - (defn align-to-rect "Move the shape so that it is aligned with the given rectangle in the given axis. Take account the form of the shape and the possible rotation. What is aligned is the rectangle that wraps the shape with the given rectangle. If the shape is a group, move also all of its recursive children." - [shape rect axis objects] + [shape rect axis] (let [wrapper-rect (gsh/selection-rect [shape]) align-pos (calc-align-pos wrapper-rect rect axis) delta {:x (- (:x align-pos) (:x wrapper-rect)) :y (- (:y align-pos) (:y wrapper-rect))}] - (recursive-move shape delta objects))) + (gsh/move shape delta))) (defn calc-align-pos [wrapper-rect rect axis] @@ -73,22 +65,22 @@ It takes into account the form of the shape and the rotation, what is distributed is the wrapping rectangles of the shapes. If any shape is a group, move also all of its recursive children." - [shapes axis objects] + [shapes axis] (let [coord (if (= axis :horizontal) :x :y) other-coord (if (= axis :horizontal) :y :x) size (if (= axis :horizontal) :width :height) - ; The rectangle that wraps the whole selection + ;; The rectangle that wraps the whole selection wrapper-rect (gsh/selection-rect shapes) - ; Sort shapes by the center point in the given axis + ;; Sort shapes by the center point in the given axis sorted-shapes (sort-by #(coord (gsh/center-shape %)) shapes) - ; Each shape wrapped in its own rectangle + ;; Each shape wrapped in its own rectangle wrapped-shapes (map #(gsh/selection-rect [%]) sorted-shapes) - ; The total space between shapes + ;; The total space between shapes space (reduce - (size wrapper-rect) (map size wrapped-shapes)) unit-space (/ space (- (count wrapped-shapes) 1)) - ; Calculate the distance we need to move each shape. - ; The new position of each one is the position of the - ; previous one plus its size plus the unit space. + ;; Calculate the distance we need to move each shape. + ;; The new position of each one is the position of the + ;; previous one plus its size plus the unit space. deltas (loop [shapes' wrapped-shapes start-pos (coord wrapper-rect) deltas []] @@ -100,11 +92,11 @@ (if (= (count shapes') 1) (conj deltas delta) (recur (rest shapes') - new-pos - (conj deltas delta)))))] + new-pos + (conj deltas delta)))))] - (mapcat #(recursive-move %1 {coord %2 other-coord 0} objects) - sorted-shapes deltas))) + (map #(gsh/move %1 {coord %2 other-coord 0}) + sorted-shapes deltas))) ;; Adjust to viewport diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 9c2e99dcb..800e68250 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -137,6 +137,7 @@ (dm/export gco/center-points) (dm/export gco/transform-points) (dm/export gco/shape->points) +(dm/export gco/shapes->rect) (dm/export gpr/make-rect) (dm/export gpr/make-selrect) diff --git a/common/src/app/common/geom/shapes/common.cljc b/common/src/app/common/geom/shapes/common.cljc index bb4d57356..4d09d08a0 100644 --- a/common/src/app/common/geom/shapes/common.cljc +++ b/common/src/app/common/geom/shapes/common.cljc @@ -12,6 +12,16 @@ [app.common.geom.shapes.rect :as gpr] [app.common.math :as mth])) +(defn shapes->rect + "Returns a rect that contains all the shapes and is aware of the + rotation of each shape. Mainly used for multiple selection." + [shapes] + (->> shapes + (keep (fn [shape] + (-> (:points shape) + (gpr/points->rect)))) + (gpr/join-rects))) + (defn center-rect [{:keys [x y width height]}] (when (d/num? x y width height) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 0af5a737b..cbf0ef63b 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1021,9 +1021,6 @@ ;; --- Shape / Selection Alignment and Distribution -(declare align-object-to-parent) -(declare align-objects-list) - (defn can-align? [selected objects] (cond (empty? selected) false @@ -1031,11 +1028,18 @@ :else (not= uuid/zero (:parent-id (get objects (first selected)))))) -(defn- move-shape - [shape] - (let [bbox (-> shape :points gsh/points->selrect) - pos (gpt/point (:x bbox) (:y bbox))] - (dwt/update-position (:id shape) pos))) +(defn align-object-to-parent + [objects object-id axis] + (let [object (get objects object-id) + parent-id (:parent-id (get objects object-id)) + parent (get objects parent-id)] + [(gal/align-to-rect object parent axis)])) + +(defn align-objects-list + [objects selected axis] + (let [selected-objs (map #(get objects %) selected) + rect (gsh/shapes->rect selected-objs)] + (map #(gal/align-to-rect % rect axis) selected-objs))) (defn align-objects [axis] @@ -1052,28 +1056,12 @@ moved (if (= 1 (count selected)) (align-object-to-parent objects (first selected) axis) (align-objects-list objects selected axis)) - ids (map :id moved) undo-id (js/Symbol)] (when (can-align? selected objects) - (rx/concat - (rx/of (dwu/start-undo-transaction undo-id)) - (->> (rx/from moved) - (rx/map move-shape)) - (rx/of (ptk/data-event :layout/update ids) - (dwu/commit-undo-transaction undo-id)))))))) - -(defn align-object-to-parent - [objects object-id axis] - (let [object (get objects object-id) - parent (:parent-id (get objects object-id)) - parent-obj (get objects parent)] - (gal/align-to-rect object parent-obj axis objects))) - -(defn align-objects-list - [objects selected axis] - (let [selected-objs (map #(get objects %) selected) - rect (gsh/selection-rect selected-objs)] - (mapcat #(gal/align-to-rect % rect axis objects) selected-objs))) + (rx/of (dwu/start-undo-transaction undo-id) + (dwt/position-shapes moved) + (ptk/data-event :layout/update selected) + (dwu/commit-undo-transaction undo-id))))))) (defn can-distribute? [selected] (cond @@ -1094,14 +1082,13 @@ objects (wsh/lookup-page-objects state page-id) selected (wsh/lookup-selected state) moved (-> (map #(get objects %) selected) - (gal/distribute-space axis objects)) - - moved (d/index-by :id moved) - ids (keys moved) - - update-fn #(get moved (:id %))] + (gal/distribute-space axis)) + undo-id (js/Symbol)] (when (can-distribute? selected) - (rx/of (dch/update-shapes ids update-fn {:reg-objects? true}))))))) + (rx/of (dwu/start-undo-transaction undo-id) + (dwt/position-shapes moved) + (ptk/data-event :layout/update selected) + (dwu/commit-undo-transaction undo-id))))))) ;; --- Shape Proportions diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 8f86d72b7..c7a6e7f6d 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -402,14 +402,15 @@ ([] (apply-modifiers nil)) - ([{:keys [modifiers undo-transation? stack-undo?] :or {undo-transation? true stack-undo? false}}] + ([{:keys [modifiers undo-transation? stack-undo? ignore-constraints ignore-snap-pixel] + :or {undo-transation? true stack-undo? false ignore-constraints false ignore-snap-pixel false}}] (ptk/reify ::apply-modifiers ptk/WatchEvent (watch [_ state _] (let [text-modifiers (get state :workspace-text-modifier) objects (wsh/lookup-page-objects state) object-modifiers (if modifiers - (calculate-modifiers state modifiers) + (calculate-modifiers state ignore-constraints ignore-snap-pixel modifiers) (get state :workspace-modifiers)) ids (or (keys object-modifiers) []) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index ad43c6bfc..35a86f804 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -687,7 +687,6 @@ (defn update-position "Move shapes to a new position" [id position] - (js/console.log "DEBUG" (pr-str position)) (dm/assert! (uuid? id)) (ptk/reify ::update-position @@ -707,8 +706,31 @@ modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))] - (rx/of (dwm/set-modifiers modif-tree false true) - (dwm/apply-modifiers)))))) + (rx/of (dwm/apply-modifiers {:modifiers modif-tree + :ignore-constraints false + :ignore-snap-pixel true})))))) + +(defn position-shapes + [shapes] + (ptk/reify ::position-shapes + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + shapes (d/index-by :id shapes) + + modif-tree + (dwm/build-modif-tree + (keys shapes) + objects + (fn [cshape] + (let [oshape (get shapes (:id cshape)) + cpos (-> cshape :points first gpt/point) + opos (-> oshape :points first gpt/point)] + (ctm/move-modifiers (gpt/subtract opos cpos)))))] + + (rx/of (dwm/apply-modifiers {:modifiers modif-tree + :ignore-constraints false + :ignore-snap-pixel true})))))) (defn- move-shapes-to-frame [ids frame-id drop-index]