mirror of
https://github.com/penpot/penpot.git
synced 2025-05-26 04:46:11 +02:00
⚡ Improve performance of z-index update
This commit is contained in:
parent
285a0d5f47
commit
32b623e82b
4 changed files with 94 additions and 54 deletions
|
@ -67,6 +67,7 @@
|
||||||
|
|
||||||
;; Indices
|
;; Indices
|
||||||
(d/export indices/calculate-z-index)
|
(d/export indices/calculate-z-index)
|
||||||
|
(d/export indices/update-z-index)
|
||||||
(d/export indices/generate-child-all-parents-index)
|
(d/export indices/generate-child-all-parents-index)
|
||||||
(d/export indices/generate-child-parent-index)
|
(d/export indices/generate-child-parent-index)
|
||||||
(d/export indices/create-mask-index)
|
(d/export indices/create-mask-index)
|
||||||
|
|
|
@ -9,43 +9,70 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages.helpers :as helpers]
|
[app.common.pages.helpers :as helpers]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]
|
||||||
|
[clojure.set :as set]))
|
||||||
|
|
||||||
|
(defn calculate-frame-z-index [z-index frame-id objects]
|
||||||
|
(let [is-frame? (fn [id] (= :frame (get-in objects [id :type])))
|
||||||
|
frame-shapes (->> objects (vals) (filterv #(= (:frame-id %) frame-id)))
|
||||||
|
children (or (get-in objects [frame-id :shapes]) [])]
|
||||||
|
|
||||||
|
(if (empty? children)
|
||||||
|
z-index
|
||||||
|
|
||||||
|
(loop [current (peek children)
|
||||||
|
pending (pop children)
|
||||||
|
current-idx (count frame-shapes)
|
||||||
|
z-index z-index]
|
||||||
|
|
||||||
|
(let [children (get-in objects [current :shapes])
|
||||||
|
is-frame? (is-frame? current)
|
||||||
|
pending (if (not is-frame?)
|
||||||
|
(d/concat pending children)
|
||||||
|
pending)]
|
||||||
|
|
||||||
|
(if (empty? pending)
|
||||||
|
(-> z-index
|
||||||
|
(assoc current current-idx))
|
||||||
|
|
||||||
|
(recur (peek pending)
|
||||||
|
(pop pending)
|
||||||
|
(dec current-idx)
|
||||||
|
(assoc z-index current current-idx))))))))
|
||||||
|
|
||||||
|
;; The z-index is really calculated per-frame. Every frame will have its own
|
||||||
|
;; internal z-index. To calculate the "final" z-index we add the shape z-index with
|
||||||
|
;; the z-index of its frame. This way we can update the z-index per frame without
|
||||||
|
;; the need of recalculate all the frames
|
||||||
(defn calculate-z-index
|
(defn calculate-z-index
|
||||||
"Given a collection of shapes calculates their z-index. Greater index
|
"Given a collection of shapes calculates their z-index. Greater index
|
||||||
means is displayed over other shapes with less index."
|
means is displayed over other shapes with less index."
|
||||||
[objects]
|
[objects]
|
||||||
(let [is-frame? (fn [id] (= :frame (get-in objects [id :type])))
|
|
||||||
root-children (or (get-in objects [uuid/zero :shapes]) [])
|
|
||||||
num-frames (->> root-children (filterv is-frame?) count)]
|
|
||||||
|
|
||||||
(when-not (empty? root-children)
|
(let [frames (helpers/select-frames objects)
|
||||||
(loop [current (peek root-children)
|
z-index (calculate-frame-z-index {} uuid/zero objects)]
|
||||||
pending (pop root-children)
|
(->> frames
|
||||||
current-idx (+ (count objects) num-frames -1)
|
(map :id)
|
||||||
z-index (transient {})]
|
(reduce #(calculate-frame-z-index %1 %2 objects) z-index))))
|
||||||
|
|
||||||
(let [children (get-in objects [current :shapes])
|
(defn update-z-index
|
||||||
assigned? (contains? z-index current)
|
"Updates the z-index given a set of ids to change and the old and new objects
|
||||||
is-frame? (is-frame? current)
|
representations"
|
||||||
|
[z-index changed-ids old-objects new-objects]
|
||||||
|
|
||||||
pending (cond
|
(let [old-frames (into #{} (map #(get-in old-objects [% :frame-id])) changed-ids)
|
||||||
(not is-frame?)
|
new-frames (into #{} (map #(get-in new-objects [% :frame-id])) changed-ids)
|
||||||
(d/concat pending children)
|
|
||||||
|
|
||||||
(not assigned?)
|
changed-frames (set/union old-frames new-frames)
|
||||||
(d/concat pending [current] children)
|
|
||||||
|
|
||||||
:else
|
frames (->> (helpers/select-frames new-objects)
|
||||||
pending)]
|
(map :id)
|
||||||
|
(filter #(contains? changed-frames %)))
|
||||||
|
|
||||||
(if (empty? pending)
|
z-index (calculate-frame-z-index z-index uuid/zero new-objects)]
|
||||||
(-> (assoc! z-index current current-idx)
|
|
||||||
(persistent!))
|
(->> frames
|
||||||
(recur (peek pending)
|
(reduce #(calculate-frame-z-index %1 %2 new-objects) z-index))))
|
||||||
(pop pending)
|
|
||||||
(dec current-idx)
|
|
||||||
(assoc! z-index current current-idx))))))))
|
|
||||||
|
|
||||||
(defn generate-child-parent-index
|
(defn generate-child-parent-index
|
||||||
[objects]
|
[objects]
|
||||||
|
|
|
@ -111,7 +111,7 @@ goog.scope(function() {
|
||||||
// Tree implementation functions
|
// Tree implementation functions
|
||||||
|
|
||||||
function isRed(branch) {
|
function isRed(branch) {
|
||||||
return branch !== null && branch.color === Color.RED;
|
return branch && branch.color === Color.RED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert recursively in the tree
|
// Insert recursively in the tree
|
||||||
|
|
|
@ -20,41 +20,44 @@
|
||||||
|
|
||||||
(defonce state (l/atom {}))
|
(defonce state (l/atom {}))
|
||||||
|
|
||||||
(defn- index-object
|
(defn index-shape
|
||||||
[objects z-index parents-index masks-index index obj]
|
[objects parents-index masks-index]
|
||||||
(let [{:keys [x y width height]} (:selrect obj)
|
(fn [index shape]
|
||||||
shape-bound #js {:x x :y y :width width :height height}
|
(let [{:keys [x y width height]} (:selrect shape)
|
||||||
|
shape-bound #js {:x x :y y :width width :height height}
|
||||||
|
|
||||||
parents (get parents-index (:id obj))
|
parents (get parents-index (:id shape))
|
||||||
masks (get masks-index (:id obj))
|
masks (get masks-index (:id shape))
|
||||||
z (get z-index (:id obj))
|
|
||||||
|
|
||||||
frame (when (and (not= :frame (:type obj))
|
frame (when (and (not= :frame (:type shape))
|
||||||
(not= (:frame-id obj) uuid/zero))
|
(not= (:frame-id shape) uuid/zero))
|
||||||
(get objects (:frame-id obj)))]
|
(get objects (:frame-id shape)))]
|
||||||
(qdt/insert index
|
(qdt/insert index
|
||||||
(:id obj)
|
(:id shape)
|
||||||
shape-bound
|
shape-bound
|
||||||
(assoc obj :frame frame :masks masks :parents parents :z z))))
|
(assoc shape :frame frame :masks masks :parents parents)))))
|
||||||
|
|
||||||
(defn- create-index
|
(defn- create-index
|
||||||
[objects]
|
[objects]
|
||||||
(let [shapes (-> objects (dissoc uuid/zero) (vals))
|
(let [shapes (-> objects (dissoc uuid/zero) (vals))
|
||||||
z-index (cp/calculate-z-index objects)
|
|
||||||
parents-index (cp/generate-child-all-parents-index objects)
|
parents-index (cp/generate-child-all-parents-index objects)
|
||||||
masks-index (cp/create-mask-index objects parents-index)
|
masks-index (cp/create-mask-index objects parents-index)
|
||||||
bounds (gsh/selection-rect shapes)
|
bounds (gsh/selection-rect shapes)
|
||||||
bounds #js {:x (:x bounds)
|
bounds #js {:x (:x bounds)
|
||||||
:y (:y bounds)
|
:y (:y bounds)
|
||||||
:width (:width bounds)
|
:width (:width bounds)
|
||||||
:height (:height bounds)}]
|
:height (:height bounds)}
|
||||||
|
|
||||||
(reduce (partial index-object objects z-index parents-index masks-index)
|
index (reduce (index-shape objects parents-index masks-index)
|
||||||
(qdt/create bounds)
|
(qdt/create bounds)
|
||||||
shapes)))
|
shapes)
|
||||||
|
|
||||||
|
z-index (cp/calculate-z-index objects)]
|
||||||
|
|
||||||
|
{:index index :z-index z-index}))
|
||||||
|
|
||||||
(defn- update-index
|
(defn- update-index
|
||||||
[index old-objects new-objects]
|
[{index :index z-index :z-index} old-objects new-objects]
|
||||||
|
|
||||||
(let [changes? (fn [id]
|
(let [changes? (fn [id]
|
||||||
(not= (get old-objects id)
|
(not= (get old-objects id)
|
||||||
|
@ -66,18 +69,21 @@
|
||||||
(keys new-objects)))
|
(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?)))
|
||||||
z-index (cp/calculate-z-index new-objects)
|
|
||||||
parents-index (cp/generate-child-all-parents-index new-objects shapes)
|
parents-index (cp/generate-child-all-parents-index new-objects shapes)
|
||||||
masks-index (cp/create-mask-index new-objects parents-index)
|
masks-index (cp/create-mask-index new-objects parents-index)
|
||||||
|
|
||||||
new-index (qdt/remove-all index changed-ids)]
|
new-index (qdt/remove-all index changed-ids)
|
||||||
|
|
||||||
(reduce (partial index-object new-objects z-index parents-index masks-index)
|
index (reduce (index-shape new-objects parents-index masks-index)
|
||||||
new-index
|
new-index
|
||||||
shapes)))
|
shapes)
|
||||||
|
|
||||||
|
z-index (cp/update-z-index z-index changed-ids old-objects new-objects)]
|
||||||
|
|
||||||
|
{:index index :z-index z-index}))
|
||||||
|
|
||||||
(defn- query-index
|
(defn- query-index
|
||||||
[index rect frame-id include-frames? include-groups? disabled-masks reverse?]
|
[{index :index z-index :z-index} rect frame-id include-frames? include-groups? disabled-masks reverse?]
|
||||||
(let [result (-> (qdt/search index (clj->js rect))
|
(let [result (-> (qdt/search index (clj->js rect))
|
||||||
(es6-iterator-seq))
|
(es6-iterator-seq))
|
||||||
|
|
||||||
|
@ -102,6 +108,11 @@
|
||||||
(some (comp not overlaps?))
|
(some (comp not overlaps?))
|
||||||
not))
|
not))
|
||||||
|
|
||||||
|
add-z-index
|
||||||
|
(fn [{:keys [id frame-id] :as shape}]
|
||||||
|
(assoc shape :z (+ (get z-index id)
|
||||||
|
(get z-index frame-id 0))))
|
||||||
|
|
||||||
;; Shapes after filters of overlapping and criteria
|
;; Shapes after filters of overlapping and criteria
|
||||||
matching-shapes
|
matching-shapes
|
||||||
(into []
|
(into []
|
||||||
|
@ -109,7 +120,8 @@
|
||||||
(filter match-criteria?)
|
(filter match-criteria?)
|
||||||
(filter (comp overlaps? :frame))
|
(filter (comp overlaps? :frame))
|
||||||
(filter (comp overlaps-masks? :masks))
|
(filter (comp overlaps-masks? :masks))
|
||||||
(filter overlaps?))
|
(filter overlaps?)
|
||||||
|
(map add-z-index))
|
||||||
result)
|
result)
|
||||||
|
|
||||||
keyfn (if reverse? (comp - :z) :z)]
|
keyfn (if reverse? (comp - :z) :z)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue