mirror of
https://github.com/penpot/penpot.git
synced 2025-05-23 17:36:10 +02:00
⚡ Improve selection performance
This commit is contained in:
parent
b05908a760
commit
fb9b023fae
6 changed files with 76 additions and 36 deletions
|
@ -149,6 +149,7 @@
|
||||||
(d/export gpr/points->rect)
|
(d/export gpr/points->rect)
|
||||||
(d/export gpr/center->rect)
|
(d/export gpr/center->rect)
|
||||||
(d/export gpr/join-rects)
|
(d/export gpr/join-rects)
|
||||||
|
(d/export gpr/contains-selrect?)
|
||||||
|
|
||||||
(d/export gtr/move)
|
(d/export gtr/move)
|
||||||
(d/export gtr/absolute-move)
|
(d/export gtr/absolute-move)
|
||||||
|
|
|
@ -121,3 +121,11 @@
|
||||||
(or (< px x2) (s= px x2))
|
(or (< px x2) (s= px x2))
|
||||||
(or (> py y1) (s= py y1))
|
(or (> py y1) (s= py y1))
|
||||||
(or (< py y2) (s= py y2)))))
|
(or (< py y2) (s= py y2)))))
|
||||||
|
|
||||||
|
(defn contains-selrect?
|
||||||
|
"Check if a selrect sr2 is contained inside sr1"
|
||||||
|
[sr1 sr2]
|
||||||
|
(and (>= (:x1 sr2) (:x1 sr1))
|
||||||
|
(<= (:x2 sr2) (:x2 sr1))
|
||||||
|
(>= (:y1 sr2) (:y1 sr1))
|
||||||
|
(<= (:y2 sr2) (:y2 sr1))))
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
(->> move-stream
|
(->> move-stream
|
||||||
;; When transforming shapes we stop querying the worker
|
;; When transforming shapes we stop querying the worker
|
||||||
(rx/filter #(not (some? (mf/ref-val transform-ref))))
|
(rx/filter #(not (some? (mf/ref-val transform-ref))))
|
||||||
(rx/switch-map query-point))
|
(rx/merge-map query-point))
|
||||||
|
|
||||||
(->> move-stream
|
(->> move-stream
|
||||||
;; When transforming shapes we stop querying the worker
|
;; When transforming shapes we stop querying the worker
|
||||||
|
|
|
@ -90,6 +90,7 @@
|
||||||
"translate(" (* zoom x) ", " (* zoom y) ")")))
|
"translate(" (* zoom x) ", " (* zoom y) ")")))
|
||||||
|
|
||||||
(mf/defc frame-title
|
(mf/defc frame-title
|
||||||
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
[{:keys [frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
||||||
(let [{:keys [width x y]} (gsh/transform-shape frame)
|
(let [{:keys [width x y]} (gsh/transform-shape frame)
|
||||||
label-pos (gpt/point x (- y (/ 10 zoom)))
|
label-pos (gpt/point x (- y (/ 10 zoom)))
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
(->> (:stream worker)
|
(->> (:stream worker)
|
||||||
(rx/filter #(= (:reply-to %) sender-id))
|
(rx/filter #(= (:reply-to %) sender-id))
|
||||||
(take-messages)
|
(take-messages)
|
||||||
|
(rx/filter (complement :dropped))
|
||||||
(rx/map handle-response)))
|
(rx/map handle-response)))
|
||||||
(rx/empty)))))
|
(rx/empty)))))
|
||||||
|
|
||||||
|
@ -91,9 +92,8 @@
|
||||||
worker))
|
worker))
|
||||||
|
|
||||||
(defn- handle-response
|
(defn- handle-response
|
||||||
[{:keys [payload error dropped]}]
|
[{:keys [payload error]}]
|
||||||
(when-not dropped
|
(if-let [{:keys [data message]} error]
|
||||||
(if-let [{:keys [data message]} error]
|
(throw (ex-info message data))
|
||||||
(throw (ex-info message data))
|
payload))
|
||||||
payload)))
|
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[okulary.core :as l]))
|
[okulary.core :as l]))
|
||||||
|
|
||||||
|
(def ^:const padding-percent 0.10)
|
||||||
|
|
||||||
(defonce state (l/atom {}))
|
(defonce state (l/atom {}))
|
||||||
|
|
||||||
(defn index-shape
|
(defn index-shape
|
||||||
|
@ -37,55 +39,71 @@
|
||||||
:clip-parents clip-parents
|
:clip-parents clip-parents
|
||||||
:parents parents)))))
|
:parents parents)))))
|
||||||
|
|
||||||
|
(defn objects-bounds
|
||||||
|
"Calculates the bounds of the quadtree given a objects map."
|
||||||
|
[objects]
|
||||||
|
(-> objects
|
||||||
|
(dissoc uuid/zero)
|
||||||
|
vals
|
||||||
|
gsh/selection-rect))
|
||||||
|
|
||||||
|
(defn add-padding-bounds
|
||||||
|
"Adds a padding to the bounds defined as a percent in the constant `padding-percent`.
|
||||||
|
For a value of 0.1 will add a 20% width increase (2 x padding)"
|
||||||
|
[bounds]
|
||||||
|
(let [width-pad (* (:width bounds) padding-percent)
|
||||||
|
height-pad (* (:height bounds) padding-percent)]
|
||||||
|
(-> bounds
|
||||||
|
(update :x - width-pad)
|
||||||
|
(update :x1 - width-pad)
|
||||||
|
(update :x2 + width-pad)
|
||||||
|
(update :y1 - height-pad)
|
||||||
|
(update :y2 + height-pad)
|
||||||
|
(update :width + width-pad width-pad)
|
||||||
|
(update :height + height-pad height-pad))))
|
||||||
|
|
||||||
(defn- create-index
|
(defn- create-index
|
||||||
[objects]
|
[objects]
|
||||||
(let [shapes (-> objects (dissoc uuid/zero) (vals))
|
(let [shapes (-> objects (dissoc uuid/zero) vals)
|
||||||
parents-index (cp/generate-child-all-parents-index objects)
|
parents-index (cp/generate-child-all-parents-index objects)
|
||||||
clip-parents-index (cp/create-clip-index objects parents-index)
|
clip-parents-index (cp/create-clip-index objects parents-index)
|
||||||
bounds #js {:x (int -0.5e7)
|
bounds (-> objects objects-bounds add-padding-bounds)
|
||||||
:y (int -0.5e7)
|
|
||||||
:width (int 1e7)
|
|
||||||
:height (int 1e7)}
|
|
||||||
|
|
||||||
index (reduce (index-shape objects parents-index clip-parents-index)
|
index (reduce (index-shape objects parents-index clip-parents-index)
|
||||||
(qdt/create bounds)
|
(qdt/create (clj->js bounds))
|
||||||
shapes)
|
shapes)
|
||||||
|
|
||||||
z-index (cp/calculate-z-index objects)]
|
z-index (cp/calculate-z-index objects)]
|
||||||
|
|
||||||
{:index index :z-index z-index}))
|
{:index index :z-index z-index :bounds bounds}))
|
||||||
|
|
||||||
(defn- update-index
|
(defn- update-index
|
||||||
[{index :index z-index :z-index :as data} old-objects new-objects]
|
[{index :index z-index :z-index :as data} old-objects new-objects]
|
||||||
|
|
||||||
(if (some? data)
|
(let [changes? (fn [id]
|
||||||
(let [changes? (fn [id]
|
(not= (get old-objects id)
|
||||||
(not= (get old-objects id)
|
(get new-objects id)))
|
||||||
(get new-objects id)))
|
|
||||||
|
|
||||||
changed-ids (into #{}
|
changed-ids (into #{}
|
||||||
(comp (filter #(not= % uuid/zero))
|
(comp (filter #(not= % uuid/zero))
|
||||||
(filter changes?)
|
(filter changes?)
|
||||||
(mapcat #(into [%] (cp/get-children % new-objects))))
|
(mapcat #(into [%] (cp/get-children % new-objects))))
|
||||||
(set/union (set (keys old-objects))
|
(set/union (set (keys old-objects))
|
||||||
(set (keys new-objects))))
|
(set (keys new-objects))))
|
||||||
|
|
||||||
shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?)))
|
shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?)))
|
||||||
parents-index (cp/generate-child-all-parents-index new-objects shapes)
|
parents-index (cp/generate-child-all-parents-index new-objects shapes)
|
||||||
clip-parents-index (cp/create-clip-index new-objects parents-index)
|
clip-parents-index (cp/create-clip-index new-objects parents-index)
|
||||||
|
|
||||||
new-index (qdt/remove-all index changed-ids)
|
new-index (qdt/remove-all index changed-ids)
|
||||||
|
|
||||||
index (reduce (index-shape new-objects parents-index clip-parents-index)
|
index (reduce (index-shape new-objects parents-index clip-parents-index)
|
||||||
new-index
|
new-index
|
||||||
shapes)
|
shapes)
|
||||||
|
|
||||||
z-index (cp/update-z-index z-index changed-ids old-objects new-objects)]
|
z-index (cp/update-z-index z-index changed-ids old-objects new-objects)]
|
||||||
|
|
||||||
{:index index :z-index z-index})
|
(assoc data :index index :z-index z-index)))
|
||||||
|
|
||||||
;; If not previous data. We need to create from scratch
|
|
||||||
(create-index new-objects)))
|
|
||||||
|
|
||||||
(defn- query-index
|
(defn- query-index
|
||||||
[{index :index z-index :z-index} rect frame-id full-frame? include-frames? clip-children? reverse?]
|
[{index :index z-index :z-index} rect frame-id full-frame? include-frames? clip-children? reverse?]
|
||||||
|
@ -154,7 +172,19 @@
|
||||||
|
|
||||||
(defmethod impl/handler :selection/update-index
|
(defmethod impl/handler :selection/update-index
|
||||||
[{:keys [page-id old-objects new-objects] :as message}]
|
[{:keys [page-id old-objects new-objects] :as message}]
|
||||||
(swap! state update page-id update-index old-objects new-objects)
|
(let [update-page-index
|
||||||
|
(fn [index]
|
||||||
|
(let [old-bounds (:bounds index)
|
||||||
|
new-bounds (objects-bounds new-objects)]
|
||||||
|
|
||||||
|
;; If the new bounds are contained within the old bounds we can
|
||||||
|
;; update the index.
|
||||||
|
;; Otherwise we need to re-create it
|
||||||
|
(if (and (some? index)
|
||||||
|
(gsh/contains-selrect? old-bounds new-bounds))
|
||||||
|
(update-index index old-objects new-objects)
|
||||||
|
(create-index new-objects))))]
|
||||||
|
(swap! state update page-id update-page-index))
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(defmethod impl/handler :selection/query
|
(defmethod impl/handler :selection/query
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue