diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 91a7043ba..619901d17 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -163,6 +163,7 @@ (d/export gtr/rotation-modifiers) (d/export gtr/merge-modifiers) (d/export gtr/transform-shape) +(d/export gtr/calc-transformed-parent-rect) (d/export gtr/calc-child-modifiers) ;; PATHS diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 220fa3014..3b2110cfc 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -503,19 +503,11 @@ (dissoc :modifiers))) shape)))) -(defn calc-child-modifiers - "Given the modifiers to apply to the parent, calculate the corresponding - modifiers for the child, depending on the child constraints." - [parent child parent-modifiers ignore-constraints] +(defn calc-transformed-parent-rect + [parent parent-modifiers] + (:selrect parent) + ;; FIXME: Improve performance (let [parent-rect (:selrect parent) - child-rect (:selrect child) - - ;; Apply the modifiers to the parent's selrect, to check the difference with - ;; the original, and calculate child transformations from this. - ;; - ;; Note that a shape's selrect is always "horizontal" (i.e. without applying - ;; the shape transform, that may include some rotation and skew). Thus, to - ;; apply the modifiers, we first apply to them the transform-inverse. parent-displacement (-> (gpt/point 0 0) (gpt/transform (get parent-modifiers :displacement (gmt/matrix))) (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) @@ -529,161 +521,179 @@ (gco/center-shape parent) (:resize-transform-inverse parent-modifiers (gmt/matrix)))) parent-vector (get parent-modifiers :resize-vector (gpt/point 1 1)) - parent-vector-2 (get parent-modifiers :resize-vector-2 (gpt/point 1 1)) + parent-vector-2 (get parent-modifiers :resize-vector-2 (gpt/point 1 1))] - transformed-parent-rect (-> parent-rect - (gpr/rect->points) - (gco/transform-points parent-displacement) - (gco/transform-points parent-origin (gmt/scale-matrix parent-vector)) - (gco/transform-points parent-origin-2 (gmt/scale-matrix parent-vector-2)) - (gpr/points->selrect)) + (-> parent-rect + (gpr/rect->points) + (gco/transform-points parent-displacement) + (gco/transform-points parent-origin (gmt/scale-matrix parent-vector)) + (gco/transform-points parent-origin-2 (gmt/scale-matrix parent-vector-2)) + (gpr/points->selrect)))) - ;; Calculate the modifiers in the horizontal and vertical directions - ;; depending on the child constraints. - constraints-h (if-not ignore-constraints - (get child :constraints-h (spec/default-constraints-h child)) - :scale) - constraints-v (if-not ignore-constraints - (get child :constraints-v (spec/default-constraints-v child)) - :scale) +(defn calc-child-modifiers + "Given the modifiers to apply to the parent, calculate the corresponding + modifiers for the child, depending on the child constraints." + ([parent child parent-modifiers ignore-constraints] + (let [transformed-parent-rect (calc-transformed-parent-rect parent parent-modifiers )] + (calc-child-modifiers parent child parent-modifiers ignore-constraints transformed-parent-rect))) - modifiers-h (case constraints-h - :left - (let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect))] + ([parent child parent-modifiers ignore-constraints transformed-parent-rect] + (let [parent-rect (:selrect parent) + child-rect (:selrect child) - (if-not (mth/almost-zero? delta-left) - {:displacement (gpt/point delta-left 0)} ;; we convert to matrix below - {})) + ;; Apply the modifiers to the parent's selrect, to check the difference with + ;; the original, and calculate child transformations from this. + ;; + ;; Note that a shape's selrect is always "horizontal" (i.e. without applying + ;; the shape transform, that may include some rotation and skew). Thus, to + ;; apply the modifiers, we first apply to them the transform-inverse. - :right - (let [delta-right (- (:x2 transformed-parent-rect) (:x2 parent-rect))] - (if-not (mth/almost-zero? delta-right) - {:displacement (gpt/point delta-right 0)} - {})) + ;; Calculate the modifiers in the horizontal and vertical directions + ;; depending on the child constraints. + constraints-h (if-not ignore-constraints + (get child :constraints-h (spec/default-constraints-h child)) + :scale) + constraints-v (if-not ignore-constraints + (get child :constraints-v (spec/default-constraints-v child)) + :scale) - :leftright - (let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect)) - delta-width (- (:width transformed-parent-rect) (:width parent-rect))] - (if (or (not (mth/almost-zero? delta-left)) - (not (mth/almost-zero? delta-width))) - {:displacement (gpt/point delta-left 0) - :resize-origin (-> (gpt/point (+ (:x1 child-rect) delta-left) - (:y1 child-rect)) - (transform-point-center + modifiers-h (case constraints-h + :left + (let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect))] + + (if-not (mth/almost-zero? delta-left) + {:displacement (gpt/point delta-left 0)} ;; we convert to matrix below + {})) + + :right + (let [delta-right (- (:x2 transformed-parent-rect) (:x2 parent-rect))] + (if-not (mth/almost-zero? delta-right) + {:displacement (gpt/point delta-right 0)} + {})) + + :leftright + (let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect)) + delta-width (- (:width transformed-parent-rect) (:width parent-rect))] + (if (or (not (mth/almost-zero? delta-left)) + (not (mth/almost-zero? delta-width))) + {:displacement (gpt/point delta-left 0) + :resize-origin (-> (gpt/point (+ (:x1 child-rect) delta-left) + (:y1 child-rect)) + (transform-point-center (gco/center-rect child-rect) (:transform child (gmt/matrix)))) - :resize-vector (gpt/point (/ (+ (:width child-rect) delta-width) - (:width child-rect)) 1)} - {})) + :resize-vector (gpt/point (/ (+ (:width child-rect) delta-width) + (:width child-rect)) 1)} + {})) - :center - (let [parent-center (gco/center-rect parent-rect) - transformed-parent-center (gco/center-rect transformed-parent-rect) - delta-center (- (:x transformed-parent-center) (:x parent-center))] - (if-not (mth/almost-zero? delta-center) - {:displacement (gpt/point delta-center 0)} - {})) + :center + (let [parent-center (gco/center-rect parent-rect) + transformed-parent-center (gco/center-rect transformed-parent-rect) + delta-center (- (:x transformed-parent-center) (:x parent-center))] + (if-not (mth/almost-zero? delta-center) + {:displacement (gpt/point delta-center 0)} + {})) - :scale - (cond-> {} - (and (:resize-vector parent-modifiers) - (not (mth/close? (:x (:resize-vector parent-modifiers)) 1))) - (assoc :resize-origin (:resize-origin parent-modifiers) - :resize-vector (gpt/point (:x (:resize-vector parent-modifiers)) 1)) + :scale + (cond-> {} + (and (:resize-vector parent-modifiers) + (not (mth/close? (:x (:resize-vector parent-modifiers)) 1))) + (assoc :resize-origin (:resize-origin parent-modifiers) + :resize-vector (gpt/point (:x (:resize-vector parent-modifiers)) 1)) - ;; resize-vector-2 is always for vertical modifiers, so no need to - ;; check it here. + ;; resize-vector-2 is always for vertical modifiers, so no need to + ;; check it here. - (:displacement parent-modifiers) - (assoc :displacement - (gpt/point (-> (gpt/point 0 0) - (gpt/transform (:displacement parent-modifiers)) - (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) - (:x)) - 0))) - {}) + (:displacement parent-modifiers) + (assoc :displacement + (gpt/point (-> (gpt/point 0 0) + (gpt/transform (:displacement parent-modifiers)) + (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) + (:x)) + 0))) + {}) - modifiers-v (case constraints-v - :top - (let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect))] - (if-not (mth/almost-zero? delta-top) - {:displacement (gpt/point 0 delta-top)} ;; we convert to matrix below - {})) + modifiers-v (case constraints-v + :top + (let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect))] + (if-not (mth/almost-zero? delta-top) + {:displacement (gpt/point 0 delta-top)} ;; we convert to matrix below + {})) - :bottom - (let [delta-bottom (- (:y2 transformed-parent-rect) (:y2 parent-rect))] - (if-not (mth/almost-zero? delta-bottom) - {:displacement (gpt/point 0 delta-bottom)} - {})) + :bottom + (let [delta-bottom (- (:y2 transformed-parent-rect) (:y2 parent-rect))] + (if-not (mth/almost-zero? delta-bottom) + {:displacement (gpt/point 0 delta-bottom)} + {})) - :topbottom - (let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect)) - delta-height (- (:height transformed-parent-rect) (:height parent-rect))] - (if (or (not (mth/almost-zero? delta-top)) - (not (mth/almost-zero? delta-height))) - {:displacement (gpt/point 0 delta-top) - :resize-origin (-> (gpt/point (:x1 child-rect) - (+ (:y1 child-rect) delta-top)) - (transform-point-center + :topbottom + (let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect)) + delta-height (- (:height transformed-parent-rect) (:height parent-rect))] + (if (or (not (mth/almost-zero? delta-top)) + (not (mth/almost-zero? delta-height))) + {:displacement (gpt/point 0 delta-top) + :resize-origin (-> (gpt/point (:x1 child-rect) + (+ (:y1 child-rect) delta-top)) + (transform-point-center (gco/center-rect child-rect) (:transform child (gmt/matrix)))) - :resize-vector (gpt/point 1 (/ (+ (:height child-rect) delta-height) - (:height child-rect)))} - {})) + :resize-vector (gpt/point 1 (/ (+ (:height child-rect) delta-height) + (:height child-rect)))} + {})) - :center - (let [parent-center (gco/center-rect parent-rect) - transformed-parent-center (gco/center-rect transformed-parent-rect) - delta-center (- (:y transformed-parent-center) (:y parent-center))] - (if-not (mth/almost-zero? delta-center) - {:displacement (gpt/point 0 delta-center)} - {})) + :center + (let [parent-center (gco/center-rect parent-rect) + transformed-parent-center (gco/center-rect transformed-parent-rect) + delta-center (- (:y transformed-parent-center) (:y parent-center))] + (if-not (mth/almost-zero? delta-center) + {:displacement (gpt/point 0 delta-center)} + {})) - :scale - (cond-> {} - (and (:resize-vector parent-modifiers) - (not (mth/close? (:y (:resize-vector parent-modifiers)) 1))) - (assoc :resize-origin (:resize-origin parent-modifiers) - :resize-vector (gpt/point 1 (:y (:resize-vector parent-modifiers)))) + :scale + (cond-> {} + (and (:resize-vector parent-modifiers) + (not (mth/close? (:y (:resize-vector parent-modifiers)) 1))) + (assoc :resize-origin (:resize-origin parent-modifiers) + :resize-vector (gpt/point 1 (:y (:resize-vector parent-modifiers)))) - ;; If there is a resize-vector-2, this means that we come from a recursive - ;; call, and the resize-vector has no vertical data, so we may override it. - (and (:resize-vector-2 parent-modifiers) - (not (mth/close? (:y (:resize-vector-2 parent-modifiers)) 1))) - (assoc :resize-origin (:resize-origin-2 parent-modifiers) - :resize-vector (gpt/point 1 (:y (:resize-vector-2 parent-modifiers)))) + ;; If there is a resize-vector-2, this means that we come from a recursive + ;; call, and the resize-vector has no vertical data, so we may override it. + (and (:resize-vector-2 parent-modifiers) + (not (mth/close? (:y (:resize-vector-2 parent-modifiers)) 1))) + (assoc :resize-origin (:resize-origin-2 parent-modifiers) + :resize-vector (gpt/point 1 (:y (:resize-vector-2 parent-modifiers)))) - (:displacement parent-modifiers) - (assoc :displacement - (gpt/point 0 (-> (gpt/point 0 0) - (gpt/transform (:displacement parent-modifiers)) - (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) - (:y))))) - {})] + (:displacement parent-modifiers) + (assoc :displacement + (gpt/point 0 (-> (gpt/point 0 0) + (gpt/transform (:displacement parent-modifiers)) + (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) + (:y))))) + {})] - ;; Build final child modifiers. Apply transform again to the result, to get the - ;; real modifiers that need to be applied to the child, including rotation as needed. - (cond-> {} - (or (:displacement modifiers-h) (:displacement modifiers-v)) - (assoc :displacement (gmt/translate-matrix + ;; Build final child modifiers. Apply transform again to the result, to get the + ;; real modifiers that need to be applied to the child, including rotation as needed. + (cond-> {} + (or (:displacement modifiers-h) (:displacement modifiers-v)) + (assoc :displacement (gmt/translate-matrix (-> (gpt/point (get (:displacement modifiers-h) :x 0) (get (:displacement modifiers-v) :y 0)) (gpt/transform - (:resize-transform parent-modifiers (gmt/matrix)))))) + (:resize-transform parent-modifiers (gmt/matrix)))))) - (:resize-vector modifiers-h) - (assoc :resize-origin (:resize-origin modifiers-h) - :resize-vector (gpt/point (get (:resize-vector modifiers-h) :x 1) - (get (:resize-vector modifiers-h) :y 1))) + (:resize-vector modifiers-h) + (assoc :resize-origin (:resize-origin modifiers-h) + :resize-vector (gpt/point (get (:resize-vector modifiers-h) :x 1) + (get (:resize-vector modifiers-h) :y 1))) - (:resize-vector modifiers-v) - (assoc :resize-origin-2 (:resize-origin modifiers-v) - :resize-vector-2 (gpt/point (get (:resize-vector modifiers-v) :x 1) - (get (:resize-vector modifiers-v) :y 1))) + (:resize-vector modifiers-v) + (assoc :resize-origin-2 (:resize-origin modifiers-v) + :resize-vector-2 (gpt/point (get (:resize-vector modifiers-v) :x 1) + (get (:resize-vector modifiers-v) :y 1))) - (:resize-transform parent-modifiers) - (assoc :resize-transform (:resize-transform parent-modifiers) - :resize-transform-inverse (:resize-transform-inverse parent-modifiers))))) + (:resize-transform parent-modifiers) + (assoc :resize-transform (:resize-transform parent-modifiers) + :resize-transform-inverse (:resize-transform-inverse parent-modifiers)))))) (defn selection-rect diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc index 01b314bc5..a9b73755f 100644 --- a/common/src/app/common/pages/changes.cljc +++ b/common/src/app/common/pages/changes.cljc @@ -40,7 +40,9 @@ (defmulti process-operation (fn [_ op] (:type op))) (defn process-changes - ([data items] (process-changes data items true)) + ([data items] + (process-changes data items true)) + ([data items verify?] ;; When verify? false we spec the schema validation. Currently used to make just ;; 1 validation even if the changes are applied twice @@ -152,6 +154,7 @@ ;; reg-objects operation "regenerates" the geometry and selrect of the parent groups (defmethod process-change :reg-objects [data {:keys [page-id component-id shapes]}] + ;; FIXME: Improve performance (letfn [(reg-objects [objects] (reduce #(d/update-when %1 %2 update-group %1) objects (sequence (comp @@ -469,4 +472,3 @@ (ex/raise :type :not-implemented :code :operation-not-implemented :context {:type (:type op)})) - diff --git a/common/src/app/common/perf.cljc b/common/src/app/common/perf.cljc new file mode 100644 index 000000000..ca235835b --- /dev/null +++ b/common/src/app/common/perf.cljc @@ -0,0 +1,21 @@ +(ns app.common.perf + (:require + [app.common.uuid :as uuid])) + +(defn timestamp [] + #?(:cljs (js/performance.now) + :clj (. System (nanoTime)))) + +(defonce measures (atom {})) + +(defn start + ([] + (start (uuid/next))) + + ([key] + (swap! measures assoc key (timestamp)) + key)) + +(defn measure + [key] + (- (timestamp) (get @measures key))) diff --git a/frontend/resources/styles/main/partials/modal.scss b/frontend/resources/styles/main/partials/modal.scss index 6e7328706..24378e292 100644 --- a/frontend/resources/styles/main/partials/modal.scss +++ b/frontend/resources/styles/main/partials/modal.scss @@ -334,13 +334,15 @@ color: $color-black; .file-name-label { - flex: 1; - white-space: nowrap; - display: flex; align-items: center; + flex: 1; height: 2rem; margin-left: -0.25rem; + overflow: hidden; padding-left: 0.25rem; + padding-top: 0.25rem; + text-overflow: ellipsis; + white-space: nowrap; .icon-library { width: 14px; diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index ef8fecf0b..961b0652e 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -8,6 +8,7 @@ display: flex; flex-direction: column; width: 100%; + height: 100%; .element-icons { background-color: $color-gray-60; diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 19c667d2c..724d66ade 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -207,21 +207,26 @@ modifiers (assoc modifiers :ignore-geometry? ignore-geometry?) - set-child (fn [modif-tree child] - (let [child-modifiers (gsh/calc-child-modifiers shape - child - modifiers - ignore-constraints)] - (set-modifiers-recursive modif-tree - objects - child - child-modifiers - root - transformed-root - ignore-constraints)))] - (reduce set-child - (assoc-in modif-tree [(:id shape) :modifiers] modifiers) - children))) + transformed-rect (gsh/calc-transformed-parent-rect shape modifiers) + + set-child + (fn [modif-tree child] + (let [child-modifiers + (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)] + + (set-modifiers-recursive modif-tree + objects + child + child-modifiers + root + transformed-root + ignore-constraints))) + + modif-tree + (-> modif-tree + (assoc-in [(:id shape) :modifiers] modifiers))] + + (reduce set-child modif-tree children))) (defn- check-delta "If the shape is a component instance, check its relative position respect the diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index adc3cfaf6..c174f3505 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -241,6 +241,7 @@ (fn [state] (let [objects (wsh/lookup-page-objects state) modifiers (:workspace-modifiers state) + ;; FIXME: Improve performance objects (cond-> objects with-modifiers? (gsh/merge-modifiers modifiers)) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index bf362f23f..c43fd2c46 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -51,32 +51,45 @@ ;; DEBUG :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]))) -;; This custom deferred don't defer rendering when ghost rendering is +(mf/defc frame-placeholder + {::mf/wrap-props false} + [props] + (let [{:keys [x y width height fill-color] :as shape} (obj/get props "shape")] + (if (some? (:thumbnail shape)) + [:& thumbnail {:shape shape}] + [:rect {:x x :y y :width width :height height :style {:fill (or fill-color "white")}}]))) + ;; used. (defn custom-deferred [component] (mf/fnc deferred {::mf/wrap-props false} [props] - (let [shape (obj/get props "shape") - shape (-> (select-keys shape [:selrect]) + (let [shape (-> (obj/get props "shape") + (select-keys [:x :y :width :height]) (hooks/use-equal-memo)) tmp (mf/useState false) ^boolean render? (aget tmp 0) - ^js set-render (aget tmp 1)] + ^js set-render (aget tmp 1) + prev-shape-ref (mf/use-ref shape)] - (mf/use-layout-effect + (mf/use-effect (mf/deps shape) (fn [] + (mf/set-ref-val! prev-shape-ref shape) (set-render false))) - (mf/use-layout-effect - (mf/deps shape) + (mf/use-effect + (mf/deps render? shape) (fn [] - (let [sem (ts/schedule-on-idle #(set-render true))] - #(rx/dispose! sem)))) - (when render? (mf/create-element component props))))) + (when-not render? + (let [sem (ts/schedule-on-idle #(set-render true))] + #(rx/dispose! sem))))) + + (if (and render? (= shape (mf/ref-val prev-shape-ref))) + (mf/create-element component props) + (mf/create-element frame-placeholder props))))) (defn frame-wrapper-factory [shape-wrapper] @@ -90,9 +103,11 @@ thumbnail? (unchecked-get props "thumbnail?") shape (gsh/transform-shape shape) - children (mapv #(get objects %) (:shapes shape)) + children (-> (mapv #(get objects %) (:shapes shape)) + (hooks/use-equal-memo)) - all-children (cp/get-children-objects (:id shape) objects) + all-children (-> (cp/get-children-objects (:id shape) objects) + (hooks/use-equal-memo)) rendered? (mf/use-state false) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 068d4701a..3894d5a35 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -188,7 +188,7 @@ (reduce extract-attrs [[] []] shapes))) (mf/defc options - {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "shapes-with-children"]))] + {::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "shapes-with-children"]))] ::mf/wrap-props false} [props] (let [shapes (unchecked-get props "shapes") @@ -203,7 +203,10 @@ [shadow-ids shadow-values] (get-attrs shapes objects :shadow) [blur-ids blur-values] (get-attrs shapes objects :blur) [stroke-ids stroke-values] (get-attrs shapes objects :stroke) - [text-ids text-values] (get-attrs shapes objects :text)] + + ;; FIXME: Improve performance + [text-ids text-values] (get-attrs shapes objects :text) + ] [:div.options (when-not (empty? measure-ids)