0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 10:38:13 -05:00

Improved snap performance

This commit is contained in:
alonso.torres 2021-01-19 23:02:51 +01:00
parent 27e83342f9
commit d19dc1cf56
3 changed files with 101 additions and 91 deletions

View file

@ -177,15 +177,15 @@
(join [1 2 3] [:a :b]) => ([1 :a] [1 :b] [2 :a] [2 :b] [3 :a] [3 :b])
You can pass a function to merge the items. By default is `vector`:
(join [1 2 3] [1 10 100] *) => (1 10 100 2 20 200 3 30 300)"
([col1 col2] (join col1 col2 vector '()))
([col1 col2 join-fn] (join col1 col2 join-fn '()))
([col1 col2] (join col1 col2 vector []))
([col1 col2 join-fn] (join col1 col2 join-fn []))
([col1 col2 join-fn acc]
(cond
(empty? col1) acc
(empty? col2) acc
:else (recur (rest col1) col2 join-fn
(core/concat acc (map (partial join-fn (first col1)) col2))))))
(let [other (mapv (partial join-fn (first col1)) col2)]
(concat acc other))))))
(def sentinel
#?(:clj (Object.)

View file

@ -20,8 +20,8 @@
[app.main.refs :as refs]
[app.util.geom.snap-points :as sp]))
(def ^:private snap-accuracy 5)
(def ^:private snap-distance-accuracy 10)
(defonce ^:private snap-accuracy 5)
(defonce ^:private snap-distance-accuracy 10)
(defn- remove-from-snap-points
[remove-id?]
@ -96,57 +96,69 @@
;; snap-x is the second parameter because is the "source" to combine
(rx/combine-latest snap->vector snap-y snap-x)))
(defn search-snap-distance [selrect coord shapes-lt shapes-gt]
(defn calculate-snap [coord selrect shapes-lt shapes-gt]
(let [dist (fn [[sh1 sh2]] (-> sh1 (gsh/distance-shapes sh2) coord))
dist-lt (fn [other] (-> (:selrect other) (gsh/distance-selrect selrect) coord))
dist-gt (fn [other] (-> selrect (gsh/distance-selrect (:selrect other)) coord))
;; Calculates the distance between all the shapes given as argument
inner-distance (fn [shapes]
(->> shapes
(sort-by coord)
(d/map-perm vector)
(filter (fn [[sh1 sh2]] (gsh/overlap-coord? coord sh1 sh2)))
(map dist)
(filter #(> % 0))))
;; Calculates the snap distance when in the middle of two shapes
between-snap (fn [[sh-lt sh-gt]]
;; To calculate the middle snap.
;; Given x, the distance to a left shape and y to a right shape
;; x - v = y + v => v = (x - y)/2
;; v will be the vector that we need to move the shape so it "snaps"
;; in the middle
(/ (- (dist-gt sh-gt)
(dist-lt sh-lt)) 2))
]
(->> shapes-lt
(rx/combine-latest vector shapes-gt)
(rx/map (fn [[shapes-lt shapes-gt]]
(let [;; Distance between the elements in an area, these are the snap
;; candidates to either side
lt-cand (inner-distance shapes-lt)
gt-cand (inner-distance shapes-gt)
between-snap
(fn [[sh-lt sh-gt]]
;; To calculate the middle snap.
;; Given x, the distance to a left shape and y to a right shape
;; x - v = y + v => v = (x - y)/2
;; v will be the vector that we need to move the shape so it "snaps"
;; in the middle
(/ (- (dist-gt sh-gt)
(dist-lt sh-lt)) 2))
;; Distance between the elements to either side and the current shape
;; this is the distance that will "snap"
lt-dist (map dist-lt shapes-lt)
gt-dist (map dist-gt shapes-gt)
;; Calculates the distance between all the shapes given as argument
inner-distance
(fn [shapes]
(->> shapes
(sort-by coord)
(d/map-perm vector)
(filter (fn [[sh1 sh2]] (gsh/overlap-coord? coord sh1 sh2)))
(map dist)
(filterv #(> % 0))))
;; Calculate the snaps, we need to reverse depending on area
lt-snap (d/join lt-cand lt-dist -)
gt-snap (d/join gt-dist gt-cand -)
best-snap
(fn [acc val]
;; Using a number is faster than accesing the variable.
;; Keep up to date with `snap-distance-accuracy`
(if (and (<= val 10) (>= val (- 10)))
(min acc val)
acc))
;; Calculate snap-between
between-snap (->> (d/join shapes-lt shapes-gt)
(map between-snap))
;; Distance between the elements in an area, these are the snap
;; candidates to either side
lt-cand (inner-distance shapes-lt)
gt-cand (inner-distance shapes-gt)
;; Search the minimum snap
min-snap (->> (concat lt-snap gt-snap between-snap)
(filter #(<= (mth/abs %) snap-distance-accuracy))
(reduce min ##Inf))]
;; Distance between the elements to either side and the current shape
;; this is the distance that will "snap"
lt-dist (mapv dist-lt shapes-lt)
gt-dist (mapv dist-gt shapes-gt)
(if (mth/finite? min-snap) [0 min-snap] nil)))))))
;; Calculate the snaps, we need to reverse depending on area
lt-snap (d/join lt-cand lt-dist -)
gt-snap (d/join gt-dist gt-cand -)
;; Calculate snap-between
between-snap (->> (d/join shapes-lt shapes-gt)
(map between-snap))
;; Search the minimum snap
snap-list (-> [] (d/concat lt-snap) (d/concat gt-snap) (d/concat between-snap))
min-snap (reduce best-snap ##Inf snap-list)]
(if (mth/finite? min-snap) [0 min-snap] nil)))
(defn search-snap-distance [selrect coord shapes-lt shapes-gt]
(->> shapes-lt
(rx/combine-latest vector shapes-gt)
(rx/map (fn [[shapes-lt shapes-gt]]
(calculate-snap coord selrect shapes-lt shapes-gt)))))
(defn select-shapes-area
[page-id shapes objects area-selrect]

View file

@ -112,52 +112,11 @@
:x2 x2 :y2 y2
:style {:stroke line-color :stroke-width (str (/ 1 zoom))}}])]))
(mf/defc shape-distance
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
selrect (unchecked-get props "selrect")
page-id (unchecked-get props "page-id")
zoom (unchecked-get props "zoom")
coord (unchecked-get props "coord")
selected (unchecked-get props "selected")
subject (mf/use-memo #(rx/subject))
to-measure (mf/use-state [])
pair->distance+pair
(defn calculate-segments [coord selrect lt-shapes gt-shapes]
(let [pair->distance+pair
(fn [[sh1 sh2]]
[(-> (gsh/distance-shapes sh1 sh2) coord (mth/precision 0)) [sh1 sh2]])
contains-selected?
(fn [selected pairs]
(let [has-selected?
(fn [[_ [sh1 sh2]]]
(or (selected (:id sh1))
(selected (:id sh2))))]
(some has-selected? pairs)))
query-worker
(fn [[selrect selected frame]]
(let [lt-side (if (= coord :x) :left :top)
gt-side (if (= coord :x) :right :bottom)
container-selrec (or (:selrect frame)
(gsh/rect->selrect @refs/vbox))
areas (gsh/selrect->areas container-selrec selrect)
query-side (fn [side]
(let [rect (gsh/pad-selrec (areas side))]
(if (and (> (:width rect) 0) (> (:height rect) 0))
(->> (uw/ask! {:cmd :selection/query
:page-id page-id
:frame-id (:id frame)
:rect rect})
(rx/map #(set/difference % selected))
(rx/map #(->> % (map (partial get @refs/workspace-page-objects)))))
(rx/of nil))))]
(->> (query-side lt-side)
(rx/combine-latest vector (query-side gt-side)))))
distance-to-selrect
(fn [shape]
(let [sr (:selrect shape)]
@ -183,7 +142,6 @@
(some #(<= (mth/abs (- value %)) 1))))
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter
[lt-shapes gt-shapes] @to-measure
;; Gets the distance to the current selection
lt-distances (->> lt-shapes (map distance-to-selrect) (filter pos?) (into #{}))
@ -225,6 +183,46 @@
(map #(vector selrect (:selrect %))))
segments-to-display (d/concat #{} other-shapes-segments selection-segments)]
segments-to-display))
(mf/defc shape-distance
{::mf/wrap-props false}
[props]
(let [frame (unchecked-get props "frame")
selrect (unchecked-get props "selrect")
page-id (unchecked-get props "page-id")
zoom (unchecked-get props "zoom")
coord (unchecked-get props "coord")
selected (unchecked-get props "selected")
subject (mf/use-memo #(rx/subject))
to-measure (mf/use-state [])
query-worker
(fn [[selrect selected frame]]
(let [lt-side (if (= coord :x) :left :top)
gt-side (if (= coord :x) :right :bottom)
container-selrec (or (:selrect frame)
(gsh/rect->selrect @refs/vbox))
areas (gsh/selrect->areas container-selrec selrect)
query-side (fn [side]
(let [rect (gsh/pad-selrec (areas side))]
(if (and (> (:width rect) 0) (> (:height rect) 0))
(->> (uw/ask! {:cmd :selection/query
:page-id page-id
:frame-id (:id frame)
:rect rect})
(rx/map #(set/difference % selected))
(rx/map #(->> % (map (partial get @refs/workspace-page-objects)))))
(rx/of nil))))]
(->> (query-side lt-side)
(rx/combine-latest vector (query-side gt-side)))))
[lt-shapes gt-shapes] @to-measure
segments-to-display (calculate-segments coord selrect lt-shapes gt-shapes)]
(mf/use-effect
(fn []