mirror of
https://github.com/penpot/penpot.git
synced 2025-05-12 12:56:39 +02:00
🐛 Fix problem with alignment performance
This commit is contained in:
parent
859146ddc2
commit
fe3740e329
7 changed files with 82 additions and 63 deletions
|
@ -1,5 +1,11 @@
|
||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 1.19.5
|
||||||
|
|
||||||
|
### :bug: New features
|
||||||
|
|
||||||
|
- Fix problem with alignment performance
|
||||||
|
|
||||||
## 1.19.4
|
## 1.19.4
|
||||||
|
|
||||||
### :sparkles: New features
|
### :sparkles: New features
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
|
|
||||||
(ns app.common.geom.align
|
(ns app.common.geom.align
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]))
|
||||||
[app.common.pages.helpers :refer [get-children]]))
|
|
||||||
|
|
||||||
;; --- Alignment
|
;; --- Alignment
|
||||||
|
|
||||||
|
@ -16,25 +15,18 @@
|
||||||
|
|
||||||
(declare calc-align-pos)
|
(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
|
(defn align-to-rect
|
||||||
"Move the shape so that it is aligned with the given rectangle
|
"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
|
in the given axis. Take account the form of the shape and the
|
||||||
possible rotation. What is aligned is the rectangle that wraps
|
possible rotation. What is aligned is the rectangle that wraps
|
||||||
the shape with the given rectangle. If the shape is a group,
|
the shape with the given rectangle. If the shape is a group,
|
||||||
move also all of its recursive children."
|
move also all of its recursive children."
|
||||||
[shape rect axis objects]
|
[shape rect axis]
|
||||||
(let [wrapper-rect (gsh/selection-rect [shape])
|
(let [wrapper-rect (gsh/selection-rect [shape])
|
||||||
align-pos (calc-align-pos wrapper-rect rect axis)
|
align-pos (calc-align-pos wrapper-rect rect axis)
|
||||||
delta {:x (- (:x align-pos) (:x wrapper-rect))
|
delta {:x (- (:x align-pos) (:x wrapper-rect))
|
||||||
:y (- (:y align-pos) (:y wrapper-rect))}]
|
:y (- (:y align-pos) (:y wrapper-rect))}]
|
||||||
(recursive-move shape delta objects)))
|
(gsh/move shape delta)))
|
||||||
|
|
||||||
(defn calc-align-pos
|
(defn calc-align-pos
|
||||||
[wrapper-rect rect axis]
|
[wrapper-rect rect axis]
|
||||||
|
@ -73,22 +65,22 @@
|
||||||
It takes into account the form of the shape and the rotation,
|
It takes into account the form of the shape and the rotation,
|
||||||
what is distributed is the wrapping rectangles of the shapes.
|
what is distributed is the wrapping rectangles of the shapes.
|
||||||
If any shape is a group, move also all of its recursive children."
|
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)
|
(let [coord (if (= axis :horizontal) :x :y)
|
||||||
other-coord (if (= axis :horizontal) :y :x)
|
other-coord (if (= axis :horizontal) :y :x)
|
||||||
size (if (= axis :horizontal) :width :height)
|
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)
|
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)
|
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)
|
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))
|
space (reduce - (size wrapper-rect) (map size wrapped-shapes))
|
||||||
unit-space (/ space (- (count wrapped-shapes) 1))
|
unit-space (/ space (- (count wrapped-shapes) 1))
|
||||||
; Calculate the distance we need to move each shape.
|
;; Calculate the distance we need to move each shape.
|
||||||
; The new position of each one is the position of the
|
;; The new position of each one is the position of the
|
||||||
; previous one plus its size plus the unit space.
|
;; previous one plus its size plus the unit space.
|
||||||
deltas (loop [shapes' wrapped-shapes
|
deltas (loop [shapes' wrapped-shapes
|
||||||
start-pos (coord wrapper-rect)
|
start-pos (coord wrapper-rect)
|
||||||
deltas []]
|
deltas []]
|
||||||
|
@ -100,11 +92,11 @@
|
||||||
(if (= (count shapes') 1)
|
(if (= (count shapes') 1)
|
||||||
(conj deltas delta)
|
(conj deltas delta)
|
||||||
(recur (rest shapes')
|
(recur (rest shapes')
|
||||||
new-pos
|
new-pos
|
||||||
(conj deltas delta)))))]
|
(conj deltas delta)))))]
|
||||||
|
|
||||||
(mapcat #(recursive-move %1 {coord %2 other-coord 0} objects)
|
(map #(gsh/move %1 {coord %2 other-coord 0})
|
||||||
sorted-shapes deltas)))
|
sorted-shapes deltas)))
|
||||||
|
|
||||||
;; Adjust to viewport
|
;; Adjust to viewport
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@
|
||||||
(dm/export gco/center-points)
|
(dm/export gco/center-points)
|
||||||
(dm/export gco/transform-points)
|
(dm/export gco/transform-points)
|
||||||
(dm/export gco/shape->points)
|
(dm/export gco/shape->points)
|
||||||
|
(dm/export gco/shapes->rect)
|
||||||
|
|
||||||
(dm/export gpr/make-rect)
|
(dm/export gpr/make-rect)
|
||||||
(dm/export gpr/make-selrect)
|
(dm/export gpr/make-selrect)
|
||||||
|
|
|
@ -12,6 +12,16 @@
|
||||||
[app.common.geom.shapes.rect :as gpr]
|
[app.common.geom.shapes.rect :as gpr]
|
||||||
[app.common.math :as mth]))
|
[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
|
(defn center-rect
|
||||||
[{:keys [x y width height]}]
|
[{:keys [x y width height]}]
|
||||||
(when (d/num? x y width height)
|
(when (d/num? x y width height)
|
||||||
|
|
|
@ -1021,9 +1021,6 @@
|
||||||
|
|
||||||
;; --- Shape / Selection Alignment and Distribution
|
;; --- Shape / Selection Alignment and Distribution
|
||||||
|
|
||||||
(declare align-object-to-parent)
|
|
||||||
(declare align-objects-list)
|
|
||||||
|
|
||||||
(defn can-align? [selected objects]
|
(defn can-align? [selected objects]
|
||||||
(cond
|
(cond
|
||||||
(empty? selected) false
|
(empty? selected) false
|
||||||
|
@ -1031,11 +1028,18 @@
|
||||||
:else
|
:else
|
||||||
(not= uuid/zero (:parent-id (get objects (first selected))))))
|
(not= uuid/zero (:parent-id (get objects (first selected))))))
|
||||||
|
|
||||||
(defn- move-shape
|
(defn align-object-to-parent
|
||||||
[shape]
|
[objects object-id axis]
|
||||||
(let [bbox (-> shape :points gsh/points->selrect)
|
(let [object (get objects object-id)
|
||||||
pos (gpt/point (:x bbox) (:y bbox))]
|
parent-id (:parent-id (get objects object-id))
|
||||||
(dwt/update-position (:id shape) pos)))
|
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
|
(defn align-objects
|
||||||
[axis]
|
[axis]
|
||||||
|
@ -1052,28 +1056,12 @@
|
||||||
moved (if (= 1 (count selected))
|
moved (if (= 1 (count selected))
|
||||||
(align-object-to-parent objects (first selected) axis)
|
(align-object-to-parent objects (first selected) axis)
|
||||||
(align-objects-list objects selected axis))
|
(align-objects-list objects selected axis))
|
||||||
ids (map :id moved)
|
|
||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
(when (can-align? selected objects)
|
(when (can-align? selected objects)
|
||||||
(rx/concat
|
(rx/of (dwu/start-undo-transaction undo-id)
|
||||||
(rx/of (dwu/start-undo-transaction undo-id))
|
(dwt/position-shapes moved)
|
||||||
(->> (rx/from moved)
|
(ptk/data-event :layout/update selected)
|
||||||
(rx/map move-shape))
|
(dwu/commit-undo-transaction undo-id)))))))
|
||||||
(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)))
|
|
||||||
|
|
||||||
(defn can-distribute? [selected]
|
(defn can-distribute? [selected]
|
||||||
(cond
|
(cond
|
||||||
|
@ -1094,14 +1082,13 @@
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
selected (wsh/lookup-selected state)
|
selected (wsh/lookup-selected state)
|
||||||
moved (-> (map #(get objects %) selected)
|
moved (-> (map #(get objects %) selected)
|
||||||
(gal/distribute-space axis objects))
|
(gal/distribute-space axis))
|
||||||
|
undo-id (js/Symbol)]
|
||||||
moved (d/index-by :id moved)
|
|
||||||
ids (keys moved)
|
|
||||||
|
|
||||||
update-fn #(get moved (:id %))]
|
|
||||||
(when (can-distribute? selected)
|
(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
|
;; --- Shape Proportions
|
||||||
|
|
||||||
|
|
|
@ -402,14 +402,15 @@
|
||||||
([]
|
([]
|
||||||
(apply-modifiers nil))
|
(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/reify ::apply-modifiers
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [text-modifiers (get state :workspace-text-modifier)
|
(let [text-modifiers (get state :workspace-text-modifier)
|
||||||
objects (wsh/lookup-page-objects state)
|
objects (wsh/lookup-page-objects state)
|
||||||
object-modifiers (if modifiers
|
object-modifiers (if modifiers
|
||||||
(calculate-modifiers state modifiers)
|
(calculate-modifiers state ignore-constraints ignore-snap-pixel modifiers)
|
||||||
(get state :workspace-modifiers))
|
(get state :workspace-modifiers))
|
||||||
|
|
||||||
ids (or (keys object-modifiers) [])
|
ids (or (keys object-modifiers) [])
|
||||||
|
|
|
@ -687,7 +687,6 @@
|
||||||
(defn update-position
|
(defn update-position
|
||||||
"Move shapes to a new position"
|
"Move shapes to a new position"
|
||||||
[id position]
|
[id position]
|
||||||
(js/console.log "DEBUG" (pr-str position))
|
|
||||||
(dm/assert! (uuid? id))
|
(dm/assert! (uuid? id))
|
||||||
|
|
||||||
(ptk/reify ::update-position
|
(ptk/reify ::update-position
|
||||||
|
@ -707,8 +706,31 @@
|
||||||
|
|
||||||
modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
||||||
|
|
||||||
(rx/of (dwm/set-modifiers modif-tree false true)
|
(rx/of (dwm/apply-modifiers {:modifiers modif-tree
|
||||||
(dwm/apply-modifiers))))))
|
: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
|
(defn- move-shapes-to-frame
|
||||||
[ids frame-id drop-index]
|
[ids frame-id drop-index]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue