0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-01 17:41:50 -05:00

Improve transforms performance

This commit is contained in:
alonso.torres 2022-11-23 13:03:37 +01:00
parent 694d90d485
commit 69bb4654c9
20 changed files with 402 additions and 247 deletions

View file

@ -6,7 +6,7 @@
(ns app.common.attrs
(:require
[app.common.geom.shapes.transforms :as gst]
[app.common.geom.shapes.transforms :as gtr]
[app.common.math :as mth]))
(defn- get-attr
@ -24,7 +24,7 @@
value
(if-let [points (:points obj)]
(if (not= points :multiple)
(let [rect (gst/selection-rect [obj])]
(let [rect (gtr/selection-rect [obj])]
(if (= attr :ox) (:x rect) (:y rect)))
:multiple)
(get obj attr ::unset)))

View file

@ -91,27 +91,34 @@
(defn multiply
([^Matrix m1 ^Matrix m2]
(let [m1a (.-a m1)
m1b (.-b m1)
m1c (.-c m1)
m1d (.-d m1)
m1e (.-e m1)
m1f (.-f m1)
(cond
;; nil matrixes are equivalent to unit-matrix
(and (nil? m1) (nil? m2)) (matrix)
(nil? m1) m2
(nil? m2) m1
m2a (.-a m2)
m2b (.-b m2)
m2c (.-c m2)
m2d (.-d m2)
m2e (.-e m2)
m2f (.-f m2)]
:else
(let [m1a (.-a m1)
m1b (.-b m1)
m1c (.-c m1)
m1d (.-d m1)
m1e (.-e m1)
m1f (.-f m1)
(Matrix.
(+ (* m1a m2a) (* m1c m2b))
(+ (* m1b m2a) (* m1d m2b))
(+ (* m1a m2c) (* m1c m2d))
(+ (* m1b m2c) (* m1d m2d))
(+ (* m1a m2e) (* m1c m2f) m1e)
(+ (* m1b m2e) (* m1d m2f) m1f))))
m2a (.-a m2)
m2b (.-b m2)
m2c (.-c m2)
m2d (.-d m2)
m2e (.-e m2)
m2f (.-f m2)]
(Matrix.
(+ (* m1a m2a) (* m1c m2b))
(+ (* m1b m2a) (* m1d m2b))
(+ (* m1a m2c) (* m1c m2d))
(+ (* m1b m2c) (* m1d m2d))
(+ (* m1a m2e) (* m1c m2f) m1e)
(+ (* m1b m2e) (* m1d m2f) m1f)))))
([m1 m2 & others]
(reduce multiply (multiply m1 m2) others)))

View file

@ -189,6 +189,10 @@
(defn angle-sign [v1 v2]
(if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1))
(defn signed-angle-with-other
[v1 v2]
(* (angle-sign v1 v2) (angle-with-other v1 v2)))
(defn update-angle
"Update the angle of the point."
[p angle]

View file

@ -13,7 +13,7 @@
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.constraints :as gct]
[app.common.geom.shapes.corners :as gsc]
[app.common.geom.shapes.intersect :as gin]
[app.common.geom.shapes.intersect :as gsi]
[app.common.geom.shapes.modifiers :as gsm]
[app.common.geom.shapes.path :as gsp]
[app.common.geom.shapes.rect :as gpr]
@ -178,8 +178,6 @@
(dm/export gtr/transform-bounds)
(dm/export gtr/move-position-data)
(dm/export gtr/apply-objects-modifiers)
(dm/export gtr/parent-coords-rect)
(dm/export gtr/parent-coords-points)
;; Constratins
(dm/export gct/calc-child-modifiers)
@ -190,10 +188,10 @@
(dm/export gsp/open-path?)
;; Intersection
(dm/export gin/overlaps?)
(dm/export gin/has-point?)
(dm/export gin/has-point-rect?)
(dm/export gin/rect-contains-shape?)
(dm/export gsi/overlaps?)
(dm/export gsi/has-point?)
(dm/export gsi/has-point-rect?)
(dm/export gsi/rect-contains-shape?)
;; Bool

View file

@ -6,12 +6,11 @@
(ns app.common.geom.shapes.constraints
(:require
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.intersect :as gsi]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.rect :as gre]
[app.common.geom.shapes.transforms :as gst]
[app.common.geom.shapes.transforms :as gtr]
[app.common.math :as mth]
[app.common.types.modifiers :as ctm]
[app.common.uuid :as uuid]))
@ -184,7 +183,7 @@
(ctm/move-modifiers (displacement end-before end-after))))
(defmethod constraint-modifier :fixed
[_ axis child-points-before parent-points-before child-points-after parent-points-after transformed-parent]
[_ axis child-points-before parent-points-before child-points-after parent-points-after {:keys [transform transform-inverse]} modifiers]
(let [;; Same as constraint end
end-before (end-vector axis child-points-before parent-points-before)
end-after (end-vector axis child-points-after parent-points-after)
@ -203,11 +202,16 @@
;; displacement (so its left+top position is constant)
scale (/ (gpt/length after-vec) (gpt/length before-vec))
resize-origin (first child-points-after)
{:keys [transform transform-inverse]} transformed-parent]
resize-origin (gpo/origin child-points-after)
modif-transform (ctm/modifiers->transform modifiers)
modif-transform-inverse (gmt/inverse modif-transform)
resize-transform (gmt/multiply modif-transform transform)
resize-transform-inverse (gmt/multiply transform-inverse modif-transform-inverse)
resize-vector (get-scale axis scale)]
(-> (ctm/empty)
(ctm/resize (get-scale axis scale) resize-origin transform transform-inverse)
(ctm/resize resize-vector resize-origin resize-transform resize-transform-inverse)
(ctm/move disp-start))))
(defmethod constraint-modifier :center
@ -245,37 +249,36 @@
:top
:scale)))
(defn bounding-box-parent-transform
"Returns a bounding box for the child in the same coordinate system
as the parent.
Returns a points array"
[child parent]
(-> child
:points
(gco/transform-points (:transform-inverse parent))
(gre/points->rect)
(gre/rect->points) ;; Restore to points so we can transform them
(gco/transform-points (:transform parent))))
(defn normalize-modifiers
"Before aplying constraints we need to remove the deformation caused by the resizing of the parent"
[constraints-h constraints-v modifiers child parent transformed-child {:keys [transform transform-inverse] :as transformed-parent}]
[constraints-h constraints-v modifiers child {:keys [transform transform-inverse] :as parent} transformed-child-bounds transformed-parent-bounds]
(let [child-bb-before (gst/parent-coords-rect child parent)
child-bb-after (gst/parent-coords-rect transformed-child transformed-parent)
scale-x (/ (:width child-bb-before) (:width child-bb-after))
scale-y (/ (:height child-bb-before) (:height child-bb-after))
resize-origin (-> transformed-parent :points gpo/origin)]
(let [child-bb-before (gpo/parent-coords-bounds (:points child) (:points parent))
child-bb-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds)
(cond-> modifiers
(not= :scale constraints-h)
(ctm/resize (gpt/point scale-x 1) resize-origin transform transform-inverse)
scale-x (if (= :scale constraints-h)
1
(/ (gpo/width-points child-bb-before) (gpo/width-points child-bb-after)))
(not= :scale constraints-v)
(ctm/resize (gpt/point 1 scale-y) resize-origin transform transform-inverse))))
scale-y (if (= :scale constraints-v)
1
(/ (gpo/height-points child-bb-before) (gpo/height-points child-bb-after)))
resize-vector (gpt/point scale-x scale-y)
modif-transform (ctm/modifiers->transform modifiers)
modif-transform-inverse (gmt/inverse modif-transform)
resize-transform (gmt/multiply modif-transform transform)
resize-transform-inverse (gmt/multiply transform-inverse modif-transform-inverse)
resize-origin (gpo/origin transformed-child-bounds)]
(-> modifiers
(ctm/resize
resize-vector
resize-origin
resize-transform
resize-transform-inverse))))
(defn calc-child-modifiers
[parent child modifiers ignore-constraints transformed-parent]
[parent child modifiers ignore-constraints parent-bounds transformed-parent-bounds]
(let [modifiers (ctm/select-child-modifiers modifiers)
@ -292,26 +295,24 @@
(if (and (= :scale constraints-h) (= :scale constraints-v))
modifiers
(let [transformed-child (gst/transform-shape child (ctm/select-child-modifiers modifiers))
modifiers (normalize-modifiers constraints-h constraints-v modifiers child parent transformed-child transformed-parent)
(let [child-bounds (:points child)
modifiers (ctm/select-child-modifiers modifiers)
transformed-child-bounds (gtr/transform-bounds child-bounds modifiers)
modifiers (normalize-modifiers constraints-h constraints-v modifiers child parent transformed-child-bounds transformed-parent-bounds)
transformed-child-bounds (gtr/transform-bounds child-bounds modifiers)
transformed-child (gst/transform-shape child modifiers)
parent-points-before (bounding-box-parent-transform parent parent)
child-points-before (bounding-box-parent-transform child parent)
parent-points-after (bounding-box-parent-transform transformed-parent transformed-parent)
child-points-after (bounding-box-parent-transform transformed-child transformed-parent)
child-points-before (gpo/parent-coords-bounds child-bounds parent-bounds)
child-points-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds)
modifiers-h (constraint-modifier (constraints-h const->type+axis) :x
child-points-before parent-points-before
child-points-after parent-points-after
transformed-parent)
child-points-before parent-bounds
child-points-after transformed-parent-bounds
parent modifiers)
modifiers-v (constraint-modifier (constraints-v const->type+axis) :y
child-points-before parent-points-before
child-points-after parent-points-after
transformed-parent)]
child-points-before parent-bounds
child-points-after transformed-parent-bounds
parent modifiers)]
(-> modifiers
(ctm/add-modifiers modifiers-h)
(ctm/add-modifiers modifiers-v))))))

View file

@ -10,25 +10,24 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.flex-layout.lines :as fli]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.rect :as gsr]
[app.common.pages.helpers :as cph]
[app.common.types.shape.layout :as ctl]))
(defn drop-child-areas
[{:keys [transform-inverse] :as frame} parent-rect child index reverse? prev-x prev-y last?]
[frame parent-rect child-bounds index reverse? prev-x prev-y last?]
(let [col? (ctl/col? frame)
row? (ctl/row? frame)
[layout-gap-row layout-gap-col] (ctl/gaps frame)
start-p (-> child :points first)
center (gco/center-shape frame)
start-p (gmt/transform-point-center start-p center transform-inverse)
start-p (gpo/origin child-bounds)
box-x (:x start-p)
box-y (:y start-p)
box-width (-> child :selrect :width)
box-height (-> child :selrect :height)
box-width (gpo/width-points child-bounds)
box-height (gpo/height-points child-bounds)
x (if col? (:x parent-rect) prev-x)
y (if row? (:y parent-rect) prev-y)
@ -148,47 +147,45 @@
from-idx 0
prev-line-x (:x frame)
prev-line-y (:y frame)
lines (seq lines)]
current-line (first lines)
lines (rest lines)]
(if (nil? current-line)
(if (empty? lines)
areas
(let [line-area (drop-line-area frame current-line prev-line-x prev-line-y (nil? (first lines)))
(let [current-line (first lines)
line-area (drop-line-area frame current-line prev-line-x prev-line-y (empty? (rest lines)))
children (subvec children from-idx (+ from-idx (:num-children current-line)))
next-areas
(loop [areas areas
prev-child-x (:x line-area)
prev-child-y (:y line-area)
[index child] (first children)
children (rest children)]
children (seq children)]
(if (nil? child)
(if (empty? children)
areas
(let [[child-area child-area-start child-area-end]
(drop-child-areas frame line-area child index (not reverse?) prev-child-x prev-child-y (nil? (first children)))]
(let [[index [child-bounds _]] (first children)
[child-area child-area-start child-area-end]
(drop-child-areas frame line-area child-bounds index (not reverse?) prev-child-x prev-child-y (empty? (rest children)))]
(recur (conj areas child-area-start child-area-end)
(+ (:x child-area) (:width child-area))
(+ (:y child-area) (:height child-area))
(first children)
(rest children)))))]
(recur next-areas
(+ from-idx (:num-children current-line))
(+ (:x line-area) (:width line-area))
(+ (:y line-area) (:height line-area))
(first lines)
(rest lines)))))))
(defn get-drop-index
[frame-id objects position]
(let [frame (get objects frame-id)
position (gmt/transform-point-center position (gco/center-shape frame) (:transform-inverse frame))
children (cph/get-immediate-children objects frame-id)
layout-data (fli/calc-layout-data frame children)
children (->> (cph/get-immediate-children objects frame-id)
(map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
layout-data (fli/calc-layout-data frame children (:points frame))
drop-areas (layout-drop-areas frame layout-data children)
area (d/seek #(gsr/contains-point? % position) drop-areas)]
(:index area)))

View file

@ -9,21 +9,20 @@
[app.common.data :as d]
[app.common.geom.shapes.flex-layout.positions :as flp]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.transforms :as gst]
[app.common.math :as mth]
[app.common.types.shape.layout :as ctl]))
(def conjv (fnil conj []))
(defn layout-bounds
[{:keys [layout-padding layout-padding-type] :as shape}]
[{:keys [layout-padding layout-padding-type] :as shape} shape-bounds]
(let [;; Add padding to the bounds
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
[pad-top pad-right pad-bottom pad-left]
(if (= layout-padding-type :multiple)
[pad-top pad-right pad-bottom pad-left]
[pad-top pad-top pad-top pad-top])]
(gpo/pad-points (:points shape) pad-top pad-right pad-bottom pad-left)))
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
(defn init-layout-lines
"Calculates the lines basic data and accumulated values. The positions will be calculated in a different operation"
@ -43,18 +42,17 @@
(loop [line-data nil
result []
child (first children)
children (rest children)]
children (seq children)]
(if (nil? child)
(if (empty? children)
(cond-> result (some? line-data) (conj line-data))
(let [{:keys [line-min-width line-min-height
(let [[child-bounds child] (first children)
{:keys [line-min-width line-min-height
line-max-width line-max-height
num-children
children-data]} line-data
child-bounds (gst/parent-coords-points child shape)
child-width (gpo/width-points child-bounds)
child-height (gpo/height-points child-bounds)
child-min-width (ctl/child-min-width child)
@ -98,7 +96,6 @@
:num-children (inc num-children)
:children-data (conjv children-data child-data)}
result
(first children)
(rest children))
(recur {:line-min-width next-min-width
@ -108,7 +105,6 @@
:num-children 1
:children-data [child-data]}
(cond-> result (some? line-data) (conj line-data))
(first children)
(rest children))))))))
(defn add-space-to-items
@ -300,9 +296,9 @@
(defn calc-layout-data
"Digest the layout data to pass it to the constrains"
[shape children]
[shape children shape-bounds]
(let [layout-bounds (layout-bounds shape)
(let [layout-bounds (layout-bounds shape shape-bounds)
reverse? (ctl/reverse? shape)
children (cond->> children (not reverse?) reverse)
@ -310,10 +306,10 @@
layout-lines
(->> (init-layout-lines shape children layout-bounds)
(add-lines-positions shape layout-bounds)
(into []
(comp (map (partial add-line-spacing shape layout-bounds))
(map (partial add-children-resizes shape)))))]
(into [] (comp (map (partial add-line-spacing shape layout-bounds))
(map (partial add-children-resizes shape)))))]
{:layout-lines layout-lines
:layout-bounds layout-bounds
:reverse? reverse?}))

View file

@ -6,27 +6,43 @@
(ns app.common.geom.shapes.flex-layout.modifiers
(:require
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.flex-layout.positions :as fpo]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.transforms :as gst]
[app.common.geom.shapes.transforms :as gtr]
[app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl]))
(defn normalize-child-modifiers
"Apply the modifiers and then normalized them against the parent coordinates"
[modifiers parent child {:keys [transform transform-inverse] :as transformed-parent}]
[modifiers {:keys [transform transform-inverse] :as parent} child transformed-parent-bounds]
(let [child-bounds (:points child)
parent-bounds (:points parent)
transformed-child-bounds (gtr/transform-bounds child-bounds modifiers)
child-bb-before (gpo/parent-coords-bounds child-bounds parent-bounds)
child-bb-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds)
scale-x (/ (gpo/width-points child-bb-before) (gpo/width-points child-bb-after))
scale-y (/ (gpo/height-points child-bb-before) (gpo/height-points child-bb-after))
resize-vector (gpt/point scale-x scale-y)
modif-transform (ctm/modifiers->transform modifiers)
modif-transform-inverse (gmt/inverse modif-transform)
resize-transform (gmt/multiply modif-transform transform)
resize-transform-inverse (gmt/multiply transform-inverse modif-transform-inverse)
resize-origin (gpo/origin transformed-child-bounds)]
(let [transformed-child (gst/transform-shape child modifiers)
child-bb-before (gst/parent-coords-rect child parent)
child-bb-after (gst/parent-coords-rect transformed-child transformed-parent)
scale-x (/ (:width child-bb-before) (:width child-bb-after))
scale-y (/ (:height child-bb-before) (:height child-bb-after))
resize-origin (-> transformed-parent :points gpo/origin)
resize-vector (gpt/point scale-x scale-y)]
(-> modifiers
(ctm/select-child-modifiers)
(ctm/resize resize-vector resize-origin transform transform-inverse))))
(ctm/resize
resize-vector
resize-origin
resize-transform
resize-transform-inverse))))
(defn calc-fill-width-data
"Calculates the size and modifiers for the width of an auto-fill child"
@ -74,10 +90,8 @@
(defn layout-child-modifiers
"Calculates the modifiers for the layout"
[parent child layout-line]
(let [child-bounds (gst/parent-coords-points child parent)
child-origin (gpo/origin child-bounds)
[parent child child-bounds layout-line]
(let [child-origin (gpo/origin child-bounds)
child-width (gpo/width-points child-bounds)
child-height (gpo/height-points child-bounds)

View file

@ -167,7 +167,7 @@
"Calculates the position for the current shape given the layout-data context"
[parent child
child-width child-height
{:keys [start-p layout-gap-row layout-gap-col margin-x margin-y line-height line-width] :as layout-data}]
{:keys [start-p layout-gap-row layout-gap-col margin-x margin-y line-height line-width layout-bounds] :as layout-data}]
(let [row? (ctl/row? parent)
col? (ctl/col? parent)
@ -193,9 +193,8 @@
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)
points (:points parent)
hv (partial gpo/start-hv points)
vv (partial gpo/start-vv points)
hv (partial gpo/start-hv layout-bounds)
vv (partial gpo/start-vv layout-bounds)
corner-p
(cond-> start-p
@ -247,13 +246,13 @@
(gpt/add (vv margin-y)))
;; Fix position when layout is flipped
corner-p
(cond-> corner-p
(:flip-x parent)
(gpt/add (hv child-width))
(:flip-y parent)
(gpt/add (vv child-height)))
;;corner-p
;;(cond-> corner-p
;; (:flip-x parent)
;; (gpt/add (hv child-width))
;;
;; (:flip-y parent)
;; (gpt/add (vv child-height)))
next-p
(cond-> start-p

View file

@ -12,6 +12,7 @@
[app.common.geom.shapes.constraints :as gct]
[app.common.geom.shapes.flex-layout :as gcl]
[app.common.geom.shapes.pixel-precision :as gpp]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.transforms :as gtr]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
@ -91,12 +92,12 @@
(defn- set-children-modifiers
"Propagates the modifiers from a parent too its children applying constraints if necesary"
[modif-tree objects parent transformed-parent ignore-constraints]
[modif-tree objects parent transformed-parent-bounds ignore-constraints]
(let [children (:shapes parent)
modifiers (dm/get-in modif-tree [(:id parent) :modifiers])]
;; Move modifiers don't need to calculate constraints
(if (ctm/only-move? modifiers)
;; Move modifiers don't need to calculate constraints
(loop [modif-tree modif-tree
children (seq children)]
(if-let [current (first children)]
@ -105,49 +106,65 @@
modif-tree))
;; Check the constraints, then resize
(let [parent (gtr/transform-shape parent (ctm/select-parent-modifiers modifiers))]
(let [parent-bounds (gtr/transform-bounds (:points parent) (ctm/select-parent-modifiers modifiers))]
(loop [modif-tree modif-tree
children (seq children)]
(if-let [current (first children)]
(let [child-modifiers (gct/calc-child-modifiers parent (get objects current) modifiers ignore-constraints transformed-parent)]
(if (empty? children)
modif-tree
(let [child-id (first children)
child (get objects child-id)
child-modifiers (gct/calc-child-modifiers parent child modifiers ignore-constraints parent-bounds @transformed-parent-bounds)]
(recur (cond-> modif-tree
(not (ctm/empty? child-modifiers))
(update-in [current :modifiers] ctm/add-modifiers child-modifiers))
(rest children)))
modif-tree))))))
(update-in [child-id :modifiers] ctm/add-modifiers child-modifiers))
(rest children)))))))))
(defn- process-layout-children
[modif-tree objects parent transformed-parent]
[modif-tree objects parent transformed-parent-bounds]
(letfn [(process-child [modif-tree child]
(let [modifiers (dm/get-in modif-tree [(:id parent) :modifiers])
child-modifiers (-> modifiers
(ctm/select-child-geometry-modifiers)
(gcl/normalize-child-modifiers parent child transformed-parent))]
(gcl/normalize-child-modifiers parent child @transformed-parent-bounds))]
(cond-> modif-tree
(not (ctm/empty? child-modifiers))
(update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers))))]
(let [children (map (d/getf objects) (:shapes transformed-parent))]
(let [children (map (d/getf objects) (:shapes parent))]
(reduce process-child modif-tree children))))
(defn get-bounds
[objects modif-tree shape]
(let [modifiers (-> (dm/get-in modif-tree [(:id shape) :modifiers])
(ctm/select-geometry))
children (cph/get-immediate-children objects (:id shape))
bounds (cond
(cph/group-shape? shape)
(let [children-bounds (->> children (mapv (partial get-bounds objects modif-tree)))]
(gtr/group-bounds shape children-bounds))
(cph/mask-shape? shape)
(get-bounds objects modif-tree (-> children first))
:else
(:points shape))]
(gtr/transform-bounds bounds modifiers)))
(defn- set-layout-modifiers
[modif-tree objects parent]
[modif-tree objects parent transformed-parent-bounds]
(letfn [(apply-modifiers [modif-tree child]
(let [modifiers (-> (dm/get-in modif-tree [(:id child) :modifiers])
(ctm/select-geometry))]
(cond
(cph/group-like-shape? child)
(gtr/apply-group-modifiers child objects modif-tree)
(letfn [(apply-modifiers [child]
[(-> (get-bounds objects modif-tree child)
(gpo/parent-coords-bounds @transformed-parent-bounds))
child])
(some? modifiers)
(gtr/transform-shape child modifiers)
:else
child)))
(set-child-modifiers [parent [layout-line modif-tree] child]
(set-child-modifiers [[layout-line modif-tree] [child-bounds child]]
(let [[modifiers layout-line]
(gcl/layout-child-modifiers parent child layout-line)
(gcl/layout-child-modifiers parent child child-bounds layout-line)
modif-tree
(cond-> modif-tree
@ -156,13 +173,12 @@
[layout-line modif-tree]))]
(let [children (map (d/getf objects) (:shapes parent))
children (->> children (map (partial apply-modifiers modif-tree)))
layout-data (gcl/calc-layout-data parent children)
(let [children (->> (:shapes parent)
(map (comp apply-modifiers (d/getf objects))))
layout-data (gcl/calc-layout-data parent children @transformed-parent-bounds)
children (into [] (cond-> children (not (:reverse? layout-data)) reverse))
max-idx (dec (count children))
layout-lines (:layout-lines layout-data)]
(loop [modif-tree modif-tree
layout-line (first layout-lines)
pending (rest layout-lines)
@ -172,7 +188,7 @@
children (subvec children from-idx to-idx)
[_ modif-tree]
(reduce (partial set-child-modifiers parent) [layout-line modif-tree] children)]
(reduce set-child-modifiers [layout-line modif-tree] children)]
(recur modif-tree (first pending) (rest pending) to-idx))
modif-tree)))))
@ -195,11 +211,9 @@
(ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))]
(let [children (->> parent :shapes (map (d/getf objects)))
{auto-width :width auto-height :height}
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
(gcl/layout-content-bounds parent children))]
(cond-> (ctm/empty)
(and (some? auto-width) (ctl/auto-width? parent))
(set-parent-auto-width auto-width)
@ -214,11 +228,12 @@
root? (= uuid/zero parent-id)
modifiers (-> (dm/get-in modif-tree [parent-id :modifiers])
(ctm/select-geometry))
transformed-parent (gtr/transform-shape parent modifiers)
transformed-parent-bounds (delay (gtr/transform-bounds (:points parent) modifiers))
has-modifiers? (ctm/child-modifiers? modifiers)
layout? (ctl/layout? parent)
auto? (or (ctl/auto-height? transformed-parent) (ctl/auto-width? transformed-parent))
auto? (or (ctl/auto-height? parent) (ctl/auto-width? parent))
parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent))
;; If the current child is inside the layout we ignore the constraints
@ -226,11 +241,11 @@
[(cond-> modif-tree
(and (not layout?) has-modifiers? parent? (not root?))
(set-children-modifiers objects parent transformed-parent (or ignore-constraints inside-layout?))
(set-children-modifiers objects parent transformed-parent-bounds (or ignore-constraints inside-layout?))
layout?
(-> (process-layout-children objects parent transformed-parent)
(set-layout-modifiers objects transformed-parent)))
(-> (process-layout-children objects parent transformed-parent-bounds)
(set-layout-modifiers objects parent transformed-parent-bounds)))
;; Auto-width/height can change the positions in the parent so we need to recalculate
(cond-> autolayouts auto? (conj (:id parent)))]))
@ -247,21 +262,21 @@
[objects tree-seq modif-tree]
(letfn [(apply-shape [objects {:keys [id] :as shape}]
(if (cph/group-shape? shape)
(let [children (cph/get-children objects id)]
(assoc objects id
(cond
(cph/mask-shape? shape)
(gtr/update-mask-selrect shape children)
(let [modifiers (get-in modif-tree [id :modifiers])
object
(cond
(cph/mask-shape? shape)
(gtr/update-mask-selrect shape (cph/get-children objects id))
:else
(gtr/update-group-selrect shape children))))
(cph/group-shape? shape)
(gtr/update-group-selrect shape (cph/get-children objects id))
(let [modifiers (get-in modif-tree [id :modifiers])
object (cond-> shape
(some? modifiers)
(gtr/transform-shape modifiers))]
(assoc objects id object))))]
(some? modifiers)
(gtr/transform-shape shape modifiers)
:else
shape)]
(assoc objects id object)))]
(reduce apply-shape objects (reverse tree-seq))))

View file

@ -9,15 +9,15 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.rect :as gpr]
[app.common.geom.shapes.transforms :as gtr]
[app.common.math :as mth]
[app.common.pages.helpers :as cph]
[app.common.types.modifiers :as ctm]))
(defn size-pixel-precision
[modifiers {:keys [points transform transform-inverse] :as shape}]
[modifiers {:keys [transform transform-inverse] :as shape} points]
(let [origin (gpo/origin points)
curr-width (gpo/width-points points)
curr-height (gpo/height-points points)
@ -36,7 +36,7 @@
(ctm/resize scalev origin transform transform-inverse))))
(defn position-pixel-precision
[modifiers {:keys [points]}]
[modifiers _ points]
(let [bounds (gpr/points->rect points)
corner (gpt/point bounds)
target-corner (gpt/round corner)
@ -47,24 +47,28 @@
(defn set-pixel-precision
"Adjust modifiers so they adjust to the pixel grid"
[modifiers shape]
(let [move? (ctm/only-move? modifiers)]
(cond-> modifiers
(not move?)
(size-pixel-precision shape)
(let [points (-> shape :points (gco/transform-points (ctm/modifiers->transform modifiers)))
has-resize? (not (ctm/only-move? modifiers))
:always
(position-pixel-precision shape))))
[modifiers points]
(let [modifiers
(cond-> modifiers
has-resize? (size-pixel-precision shape points))
points
(cond-> (:points shape)
has-resize? (gco/transform-points (ctm/modifiers->transform modifiers)))]
[modifiers points])]
(position-pixel-precision modifiers shape points)))
(defn adjust-pixel-precision
[modif-tree objects]
(let [update-modifiers
(fn [modif-tree shape]
(let [modifiers (dm/get-in modif-tree [(:id shape) :modifiers])]
(if-not (ctm/has-geometry? modifiers)
modif-tree
(let [shape (gtr/transform-shape shape modifiers)]
(-> modif-tree
(update-in [(:id shape) :modifiers] set-pixel-precision shape))))))]
(cond-> modif-tree
(ctm/has-geometry? modifiers)
(update-in [(:id shape) :modifiers] set-pixel-precision shape))))]
(->> (keys modif-tree)
(map (d/getf objects))

View file

@ -6,12 +6,21 @@
(ns app.common.geom.shapes.points
(:require
[app.common.geom.point :as gpt]))
[app.common.geom.point :as gpt]
[app.common.geom.shapes.intersect :as gsi]))
(defn origin
[points]
(nth points 0))
(defn hv
[[p0 p1 _ _]]
(gpt/to-vec p0 p1))
(defn vv
[[p0 _ _ p3]]
(gpt/to-vec p0 p3))
(defn start-hv
"Horizontal vector from the origin with a magnitude `val`"
[[p0 p1 _ _] val]
@ -60,3 +69,46 @@
(-> p1 (gpt/add right-v) (gpt/add top-v))
(-> p2 (gpt/add right-v) (gpt/add bottom-v))
(-> p3 (gpt/add left-v) (gpt/add bottom-v))])))
#_(defn parent-coords-rect
[child-bounds parent-bounds]
#_(-> child-bounds
(gco/transform-points (:transform-inverse parent))
(gpr/points->rect)))
(defn closest-first
"Reorders the points so the closest to the line start-end is the first"
[[a b c d] start end]
(let [da (gpt/point-line-distance a start end)
db (gpt/point-line-distance b start end)
dc (gpt/point-line-distance c start end)
dd (gpt/point-line-distance d start end)]
(cond
(and (<= da db) (<= da dc) (<= da dd))
[a b c d]
(and (<= db da) (<= db dc) (<= db dd))
[b c d a]
(and (<= dc da) (<= dc db) (<= dc dd))
[c d a b]
:else
[d a b c])))
(defn parent-coords-bounds
[bounds [p1 p2 _ p4]]
(let [[b1 b2 b3 b4] (closest-first bounds p1 p2)
hv (gpt/to-vec p1 p2)
vv (gpt/to-vec p1 p4)
i1 (gsi/line-line-intersect b1 (gpt/add hv b1) b4 (gpt/add b4 vv))
i2 (gsi/line-line-intersect b1 (gpt/add hv b1) b2 (gpt/add b2 vv))
i3 (gsi/line-line-intersect b3 (gpt/add hv b3) b2 (gpt/add b2 vv))
i4 (gsi/line-line-intersect b3 (gpt/add hv b3) b4 (gpt/add b4 vv))]
[i1 i2 i3 i4]))

View file

@ -369,6 +369,16 @@
(update :width + (:width deltas))
(update :height + (:height deltas)))))))
(defn group-bounds
[group children-bounds]
(let [shape-center (gco/center-shape group)
points (flatten children-bounds)
points (if (empty? points) (:points group) points)]
(-> points
(gco/transform-points shape-center (:transform-inverse group (gmt/matrix)))
(gpr/squared-points)
(gco/transform-points shape-center (:transform group (gmt/matrix))))))
(defn update-group-selrect
[group children]
(let [shape-center (gco/center-shape group)
@ -541,19 +551,3 @@
:else
group))))
(defn parent-coords-rect
[child parent]
(-> child
:points
(gco/transform-points (:transform-inverse parent))
(gpr/points->rect)))
(defn parent-coords-points
[child parent]
(-> child
:points
(gco/transform-points (:transform-inverse parent))
(gpr/points->rect)
(gpr/rect->points)
(gco/transform-points (:transform parent))))

View file

@ -8,6 +8,7 @@
(:refer-clojure :exclude [empty empty?])
(: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]
@ -50,7 +51,6 @@
(or (not (mth/almost-zero? (- (:x vector) 1)))
(not (mth/almost-zero? (- (:y vector) 1)))))
(defn- mergeable-move?
[op1 op2]
(and (= :move (:type op1))
@ -106,6 +106,13 @@
(conj item)))
(conj operations op)))))
(defn valid-vector?
[{:keys [x y]}]
(and (some? x)
(some? y)
(not (mth/nan? x))
(not (mth/nan? y))))
;; Public builder API
(defn empty []
@ -116,12 +123,14 @@
(move-parent modifiers (gpt/point x y)))
([modifiers vector]
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(cond-> modifiers
(move-vec? vector)
(update :geometry-parent conjv {:type :move :vector vector}))))
(update :geometry-parent maybe-add-move {:type :move :vector vector}))))
(defn resize-parent
([modifiers vector origin]
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(cond-> modifiers
(resize-vec? vector)
(update :geometry-parent maybe-add-resize {:type :resize
@ -129,6 +138,7 @@
:origin origin})))
([modifiers vector origin transform transform-inverse]
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(cond-> modifiers
(resize-vec? vector)
(update :geometry-parent maybe-add-resize {:type :resize
@ -141,12 +151,14 @@
(move modifiers (gpt/point x y)))
([modifiers vector]
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(cond-> modifiers
(move-vec? vector)
(update :geometry-child maybe-add-move {:type :move :vector vector}))))
(defn resize
([modifiers vector origin]
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(cond-> modifiers
(resize-vec? vector)
(update :geometry-child maybe-add-resize {:type :resize
@ -154,6 +166,7 @@
:origin origin})))
([modifiers vector origin transform transform-inverse]
(assert (valid-vector? vector) (dm/str "Invalid move vector: " (:x vector) "," (:y vector)))
(cond-> modifiers
(resize-vec? vector)
(update :geometry-child maybe-add-resize {:type :resize
@ -355,8 +368,9 @@
(defn only-move?
"Returns true if there are only move operations"
[{:keys [geometry-child geometry-parent]}]
(and (every? #(= :move (:type %)) geometry-child)
(every? #(= :move (:type %)) geometry-parent)))
(let [move-op? #(= :move (:type %))]
(and (every? move-op? geometry-child)
(every? move-op? geometry-parent))))
(defn has-geometry?
[{:keys [geometry-parent geometry-child]}]
@ -418,16 +432,19 @@
(gmt/multiply (gmt/translate-matrix vector) matrix)
:resize
(gmt/multiply
(-> (gmt/matrix)
(gmt/translate origin)
(cond-> (some? transform)
(gmt/multiply transform))
(gmt/scale vector)
(cond-> (some? transform-inverse)
(gmt/multiply transform-inverse))
(gmt/translate (gpt/negate origin)))
matrix)
(let [origin (cond-> origin
(or (some? transform-inverse)(some? transform))
(gpt/transform transform-inverse))]
(gmt/multiply
(-> (gmt/matrix)
(cond-> (some? transform)
(gmt/multiply transform))
(gmt/translate origin)
(gmt/scale vector)
(gmt/translate (gpt/negate origin))
(cond-> (some? transform-inverse)
(gmt/multiply transform-inverse)))
matrix))
:rotation
(gmt/multiply

View file

@ -12,9 +12,8 @@
[app.common.files.features :as ffeat]
[app.common.geom.align :as gal]
[app.common.geom.point :as gpt]
[app.common.geom.proportions :as gpr]
[app.common.geom.proportions :as gpp]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.rect :as gpsr]
[app.common.logging :as log]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
@ -937,7 +936,7 @@
(if-not lock
(assoc shape :proportion-lock false)
(-> (assoc shape :proportion-lock true)
(gpr/assign-proportions))))]
(gpp/assign-proportions))))]
(rx/of (dch/update-shapes [id] assign-proportions))))))
(defn toggle-proportion-lock
@ -1776,10 +1775,10 @@
media (vals (:media file-data'))
media-points
(map #(assoc % :points (gpsr/rect->points {:x 0
:y 0
:width (:width %)
:height (:height %)}))
(map #(assoc % :points (gsh/rect->points {:x 0
:y 0
:width (:width %)
:height (:height %)}))
media)
shape-grid

View file

@ -8,7 +8,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.proportions :as gpr]
[app.common.geom.proportions :as gpp]
[app.common.geom.shapes :as gsh]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
@ -69,7 +69,7 @@
(get-shape-layer-position objects selected-non-frames attrs)]
(-> (merge default-attrs attrs)
(gpr/setup-proportions)
(gpp/setup-proportions)
(assoc :frame-id frame-id
:parent-id parent-id
:index index))))

