0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-22 14:39:45 -05:00

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)
: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
(defmethod print-method clojure.lang.PersistentQueue [q, w]
;; Overload the printer for queues so they look like fish

View file

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

View file

@ -6,6 +6,7 @@
(ns app.common.files.libraries-helpers
(:require
[app.common.data :as d]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[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
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]
(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]
(if (and (= (count shapes) 1)
(or (and (= (:type (first shapes)) :group) (not components-v2))
(= (:type (first shapes)) :frame))
(not (ctk/instance-head? (first shapes))))
[(first shapes)
(if (and (= shapes-count 1)
(or (and (cfh/group-shape? first-shape)
(not components-v2))
(cfh/frame-shape? first-shape))
(not (ctk/instance-head? first-shape)))
[first-shape
(-> (pcb/empty-changes it page-id)
(pcb/with-objects objects))
(:shapes (first shapes))]
(:shapes first-shape)]
(let [root-name (if (= 1 (count shapes))
(:name (first shapes))
(let [root-name (if (= 1 shapes-count)
(:name first-shape)
"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
objects ; to avoid a circular dependence
page-id
shapes
root-name
(not (ctk/instance-head? (first shapes))))
(not (ctk/instance-head? first-shape)))
(prepare-create-board changes
(uuid/next)
(:parent-id (first shapes))
(:parent-id first-shape)
objects
(map :id shapes)
shape-ids
nil
root-name
true))]
[root changes (map :id shapes)]))
[root changes shape-ids]))
changes
(cond-> changes
@ -79,8 +89,7 @@
(pcb/update-shapes
(:shapes root)
(fn [shape]
(-> shape
(assoc :constraints-h :scale :constraints-v :scale)))))
(assoc shape :constraints-h :scale :constraints-v :scale))))
objects' (assoc objects (:id root) root)

View file

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

View file

@ -72,13 +72,15 @@
(watch [it state _]
(let [page-id (:current-page-id state)
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)
(pcb/with-objects objects))
changes (cfsh/prepare-move-shapes-into-frame changes
frame-id
shapes
objects)]
changes (cfsh/prepare-move-shapes-into-frame changes frame-id shapes objects)]
(if (some? changes)
(rx/of (dch/commit-changes changes))
(rx/empty))))))