From 3ab67e454536751cf82dfb995776606bd08fa380 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 20 Jun 2023 11:47:19 +0200 Subject: [PATCH] :zap: Add lightweight optimization to modifiers handling Mainly using controlled internal mutation when is possible --- common/src/app/common/types/modifiers.cljc | 120 ++++++++++++++---- .../app/main/data/workspace/modifiers.cljs | 16 ++- 2 files changed, 103 insertions(+), 33 deletions(-) diff --git a/common/src/app/common/types/modifiers.cljc b/common/src/app/common/types/modifiers.cljc index 35c138739e..8db511edd0 100644 --- a/common/src/app/common/types/modifiers.cljc +++ b/common/src/app/common/types/modifiers.cljc @@ -19,8 +19,7 @@ [app.common.pages.helpers :as cph] [app.common.text :as txt] [app.common.types.shape.layout :as ctl] - #?(:cljs [cljs.core :as c] - :clj [clojure.core :as c]))) + [clojure.core :as c])) ;; --- Modifiers @@ -106,18 +105,17 @@ [property value] (StructureOperation. :change-property property value nil)) - ;; Private aux functions (defn- move-vec? [vector] - (or (not (mth/almost-zero? (dm/get-prop vector :x))) - (not (mth/almost-zero? (dm/get-prop vector :y))))) + (or (not ^boolean (mth/almost-zero? (dm/get-prop vector :x))) + (not ^boolean (mth/almost-zero? (dm/get-prop vector :y))))) (defn- resize-vec? [vector] - (or (not (mth/almost-zero? (- (dm/get-prop vector :x) 1))) - (not (mth/almost-zero? (- (dm/get-prop vector :y) 1))))) + (or (not ^boolean (mth/almost-zero? (- (dm/get-prop vector :x) 1))) + (not ^boolean (mth/almost-zero? (- (dm/get-prop vector :y) 1))))) (defn- mergeable-move? [op1 op2] @@ -128,22 +126,24 @@ (defn- mergeable-resize? [op1 op2] (let [type-op1 (dm/get-prop op1 :type) - transform-op1 (or (dm/get-prop op1 :transform) (gmt/matrix)) - transform-inv-op1 (or (dm/get-prop op1 :transform-inverse) (gmt/matrix)) + transform-op1 (d/nilv (dm/get-prop op1 :transform) gmt/base) + transform-inv-op1 (d/nilv (dm/get-prop op1 :transform-inverse) gmt/base) origin-op1 (dm/get-prop op1 :origin) type-op2 (dm/get-prop op2 :type) - transform-op2 (or (dm/get-prop op2 :transform) (gmt/matrix)) - transform-inv-op2 (or (dm/get-prop op2 :transform-inverse) (gmt/matrix)) + transform-op2 (d/nilv (dm/get-prop op2 :transform) gmt/base) + transform-inv-op2 (d/nilv (dm/get-prop op2 :transform-inverse) gmt/base) origin-op2 (dm/get-prop op2 :origin)] - (and (= :resize type-op1) (= :resize type-op2) + + (and (= :resize type-op1) + (= :resize type-op2) ;; Same origin - (gpt/close? origin-op1 origin-op2) + ^boolean (gpt/close? origin-op1 origin-op2) ;; Same transforms - (gmt/close? transform-op1 transform-op2) - (gmt/close? transform-inv-op1 transform-inv-op2)))) + ^boolean (gmt/close? transform-op1 transform-op2) + ^boolean (gmt/close? transform-inv-op1 transform-inv-op2)))) (defn- merge-move [op1 op2] @@ -155,14 +155,15 @@ (defn- merge-resize [op1 op2] (let [op1-vector (dm/get-prop op1 :vector) - op1-x (dm/get-prop op1-vector :x) - op1-y (dm/get-prop op1-vector :y) + op1-x (dm/get-prop op1-vector :x) + op1-y (dm/get-prop op1-vector :y) op2-vector (dm/get-prop op2 :vector) - op2-x (dm/get-prop op2-vector :x) - op2-y (dm/get-prop op2-vector :y) + op2-x (dm/get-prop op2-vector :x) + op2-y (dm/get-prop op2-vector :y) - vector (gpt/point (* op1-x op2-x) (* op1-y op2-y))] + vector (gpt/point (* op1-x op2-x) + (* op1-y op2-y))] (assoc op1 :vector vector))) (defn- maybe-add-move @@ -198,10 +199,7 @@ [vector] (let [x (dm/get-prop vector :x) y (dm/get-prop vector :y)] - (and (some? x) - (some? y) - (not (mth/nan? x)) - (not (mth/nan? y))))) + (d/num? x y))) ;; Public builder API @@ -245,8 +243,11 @@ (move modifiers (gpt/point x y))) ([modifiers vector] - (assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector))) - (let [modifiers (or modifiers (empty)) + (dm/assert! + ["Invalid move vector: %1,%2" (:x vector) (:y vector)] + (valid-vector? vector)) + + (let [modifiers (or ^boolean modifiers (empty)) order (inc (dm/get-prop modifiers :last-order)) modifiers (assoc modifiers :last-order order)] (cond-> modifiers @@ -256,7 +257,7 @@ (defn resize ([modifiers vector origin] (assert (valid-vector? vector) (dm/str "Invalid resize vector: " (:x vector) "," (:y vector))) - (let [modifiers (or modifiers (empty)) + (let [modifiers (or ^boolean modifiers (empty)) order (inc (dm/get-prop modifiers :last-order)) modifiers (assoc modifiers :last-order order)] (cond-> modifiers @@ -594,7 +595,72 @@ ;; Main transformation functions +(defn transform-move! + "Transforms a matrix by the translation modifier" + [matrix modifier] + (-> (dm/get-prop modifier :vector) + (gmt/translate-matrix) + (gmt/multiply! matrix))) + + +(defn transform-resize! + "Transforms a matrix by the resize modifier" + [matrix modifier] + (let [tf (dm/get-prop modifier :transform) + tfi (dm/get-prop modifier :transform-inverse) + vector (dm/get-prop modifier :vector) + origin (dm/get-prop modifier :origin) + origin (if ^boolean (some? tfi) + (gpt/transform origin tfi) + origin)] + + (gmt/multiply! + (-> (gmt/matrix) + (cond-> ^boolean (some? tf) + (gmt/multiply! tf)) + (gmt/translate! origin) + (gmt/scale! vector) + (gmt/translate! (gpt/negate origin)) + (cond-> ^boolean (some? tfi) + (gmt/multiply! tfi))) + matrix))) + +(defn transform-rotate! + "Transforms a matrix by the rotation modifier" + [matrix modifier] + (let [center (dm/get-prop modifier :center) + rotation (dm/get-prop modifier :rotation)] + (gmt/multiply! + (-> (gmt/matrix) + (gmt/translate! center) + (gmt/multiply! (gmt/rotate-matrix rotation)) + (gmt/translate! (gpt/negate center))) + matrix))) + +(defn transform! + "Returns a matrix transformed by the modifier" + [matrix modifier] + (let [type (dm/get-prop modifier :type)] + (case type + :move (transform-move! matrix modifier) + :resize (transform-resize! matrix modifier) + :rotation (transform-rotate! matrix modifier)))) + +(defn modifiers->transform1 + "A multiplatform version of modifiers->transform." + [modifiers] + (reduce transform! (gmt/matrix) modifiers)) + (defn modifiers->transform + "Given a set of modifiers returns its transformation matrix" + [modifiers] + (let [modifiers (concat (dm/get-prop modifiers :geometry-parent) + (dm/get-prop modifiers :geometry-child)) + modifiers (sort-by #(dm/get-prop % :order) modifiers) + ] + (modifiers->transform1 modifiers))) + +(defn modifiers->transform-old "Given a set of modifiers returns its transformation matrix" [modifiers] (let [modifiers (->> (concat (dm/get-prop modifiers :geometry-parent) diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 7260036298..fe0c6b55b0 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -377,7 +377,15 @@ (update [_ state] (assoc state :workspace-modifiers (calculate-modifiers state ignore-constraints ignore-snap-pixel modif-tree params)))))) -;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints). +(def ^:private + xf-rotation-shape + (comp + (remove #(get % :blocked false)) + (filter #(:rotation (get editable-attrs (:type %)))) + (map :id))) + +;; Rotation use different algorithm to calculate children +;; modifiers (and do not use child constraints). (defn set-rotation-modifiers ([angle shapes] (set-rotation-modifiers angle shapes (-> shapes gsh/shapes->rect grc/rect->center))) @@ -387,11 +395,7 @@ ptk/UpdateEvent (update [_ state] (let [objects (wsh/lookup-page-objects state) - ids - (->> shapes - (remove #(get % :blocked false)) - (filter #(:rotation (get editable-attrs (:type %)))) - (map :id)) + ids (sequence xf-rotation-shape shapes) get-modifier (fn [shape]