View file

@ -146,8 +146,8 @@
(fn [id]
(let [new-shape (get text-shapes id)
old-shape (get prev-text-shapes id)
old-modifiers (get prev-modifiers id)
new-modifiers (get modifiers id)
old-modifiers (ctm/select-geometry (get prev-modifiers id))
new-modifiers (ctm/select-geometry (get modifiers id))
remote? (some? (-> new-shape meta :session-id)) ]
@ -155,10 +155,9 @@
(not (identical? old-shape new-shape))
(not= (dissoc old-shape :migrate)
(dissoc new-shape :migrate)))
(and (not= new-modifiers old-modifiers)
(or (nil? new-modifiers)
(nil? old-modifiers)
(not (ctm/only-move? new-modifiers))
(or (not (ctm/only-move? new-modifiers))
(not (ctm/only-move? old-modifiers))))
;; When the position data is nil we force to recalculate

View file

@ -429,6 +429,12 @@
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (debug? :parent-bounds)
[:& wvd/debug-parent-bounds {:selected-shapes selected-shapes
:objects objects-modified
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when show-selection-handlers?
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
[:defs

View file

@ -14,6 +14,7 @@
[app.common.geom.shapes.points :as gpo]
[app.common.pages.helpers :as cph]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
@ -76,7 +77,7 @@
col? (ctl/col? shape)
children (cph/get-immediate-children objects (:id shape))
layout-data (gsl/calc-layout-data shape children)
layout-data (gsl/calc-layout-data shape children (:points shape))
layout-bounds (:layout-bounds layout-data)
xv #(gpo/start-hv layout-bounds %)
@ -112,8 +113,9 @@
shape (or selected-frame (get objects hover-top-frame-id))]
(when (and shape (:layout shape))
(let [children (cph/get-immediate-children objects (:id shape))
layout-data (gsl/calc-layout-data shape children)
(let [children (->> (cph/get-immediate-children objects (:id shape))
(map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %)))
layout-data (gsl/calc-layout-data shape children (:points shape))
drop-areas (gsl/layout-drop-areas shape layout-data children)]
[:g.debug-layout {:pointer-events "none"
:transform (gsh/transform-str shape)}
@ -135,3 +137,51 @@
:alignment-baseline "hanging"
:fill "black"}
(:index drop-area)]])]))))
(mf/defc shape-parent-bound
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "parent"]))]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
parent (unchecked-get props "parent")
zoom (unchecked-get props "zoom")
[i1 i2 i3 i4] (gpo/parent-coords-bounds (:points shape) (:points parent))]
[:*
[:polygon {:points (->> [i1 i2 i3 i4] (map #(dm/fmt "%,%" (:x %) (:y %))) (str/join ","))
:style {:fill "none" :stroke "red" :stroke-width (/ 1 zoom)}}]
[:line {:x1 (:x i1)
:y1 (:y i1)
:x2 (:x i2)
:y2 (:y i2)
:style {:stroke "green" :stroke-width (/ 1 zoom)}}]
[:line {:x1 (:x i1)
:y1 (:y i1)
:x2 (:x i4)
:y2 (:y i4)
:style {:stroke "blue" :stroke-width (/ 1 zoom)}}]]))
(mf/defc debug-parent-bounds
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "objects")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
parent (or selected-frame (get objects hover-top-frame-id))]
(when (and (some? parent) (not= uuid/zero (:id parent)))
(let [children (cph/get-immediate-children objects (:id parent))]
[:g.debug-parent-bounds {:pointer-events "none"}
(for [[idx child] (d/enumerate children)]
[:> shape-parent-bound {:key (dm/str "bound-" idx)
:zoom zoom
:shape child
:parent parent}])]))))

View file

@ -76,6 +76,9 @@
;; Makes the pixel grid red so its more visibile
:pixel-grid
;; Show the bounds relative to the parent
:parent-bounds
})
;; These events are excluded when we activate the :events flag