0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-15 19:48:22 -05:00

Viewport and hug performance enhances

This commit is contained in:
alonso.torres 2022-11-24 12:16:59 +01:00
parent 399d57ace0
commit f579bb0c8d
16 changed files with 270 additions and 199 deletions

View file

@ -688,3 +688,12 @@
(if (contains? set value) (if (contains? set value)
(disj set value) (disj set value)
(conj set value))))) (conj set value)))))
(defn lazy-map
"Creates a map with lazy values given the generator function that receives as argument
the key for the value to be generated"
[keys generator-fn]
(into {}
(map (fn [key]
[key (delay (generator-fn key))]))
keys))

View file

@ -252,6 +252,10 @@
(let [v-length (length v)] (let [v-length (length v)]
(divide v (point v-length v-length)))) (divide v (point v-length v-length))))
(defn perpendicular
[{:keys [x y]}]
(Point. (- y) x))
(defn project (defn project
"V1 perpendicular projection on vector V2" "V1 perpendicular projection on vector V2"
[v1 v2] [v1 v2]

View file

@ -251,9 +251,11 @@
(defn normalize-modifiers (defn normalize-modifiers
"Before aplying constraints we need to remove the deformation caused by the resizing of the parent" "Before aplying constraints we need to remove the deformation caused by the resizing of the parent"
[constraints-h constraints-v modifiers child {:keys [transform transform-inverse] :as parent} transformed-child-bounds transformed-parent-bounds] [constraints-h constraints-v modifiers
{:keys [transform transform-inverse] :as parent}
child-bounds transformed-child-bounds parent-bounds transformed-parent-bounds]
(let [child-bb-before (gpo/parent-coords-bounds (:points child) (:points parent)) (let [child-bb-before (gpo/parent-coords-bounds child-bounds parent-bounds)
child-bb-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds) child-bb-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds)
scale-x (if (= :scale constraints-h) scale-x (if (= :scale constraints-h)
@ -278,7 +280,7 @@
resize-transform-inverse)))) resize-transform-inverse))))
(defn calc-child-modifiers (defn calc-child-modifiers
[parent child modifiers ignore-constraints parent-bounds transformed-parent-bounds] [parent child modifiers ignore-constraints child-bounds parent-bounds transformed-parent-bounds]
(let [modifiers (ctm/select-child-modifiers modifiers) (let [modifiers (ctm/select-child-modifiers modifiers)
@ -295,10 +297,13 @@
(if (and (= :scale constraints-h) (= :scale constraints-v)) (if (and (= :scale constraints-h) (= :scale constraints-v))
modifiers modifiers
(let [child-bounds (:points child) (let [transformed-parent-bounds @transformed-parent-bounds
modifiers (ctm/select-child-modifiers modifiers) modifiers (ctm/select-child-modifiers modifiers)
transformed-child-bounds (gtr/transform-bounds child-bounds 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) modifiers (normalize-modifiers constraints-h constraints-v
modifiers parent
child-bounds transformed-child-bounds parent-bounds transformed-parent-bounds)
transformed-child-bounds (gtr/transform-bounds child-bounds modifiers) transformed-child-bounds (gtr/transform-bounds child-bounds modifiers)
child-points-before (gpo/parent-coords-bounds child-bounds parent-bounds) child-points-before (gpo/parent-coords-bounds child-bounds parent-bounds)

View file

@ -7,21 +7,19 @@
(ns app.common.geom.shapes.flex-layout.bounds (ns app.common.geom.shapes.flex-layout.bounds
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.points :as gpo] [app.common.geom.shapes.points :as gpo]
[app.common.geom.shapes.rect :as gre]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.types.shape.layout :as ctl])) [app.common.types.shape.layout :as ctl]))
(defn- child-layout-bound-points (defn- child-layout-bound-points
"Returns the bounds of the children as points" "Returns the bounds of the children as points"
[parent child] [parent child parent-bounds child-bounds]
(let [row? (ctl/row? parent) (let [row? (ctl/row? parent)
col? (ctl/col? parent) col? (ctl/col? parent)
hv (partial gpo/start-hv (:points parent)) hv (partial gpo/start-hv parent-bounds)
vv (partial gpo/start-vv (:points parent)) vv (partial gpo/start-vv parent-bounds)
v-start? (ctl/v-start? parent) v-start? (ctl/v-start? parent)
v-center? (ctl/v-center? parent) v-center? (ctl/v-center? parent)
@ -30,10 +28,10 @@
h-center? (ctl/h-center? parent) h-center? (ctl/h-center? parent)
h-end? (ctl/h-end? parent) h-end? (ctl/h-end? parent)
base-p (first (:points child)) base-p (gpo/origin child-bounds)
width (-> child :selrect :width) width (gpo/width-points child-bounds)
height (-> child :selrect :height) height (gpo/height-points child-bounds)
min-width (if (ctl/fill-width? child) min-width (if (ctl/fill-width? child)
(ctl/child-min-width child) (ctl/child-min-width child)
@ -91,22 +89,29 @@
(gpt/subtract (vv min-height))))))) (gpt/subtract (vv min-height)))))))
(defn layout-content-bounds (defn layout-content-bounds
[{:keys [layout-padding] :as parent} children] [bounds {:keys [layout-padding] :as parent} children]
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding (let [parent-id (:id parent)
parent-bounds @(get bounds parent-id)
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
pad-top (or pad-top 0) pad-top (or pad-top 0)
pad-right (or pad-right 0) pad-right (or pad-right 0)
pad-bottom (or pad-bottom 0) pad-bottom (or pad-bottom 0)
pad-left (or pad-left 0) pad-left (or pad-left 0)
child-bounds child-bounds
(fn [{:keys [points] :as child}] (fn [child]
(let [child-id (:id child)
child-bounds @(get bounds child-id)
child-bounds
(if (or (ctl/fill-height? child) (ctl/fill-height? child)) (if (or (ctl/fill-height? child) (ctl/fill-height? child))
(child-layout-bound-points parent child) (child-layout-bound-points parent child parent-bounds parent-bounds)
points))] child-bounds)]
(gpo/parent-coords-bounds child-bounds parent-bounds)))]
(as-> children $
(map child-bounds $)
(gpo/merge-parent-coords-bounds $ parent-bounds)
(gpo/pad-points $ (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))))
(-> (mapcat child-bounds children)
(gco/transform-points (gco/center-shape parent) (:transform-inverse parent))
(gre/squared-points)
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left))
(gre/points->rect))))

View file

@ -16,12 +16,9 @@
(defn normalize-child-modifiers (defn normalize-child-modifiers
"Apply the modifiers and then normalized them against the parent coordinates" "Apply the modifiers and then normalized them against the parent coordinates"
[modifiers {:keys [transform transform-inverse] :as parent} child transformed-parent-bounds] [modifiers {:keys [transform transform-inverse] :as parent} child-bounds parent-bounds transformed-parent-bounds]
(let [child-bounds (:points child) (let [transformed-child-bounds (gtr/transform-bounds child-bounds modifiers)
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-before (gpo/parent-coords-bounds child-bounds parent-bounds)
child-bb-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds) child-bb-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds)
@ -30,7 +27,7 @@
scale-y (/ (gpo/height-points child-bb-before) (gpo/height-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) resize-vector (gpt/point scale-x scale-y)
modif-transform (ctm/modifiers->transform modifiers) modif-transform (or (ctm/modifiers->transform modifiers) (gmt/matrix))
modif-transform-inverse (gmt/inverse modif-transform) modif-transform-inverse (gmt/inverse modif-transform)
resize-transform (gmt/multiply modif-transform transform) resize-transform (gmt/multiply modif-transform transform)
resize-transform-inverse (gmt/multiply transform-inverse modif-transform-inverse) resize-transform-inverse (gmt/multiply transform-inverse modif-transform-inverse)

View file

@ -92,7 +92,7 @@
(defn- set-children-modifiers (defn- set-children-modifiers
"Propagates the modifiers from a parent too its children applying constraints if necesary" "Propagates the modifiers from a parent too its children applying constraints if necesary"
[modif-tree objects parent transformed-parent-bounds ignore-constraints] [modif-tree objects bounds parent transformed-parent-bounds ignore-constraints]
(let [children (:shapes parent) (let [children (:shapes parent)
modifiers (dm/get-in modif-tree [(:id parent) :modifiers])] modifiers (dm/get-in modif-tree [(:id parent) :modifiers])]
@ -106,59 +106,67 @@
modif-tree)) modif-tree))
;; Check the constraints, then resize ;; Check the constraints, then resize
(let [parent-bounds (gtr/transform-bounds (:points parent) (ctm/select-parent-modifiers modifiers))] (let [parent-id (:id parent)
parent-bounds (gtr/transform-bounds @(get bounds parent-id) (ctm/select-parent-modifiers modifiers))]
(loop [modif-tree modif-tree (loop [modif-tree modif-tree
children (seq children)] children (seq children)]
(if (empty? children) (if (empty? children)
modif-tree modif-tree
(let [child-id (first children) (let [child-id (first children)
child (get objects child-id) child (get objects child-id)
child-modifiers (gct/calc-child-modifiers parent child modifiers ignore-constraints parent-bounds @transformed-parent-bounds)] child-bounds @(get bounds child-id)
child-modifiers (gct/calc-child-modifiers parent child modifiers ignore-constraints child-bounds parent-bounds transformed-parent-bounds)]
(recur (cond-> modif-tree (recur (cond-> modif-tree
(not (ctm/empty? child-modifiers)) (not (ctm/empty? child-modifiers))
(update-in [child-id :modifiers] ctm/add-modifiers child-modifiers)) (update-in [child-id :modifiers] ctm/add-modifiers child-modifiers))
(rest children))))))))) (rest children)))))))))
(defn- process-layout-children (defn- process-layout-children
[modif-tree objects parent transformed-parent-bounds] [modif-tree objects bounds parent transformed-parent-bounds]
(letfn [(process-child [modif-tree child] (letfn [(process-child [modif-tree child]
(let [modifiers (dm/get-in modif-tree [(:id parent) :modifiers]) (let [child-id (:id child)
parent-id (:id parent)
modifiers (dm/get-in modif-tree [parent-id :modifiers])
child-bounds @(get bounds child-id)
parent-bounds @(get bounds parent-id)
child-modifiers (-> modifiers child-modifiers (-> modifiers
(ctm/select-child-geometry-modifiers) (ctm/select-child-geometry-modifiers)
(gcl/normalize-child-modifiers parent child @transformed-parent-bounds))] (gcl/normalize-child-modifiers parent child-bounds parent-bounds @transformed-parent-bounds))]
(cond-> modif-tree (cond-> modif-tree
(not (ctm/empty? child-modifiers)) (not (ctm/empty? child-modifiers))
(update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers))))] (update-in [child-id :modifiers] ctm/add-modifiers child-modifiers))))]
(let [children (map (d/getf objects) (:shapes parent))] (let [children (map (d/getf objects) (:shapes parent))]
(reduce process-child modif-tree children)))) (reduce process-child modif-tree children))))
(defn get-bounds (defn get-group-bounds
[objects modif-tree shape] [objects bounds modif-tree shape]
(let [shape-id (:id shape)
(let [modifiers (-> (dm/get-in modif-tree [(:id shape) :modifiers]) modifiers (-> (dm/get-in modif-tree [shape-id :modifiers])
(ctm/select-geometry)) (ctm/select-geometry))
children (cph/get-immediate-children objects (:id shape)) children (cph/get-immediate-children objects shape-id)
bounds (cond group-bounds
(cond
(cph/group-shape? shape) (cph/group-shape? shape)
(let [children-bounds (->> children (mapv (partial get-bounds objects modif-tree)))] (let [children-bounds (->> children (mapv #(get-group-bounds objects bounds modif-tree %)))]
(gtr/group-bounds shape children-bounds)) (gtr/group-bounds shape children-bounds))
(cph/mask-shape? shape) (cph/mask-shape? shape)
(get-bounds objects modif-tree (-> children first)) (get-group-bounds objects bounds modif-tree (-> children first))
:else :else
(:points shape))] @(get bounds shape-id))]
(gtr/transform-bounds bounds modifiers)))
(cond-> group-bounds
(not (ctm/empty? modifiers))
(gtr/transform-bounds modifiers))))
(defn- set-layout-modifiers (defn- set-layout-modifiers
[modif-tree objects parent transformed-parent-bounds] [modif-tree objects bounds parent transformed-parent-bounds]
(letfn [(apply-modifiers [child] (letfn [(apply-modifiers [child]
[(-> (get-bounds objects modif-tree child) [(-> (get-group-bounds objects bounds modif-tree child)
(gpo/parent-coords-bounds @transformed-parent-bounds)) (gpo/parent-coords-bounds @transformed-parent-bounds))
child]) child])
@ -195,57 +203,63 @@
(defn- calc-auto-modifiers (defn- calc-auto-modifiers
"Calculates the modifiers to adjust the bounds for auto-width/auto-height shapes" "Calculates the modifiers to adjust the bounds for auto-width/auto-height shapes"
[objects parent] [objects bounds parent]
(letfn [(set-parent-auto-width (let [parent-id (:id parent)
[modifiers auto-width] parent-bounds (get bounds parent-id)
(let [origin (-> parent :points first)
scale-width (/ auto-width (-> parent :selrect :width) )] set-parent-auto-width
(fn [modifiers auto-width]
(let [origin (gpo/origin @parent-bounds)
scale-width (/ auto-width (gpo/width-points @parent-bounds))]
(-> modifiers (-> modifiers
(ctm/resize-parent (gpt/point scale-width 1) origin (:transform parent) (:transform-inverse parent))))) (ctm/resize-parent (gpt/point scale-width 1) origin (:transform parent) (:transform-inverse parent)))))
(set-parent-auto-height set-parent-auto-height
[modifiers auto-height] (fn [modifiers auto-height]
(let [origin (-> parent :points first) (let [origin (gpo/origin @parent-bounds)
scale-height (/ auto-height (-> parent :selrect :height) )] scale-height (/ auto-height (gpo/height-points @parent-bounds))]
(-> modifiers (-> modifiers
(ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))] (ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))
(let [children (->> parent :shapes (map (d/getf objects))) children (->> parent :shapes (map (d/getf objects)))
{auto-width :width auto-height :height} content-bounds
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent))) (when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
(gcl/layout-content-bounds parent children))] (gcl/layout-content-bounds bounds parent children))
auto-width (when content-bounds (gpo/width-points content-bounds))
auto-height (when content-bounds (gpo/height-points content-bounds))]
(cond-> (ctm/empty) (cond-> (ctm/empty)
(and (some? auto-width) (ctl/auto-width? parent)) (and (some? auto-width) (ctl/auto-width? parent))
(set-parent-auto-width auto-width) (set-parent-auto-width auto-width)
(and (some? auto-height) (ctl/auto-height? parent)) (and (some? auto-height) (ctl/auto-height? parent))
(set-parent-auto-height auto-height))))) (set-parent-auto-height auto-height))))
(defn- propagate-modifiers (defn- propagate-modifiers
"Propagate modifiers to its children" "Propagate modifiers to its children"
[objects ignore-constraints [modif-tree autolayouts] parent] [objects bounds ignore-constraints [modif-tree autolayouts] parent]
(let [parent-id (:id parent) (let [parent-id (:id parent)
root? (= uuid/zero parent-id) root? (= uuid/zero parent-id)
modifiers (-> (dm/get-in modif-tree [parent-id :modifiers]) modifiers (-> (dm/get-in modif-tree [parent-id :modifiers])
(ctm/select-geometry)) (ctm/select-geometry))
transformed-parent-bounds (delay (gtr/transform-bounds (:points parent) modifiers))
has-modifiers? (ctm/child-modifiers? modifiers) has-modifiers? (ctm/child-modifiers? modifiers)
layout? (ctl/layout? parent) layout? (ctl/layout? parent)
auto? (or (ctl/auto-height? parent) (ctl/auto-width? parent)) auto? (or (ctl/auto-height? parent) (ctl/auto-width? parent))
parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent)) parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent))
;; If the current child is inside the layout we ignore the constraints ;; If the current child is inside the layout we ignore the constraints
inside-layout? (ctl/inside-layout? objects parent)] inside-layout? (ctl/inside-layout? objects parent)
transformed-parent-bounds (delay (gtr/transform-bounds @(get bounds parent-id) modifiers))]
[(cond-> modif-tree [(cond-> modif-tree
(and (not layout?) has-modifiers? parent? (not root?)) (and (not layout?) has-modifiers? parent? (not root?))
(set-children-modifiers objects parent transformed-parent-bounds (or ignore-constraints inside-layout?)) (set-children-modifiers objects bounds parent transformed-parent-bounds (or ignore-constraints inside-layout?))
layout? layout?
(-> (process-layout-children objects parent transformed-parent-bounds) (-> (process-layout-children objects bounds parent transformed-parent-bounds)
(set-layout-modifiers objects parent transformed-parent-bounds))) (set-layout-modifiers objects bounds parent transformed-parent-bounds)))
;; Auto-width/height can change the positions in the parent so we need to recalculate ;; Auto-width/height can change the positions in the parent so we need to recalculate
(cond-> autolayouts auto? (conj (:id parent)))])) (cond-> autolayouts auto? (conj (:id parent)))]))
@ -258,28 +272,6 @@
(update id ctm/apply-structure-modifiers modifiers)))] (update id ctm/apply-structure-modifiers modifiers)))]
(reduce apply-shape objects modif-tree))) (reduce apply-shape objects modif-tree)))
(defn- apply-partial-objects-modifiers
[objects tree-seq modif-tree]
(letfn [(apply-shape [objects {:keys [id] :as shape}]
(let [modifiers (get-in modif-tree [id :modifiers])
object
(cond
(cph/mask-shape? shape)
(gtr/update-mask-selrect shape (cph/get-children objects id))
(cph/group-shape? shape)
(gtr/update-group-selrect shape (cph/get-children objects id))
(some? modifiers)
(gtr/transform-shape shape modifiers)
:else
shape)]
(assoc objects id object)))]
(reduce apply-shape objects (reverse tree-seq))))
(defn merge-modif-tree (defn merge-modif-tree
[modif-tree other-tree] [modif-tree other-tree]
(reduce (fn [modif-tree [id {:keys [modifiers]}]] (reduce (fn [modif-tree [id {:keys [modifiers]}]]
@ -287,41 +279,58 @@
modif-tree modif-tree
other-tree)) other-tree))
(defn transform-bounds
[bounds objects modif-tree]
(loop [result bounds
ids (keys modif-tree)]
(if (empty? ids)
result
(let [id (first ids)
shape (get objects id)
new-bounds (delay (get-group-bounds objects bounds modif-tree shape))
result (assoc result id new-bounds)]
(recur result (rest ids))))))
(defn sizing-auto-modifiers (defn sizing-auto-modifiers
"Recalculates the layouts to adjust the sizing: auto new sizes" "Recalculates the layouts to adjust the sizing: auto new sizes"
[modif-tree sizing-auto-layouts objects ignore-constraints] [modif-tree sizing-auto-layouts objects bounds ignore-constraints]
(loop [modif-tree modif-tree (loop [modif-tree modif-tree
bounds (transform-bounds bounds objects modif-tree)
sizing-auto-layouts (reverse sizing-auto-layouts)] sizing-auto-layouts (reverse sizing-auto-layouts)]
(if-let [current (first sizing-auto-layouts)] (if-let [current (first sizing-auto-layouts)]
(let [parent-base (get objects current) (let [parent-base (get objects current)
tree-seq (resolve-tree-sequence #{current} objects)
;; Apply the current stack of transformations so we can calculate the auto-layouts
objects (apply-partial-objects-modifiers objects tree-seq modif-tree)
resize-modif-tree resize-modif-tree
{current {:modifiers (calc-auto-modifiers objects parent-base)}} {current {:modifiers (calc-auto-modifiers objects bounds parent-base)}}
tree-seq (resolve-tree-sequence #{current} objects) tree-seq (resolve-tree-sequence #{current} objects)
[resize-modif-tree _] [resize-modif-tree _]
(reduce (partial propagate-modifiers objects ignore-constraints) [resize-modif-tree #{}] tree-seq) (reduce #(propagate-modifiers objects bounds ignore-constraints %1 %2) [resize-modif-tree #{}] tree-seq)
bounds (transform-bounds bounds objects resize-modif-tree)
modif-tree (merge-modif-tree modif-tree resize-modif-tree)] modif-tree (merge-modif-tree modif-tree resize-modif-tree)]
(recur modif-tree (rest sizing-auto-layouts))) (recur modif-tree bounds (rest sizing-auto-layouts)))
modif-tree))) modif-tree)))
(defn set-objects-modifiers (defn set-objects-modifiers
[modif-tree objects ignore-constraints snap-pixel?] [modif-tree objects ignore-constraints snap-pixel?]
(let [objects (apply-structure-modifiers objects modif-tree) (let [objects (apply-structure-modifiers objects modif-tree)
bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects) shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects)
[modif-tree sizing-auto-layouts] [modif-tree sizing-auto-layouts]
(reduce (partial propagate-modifiers objects ignore-constraints) [modif-tree #{}] shapes-tree) (reduce #(propagate-modifiers objects bounds ignore-constraints %1 %2) [modif-tree #{}] shapes-tree)
;; Calculate hug layouts positions ;; Calculate hug layouts positions
modif-tree (sizing-auto-modifiers modif-tree sizing-auto-layouts objects ignore-constraints) modif-tree
(-> modif-tree
(sizing-auto-modifiers sizing-auto-layouts objects bounds ignore-constraints))
modif-tree modif-tree
(cond-> modif-tree (cond-> modif-tree

View file

@ -37,7 +37,7 @@
(defn position-pixel-precision (defn position-pixel-precision
[modifiers _ points] [modifiers _ points]
(let [bounds (gpr/points->rect points) (let [bounds (gpr/bounds->rect points)
corner (gpt/point bounds) corner (gpt/point bounds)
target-corner (gpt/round corner) target-corner (gpt/round corner)
deltav (gpt/to-vec corner target-corner)] deltav (gpt/to-vec corner target-corner)]

View file

@ -7,7 +7,8 @@
(ns app.common.geom.shapes.points (ns app.common.geom.shapes.points
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes.intersect :as gsi])) [app.common.geom.shapes.intersect :as gsi]
[app.common.math :as mth]))
(defn origin (defn origin
[points] [points]
@ -70,45 +71,66 @@
(-> p2 (gpt/add right-v) (gpt/add bottom-v)) (-> p2 (gpt/add right-v) (gpt/add bottom-v))
(-> p3 (gpt/add left-v) (gpt/add bottom-v))]))) (-> p3 (gpt/add left-v) (gpt/add bottom-v))])))
(defn- project-t
"Given a point and a line returns the parametric t the cross point with the line going through the other axis projected"
[point [start end] other-axis-vec]
(let [line-vec (gpt/to-vec start end)
#_(defn parent-coords-rect pr-point (gsi/line-line-intersect point (gpt/add point other-axis-vec) start end)]
[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 (cond
(and (<= da db) (<= da dc) (<= da dd)) (not (mth/almost-zero? (:x line-vec)))
[a b c d] (/ (- (:x pr-point) (:x start)) (:x line-vec))
(and (<= db da) (<= db dc) (<= db dd)) (not (mth/almost-zero? (:y line-vec)))
[b c d a] (/ (- (:y pr-point) (:y start)) (:y line-vec))
(and (<= dc da) (<= dc db) (<= dc dd))
[c d a b]
;; Vector is almost zero
:else :else
[d a b c]))) 0)))
(defn parent-coords-bounds (defn parent-coords-bounds
[bounds [p1 p2 _ p4]] [child-bounds [p1 p2 _ p4 :as parent-bounds]]
(if (empty? child-bounds)
parent-bounds
(let [rh [p1 p2]
rv [p1 p4]
(let [[b1 b2 b3 b4] (closest-first bounds p1 p2)
hv (gpt/to-vec p1 p2) hv (gpt/to-vec p1 p2)
vv (gpt/to-vec p1 p4) vv (gpt/to-vec p1 p4)
i1 (gsi/line-line-intersect b1 (gpt/add hv b1) b4 (gpt/add b4 vv)) ph #(gpt/add p1 (gpt/scale hv %))
i2 (gsi/line-line-intersect b1 (gpt/add hv b1) b2 (gpt/add b2 vv)) pv #(gpt/add p1 (gpt/scale 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))] find-boundary-ts
[i1 i2 i3 i4])) (fn [[th-min th-max tv-min tv-max] current-point]
(let [cth (project-t current-point rh vv)
ctv (project-t current-point rv hv)]
[(min th-min cth)
(max th-max cth)
(min tv-min ctv)
(max tv-max ctv)]))
[th-min th-max tv-min tv-max]
(->> child-bounds (reduce find-boundary-ts [##Inf ##-Inf ##Inf ##-Inf]))
minv-start (pv tv-min)
minv-end (gpt/add minv-start hv)
minh-start (ph th-min)
minh-end (gpt/add minh-start vv)
maxv-start (pv tv-max)
maxv-end (gpt/add maxv-start hv)
maxh-start (ph th-max)
maxh-end (gpt/add maxh-start vv)
i1 (gsi/line-line-intersect minv-start minv-end minh-start minh-end)
i2 (gsi/line-line-intersect minv-start minv-end maxh-start maxh-end)
i3 (gsi/line-line-intersect maxv-start maxv-end maxh-start maxh-end)
i4 (gsi/line-line-intersect maxv-start maxv-end minh-start minh-end)]
[i1 i2 i3 i4])))
(defn merge-parent-coords-bounds
[bounds parent-bounds]
(parent-coords-bounds (flatten bounds) parent-bounds))

View file

@ -91,6 +91,15 @@
(when (d/num? minx miny maxx maxy) (when (d/num? minx miny maxx maxy)
(make-rect minx miny (- maxx minx) (- maxy miny)))))) (make-rect minx miny (- maxx minx) (- maxy miny))))))
(defn bounds->rect
[[{ax :x ay :y} {bx :x by :y} {cx :x cy :y} {dx :x dy :y}]]
(let [minx (min ax bx cx dx)
miny (min ay by cy dy)
maxx (max ax bx cx dx)
maxy (max ay by cy dy)]
(when (d/num? minx miny maxx maxy)
(make-rect minx miny (- maxx minx) (- maxy miny)))))
(defn squared-points (defn squared-points
[points] [points]
(when (d/not-empty? points) (when (d/not-empty? points)

View file

@ -463,18 +463,19 @@
(apply-modifiers modifiers))))) (apply-modifiers modifiers)))))
(defn apply-objects-modifiers (defn apply-objects-modifiers
[objects modifiers] ([objects modifiers]
(apply-objects-modifiers objects modifiers (keys modifiers)))
([objects modifiers ids]
(loop [objects objects (loop [objects objects
entry (first modifiers) ids (seq ids)]
modifiers (rest modifiers)] (if (empty? ids)
(if (nil? entry)
objects objects
(let [[id modifier] entry]
(recur (d/update-when objects id transform-shape (:modifiers modifier)) (let [id (first ids)
(first modifiers) modifier (dm/get-in modifiers [id :modifiers])]
(rest modifiers)))))) (recur (d/update-when objects id transform-shape modifier)
(rest ids)))))))
(defn transform-bounds (defn transform-bounds
([points modifiers] ([points modifiers]

View file

@ -106,6 +106,12 @@
parent (get objects parent-id)] parent (get objects parent-id)]
(layout? parent))) (layout? parent)))
(defn layout-child-id? [objects id]
(let [shape (get objects id)
parent-id (:parent-id shape)
parent (get objects parent-id)]
(layout? parent)))
(defn inside-layout? (defn inside-layout?
"Check if the shape is inside a layout" "Check if the shape is inside a layout"
[objects shape] [objects shape]

View file

@ -14,6 +14,7 @@
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shapes-update-layout :as dwul]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -174,15 +175,22 @@
(cph/frame-shape? shape) (cph/frame-shape? shape)
(remove-frame-changes it page-id shape objects)))) (remove-frame-changes it page-id shape objects))))
selected (wsh/lookup-selected state)
changes-list (sequence changes-list (sequence
(keep prepare) (keep prepare)
(wsh/lookup-selected state)) selected)
parents (into #{}
(comp (map #(cph/get-parent objects %))
(keep :id))
selected)
changes {:redo-changes (vec (mapcat :redo-changes changes-list)) changes {:redo-changes (vec (mapcat :redo-changes changes-list))
:undo-changes (vec (mapcat :undo-changes changes-list)) :undo-changes (vec (mapcat :undo-changes changes-list))
:origin it}] :origin it}]
(rx/of (dch/commit-changes changes)))))) (rx/of (dch/commit-changes changes)
(dwul/update-layout-positions parents))))))
(def mask-group (def mask-group
(ptk/reify ::mask-group (ptk/reify ::mask-group

View file

@ -443,7 +443,8 @@
exclude-frames-siblings exclude-frames-siblings
(into exclude-frames (into exclude-frames
(mapcat (partial cph/get-siblings-ids objects)) (comp (mapcat (partial cph/get-siblings-ids objects))
(filter (partial ctl/layout-child-id? objects)))
selected) selected)
fix-axis fix-axis

View file

@ -80,7 +80,8 @@
modifiers (mf/deref refs/workspace-modifiers) modifiers (mf/deref refs/workspace-modifiers)
objects-modified (mf/with-memo [base-objects modifiers] objects-modified (mf/with-memo [base-objects modifiers]
(gsh/apply-objects-modifiers base-objects modifiers)) (gsh/apply-objects-modifiers base-objects modifiers selected))
background (get options :background clr/canvas) background (get options :background clr/canvas)
;; STATE ;; STATE
@ -423,8 +424,14 @@
:hover-top-frame-id @hover-top-frame-id :hover-top-frame-id @hover-top-frame-id
:zoom zoom}]) :zoom zoom}])
(when (debug? :layout-content-bounds)
[:& wvd/debug-content-bounds {:selected-shapes selected-shapes
:objects objects-modified
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (debug? :layout-lines) (when (debug? :layout-lines)
[:& wvd/debug-layout {:selected-shapes selected-shapes [:& wvd/debug-layout-lines {:selected-shapes selected-shapes
:objects objects-modified :objects objects-modified
:hover-top-frame-id @hover-top-frame-id :hover-top-frame-id @hover-top-frame-id
:zoom zoom}]) :zoom zoom}])

View file

@ -19,7 +19,7 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
;; Helper to debug the bounds when set the "hug" content property ;; Helper to debug the bounds when set the "hug" content property
#_(mf/defc debug-layout (mf/defc debug-content-bounds
"Debug component to show the auto-layout drop areas" "Debug component to show the auto-layout drop areas"
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
@ -36,27 +36,12 @@
(when (and shape (:layout shape)) (when (and shape (:layout shape))
(let [children (cph/get-immediate-children objects (:id shape)) (let [children (cph/get-immediate-children objects (:id shape))
layout-data (gsl/calc-layout-data shape children) layout-bounds (gsl/layout-content-bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points])) shape children)]
[:g.debug-layout {:pointer-events "none"}
[:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
:style {:stroke "red" :fill "none"}}]]))))
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} (:layout-padding shape) (mf/defc debug-layout-lines
pad-top (or pad-top 0)
pad-right (or pad-right 0)
pad-bottom (or pad-bottom 0)
pad-left (or pad-left 0)
layout-bounds (gsl/layout-content-bounds shape children)]
[:g.debug-layout {:pointer-events "none"
:transform (gsh/transform-str shape)}
[:rect {:x (:x layout-bounds)
:y (:y layout-bounds)
:width (:width layout-bounds)
:height (:height layout-bounds)
:style {:stroke "red"
:fill "none"}}]]))))
(mf/defc debug-layout
"Debug component to show the auto-layout drop areas" "Debug component to show the auto-layout drop areas"
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]

View file

@ -74,6 +74,9 @@
;; Display the layout lines ;; Display the layout lines
:layout-lines :layout-lines
;; Display the bounds for the hug content adjust
:layout-content-bounds
;; Makes the pixel grid red so its more visibile ;; Makes the pixel grid red so its more visibile
:pixel-grid :pixel-grid