mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 09:08:31 -05:00
⚡ Improved performance of snap to distances
This commit is contained in:
parent
2c1fb1424c
commit
c459c56f37
4 changed files with 73 additions and 71 deletions
|
@ -82,24 +82,12 @@
|
|||
(update :height (comp inc inc))))))
|
||||
|
||||
(defn selrect->areas [bounds selrect]
|
||||
(let [make-selrect
|
||||
(fn [x1 y1 x2 y2]
|
||||
(let [x1 (min x1 x2)
|
||||
x2 (max x1 x2)
|
||||
y1 (min y1 y2)
|
||||
y2 (max y1 y2)]
|
||||
{:x1 x1 :y1 y1
|
||||
:x2 x2 :y2 y2
|
||||
:x x1 :y y1
|
||||
:width (- x2 x1)
|
||||
:height (- y2 y1)
|
||||
:type :rect}))
|
||||
{bound-x1 :x1 bound-x2 :x2 bound-y1 :y1 bound-y2 :y2} bounds
|
||||
(let [{bound-x1 :x1 bound-x2 :x2 bound-y1 :y1 bound-y2 :y2} bounds
|
||||
{sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2} selrect]
|
||||
{:left (make-selrect bound-x1 sr-y1 sr-x1 sr-y2)
|
||||
:top (make-selrect sr-x1 bound-y1 sr-x2 sr-y1)
|
||||
:right (make-selrect sr-x2 sr-y1 bound-x2 sr-y2)
|
||||
:bottom (make-selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
|
||||
{:left (gpr/corners->selrect bound-x1 sr-y1 sr-x1 sr-y2)
|
||||
:top (gpr/corners->selrect sr-x1 bound-y1 sr-x2 sr-y1)
|
||||
:right (gpr/corners->selrect sr-x2 sr-y1 bound-x2 sr-y2)
|
||||
:bottom (gpr/corners->selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
|
||||
|
||||
(defn distance-selrect [selrect other]
|
||||
(let [{:keys [x1 y1]} other
|
||||
|
@ -161,6 +149,7 @@
|
|||
(dm/export gpr/contains-selrect?)
|
||||
(dm/export gpr/contains-point?)
|
||||
(dm/export gpr/close-selrect?)
|
||||
(dm/export gpr/clip-selrect)
|
||||
|
||||
(dm/export gtr/move)
|
||||
(dm/export gtr/absolute-move)
|
||||
|
|
|
@ -212,9 +212,13 @@
|
|||
(<= (: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)))))
|
||||
([p1 p2]
|
||||
(corners->selrect (:x p1) (:y p1) (:x p2) (:y p2)))
|
||||
([xp1 yp1 xp2 yp2]
|
||||
(make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2)))))
|
||||
|
||||
(defn clip-selrect
|
||||
[{:keys [x1 y1 x2 y2] :as sr} bounds]
|
||||
(when (some? sr)
|
||||
(let [{bx1 :x1 by1 :y1 bx2 :x2 by2 :y2} (rect->selrect bounds)]
|
||||
(corners->selrect (max bx1 x1) (max by1 y1) (min bx2 x2) (min by2 y2)))))
|
||||
|
|
|
@ -144,17 +144,6 @@
|
|||
dist-lt (fn [other] (sr-distance coord (:selrect other) selrect))
|
||||
dist-gt (fn [other] (sr-distance coord selrect (:selrect other)))
|
||||
|
||||
;; 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))
|
||||
|
||||
;; Calculates the distance between all the shapes given as argument
|
||||
inner-distance
|
||||
(fn [selrects]
|
||||
|
@ -164,14 +153,6 @@
|
|||
#(overlap? coord %1 %2)
|
||||
#{})))
|
||||
|
||||
best-snap
|
||||
(fn [acc val]
|
||||
;; Using a number is faster than accessing the variable.
|
||||
;; Keep up to date with `snap-distance-accuracy`
|
||||
(if (and (<= val snap-distance-accuracy) (>= val (- snap-distance-accuracy)))
|
||||
(min acc val)
|
||||
acc))
|
||||
|
||||
;; Distance between the elements in an area, these are the snap
|
||||
;; candidates to either side
|
||||
lt-cand (inner-distance (mapv :selrect shapes-lt))
|
||||
|
@ -182,17 +163,46 @@
|
|||
lt-dist (into #{} (map dist-lt) shapes-lt)
|
||||
gt-dist (into #{} (map dist-gt) shapes-gt)
|
||||
|
||||
;; 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 -)
|
||||
get-side-snaps
|
||||
(fn [candidates distances]
|
||||
;; We add to the range tree the distrances between elements
|
||||
;; then, for each distance from the selection we query the tree
|
||||
;; to find a snap
|
||||
(let [range-tree (rt/make-tree)
|
||||
range-tree
|
||||
(->> candidates
|
||||
(reduce #(rt/insert %1 %2 %2) range-tree))]
|
||||
(->> distances
|
||||
(mapcat
|
||||
(fn [cd]
|
||||
(->> (rt/range-query
|
||||
range-tree
|
||||
(- cd snap-distance-accuracy)
|
||||
(+ cd snap-distance-accuracy))
|
||||
(map #(- (first %) cd ))))))))
|
||||
|
||||
;; Calculate snap-between
|
||||
between-snap (->> (d/join shapes-lt shapes-gt)
|
||||
(map between-snap))
|
||||
get-middle-snaps
|
||||
(fn [lt-dist gt-dist]
|
||||
(let [range-tree (rt/make-tree)
|
||||
range-tree (->> lt-dist
|
||||
(reduce #(rt/insert %1 %2 %2) range-tree))]
|
||||
(->> gt-dist
|
||||
(mapcat (fn [cd]
|
||||
(->> (rt/range-query
|
||||
range-tree
|
||||
(- cd (* snap-distance-accuracy 2))
|
||||
(+ cd (* snap-distance-accuracy 2)))
|
||||
(map #(/ (- cd (first %)) 2))))))))
|
||||
|
||||
;; Calculate the snaps, we need to reverse depending on area
|
||||
lt-snap (get-side-snaps lt-cand lt-dist)
|
||||
gt-snap (get-side-snaps gt-dist gt-cand)
|
||||
md-snap (get-middle-snaps lt-dist gt-dist)
|
||||
|
||||
;; Search the minimum snap
|
||||
snap-list (d/concat-vec lt-snap gt-snap between-snap)
|
||||
min-snap (reduce best-snap ##Inf snap-list)]
|
||||
snap-list (d/concat-vec lt-snap gt-snap md-snap)
|
||||
|
||||
min-snap (reduce min ##Inf snap-list)]
|
||||
|
||||
(if (d/num? min-snap) [0 min-snap] nil)))
|
||||
|
||||
|
@ -202,14 +212,14 @@
|
|||
(calculate-snap coord selrect shapes-lt shapes-gt zoom)))))
|
||||
|
||||
(defn select-shapes-area
|
||||
[page-id shapes objects area-selrect]
|
||||
[page-id frame-id selected objects area]
|
||||
(->> (uw/ask! {:cmd :selection/query
|
||||
:page-id page-id
|
||||
:frame-id (->> shapes first :frame-id)
|
||||
:frame-id frame-id
|
||||
:include-frames? true
|
||||
:rect area-selrect})
|
||||
:rect area})
|
||||
(rx/map #(cph/clean-loops objects %))
|
||||
(rx/map #(set/difference % (into #{} (map :id shapes))))
|
||||
(rx/map #(set/difference % selected))
|
||||
(rx/map #(map (d/getf objects) %))))
|
||||
|
||||
(defn closest-distance-snap
|
||||
|
@ -220,9 +230,14 @@
|
|||
(->> (rx/of (vector frame selrect))
|
||||
(rx/merge-map
|
||||
(fn [[frame selrect]]
|
||||
(let [areas (->> (gsh/selrect->areas (or (:selrect frame)
|
||||
(gsh/rect->selrect @refs/vbox)) selrect)
|
||||
(d/mapm #(select-shapes-area page-id shapes objects %2)))
|
||||
(let [vbox (gsh/rect->selrect @refs/vbox)
|
||||
frame-id (->> shapes first :frame-id)
|
||||
selected (into #{} (map :id shapes))
|
||||
areas (->> (gsh/selrect->areas
|
||||
(or (gsh/clip-selrect (:selrect frame) vbox)
|
||||
vbox)
|
||||
selrect)
|
||||
(d/mapm #(select-shapes-area page-id frame-id selected objects %2)))
|
||||
snap-x (search-snap-distance selrect :x (:left areas) (:right areas) zoom)
|
||||
snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas) zoom)]
|
||||
(rx/combine-latest snap-x snap-y))))
|
||||
|
|
|
@ -9,11 +9,10 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.snap :as ams]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.worker :as uw]
|
||||
[beicon.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -152,7 +151,7 @@
|
|||
check-in-set
|
||||
(fn [value number-set]
|
||||
(->> number-set
|
||||
(some #(<= (mth/abs (- value %)) 0.01))))
|
||||
(some #(<= (mth/abs (- value %)) 1.5))))
|
||||
|
||||
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter)
|
||||
|
||||
|
@ -218,21 +217,16 @@
|
|||
(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)
|
||||
|
||||
vbox (gsh/rect->selrect @refs/vbox)
|
||||
areas (gsh/selrect->areas
|
||||
(or (gsh/clip-selrect (:selrect frame) vbox) vbox)
|
||||
selrect)
|
||||
|
||||
query-side (fn [side]
|
||||
(let [rect (get areas side)]
|
||||
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
||||
(->> (uw/ask! {:cmd :selection/query
|
||||
:page-id page-id
|
||||
:frame-id (:id frame)
|
||||
:include-frames? true
|
||||
:rect rect})
|
||||
(rx/map #(cph/clean-loops @refs/workspace-page-objects %))
|
||||
(rx/map #(set/difference % selected))
|
||||
(rx/map #(->> % (map (partial get @refs/workspace-page-objects)))))
|
||||
(ams/select-shapes-area page-id (:id frame) selected @refs/workspace-page-objects rect)
|
||||
(rx/of nil))))]
|
||||
(rx/combine-latest (query-side lt-side)
|
||||
(query-side gt-side))))
|
||||
|
|
Loading…
Add table
Reference in a new issue