From b2e01cd52b3d756ad063bad51b340009ec518b7d Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 13 May 2021 15:02:06 +0200 Subject: [PATCH] :zap: Performance improvements --- common/app/common/geom/shapes/intersect.cljc | 12 +- docker/devenv/files/nginx.conf | 2 +- frontend/src/app/main/data/workspace.cljs | 14 +- .../src/app/main/data/workspace/changes.cljs | 167 ++++++------------ .../app/main/data/workspace/transforms.cljs | 62 ++++--- .../app/main/ui/workspace/viewport/utils.cljs | 2 +- frontend/src/app/worker/selection.cljs | 2 +- 7 files changed, 122 insertions(+), 139 deletions(-) diff --git a/common/app/common/geom/shapes/intersect.cljc b/common/app/common/geom/shapes/intersect.cljc index 0b6fbcd6f..2b55cb339 100644 --- a/common/app/common/geom/shapes/intersect.cljc +++ b/common/app/common/geom/shapes/intersect.cljc @@ -174,9 +174,17 @@ "Checks if the given rect overlaps with the path in any point" [shape rect] - (let [rect-points (gpr/rect->points rect) + (let [;; If paths are too complex the intersection is too expensive + ;; we fallback to check its bounding box otherwise the performance penalty + ;; is too big + ;; TODO: Look for ways to optimize this operation + simple? (> (count (:content shape)) 100) + + rect-points (gpr/rect->points rect) rect-lines (points->lines rect-points) - path-lines (gpp/path->lines shape) + path-lines (if simple? + (points->lines (:points shape)) + (gpp/path->lines shape)) start-point (-> shape :content (first) :params (gpt/point))] (or (is-point-inside-nonzero? (first rect-points) path-lines) diff --git a/docker/devenv/files/nginx.conf b/docker/devenv/files/nginx.conf index eefd05cd0..ee7e37bb0 100644 --- a/docker/devenv/files/nginx.conf +++ b/docker/devenv/files/nginx.conf @@ -46,7 +46,7 @@ http { listen 3449 default_server; server_name _; - client_max_body_size 5M; + client_max_body_size 20M; charset utf-8; proxy_http_version 1.1; diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 6a3ce495f..53097aac6 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1164,11 +1164,15 @@ (ptk/reify ::update-shape-flags ptk/WatchEvent (watch [it state stream] - (letfn [(update-fn [obj] - (cond-> obj - (boolean? blocked) (assoc :blocked blocked) - (boolean? hidden) (assoc :hidden hidden)))] - (rx/of (dch/update-shapes-recursive [id] update-fn)))))) + (let [update-fn + (fn [obj] + (cond-> obj + (boolean? blocked) (assoc :blocked blocked) + (boolean? hidden) (assoc :hidden hidden))) + + objects (wsh/lookup-page-objects state) + ids (d/concat [id] (cp/get-children id objects))] + (rx/of (dch/update-shapes ids update-fn)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 56c918f38..b7be2c2eb 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -11,6 +11,7 @@ [app.common.pages.spec :as spec] [app.common.spec :as us] [app.main.data.workspace.undo :as dwu] + [app.main.data.workspace.state-helpers :as wsh] [app.main.worker :as uw] [app.main.store :as st] [app.util.logging :as log] @@ -31,127 +32,75 @@ (def commit-changes? (ptk/type? ::commit-changes)) -(defn- generate-operations - ([ma mb] (generate-operations ma mb false)) - ([ma mb undo?] - (let [ops (let [ma-keys (set (keys ma)) - mb-keys (set (keys mb)) - added (set/difference mb-keys ma-keys) - removed (set/difference ma-keys mb-keys) - both (set/intersection ma-keys mb-keys)] - (d/concat - (mapv #(array-map :type :set :attr % :val (get mb %)) added) - (mapv #(array-map :type :set :attr % :val nil) removed) - (loop [items (seq both) - result []] - (if items - (let [k (first items) - vma (get ma k) - vmb (get mb k)] - (if (= vma vmb) - (recur (next items) result) - (recur (next items) - (conj result {:type :set - :attr k - :val vmb - :ignore-touched undo?})))) - result))))] - (if undo? - (conj ops {:type :set-touched :touched (:touched mb)}) - ops)))) +(defn- generate-operation + "Given an object old and new versions and an attribute will append into changes + the set and undo operations" + [changes attr old new] + (let [old-val (get old attr) + new-val (get new attr)] + (if (= old-val new-val) + changes + (-> changes + (update :rops conj {:type :set :attr attr :val new-val}) + (update :uops conj {:type :set :attr attr :val old-val :ignore-touched true}))))) + +(defn- update-shape-changes + "Calculate the changes and undos to be done when a function is applied to a + single object" + [changes page-id objects update-fn attrs id] + (let [old-obj (get objects id) + new-obj (update-fn old-obj) + + attrs (or attrs (d/concat #{} (keys old-obj) (keys new-obj))) + + {rops :rops uops :uops} + (reduce #(generate-operation %1 %2 old-obj new-obj) + {:rops [] :uops []} + attrs) + + uops (cond-> uops + (not (empty? uops)) + (conj {:type :set-touched :touched (:touched old-obj)})) + + change {:type :mod-obj :page-id page-id :id id}] + + (cond-> changes + (not (empty? rops)) + (update :rch conj (assoc change :operations rops)) + + (not (empty? uops)) + (update :uch conj (assoc change :operations uops))))) (defn update-shapes ([ids f] (update-shapes ids f nil)) - ([ids f {:keys [reg-objects? save-undo?] - :or {reg-objects? false save-undo? true}}] + ([ids f {:keys [reg-objects? save-undo? keys] + :or {reg-objects? false save-undo? true attrs nil}}] + (us/assert ::coll-of-uuid ids) (us/assert fn? f) + (ptk/reify ::update-shapes ptk/WatchEvent (watch [it state stream] (let [page-id (:current-page-id state) - objects (get-in state [:workspace-data :pages-index page-id :objects]) - reg-objects {:type :reg-objects :page-id page-id :shapes (vec ids)}] - (loop [ids (seq ids) - rch [] - uch []] - (if (nil? ids) - (rx/of (let [has-rch? (not (empty? rch)) - has-uch? (not (empty? uch)) - rch (cond-> rch (and has-rch? reg-objects?) (conj reg-objects)) - uch (cond-> uch (and has-rch? reg-objects?) (conj reg-objects))] - (when (and has-rch? has-uch?) - (commit-changes {:redo-changes rch - :undo-changes uch - :origin it - :save-undo? save-undo?})))) + objects (wsh/lookup-page-objects state) + reg-objects {:type :reg-objects :page-id page-id :shapes (vec ids)} - (let [id (first ids) - obj1 (get objects id) - obj2 (f obj1) - rch-operations (generate-operations obj1 obj2) - uch-operations (generate-operations obj2 obj1 true) - rchg {:type :mod-obj - :page-id page-id - :operations rch-operations - :id id} - uchg {:type :mod-obj - :page-id page-id - :operations uch-operations - :id id}] - (recur (next ids) - (if (empty? rch-operations) rch (conj rch rchg)) - (if (empty? uch-operations) uch (conj uch uchg))))))))))) + {redo-changes :rch undo-changes :uch} + (reduce #(update-shape-changes %1 page-id objects f keys %2) + {:rch [] :uch []} ids)] -(defn update-shapes-recursive - [ids f] - (us/assert ::coll-of-uuid ids) - (us/assert fn? f) - (letfn [(impl-get-children [objects id] - (cons id (cp/get-children id objects))) + (when-not (empty? redo-changes) + (let [redo-changes (cond-> redo-changes + reg-objects? (conj reg-objects)) - (impl-gen-changes [objects page-id ids] - (loop [sids (seq ids) - cids (seq (impl-get-children objects (first sids))) - rchanges [] - uchanges []] - (cond - (nil? sids) - [rchanges uchanges] + undo-changes (cond-> undo-changes + reg-objects? (conj reg-objects))] - (nil? cids) - (recur (next sids) - (seq (impl-get-children objects (first (next sids)))) - rchanges - uchanges) - - :else - (let [id (first cids) - obj1 (get objects id) - obj2 (f obj1) - rops (generate-operations obj1 obj2) - uops (generate-operations obj2 obj1 true) - rchg {:type :mod-obj - :page-id page-id - :operations rops - :id id} - uchg {:type :mod-obj - :page-id page-id - :operations uops - :id id}] - (recur sids - (next cids) - (conj rchanges rchg) - (conj uchanges uchg))))))] - (ptk/reify ::update-shapes-recursive - ptk/WatchEvent - (watch [it state stream] - (let [page-id (:current-page-id state) - objects (get-in state [:workspace-data :pages-index page-id :objects]) - [rchanges uchanges] (impl-gen-changes objects page-id (seq ids))] - (rx/of (commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it}))))))) + (rx/of (commit-changes {:redo-changes redo-changes + :undo-changes undo-changes + :origin it + :save-undo? save-undo?}))))))))) (defn update-indices [page-id changes] @@ -222,4 +171,4 @@ (when (and save-undo? (seq undo-changes)) (let [entry {:undo-changes undo-changes :redo-changes redo-changes}] - (rx/of (dwu/append-undo entry))))))))))) \ No newline at end of file + (rx/of (dwu/append-undo entry))))))))))) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index dd4171ab1..e2b3b99b8 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -320,8 +320,7 @@ (rx/map snap/correct-snap-point) (rx/map start-local-displacement)) - (rx/of (set-modifiers ids) - (apply-modifiers ids) + (rx/of (apply-modifiers ids {:set-modifiers? true}) (calculate-frame-for-move ids) (finish-transform))))))))) @@ -390,8 +389,7 @@ (rx/map start-local-displacement)) (rx/of (move-selected direction shift?))) - (rx/of (set-modifiers selected) - (apply-modifiers selected) + (rx/of (apply-modifiers selected {:set-modifiers? true}) (finish-transform)))) (rx/empty)))))) @@ -469,22 +467,46 @@ (rx/of (apply-modifiers ids))))))) (defn apply-modifiers - [ids] - (us/verify (s/coll-of uuid?) ids) - (ptk/reify ::apply-modifiers - ptk/WatchEvent - (watch [it state stream] - (let [objects (wsh/lookup-page-objects state) - children-ids (->> ids (mapcat #(cp/get-children % objects))) - ids-with-children (d/concat [] children-ids ids) - object-modifiers (get state :workspace-modifiers)] - (rx/of (dwu/start-undo-transaction) - (dch/update-shapes ids-with-children (fn [shape] - (-> shape - (merge (get object-modifiers (:id shape))) - (gsh/transform-shape))) {:reg-objects? true}) - (clear-local-transform) - (dwu/commit-undo-transaction)))))) + ([ids] + (apply-modifiers ids nil)) + + ([ids {:keys [set-modifiers?] + :or {set-modifiers? false}}] + (us/verify (s/coll-of uuid?) ids) + (ptk/reify ::apply-modifiers + ptk/WatchEvent + (watch [it state stream] + (let [objects (wsh/lookup-page-objects state) + children-ids (->> ids (mapcat #(cp/get-children % objects))) + ids-with-children (d/concat [] children-ids ids) + + state (if set-modifiers? + (ptk/update (set-modifiers ids) state) + state) + object-modifiers (get state :workspace-modifiers)] + + (rx/of (dwu/start-undo-transaction) + (dch/update-shapes + ids-with-children + (fn [shape] + (-> shape + (merge (get object-modifiers (:id shape))) + (gsh/transform-shape))) + {:reg-objects? true + ;; Attributes that can change in the transform. This way we don't have to check + ;; all the attributes + :attrs [:selrect :points + :x :y + :width :height + :content + :transform + :transform-inverse + :rotation + :flip-x + :flip-y] + }) + (clear-local-transform) + (dwu/commit-undo-transaction))))))) ;; --- Update Dimensions diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index a1e6f6a83..092da6bbc 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -24,7 +24,7 @@ (.-parentNode shape-node) (and (some? thumb-node) (= :frame type)) - thumb-node + (.-parentNode thumb-node) :else shape-node)] diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index b78f3c7f1..21667849b 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -122,9 +122,9 @@ (into [] (comp (map #(unchecked-get % "data")) (filter match-criteria?) + (filter overlaps?) (filter (comp overlaps? :frame)) (filter (comp overlaps-masks? :masks)) - (filter overlaps?) (map add-z-index)) result)