0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-16 01:31:22 -05:00

Improved frame defered handling

This commit is contained in:
alonso.torres 2021-11-24 16:00:05 +01:00
parent 2b32e864fd
commit 9024408ed2
10 changed files with 234 additions and 173 deletions

View file

@ -163,6 +163,7 @@
(d/export gtr/rotation-modifiers) (d/export gtr/rotation-modifiers)
(d/export gtr/merge-modifiers) (d/export gtr/merge-modifiers)
(d/export gtr/transform-shape) (d/export gtr/transform-shape)
(d/export gtr/calc-transformed-parent-rect)
(d/export gtr/calc-child-modifiers) (d/export gtr/calc-child-modifiers)
;; PATHS ;; PATHS

View file

@ -503,19 +503,11 @@
(dissoc :modifiers))) (dissoc :modifiers)))
shape)))) shape))))
(defn calc-child-modifiers (defn calc-transformed-parent-rect
"Given the modifiers to apply to the parent, calculate the corresponding [parent parent-modifiers]
modifiers for the child, depending on the child constraints." (:selrect parent)
[parent child parent-modifiers ignore-constraints] ;; FIXME: Improve performance
(let [parent-rect (:selrect parent) (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) parent-displacement (-> (gpt/point 0 0)
(gpt/transform (get parent-modifiers :displacement (gmt/matrix))) (gpt/transform (get parent-modifiers :displacement (gmt/matrix)))
(gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix)))
@ -529,161 +521,179 @@
(gco/center-shape parent) (gco/center-shape parent)
(:resize-transform-inverse parent-modifiers (gmt/matrix)))) (:resize-transform-inverse parent-modifiers (gmt/matrix))))
parent-vector (get parent-modifiers :resize-vector (gpt/point 1 1)) 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 (-> parent-rect
(gpr/rect->points) (gpr/rect->points)
(gco/transform-points parent-displacement) (gco/transform-points parent-displacement)
(gco/transform-points parent-origin (gmt/scale-matrix parent-vector)) (gco/transform-points parent-origin (gmt/scale-matrix parent-vector))
(gco/transform-points parent-origin-2 (gmt/scale-matrix parent-vector-2)) (gco/transform-points parent-origin-2 (gmt/scale-matrix parent-vector-2))
(gpr/points->selrect)) (gpr/points->selrect))))
;; Calculate the modifiers in the horizontal and vertical directions (defn calc-child-modifiers
;; depending on the child constraints. "Given the modifiers to apply to the parent, calculate the corresponding
constraints-h (if-not ignore-constraints modifiers for the child, depending on the child constraints."
(get child :constraints-h (spec/default-constraints-h child)) ([parent child parent-modifiers ignore-constraints]
:scale) (let [transformed-parent-rect (calc-transformed-parent-rect parent parent-modifiers )]
constraints-v (if-not ignore-constraints (calc-child-modifiers parent child parent-modifiers ignore-constraints transformed-parent-rect)))
(get child :constraints-v (spec/default-constraints-v child))
:scale)
modifiers-h (case constraints-h ([parent child parent-modifiers ignore-constraints transformed-parent-rect]
:left (let [parent-rect (:selrect parent)
(let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect))] child-rect (:selrect child)
(if-not (mth/almost-zero? delta-left) ;; Apply the modifiers to the parent's selrect, to check the difference with
{:displacement (gpt/point delta-left 0)} ;; we convert to matrix below ;; 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 ;; Calculate the modifiers in the horizontal and vertical directions
(let [delta-right (- (:x2 transformed-parent-rect) (:x2 parent-rect))] ;; depending on the child constraints.
(if-not (mth/almost-zero? delta-right) constraints-h (if-not ignore-constraints
{:displacement (gpt/point delta-right 0)} (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 modifiers-h (case constraints-h
(let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect)) :left
delta-width (- (:width transformed-parent-rect) (:width parent-rect))] (let [delta-left (- (:x1 transformed-parent-rect) (:x1 parent-rect))]
(if (or (not (mth/almost-zero? delta-left))
(not (mth/almost-zero? delta-width))) (if-not (mth/almost-zero? delta-left)
{:displacement (gpt/point delta-left 0) {:displacement (gpt/point delta-left 0)} ;; we convert to matrix below
:resize-origin (-> (gpt/point (+ (:x1 child-rect) delta-left) {}))
(:y1 child-rect))
(transform-point-center :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) (gco/center-rect child-rect)
(:transform child (gmt/matrix)))) (:transform child (gmt/matrix))))
:resize-vector (gpt/point (/ (+ (:width child-rect) delta-width) :resize-vector (gpt/point (/ (+ (:width child-rect) delta-width)
(:width child-rect)) 1)} (:width child-rect)) 1)}
{})) {}))
:center :center
(let [parent-center (gco/center-rect parent-rect) (let [parent-center (gco/center-rect parent-rect)
transformed-parent-center (gco/center-rect transformed-parent-rect) transformed-parent-center (gco/center-rect transformed-parent-rect)
delta-center (- (:x transformed-parent-center) (:x parent-center))] delta-center (- (:x transformed-parent-center) (:x parent-center))]
(if-not (mth/almost-zero? delta-center) (if-not (mth/almost-zero? delta-center)
{:displacement (gpt/point delta-center 0)} {:displacement (gpt/point delta-center 0)}
{})) {}))
:scale :scale
(cond-> {} (cond-> {}
(and (:resize-vector parent-modifiers) (and (:resize-vector parent-modifiers)
(not (mth/close? (:x (:resize-vector parent-modifiers)) 1))) (not (mth/close? (:x (:resize-vector parent-modifiers)) 1)))
(assoc :resize-origin (:resize-origin parent-modifiers) (assoc :resize-origin (:resize-origin parent-modifiers)
:resize-vector (gpt/point (:x (:resize-vector parent-modifiers)) 1)) :resize-vector (gpt/point (:x (:resize-vector parent-modifiers)) 1))
;; resize-vector-2 is always for vertical modifiers, so no need to ;; resize-vector-2 is always for vertical modifiers, so no need to
;; check it here. ;; check it here.
(:displacement parent-modifiers) (:displacement parent-modifiers)
(assoc :displacement (assoc :displacement
(gpt/point (-> (gpt/point 0 0) (gpt/point (-> (gpt/point 0 0)
(gpt/transform (:displacement parent-modifiers)) (gpt/transform (:displacement parent-modifiers))
(gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix)))
(:x)) (:x))
0))) 0)))
{}) {})
modifiers-v (case constraints-v modifiers-v (case constraints-v
:top :top
(let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect))] (let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect))]
(if-not (mth/almost-zero? delta-top) (if-not (mth/almost-zero? delta-top)
{:displacement (gpt/point 0 delta-top)} ;; we convert to matrix below {:displacement (gpt/point 0 delta-top)} ;; we convert to matrix below
{})) {}))
:bottom :bottom
(let [delta-bottom (- (:y2 transformed-parent-rect) (:y2 parent-rect))] (let [delta-bottom (- (:y2 transformed-parent-rect) (:y2 parent-rect))]
(if-not (mth/almost-zero? delta-bottom) (if-not (mth/almost-zero? delta-bottom)
{:displacement (gpt/point 0 delta-bottom)} {:displacement (gpt/point 0 delta-bottom)}
{})) {}))
:topbottom :topbottom
(let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect)) (let [delta-top (- (:y1 transformed-parent-rect) (:y1 parent-rect))
delta-height (- (:height transformed-parent-rect) (:height parent-rect))] delta-height (- (:height transformed-parent-rect) (:height parent-rect))]
(if (or (not (mth/almost-zero? delta-top)) (if (or (not (mth/almost-zero? delta-top))
(not (mth/almost-zero? delta-height))) (not (mth/almost-zero? delta-height)))
{:displacement (gpt/point 0 delta-top) {:displacement (gpt/point 0 delta-top)
:resize-origin (-> (gpt/point (:x1 child-rect) :resize-origin (-> (gpt/point (:x1 child-rect)
(+ (:y1 child-rect) delta-top)) (+ (:y1 child-rect) delta-top))
(transform-point-center (transform-point-center
(gco/center-rect child-rect) (gco/center-rect child-rect)
(:transform child (gmt/matrix)))) (:transform child (gmt/matrix))))
:resize-vector (gpt/point 1 (/ (+ (:height child-rect) delta-height) :resize-vector (gpt/point 1 (/ (+ (:height child-rect) delta-height)
(:height child-rect)))} (:height child-rect)))}
{})) {}))
:center :center
(let [parent-center (gco/center-rect parent-rect) (let [parent-center (gco/center-rect parent-rect)
transformed-parent-center (gco/center-rect transformed-parent-rect) transformed-parent-center (gco/center-rect transformed-parent-rect)
delta-center (- (:y transformed-parent-center) (:y parent-center))] delta-center (- (:y transformed-parent-center) (:y parent-center))]
(if-not (mth/almost-zero? delta-center) (if-not (mth/almost-zero? delta-center)
{:displacement (gpt/point 0 delta-center)} {:displacement (gpt/point 0 delta-center)}
{})) {}))
:scale :scale
(cond-> {} (cond-> {}
(and (:resize-vector parent-modifiers) (and (:resize-vector parent-modifiers)
(not (mth/close? (:y (:resize-vector parent-modifiers)) 1))) (not (mth/close? (:y (:resize-vector parent-modifiers)) 1)))
(assoc :resize-origin (:resize-origin parent-modifiers) (assoc :resize-origin (:resize-origin parent-modifiers)
:resize-vector (gpt/point 1 (:y (:resize-vector 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 ;; 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. ;; call, and the resize-vector has no vertical data, so we may override it.
(and (:resize-vector-2 parent-modifiers) (and (:resize-vector-2 parent-modifiers)
(not (mth/close? (:y (:resize-vector-2 parent-modifiers)) 1))) (not (mth/close? (:y (:resize-vector-2 parent-modifiers)) 1)))
(assoc :resize-origin (:resize-origin-2 parent-modifiers) (assoc :resize-origin (:resize-origin-2 parent-modifiers)
:resize-vector (gpt/point 1 (:y (:resize-vector-2 parent-modifiers)))) :resize-vector (gpt/point 1 (:y (:resize-vector-2 parent-modifiers))))
(:displacement parent-modifiers) (:displacement parent-modifiers)
(assoc :displacement (assoc :displacement
(gpt/point 0 (-> (gpt/point 0 0) (gpt/point 0 (-> (gpt/point 0 0)
(gpt/transform (:displacement parent-modifiers)) (gpt/transform (:displacement parent-modifiers))
(gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix))) (gpt/transform (:resize-transform-inverse parent-modifiers (gmt/matrix)))
(:y))))) (:y)))))
{})] {})]
;; Build final child modifiers. Apply transform again to the result, to get the ;; 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. ;; real modifiers that need to be applied to the child, including rotation as needed.
(cond-> {} (cond-> {}
(or (:displacement modifiers-h) (:displacement modifiers-v)) (or (:displacement modifiers-h) (:displacement modifiers-v))
(assoc :displacement (gmt/translate-matrix (assoc :displacement (gmt/translate-matrix
(-> (gpt/point (get (:displacement modifiers-h) :x 0) (-> (gpt/point (get (:displacement modifiers-h) :x 0)
(get (:displacement modifiers-v) :y 0)) (get (:displacement modifiers-v) :y 0))
(gpt/transform (gpt/transform
(:resize-transform parent-modifiers (gmt/matrix)))))) (:resize-transform parent-modifiers (gmt/matrix))))))
(:resize-vector modifiers-h) (:resize-vector modifiers-h)
(assoc :resize-origin (:resize-origin modifiers-h) (assoc :resize-origin (:resize-origin modifiers-h)
:resize-vector (gpt/point (get (:resize-vector modifiers-h) :x 1) :resize-vector (gpt/point (get (:resize-vector modifiers-h) :x 1)
(get (:resize-vector modifiers-h) :y 1))) (get (:resize-vector modifiers-h) :y 1)))
(:resize-vector modifiers-v) (:resize-vector modifiers-v)
(assoc :resize-origin-2 (:resize-origin modifiers-v) (assoc :resize-origin-2 (:resize-origin modifiers-v)
:resize-vector-2 (gpt/point (get (:resize-vector modifiers-v) :x 1) :resize-vector-2 (gpt/point (get (:resize-vector modifiers-v) :x 1)
(get (:resize-vector modifiers-v) :y 1))) (get (:resize-vector modifiers-v) :y 1)))
(:resize-transform parent-modifiers) (:resize-transform parent-modifiers)
(assoc :resize-transform (:resize-transform parent-modifiers) (assoc :resize-transform (:resize-transform parent-modifiers)
:resize-transform-inverse (:resize-transform-inverse parent-modifiers))))) :resize-transform-inverse (:resize-transform-inverse parent-modifiers))))))
(defn selection-rect (defn selection-rect

View file

@ -40,7 +40,9 @@
(defmulti process-operation (fn [_ op] (:type op))) (defmulti process-operation (fn [_ op] (:type op)))
(defn process-changes (defn process-changes
([data items] (process-changes data items true)) ([data items]
(process-changes data items true))
([data items verify?] ([data items verify?]
;; When verify? false we spec the schema validation. Currently used to make just ;; When verify? false we spec the schema validation. Currently used to make just
;; 1 validation even if the changes are applied twice ;; 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 ;; reg-objects operation "regenerates" the geometry and selrect of the parent groups
(defmethod process-change :reg-objects (defmethod process-change :reg-objects
[data {:keys [page-id component-id shapes]}] [data {:keys [page-id component-id shapes]}]
;; FIXME: Improve performance
(letfn [(reg-objects [objects] (letfn [(reg-objects [objects]
(reduce #(d/update-when %1 %2 update-group %1) objects (reduce #(d/update-when %1 %2 update-group %1) objects
(sequence (comp (sequence (comp
@ -469,4 +472,3 @@
(ex/raise :type :not-implemented (ex/raise :type :not-implemented
:code :operation-not-implemented :code :operation-not-implemented
:context {:type (:type op)})) :context {:type (:type op)}))

View file

@ -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)))

View file

@ -334,13 +334,15 @@
color: $color-black; color: $color-black;
.file-name-label { .file-name-label {
flex: 1;
white-space: nowrap;
display: flex;
align-items: center; align-items: center;
flex: 1;
height: 2rem; height: 2rem;
margin-left: -0.25rem; margin-left: -0.25rem;
overflow: hidden;
padding-left: 0.25rem; padding-left: 0.25rem;
padding-top: 0.25rem;
text-overflow: ellipsis;
white-space: nowrap;
.icon-library { .icon-library {
width: 14px; width: 14px;

View file

@ -8,6 +8,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
height: 100%;
.element-icons { .element-icons {
background-color: $color-gray-60; background-color: $color-gray-60;

View file

@ -207,21 +207,26 @@
modifiers (assoc modifiers :ignore-geometry? ignore-geometry?) modifiers (assoc modifiers :ignore-geometry? ignore-geometry?)
set-child (fn [modif-tree child] transformed-rect (gsh/calc-transformed-parent-rect shape modifiers)
(let [child-modifiers (gsh/calc-child-modifiers shape
child set-child
modifiers (fn [modif-tree child]
ignore-constraints)] (let [child-modifiers
(set-modifiers-recursive modif-tree (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)]
objects
child (set-modifiers-recursive modif-tree
child-modifiers objects
root child
transformed-root child-modifiers
ignore-constraints)))] root
(reduce set-child transformed-root
(assoc-in modif-tree [(:id shape) :modifiers] modifiers) ignore-constraints)))
children)))
modif-tree
(-> modif-tree
(assoc-in [(:id shape) :modifiers] modifiers))]
(reduce set-child modif-tree children)))
(defn- check-delta (defn- check-delta
"If the shape is a component instance, check its relative position respect the "If the shape is a component instance, check its relative position respect the

View file

@ -241,6 +241,7 @@
(fn [state] (fn [state]
(let [objects (wsh/lookup-page-objects state) (let [objects (wsh/lookup-page-objects state)
modifiers (:workspace-modifiers state) modifiers (:workspace-modifiers state)
;; FIXME: Improve performance
objects (cond-> objects objects (cond-> objects
with-modifiers? with-modifiers?
(gsh/merge-modifiers modifiers)) (gsh/merge-modifiers modifiers))

View file

@ -51,32 +51,45 @@
;; DEBUG ;; DEBUG
:style {:filter (when (debug? :thumbnails) "sepia(1)")}}]))) :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. ;; used.
(defn custom-deferred (defn custom-deferred
[component] [component]
(mf/fnc deferred (mf/fnc deferred
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
(let [shape (obj/get props "shape") (let [shape (-> (obj/get props "shape")
shape (-> (select-keys shape [:selrect]) (select-keys [:x :y :width :height])
(hooks/use-equal-memo)) (hooks/use-equal-memo))
tmp (mf/useState false) tmp (mf/useState false)
^boolean render? (aget tmp 0) ^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) (mf/deps shape)
(fn [] (fn []
(mf/set-ref-val! prev-shape-ref shape)
(set-render false))) (set-render false)))
(mf/use-layout-effect (mf/use-effect
(mf/deps shape) (mf/deps render? shape)
(fn [] (fn []
(let [sem (ts/schedule-on-idle #(set-render true))] (when-not render?
#(rx/dispose! sem)))) (let [sem (ts/schedule-on-idle #(set-render true))]
(when render? (mf/create-element component props))))) #(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 (defn frame-wrapper-factory
[shape-wrapper] [shape-wrapper]
@ -90,9 +103,11 @@
thumbnail? (unchecked-get props "thumbnail?") thumbnail? (unchecked-get props "thumbnail?")
shape (gsh/transform-shape shape) 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) rendered? (mf/use-state false)

View file

@ -188,7 +188,7 @@
(reduce extract-attrs [[] []] shapes))) (reduce extract-attrs [[] []] shapes)))
(mf/defc options (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} ::mf/wrap-props false}
[props] [props]
(let [shapes (unchecked-get props "shapes") (let [shapes (unchecked-get props "shapes")
@ -203,7 +203,10 @@
[shadow-ids shadow-values] (get-attrs shapes objects :shadow) [shadow-ids shadow-values] (get-attrs shapes objects :shadow)
[blur-ids blur-values] (get-attrs shapes objects :blur) [blur-ids blur-values] (get-attrs shapes objects :blur)
[stroke-ids stroke-values] (get-attrs shapes objects :stroke) [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 [:div.options
(when-not (empty? measure-ids) (when-not (empty? measure-ids)