mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 17:18:21 -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))))))
|
(update :height (comp inc inc))))))
|
||||||
|
|
||||||
(defn selrect->areas [bounds selrect]
|
(defn selrect->areas [bounds selrect]
|
||||||
(let [make-selrect
|
(let [{bound-x1 :x1 bound-x2 :x2 bound-y1 :y1 bound-y2 :y2} bounds
|
||||||
(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
|
|
||||||
{sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2} selrect]
|
{sr-x1 :x1 sr-x2 :x2 sr-y1 :y1 sr-y2 :y2} selrect]
|
||||||
{:left (make-selrect bound-x1 sr-y1 sr-x1 sr-y2)
|
{:left (gpr/corners->selrect bound-x1 sr-y1 sr-x1 sr-y2)
|
||||||
:top (make-selrect sr-x1 bound-y1 sr-x2 sr-y1)
|
:top (gpr/corners->selrect sr-x1 bound-y1 sr-x2 sr-y1)
|
||||||
:right (make-selrect sr-x2 sr-y1 bound-x2 sr-y2)
|
:right (gpr/corners->selrect sr-x2 sr-y1 bound-x2 sr-y2)
|
||||||
:bottom (make-selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
|
:bottom (gpr/corners->selrect sr-x1 sr-y2 sr-x2 bound-y2)}))
|
||||||
|
|
||||||
(defn distance-selrect [selrect other]
|
(defn distance-selrect [selrect other]
|
||||||
(let [{:keys [x1 y1]} other
|
(let [{:keys [x1 y1]} other
|
||||||
|
@ -161,6 +149,7 @@
|
||||||
(dm/export gpr/contains-selrect?)
|
(dm/export gpr/contains-selrect?)
|
||||||
(dm/export gpr/contains-point?)
|
(dm/export gpr/contains-point?)
|
||||||
(dm/export gpr/close-selrect?)
|
(dm/export gpr/close-selrect?)
|
||||||
|
(dm/export gpr/clip-selrect)
|
||||||
|
|
||||||
(dm/export gtr/move)
|
(dm/export gtr/move)
|
||||||
(dm/export gtr/absolute-move)
|
(dm/export gtr/absolute-move)
|
||||||
|
|
|
@ -212,9 +212,13 @@
|
||||||
(<= (:y2 sr2) (:y2 sr1))))
|
(<= (:y2 sr2) (:y2 sr1))))
|
||||||
|
|
||||||
(defn corners->selrect
|
(defn corners->selrect
|
||||||
[p1 p2]
|
([p1 p2]
|
||||||
(let [xp1 (:x p1)
|
(corners->selrect (:x p1) (:y p1) (:x p2) (:y p2)))
|
||||||
xp2 (:x p2)
|
([xp1 yp1 xp2 yp2]
|
||||||
yp1 (:y p1)
|
(make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2)))))
|
||||||
yp2 (:y p2)]
|
|
||||||
(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-lt (fn [other] (sr-distance coord (:selrect other) selrect))
|
||||||
dist-gt (fn [other] (sr-distance coord selrect (:selrect other)))
|
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
|
;; Calculates the distance between all the shapes given as argument
|
||||||
inner-distance
|
inner-distance
|
||||||
(fn [selrects]
|
(fn [selrects]
|
||||||
|
@ -164,14 +153,6 @@
|
||||||
#(overlap? coord %1 %2)
|
#(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
|
;; Distance between the elements in an area, these are the snap
|
||||||
;; candidates to either side
|
;; candidates to either side
|
||||||
lt-cand (inner-distance (mapv :selrect shapes-lt))
|
lt-cand (inner-distance (mapv :selrect shapes-lt))
|
||||||
|
@ -182,17 +163,46 @@
|
||||||
lt-dist (into #{} (map dist-lt) shapes-lt)
|
lt-dist (into #{} (map dist-lt) shapes-lt)
|
||||||
gt-dist (into #{} (map dist-gt) shapes-gt)
|
gt-dist (into #{} (map dist-gt) shapes-gt)
|
||||||
|
|
||||||
;; Calculate the snaps, we need to reverse depending on area
|
get-side-snaps
|
||||||
lt-snap (d/join lt-cand lt-dist -)
|
(fn [candidates distances]
|
||||||
gt-snap (d/join gt-dist gt-cand -)
|
;; 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
|
get-middle-snaps
|
||||||
between-snap (->> (d/join shapes-lt shapes-gt)
|
(fn [lt-dist gt-dist]
|
||||||
(map between-snap))
|
(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
|
;; Search the minimum snap
|
||||||
snap-list (d/concat-vec lt-snap gt-snap between-snap)
|
snap-list (d/concat-vec lt-snap gt-snap md-snap)
|
||||||
min-snap (reduce best-snap ##Inf snap-list)]
|
|
||||||
|
min-snap (reduce min ##Inf snap-list)]
|
||||||
|
|
||||||
(if (d/num? min-snap) [0 min-snap] nil)))
|
(if (d/num? min-snap) [0 min-snap] nil)))
|
||||||
|
|
||||||
|
@ -202,14 +212,14 @@
|
||||||
(calculate-snap coord selrect shapes-lt shapes-gt zoom)))))
|
(calculate-snap coord selrect shapes-lt shapes-gt zoom)))))
|
||||||
|
|
||||||
(defn select-shapes-area
|
(defn select-shapes-area
|
||||||
[page-id shapes objects area-selrect]
|
[page-id frame-id selected objects area]
|
||||||
(->> (uw/ask! {:cmd :selection/query
|
(->> (uw/ask! {:cmd :selection/query
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:frame-id (->> shapes first :frame-id)
|
:frame-id frame-id
|
||||||
:include-frames? true
|
:include-frames? true
|
||||||
:rect area-selrect})
|
:rect area})
|
||||||
(rx/map #(cph/clean-loops objects %))
|
(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) %))))
|
(rx/map #(map (d/getf objects) %))))
|
||||||
|
|
||||||
(defn closest-distance-snap
|
(defn closest-distance-snap
|
||||||
|
@ -220,9 +230,14 @@
|
||||||
(->> (rx/of (vector frame selrect))
|
(->> (rx/of (vector frame selrect))
|
||||||
(rx/merge-map
|
(rx/merge-map
|
||||||
(fn [[frame selrect]]
|
(fn [[frame selrect]]
|
||||||
(let [areas (->> (gsh/selrect->areas (or (:selrect frame)
|
(let [vbox (gsh/rect->selrect @refs/vbox)
|
||||||
(gsh/rect->selrect @refs/vbox)) selrect)
|
frame-id (->> shapes first :frame-id)
|
||||||
(d/mapm #(select-shapes-area page-id shapes objects %2)))
|
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-x (search-snap-distance selrect :x (:left areas) (:right areas) zoom)
|
||||||
snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas) zoom)]
|
snap-y (search-snap-distance selrect :y (:top areas) (:bottom areas) zoom)]
|
||||||
(rx/combine-latest snap-x snap-y))))
|
(rx/combine-latest snap-x snap-y))))
|
||||||
|
|
|
@ -9,11 +9,10 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages.helpers :as cph]
|
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.snap :as ams]
|
||||||
[app.main.ui.formats :as fmt]
|
[app.main.ui.formats :as fmt]
|
||||||
[app.main.worker :as uw]
|
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
|
@ -152,7 +151,7 @@
|
||||||
check-in-set
|
check-in-set
|
||||||
(fn [value number-set]
|
(fn [value number-set]
|
||||||
(->> 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)
|
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter)
|
||||||
|
|
||||||
|
@ -218,21 +217,16 @@
|
||||||
(fn [[selrect selected frame]]
|
(fn [[selrect selected frame]]
|
||||||
(let [lt-side (if (= coord :x) :left :top)
|
(let [lt-side (if (= coord :x) :left :top)
|
||||||
gt-side (if (= coord :x) :right :bottom)
|
gt-side (if (= coord :x) :right :bottom)
|
||||||
container-selrec (or (:selrect frame)
|
|
||||||
(gsh/rect->selrect @refs/vbox))
|
vbox (gsh/rect->selrect @refs/vbox)
|
||||||
areas (gsh/selrect->areas container-selrec selrect)
|
areas (gsh/selrect->areas
|
||||||
|
(or (gsh/clip-selrect (:selrect frame) vbox) vbox)
|
||||||
|
selrect)
|
||||||
|
|
||||||
query-side (fn [side]
|
query-side (fn [side]
|
||||||
(let [rect (get areas side)]
|
(let [rect (get areas side)]
|
||||||
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
||||||
(->> (uw/ask! {:cmd :selection/query
|
(ams/select-shapes-area page-id (:id frame) selected @refs/workspace-page-objects rect)
|
||||||
: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)))))
|
|
||||||
(rx/of nil))))]
|
(rx/of nil))))]
|
||||||
(rx/combine-latest (query-side lt-side)
|
(rx/combine-latest (query-side lt-side)
|
||||||
(query-side gt-side))))
|
(query-side gt-side))))
|
||||||
|
|
Loading…
Add table
Reference in a new issue