0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 10:38:13 -05:00

♻️ Refactor transform matrix

This commit is contained in:
alonso.torres 2022-05-31 16:56:14 +02:00
parent a9303c37c4
commit aa584e6d35
26 changed files with 118 additions and 86 deletions

View file

@ -159,6 +159,7 @@
(dm/export gtr/move)
(dm/export gtr/absolute-move)
(dm/export gtr/transform-matrix)
(dm/export gtr/transform-str)
(dm/export gtr/inverse-transform-matrix)
(dm/export gtr/transform-point-center)
(dm/export gtr/transform-rect)

View file

@ -7,6 +7,7 @@
(ns app.common.geom.shapes.transforms
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.common :as gco]
@ -143,16 +144,29 @@
([shape params]
(transform-matrix shape params (or (gco/center-shape shape) (gpt/point 0 0))))
([{:keys [flip-x flip-y] :as shape} {:keys [no-flip]} shape-center]
([{:keys [flip-x flip-y transform] :as shape} {:keys [no-flip]} shape-center]
(-> (gmt/matrix)
(gmt/translate shape-center)
(gmt/multiply (:transform shape (gmt/matrix)))
(cond-> (some? transform)
(gmt/multiply transform))
(cond->
(and (not no-flip) flip-x) (gmt/scale (gpt/point -1 1))
(and (not no-flip) flip-y) (gmt/scale (gpt/point 1 -1)))
(gmt/translate (gpt/negate shape-center)))))
(defn transform-str
([shape]
(transform-str shape nil))
([{:keys [transform flip-x flip-y] :as shape} {:keys [no-flip]}]
(when (and (some? shape)
(or (some? transform)
(and (not no-flip) flip-x)
(and (not no-flip) flip-y)))
(dm/str (transform-matrix shape)))))
(defn inverse-transform-matrix
([shape]
(let [shape-center (or (gco/center-shape shape)

View file

@ -80,6 +80,7 @@
:x :y
:rx :ry
:r1 :r2 :r3 :r4
:rotation
:selrect
:points
:show-content

View file

@ -338,11 +338,9 @@
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
to-move-shapes (->> (cph/get-immediate-children objects)
(remove cph/frame-shape?)
(d/enumerate)
(filterv (comp shapes :id second))
(mapv second))
to-move-shapes (into []
(map (d/getf objects))
(reverse (cph/sort-z-index objects shapes)))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)

View file

@ -67,15 +67,19 @@
fid (cph/frame-id-by-position objects initial)
shape (-> state
(get-in [:workspace-drawing :object])
(cp/setup-shape {:x (:x initial)
:y (:y initial)
:width 0.01
:height 0.01})
(assoc :frame-id fid)
(assoc :initialized? true)
(assoc :click-draw? true))]
shape (get-in state [:workspace-drawing :object])
shape (-> shape
(cp/setup-shape {:x (:x initial)
:y (:y initial)
:width 0.01
:height 0.01})
(cond-> (and (cph/frame-shape? shape)
(not= fid uuid/zero))
(assoc :fills [] :show-content true :hide-in-viewer true))
(assoc :frame-id fid)
(assoc :initialized? true)
(assoc :click-draw? true))]
(rx/concat
;; Add shape to drawing state
(rx/of #(assoc-in state [:workspace-drawing :object] shape))

View file

@ -8,7 +8,7 @@
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
[app.common.geom.shapes :as gsh]
[app.common.logging :as log]
[app.common.pages :as cp]
[app.common.pages.changes-builder :as pcb]
@ -158,7 +158,7 @@
(cond-> new-shape
true
(as-> $
(geom/move $ delta)
(gsh/move $ delta)
(assoc $ :frame-id frame-id)
(assoc $ :parent-id
(or (:parent-id $) (:frame-id $)))
@ -1150,7 +1150,7 @@
origin-root-pos (shape-pos origin-root)
dest-root-pos (shape-pos dest-root)
delta (gpt/subtract dest-root-pos origin-root-pos)]
(geom/move shape delta)))
(gsh/move shape delta)))
(defn- make-change
[container change]

View file

@ -8,7 +8,7 @@
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.pages :as cp]
[app.common.pages.changes-builder :as pcb]
@ -264,7 +264,7 @@
;; in the later vector position
selected (->> children
reverse
(d/seek #(geom/has-point? % position)))]
(d/seek #(gsh/has-point? % position)))]
(when selected
(rx/of (select-shape (:id selected))))))))
@ -325,7 +325,7 @@
:frame-id uuid/zero
:shapes [])
(dissoc :use-for-thumbnail?)
(geom/move delta)
(gsh/move delta)
(d/update-when :interactions #(cti/remap-interactions % ids-map objects)))
changes (-> (pcb/add-object changes new-frame)
@ -360,7 +360,7 @@
:parent-id parent-id
:frame-id frame-id)
(dissoc :shapes)
(geom/move delta)
(gsh/move delta)
(d/update-when :interactions #(cti/remap-interactions % ids-map objects)))
changes (-> (pcb/add-object changes new-obj {:ignore-touched true})
@ -412,7 +412,7 @@
(fn [g frame]
(let [new-id (ids-map (:id frame))
new-frame (-> frame
(geom/move delta))
(gsh/move delta))
new-guides (->> guides
(vals)
(filter #(= (:frame-id %) (:id frame)))

View file

@ -7,6 +7,7 @@
(ns app.main.errors
"Generic error handling"
(:require
[cuerdas.core :as str]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
@ -200,9 +201,12 @@
(defonce uncaught-error-handler
(letfn [(on-error [event]
(.preventDefault ^js event)
(some-> (unchecked-get event "error")
(on-unhandled-error)))]
;; EvalError is a debug error that happens for unknown reason
(when-not (str/includes? (.-message event) "EvalError")
(.error js/console event)
(.preventDefault ^js event)
(some-> (unchecked-get event "error")
(on-unhandled-error))))]
(.addEventListener glob/window "error" on-error)
(fn []
(.removeEventListener glob/window "error" on-error))))

View file

@ -6,6 +6,7 @@
(ns app.main.ui.hooks.resize
(:require
[app.main.ui.hooks :as hooks]
[app.common.geom.point :as gpt]
[app.common.logging :as log]
[app.main.ui.context :as ctx]
@ -77,26 +78,31 @@
(let [prev-val-ref (mf/use-ref nil)
current-observer-ref (mf/use-ref nil)
callback-ref (hooks/use-update-var {:callback callback})
;; We use the ref as a callback when the dom node is ready (or change)
node-ref
(mf/use-callback
(mf/deps callback)
(fn [^js node]
(let [^js current-observer (mf/ref-val current-observer-ref)
^js prev-val (mf/ref-val prev-val-ref)]
(when (some? node)
(let [^js current-observer (mf/ref-val current-observer-ref)
^js prev-val (mf/ref-val prev-val-ref)]
(when (and (not= prev-val node) (some? current-observer))
(log/debug :action "disconnect" :js/prev-val prev-val :js/node node)
(.disconnect current-observer)
(mf/set-ref-val! current-observer-ref nil))
(when (and (not= prev-val node) (some? current-observer))
(log/debug :action "disconnect" :js/prev-val prev-val :js/node node)
(.disconnect current-observer)
(mf/set-ref-val! current-observer-ref nil))
(when (and (not= prev-val node) (some? node))
(let [^js observer
(js/ResizeObserver. #(callback last-resize-type (dom/get-client-size node)))]
(mf/set-ref-val! current-observer-ref observer)
(log/debug :action "observe" :js/node node :js/observer observer)
(.observe observer node))))
(mf/set-ref-val! prev-val-ref node)))]
(when (and (not= prev-val node) (some? node))
(let [^js observer
(js/ResizeObserver.
#(let [callback (get @callback-ref :callback)]
(callback last-resize-type (dom/get-client-size node))))]
(mf/set-ref-val! current-observer-ref observer)
(log/debug :action "observe" :js/node node :js/observer observer)
(.observe observer node))))
(mf/set-ref-val! prev-val-ref node))))]
(mf/use-effect
(fn []

View file

@ -6,7 +6,7 @@
(ns app.main.ui.shapes.circle
(:require
[app.common.geom.shapes :as geom]
[app.common.geom.shapes :as gsh]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]]
[app.util.object :as obj]
@ -17,7 +17,7 @@
[props]
(let [shape (unchecked-get props "shape")
{:keys [x y width height]} shape
transform (geom/transform-matrix shape)
transform (gsh/transform-str shape)
cx (+ x (/ width 2))
cy (+ y (/ height 2))

View file

@ -41,7 +41,7 @@
(cfg/resolve-file-media (:fill-image shape)))
embed (embed/use-data-uris [uri])
transform (gsh/transform-matrix shape)
transform (gsh/transform-str shape)
;; When true the image has not loaded yet
loading? (and (some? uri) (not (contains? embed uri)))

View file

@ -28,12 +28,14 @@
[{:keys [shape render-id]}]
(when (= :frame (:type shape))
(let [{:keys [x y width height]} shape
transform (gsh/transform-str shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
:width width
:height height}))
:height height
:transform transform}))
path? (some? (.-d props))]
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
(if path?
@ -46,12 +48,12 @@
(let [shape (obj/get props "shape")]
(when (:thumbnail shape)
(let [{:keys [x y width height show-content]} shape
transform (gsh/transform-matrix shape)
transform (gsh/transform-str shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
:transform (str transform)
:transform transform
:width width
:height height
:className "frame-background"}))
@ -91,13 +93,13 @@
shape (unchecked-get props "shape")
{:keys [x y width height show-content]} shape
transform (gsh/transform-matrix shape)
transform (gsh/transform-str shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
:transform (str transform)
:transform transform
:width width
:height height
:className "frame-background"}))

View file

@ -27,13 +27,15 @@
(obj/set! "penpot:width" (:width gradient))))
(mf/defc linear-gradient [{:keys [id gradient shape]}]
(let [transform (when (= :path (:type shape)) (gsh/transform-matrix shape nil (gpt/point 0.5 0.5)))
(let [transform (when (= :path (:type shape))
(gsh/transform-matrix shape nil (gpt/point 0.5 0.5)))
base-props #js {:id id
:x1 (:start-x gradient)
:y1 (:start-y gradient)
:x2 (:end-x gradient)
:y2 (:end-y gradient)
:gradientTransform transform}
:gradientTransform (dm/str transform)}
include-metadata? (mf/use-ctx ed/include-metadata-ctx)

View file

@ -18,7 +18,7 @@
(let [shape (unchecked-get props "shape")
{:keys [x y width height]} shape
transform (gsh/transform-matrix shape)
transform (gsh/transform-str shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge! (attrs/extract-border-radius-attrs shape))
(obj/merge!

View file

@ -17,7 +17,7 @@
[props]
(let [shape (unchecked-get props "shape")
{:keys [x y width height]} shape
transform (gsh/transform-matrix shape)
transform (gsh/transform-str shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!

View file

@ -60,7 +60,7 @@
(obj/set! "preserveAspectRatio" "none"))]
[:& (mf/provider svg-ids-ctx) {:value ids-mapping}
[:g.svg-raw {:transform (dm/str (gsh/transform-matrix shape))}
[:g.svg-raw {:transform (gsh/transform-str shape)}
[:> "svg" attrs children]]]))
(mf/defc svg-element

View file

@ -8,7 +8,7 @@
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.geom.shapes :as geom]
[app.common.geom.shapes :as gsh]
[app.main.ui.context :as muc]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.text.styles :as sts]
@ -192,7 +192,7 @@
::mf/forward-ref true}
[props ref]
(let [shape (obj/get props "shape")
transform (str (geom/transform-matrix shape))
transform (gsh/transform-str shape)
{:keys [id x y width height content]} shape
grow-type (obj/get props "grow-type") ;; This is only needed in workspace

View file

@ -60,7 +60,7 @@
{:keys [x y width height position-data]} shape
transform (str (gsh/transform-matrix shape {:no-flip true}))
transform (gsh/transform-str shape {:no-flip true})
;; These position attributes are not really necesary but they are convenient for for the export
group-props (-> #js {:transform transform

View file

@ -7,7 +7,7 @@
(ns app.main.ui.viewer.handoff.render
"The main container for a frame in handoff mode"
(:require
[app.common.geom.shapes :as geom]
[app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph]
[app.main.data.viewer :as dv]
[app.main.store :as st]
@ -87,7 +87,7 @@
[props]
(let [shape (unchecked-get props "shape")
childs (mapv #(get objects %) (:shapes shape))
shape (geom/transform-shape shape)
shape (gsh/transform-shape shape)
props (-> (obj/create)
(obj/merge! props)
@ -166,8 +166,8 @@
(mf/use-memo (mf/deps objects)
#(svg-raw-container-factory objects))]
(when (and shape (not (:hidden shape)))
(let [shape (-> (geom/transform-shape shape)
(geom/translate-to-frame frame))
(let [shape (-> (gsh/transform-shape shape)
(gsh/translate-to-frame frame))
opts #js {:shape shape
:frame frame}]
(case (:type shape)

View file

@ -8,7 +8,7 @@
"The main container for a frame in viewer mode"
(:require
[app.common.data :as d]
[app.common.geom.shapes :as geom]
[app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph]
[app.common.spec.interactions :as cti]
[app.main.data.viewer :as dv]
@ -204,7 +204,7 @@
:stroke-width (if interactions-show? 1 0)
:fill-opacity (if interactions-show? 0.2 0)
:style {:pointer-events (when frame? "none")}
:transform (geom/transform-matrix shape)}])))
:transform (gsh/transform-str shape)}])))
(defn generic-wrapper-factory
"Wrap some svg shape and add interaction controls"
@ -306,7 +306,7 @@
[props]
(let [shape (obj/get props "shape")
childs (mapv #(get objects %) (:shapes shape))
shape (geom/transform-shape shape)
shape (gsh/transform-shape shape)
props (obj/merge! #js {} props
#js {:shape shape
:childs childs
@ -384,9 +384,9 @@
(mf/use-memo (mf/deps objects)
#(svg-raw-container-factory objects))]
(when (and shape (not (:hidden shape)))
(let [shape (-> (geom/transform-shape shape)
(geom/translate-to-frame frame)
(cond-> fixed? (geom/move delta)))
(let [shape (-> (gsh/transform-shape shape)
(gsh/translate-to-frame frame)
(cond-> fixed? (gsh/move delta)))
opts #js {:shape shape
:objects objects}]

View file

@ -89,8 +89,9 @@
frame?
[shape-node
(dom/query shape-node ".frame-children")
(dom/query (str "#thumbnail-container-" id))
(dom/query (str "#thumbnail-" id))]
(dom/query (dm/str "#thumbnail-container-" id))
(dom/query (dm/str "#thumbnail-" id))
(dom/query (dm/str "#frame-title-" id))]
;; For groups we don't want to transform the whole group but only
;; its filters/masks

View file

@ -23,7 +23,7 @@
(some? text-modifier)
(dwt/apply-text-modifier text-modifier))
transform (gsh/transform-matrix shape {:no-flip true})
transform (gsh/transform-str shape {:no-flip true})
{:keys [x y width height]} shape]
[:rect.main.viewport-selrect
@ -31,7 +31,7 @@
:y y
:width width
:height height
:transform (str transform)
:transform transform
:style {:stroke "var(--color-select)"
:stroke-width (/ 1 zoom)
:fill "none"}}]))

View file

@ -311,7 +311,7 @@
:zoom zoom}])
[:& widgets/frame-titles
{:objects objects-modified
{:objects base-objects
:selected selected
:zoom zoom
:modifiers modifiers

View file

@ -23,7 +23,7 @@
zoom (obj/get props "zoom" 1)
color (unchecked-get props "color")
transform (gsh/transform-matrix shape)
transform (gsh/transform-str shape)
path? (= :path (:type shape))
path-data
(mf/use-memo

View file

@ -170,7 +170,7 @@
:height size
:fill (if (debug? :handlers) "blue" "none")
:stroke-width 0
:transform (str transform)
:transform (dm/str transform)
:on-mouse-down on-rotate}]))
(mf/defc resize-point-handler
@ -224,7 +224,7 @@
height (/ resize-side-height zoom)
offset-y (if (= align :outside) (- height) (- (/ height 2)))
target-y (+ y offset-y)
transform-str (str (gmt/multiply transform (gmt/rotate-matrix angle (gpt/point x y))))]
transform-str (dm/str (gmt/multiply transform (gmt/rotate-matrix angle (gpt/point x y))))]
[:g.resize-handler
(when show-handler?
[:circle {:r (/ resize-point-radius zoom)
@ -271,13 +271,13 @@
current-transform (mf/deref refs/current-transform)
selrect (:selrect shape)
transform (gsh/transform-matrix shape {:no-flip true})]
transform (gsh/transform-str shape {:no-flip true})]
(when (not (#{:move :rotate} current-transform))
[:g.controls {:pointer-events (if disable-handlers "none" "visible")}
;; Selection rect
[:& selection-rect {:rect selrect
:transform (str transform)
:transform transform
:zoom zoom
:color color
:on-move-selected on-move-selected
@ -327,7 +327,7 @@
(let [{:keys [x y width height]} shape]
[:g.controls
[:rect.main {:x x :y y
:transform (str (gsh/transform-matrix shape))
:transform (gsh/transform-str shape)
:width width
:height height
:pointer-events "visible"

View file

@ -94,9 +94,11 @@
(mf/defc frame-title
{::mf/wrap [mf/memo]}
[{:keys [frame modifiers selected? zoom show-artboard-names? on-frame-enter on-frame-leave on-frame-select]}]
(let [{:keys [width x y]} (gsh/transform-shape frame)
(let [{:keys [width x y]} frame
label-pos (gpt/point x (- y (/ 10 zoom)))
frame-transform (gsh/transform-str frame)
on-mouse-down
(mf/use-callback
(mf/deps (:id frame) on-frame-select)
@ -137,11 +139,10 @@
text-pos-x (if (:use-for-thumbnail? frame) 15 0)]
(when (not (:hidden frame))
[:*
[:g {:id (dm/str "frame-title-" (:id frame))
:transform frame-transform}
(when (:use-for-thumbnail? frame)
[:g {:transform (str (when (and selected? modifiers)
(str (:displacement modifiers) " "))
(text-transform label-pos zoom))}
[:g {:transform (dm/str (text-transform label-pos zoom))}
[:svg {:x 0
:y -9
:width 12
@ -155,9 +156,7 @@
:width width
:height 20
:class "workspace-frame-label"
:transform (str (when (and selected? modifiers)
(str (:displacement modifiers) " "))
(text-transform label-pos zoom))
:transform (dm/str (text-transform label-pos zoom))
:style {:fill (when selected? "var(--color-primary-dark)")}
:visibility (if show-artboard-names? "visible" "hidden")
:on-mouse-down on-mouse-down