Improve performance on creating component from graphic

About 25% speed improvement on average on single file migration process
This commit is contained in:
Andrey Antukh 2024-01-28 00:34:30 +01:00
parent c70acb1570
commit f104cc5477
5 changed files with 148 additions and 115 deletions

View file

@ -57,6 +57,14 @@
#?(:cljs (instance? lkm/LinkedMap o) #?(:cljs (instance? lkm/LinkedMap o)
:clj (instance? LinkedMap o))) :clj (instance? LinkedMap o)))
(defn vec2
"Creates a optimized vector compatible type of length 2 backed
internally with MapEntry impl because it has faster access method
for its fields."
[o1 o2]
#?(:clj (clojure.lang.MapEntry. o1 o2)
:cljs (cljs.core/->MapEntry o1 o2 nil)))
#?(:clj #?(:clj
(defmethod print-method clojure.lang.PersistentQueue [q, w] (defmethod print-method clojure.lang.PersistentQueue [q, w]
;; Overload the printer for queues so they look like fish ;; Overload the printer for queues so they look like fish

View file

@ -484,7 +484,7 @@
(letfn [(red-fn [cur-idx id] (letfn [(red-fn [cur-idx id]
(let [[prev-idx _] (first cur-idx) (let [[prev-idx _] (first cur-idx)
prev-idx (or prev-idx 0) prev-idx (or prev-idx 0)
cur-idx (conj cur-idx [(inc prev-idx) id])] cur-idx (conj cur-idx (d/vec2 (inc prev-idx) id))]
(rec-index cur-idx id))) (rec-index cur-idx id)))
(rec-index [cur-idx id] (rec-index [cur-idx id]
(let [object (get objects id)] (let [object (get objects id)]
@ -509,10 +509,11 @@
(defn order-by-indexed-shapes (defn order-by-indexed-shapes
[objects ids] [objects ids]
(let [ids (if (set? ids) ids (set ids))]
(->> (indexed-shapes objects) (->> (indexed-shapes objects)
(sort-by first) (filter (fn [o] (contains? ids (val o))))
(filter (comp (into #{} ids) second)) (sort-by key)
(map second))) (map val))))
(defn get-index-replacement (defn get-index-replacement
"Given a collection of shapes, calculate their positions "Given a collection of shapes, calculate their positions

View file

@ -6,6 +6,7 @@
(ns app.common.files.libraries-helpers (ns app.common.files.libraries-helpers
(:require (:require
[app.common.data :as d]
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
@ -37,41 +38,50 @@
use it as root. Otherwise, create a frame (v2) or group (v1) that contains all ids. Then, make a use it as root. Otherwise, create a frame (v2) or group (v1) that contains all ids. Then, make a
component with it, and link all shapes to their corresponding one in the component." component with it, and link all shapes to their corresponding one in the component."
[it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board] [it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board]
(let [changes (pcb/empty-changes it page-id) (let [changes (pcb/empty-changes it page-id)
shapes-count (count shapes)
first-shape (first shapes)
from-singe-frame?
(and (= 1 shapes-count)
(cfh/frame-shape? first-shape))
from-singe-frame? (and (= 1 (count shapes)) (-> shapes first cfh/frame-shape?))
[root changes old-root-ids] [root changes old-root-ids]
(if (and (= (count shapes) 1) (if (and (= shapes-count 1)
(or (and (= (:type (first shapes)) :group) (not components-v2)) (or (and (cfh/group-shape? first-shape)
(= (:type (first shapes)) :frame)) (not components-v2))
(not (ctk/instance-head? (first shapes)))) (cfh/frame-shape? first-shape))
(not (ctk/instance-head? first-shape)))
[(first shapes) [first-shape
(-> (pcb/empty-changes it page-id) (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)) (pcb/with-objects objects))
(:shapes (first shapes))] (:shapes first-shape)]
(let [root-name (if (= 1 (count shapes)) (let [root-name (if (= 1 shapes-count)
(:name (first shapes)) (:name first-shape)
"Component 1") "Component 1")
[root changes] (if-not components-v2 shape-ids (into (d/ordered-set) (map :id) shapes)
[root changes]
(if-not components-v2
(prepare-create-group it ; These functions needs to be passed as argument (prepare-create-group it ; These functions needs to be passed as argument
objects ; to avoid a circular dependence objects ; to avoid a circular dependence
page-id page-id
shapes shapes
root-name root-name
(not (ctk/instance-head? (first shapes)))) (not (ctk/instance-head? first-shape)))
(prepare-create-board changes (prepare-create-board changes
(uuid/next) (uuid/next)
(:parent-id (first shapes)) (:parent-id first-shape)
objects objects
(map :id shapes) shape-ids
nil nil
root-name root-name
true))] true))]
[root changes (map :id shapes)])) [root changes shape-ids]))
changes changes
(cond-> changes (cond-> changes
@ -79,8 +89,7 @@
(pcb/update-shapes (pcb/update-shapes
(:shapes root) (:shapes root)
(fn [shape] (fn [shape]
(-> shape (assoc shape :constraints-h :scale :constraints-v :scale))))
(assoc :constraints-h :scale :constraints-v :scale)))))
objects' (assoc objects (:id root) root) objects' (assoc objects (:id root) root)

View file

@ -39,16 +39,17 @@
(defn prepare-move-shapes-into-frame (defn prepare-move-shapes-into-frame
[changes frame-id shapes objects] [changes frame-id shapes objects]
(let [ordered-indexes (cfh/order-by-indexed-shapes objects shapes) (let [parent-id (dm/get-in objects [frame-id :parent-id])
parent-id (get-in objects [frame-id :parent-id]) shapes (remove #(= % parent-id) shapes)
ordered-indexes (->> ordered-indexes (remove #(= % parent-id))) to-move (->> shapes
to-move-shapes (map (d/getf objects) ordered-indexes)] (map (d/getf objects))
(if (d/not-empty? to-move-shapes) (not-empty))]
(if to-move
(-> changes (-> changes
(cond-> (not (ctl/any-layout? objects frame-id)) (cond-> (not (ctl/any-layout? objects frame-id))
(pcb/update-shapes ordered-indexes ctl/remove-layout-item-data)) (pcb/update-shapes shapes ctl/remove-layout-item-data))
(pcb/update-shapes ordered-indexes #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true))) (pcb/update-shapes shapes #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true)))
(pcb/change-parent frame-id to-move-shapes 0) (pcb/change-parent frame-id to-move 0)
(cond-> (ctl/grid-layout? objects frame-id) (cond-> (ctl/grid-layout? objects frame-id)
(-> (pcb/update-shapes [frame-id] ctl/assign-cells {:with-objects? true}) (-> (pcb/update-shapes [frame-id] ctl/assign-cells {:with-objects? true})
(pcb/reorder-grid-children [frame-id])))) (pcb/reorder-grid-children [frame-id]))))
@ -60,22 +61,31 @@
changes id parent-id objects selected index frame-name without-fill? nil)) changes id parent-id objects selected index frame-name without-fill? nil))
([changes id parent-id objects selected index frame-name without-fill? target-cell-id] ([changes id parent-id objects selected index frame-name without-fill? target-cell-id]
(let [selected-objs (map #(get objects %) selected) (when-let [selected-objs (->> selected
new-index (or index (map (d/getf objects))
(cfh/get-index-replacement selected objects))] (not-empty))]
(when (d/not-empty? selected)
(let [srect (gsh/shapes->rect selected-objs)
selected-id (first selected)
frame-id (dm/get-in objects [selected-id :frame-id]) (let [;; We calculate here the ordered selection because it is used
parent-id (or parent-id (dm/get-in objects [selected-id :parent-id])) ;; multiple times and this avoid the need of creating the index
;; manytimes for single operation.
selected' (cfh/order-by-indexed-shapes objects selected)
new-index (or index
(->> (first selected')
(cfh/get-position-on-parent objects)
(inc)))
srect (gsh/shapes->rect selected-objs)
selected-id (first selected)
selected-obj (get objects selected-id)
frame-id (get selected-obj :frame-id)
parent-id (or parent-id (get selected-obj :parent-id))
base-parent (get objects parent-id) base-parent (get objects parent-id)
layout-props layout-props
(when (and (= 1 (count selected)) (when (and (= 1 (count selected))
(ctl/any-layout? base-parent)) (ctl/any-layout? base-parent))
(let [shape (get objects selected-id)] (select-keys selected-obj ctl/layout-item-props))
(select-keys shape ctl/layout-item-props)))
target-cell-id target-cell-id
(if (and (nil? target-cell-id) (if (and (nil? target-cell-id)
@ -88,13 +98,15 @@
:id)) :id))
target-cell-id) target-cell-id)
attrs {:type :frame attrs
{:type :frame
:x (:x srect) :x (:x srect)
:y (:y srect) :y (:y srect)
:width (:width srect) :width (:width srect)
:height (:height srect)} :height (:height srect)}
shape (cts/setup-shape shape
(cts/setup-shape
(cond-> attrs (cond-> attrs
(some? id) (some? id)
(assoc :id id) (assoc :id id)
@ -113,13 +125,14 @@
(or (not= frame-id uuid/zero) without-fill?) (or (not= frame-id uuid/zero) without-fill?)
(assoc :fills [] :hide-in-viewer true))) (assoc :fills [] :hide-in-viewer true)))
shape (with-meta shape {:index new-index}) shape
(with-meta shape {:index new-index})
[shape changes] [shape changes]
(prepare-add-shape changes shape objects) (prepare-add-shape changes shape objects)
changes changes
(prepare-move-shapes-into-frame changes (:id shape) selected objects) (prepare-move-shapes-into-frame changes (:id shape) selected' objects)
changes changes
(cond-> changes (cond-> changes
@ -143,7 +156,7 @@
(pcb/reorder-grid-children [(:parent-id shape)])))] (pcb/reorder-grid-children [(:parent-id shape)])))]
[shape changes]))))) [shape changes]))))
(defn prepare-create-empty-artboard (defn prepare-create-empty-artboard

View file

@ -72,13 +72,15 @@
(watch [it state _] (watch [it state _]
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id) objects (wsh/lookup-page-objects state page-id)
shapes (->> shapes (remove #(dm/get-in objects [% :blocked]))) shapes (->> shapes
(remove #(dm/get-in objects [% :blocked]))
(cfh/order-by-indexed-shapes objects))
changes (-> (pcb/empty-changes it page-id) changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)) (pcb/with-objects objects))
changes (cfsh/prepare-move-shapes-into-frame changes
frame-id changes (cfsh/prepare-move-shapes-into-frame changes frame-id shapes objects)]
shapes
objects)]
(if (some? changes) (if (some? changes)
(rx/of (dch/commit-changes changes)) (rx/of (dch/commit-changes changes))
(rx/empty)))))) (rx/empty))))))