0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 23:31:21 -05:00

Improve performance of z-index update

This commit is contained in:
alonso.torres 2021-05-03 14:20:19 +02:00 committed by Andrey Antukh
parent 285a0d5f47
commit 32b623e82b
4 changed files with 94 additions and 54 deletions

View file

@ -67,6 +67,7 @@
;; Indices
(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-parent-index)
(d/export indices/create-mask-index)

View file

@ -9,43 +9,70 @@
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[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
"Given a collection of shapes calculates their z-index. Greater index
means is displayed over other shapes with less index."
[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)
(loop [current (peek root-children)
pending (pop root-children)
current-idx (+ (count objects) num-frames -1)
z-index (transient {})]
(let [frames (helpers/select-frames objects)
z-index (calculate-frame-z-index {} uuid/zero objects)]
(->> frames
(map :id)
(reduce #(calculate-frame-z-index %1 %2 objects) z-index))))
(let [children (get-in objects [current :shapes])
assigned? (contains? z-index current)
is-frame? (is-frame? current)
(defn update-z-index
"Updates the z-index given a set of ids to change and the old and new objects
representations"
[z-index changed-ids old-objects new-objects]
pending (cond
(not is-frame?)
(d/concat pending children)
(let [old-frames (into #{} (map #(get-in old-objects [% :frame-id])) changed-ids)
new-frames (into #{} (map #(get-in new-objects [% :frame-id])) changed-ids)
(not assigned?)
(d/concat pending [current] children)
changed-frames (set/union old-frames new-frames)
:else
pending)]
frames (->> (helpers/select-frames new-objects)
(map :id)
(filter #(contains? changed-frames %)))
(if (empty? pending)
(-> (assoc! z-index current current-idx)
(persistent!))
(recur (peek pending)
(pop pending)
(dec current-idx)
(assoc! z-index current current-idx))))))))
z-index (calculate-frame-z-index z-index uuid/zero new-objects)]
(->> frames
(reduce #(calculate-frame-z-index %1 %2 new-objects) z-index))))
(defn generate-child-parent-index
[objects]

View file

@ -111,7 +111,7 @@ goog.scope(function() {
// Tree implementation functions
function isRed(branch) {
return branch !== null && branch.color === Color.RED;
return branch && branch.color === Color.RED;
}
// Insert recursively in the tree

View file

@ -20,41 +20,44 @@
(defonce state (l/atom {}))
(defn- index-object
[objects z-index parents-index masks-index index obj]
(let [{:keys [x y width height]} (:selrect obj)
shape-bound #js {:x x :y y :width width :height height}
(defn index-shape
[objects parents-index masks-index]
(fn [index shape]
(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))
masks (get masks-index (:id obj))
z (get z-index (:id obj))
parents (get parents-index (:id shape))
masks (get masks-index (:id shape))
frame (when (and (not= :frame (:type obj))
(not= (:frame-id obj) uuid/zero))
(get objects (:frame-id obj)))]
(qdt/insert index
(:id obj)
shape-bound
(assoc obj :frame frame :masks masks :parents parents :z z))))
frame (when (and (not= :frame (:type shape))
(not= (:frame-id shape) uuid/zero))
(get objects (:frame-id shape)))]
(qdt/insert index
(:id shape)
shape-bound
(assoc shape :frame frame :masks masks :parents parents)))))
(defn- create-index
[objects]
(let [shapes (-> objects (dissoc uuid/zero) (vals))
z-index (cp/calculate-z-index objects)
parents-index (cp/generate-child-all-parents-index objects)
masks-index (cp/create-mask-index objects parents-index)
bounds (gsh/selection-rect shapes)
bounds #js {:x (:x bounds)
:y (:y bounds)
:width (:width bounds)
:height (:height bounds)}]
:height (:height bounds)}
(reduce (partial index-object objects z-index parents-index masks-index)
(qdt/create bounds)
shapes)))
index (reduce (index-shape objects parents-index masks-index)
(qdt/create bounds)
shapes)
z-index (cp/calculate-z-index objects)]
{:index index :z-index z-index}))
(defn- update-index
[index old-objects new-objects]
[{index :index z-index :z-index} old-objects new-objects]
(let [changes? (fn [id]
(not= (get old-objects id)
@ -66,18 +69,21 @@
(keys new-objects)))
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)
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)
new-index
shapes)))
index (reduce (index-shape new-objects parents-index masks-index)
new-index
shapes)
z-index (cp/update-z-index z-index changed-ids old-objects new-objects)]
{:index index :z-index z-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))
(es6-iterator-seq))
@ -102,6 +108,11 @@
(some (comp not overlaps?))
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
matching-shapes
(into []
@ -109,7 +120,8 @@
(filter match-criteria?)
(filter (comp overlaps? :frame))
(filter (comp overlaps-masks? :masks))
(filter overlaps?))
(filter overlaps?)
(map add-z-index))
result)
keyfn (if reverse? (comp - :z) :z)]