diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index c89794e9d..8e4424655 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -688,3 +688,12 @@ (if (contains? set value) (disj 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)) diff --git a/common/src/app/common/geom/point.cljc b/common/src/app/common/geom/point.cljc index 36b0c9fad..5bc1d4e5e 100644 --- a/common/src/app/common/geom/point.cljc +++ b/common/src/app/common/geom/point.cljc @@ -252,6 +252,10 @@ (let [v-length (length v)] (divide v (point v-length v-length)))) +(defn perpendicular + [{:keys [x y]}] + (Point. (- y) x)) + (defn project "V1 perpendicular projection on vector V2" [v1 v2] diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index b5e060214..35ddd66a8 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -251,9 +251,11 @@ (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 {: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) scale-x (if (= :scale constraints-h) @@ -278,7 +280,7 @@ resize-transform-inverse)))) (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) @@ -295,10 +297,13 @@ (if (and (= :scale constraints-h) (= :scale constraints-v)) modifiers - (let [child-bounds (:points child) + (let [transformed-parent-bounds @transformed-parent-bounds + 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) + 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) child-points-before (gpo/parent-coords-bounds child-bounds parent-bounds) diff --git a/common/src/app/common/geom/shapes/flex_layout/bounds.cljc b/common/src/app/common/geom/shapes/flex_layout/bounds.cljc index 07552e540..a24e81214 100644 --- a/common/src/app/common/geom/shapes/flex_layout/bounds.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/bounds.cljc @@ -7,21 +7,19 @@ (ns app.common.geom.shapes.flex-layout.bounds (:require [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 gre] [app.common.math :as mth] [app.common.types.shape.layout :as ctl])) (defn- child-layout-bound-points "Returns the bounds of the children as points" - [parent child] + [parent child parent-bounds child-bounds] (let [row? (ctl/row? parent) col? (ctl/col? parent) - hv (partial gpo/start-hv (:points parent)) - vv (partial gpo/start-vv (:points parent)) + hv (partial gpo/start-hv parent-bounds) + vv (partial gpo/start-vv parent-bounds) v-start? (ctl/v-start? parent) v-center? (ctl/v-center? parent) @@ -30,10 +28,10 @@ h-center? (ctl/h-center? parent) h-end? (ctl/h-end? parent) - base-p (first (:points child)) + base-p (gpo/origin child-bounds) - width (-> child :selrect :width) - height (-> child :selrect :height) + width (gpo/width-points child-bounds) + height (gpo/height-points child-bounds) min-width (if (ctl/fill-width? child) (ctl/child-min-width child) @@ -91,22 +89,29 @@ (gpt/subtract (vv min-height))))))) (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-right (or pad-right 0) pad-bottom (or pad-bottom 0) pad-left (or pad-left 0) child-bounds - (fn [{:keys [points] :as child}] - (if (or (ctl/fill-height? child) (ctl/fill-height? child)) - (child-layout-bound-points parent child) - points))] + (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)) + (child-layout-bound-points parent child parent-bounds parent-bounds) + 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)))) diff --git a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc index 406a31927..8ad936478 100644 --- a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc @@ -16,12 +16,9 @@ (defn normalize-child-modifiers "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) - parent-bounds (:points parent) - - transformed-child-bounds (gtr/transform-bounds child-bounds modifiers) + (let [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) @@ -30,7 +27,7 @@ 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 (or (ctm/modifiers->transform modifiers) (gmt/matrix)) modif-transform-inverse (gmt/inverse modif-transform) resize-transform (gmt/multiply modif-transform transform) resize-transform-inverse (gmt/multiply transform-inverse modif-transform-inverse) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 5b022868e..d0588cfa9 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -92,7 +92,7 @@ (defn- set-children-modifiers "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) modifiers (dm/get-in modif-tree [(:id parent) :modifiers])] @@ -106,59 +106,67 @@ modif-tree)) ;; 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 children (seq children)] (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)] + (let [child-id (first children) + child (get objects child-id) + 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 (not (ctm/empty? child-modifiers)) (update-in [child-id :modifiers] ctm/add-modifiers child-modifiers)) (rest 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] - (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 (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 (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))] (reduce process-child modif-tree children)))) -(defn get-bounds - [objects modif-tree shape] - - (let [modifiers (-> (dm/get-in modif-tree [(:id shape) :modifiers]) +(defn get-group-bounds + [objects bounds modif-tree shape] + (let [shape-id (:id shape) + modifiers (-> (dm/get-in modif-tree [shape-id :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)) + children (cph/get-immediate-children objects shape-id) + group-bounds + (cond + (cph/group-shape? shape) + (let [children-bounds (->> children (mapv #(get-group-bounds objects bounds modif-tree %)))] + (gtr/group-bounds shape children-bounds)) - (cph/mask-shape? shape) - (get-bounds objects modif-tree (-> children first)) + (cph/mask-shape? shape) + (get-group-bounds objects bounds modif-tree (-> children first)) - :else - (:points shape))] - - (gtr/transform-bounds bounds modifiers))) + :else + @(get bounds shape-id))] + (cond-> group-bounds + (not (ctm/empty? modifiers)) + (gtr/transform-bounds modifiers)))) (defn- set-layout-modifiers - [modif-tree objects parent transformed-parent-bounds] + [modif-tree objects bounds parent transformed-parent-bounds] (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)) child]) @@ -195,57 +203,63 @@ (defn- calc-auto-modifiers "Calculates the modifiers to adjust the bounds for auto-width/auto-height shapes" - [objects parent] - (letfn [(set-parent-auto-width - [modifiers auto-width] - (let [origin (-> parent :points first) - scale-width (/ auto-width (-> parent :selrect :width) )] - (-> modifiers - (ctm/resize-parent (gpt/point scale-width 1) origin (:transform parent) (:transform-inverse parent))))) + [objects bounds parent] + (let [parent-id (:id parent) + parent-bounds (get bounds parent-id) - (set-parent-auto-height - [modifiers auto-height] - (let [origin (-> parent :points first) - scale-height (/ auto-height (-> parent :selrect :height) )] - (-> modifiers - (ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))] + set-parent-auto-width + (fn [modifiers auto-width] + (let [origin (gpo/origin @parent-bounds) + scale-width (/ auto-width (gpo/width-points @parent-bounds))] + (-> modifiers + (ctm/resize-parent (gpt/point scale-width 1) 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) + set-parent-auto-height + (fn [modifiers auto-height] + (let [origin (gpo/origin @parent-bounds) + scale-height (/ auto-height (gpo/height-points @parent-bounds))] + (-> modifiers + (ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent))))) - (and (some? auto-height) (ctl/auto-height? parent)) - (set-parent-auto-height auto-height))))) + children (->> parent :shapes (map (d/getf objects))) + content-bounds + (when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent))) + (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) + (and (some? auto-width) (ctl/auto-width? parent)) + (set-parent-auto-width auto-width) + + (and (some? auto-height) (ctl/auto-height? parent)) + (set-parent-auto-height auto-height)))) (defn- propagate-modifiers "Propagate modifiers to its children" - [objects ignore-constraints [modif-tree autolayouts] parent] - (let [parent-id (:id parent) - root? (= uuid/zero parent-id) - modifiers (-> (dm/get-in modif-tree [parent-id :modifiers]) - (ctm/select-geometry)) - - transformed-parent-bounds (delay (gtr/transform-bounds (:points parent) modifiers)) - + [objects bounds ignore-constraints [modif-tree autolayouts] parent] + (let [parent-id (:id parent) + root? (= uuid/zero parent-id) + modifiers (-> (dm/get-in modif-tree [parent-id :modifiers]) + (ctm/select-geometry)) has-modifiers? (ctm/child-modifiers? modifiers) - layout? (ctl/layout? parent) - auto? (or (ctl/auto-height? parent) (ctl/auto-width? parent)) - parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent)) + layout? (ctl/layout? 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 - 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 (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? - (-> (process-layout-children objects parent transformed-parent-bounds) - (set-layout-modifiers objects parent transformed-parent-bounds))) + (-> (process-layout-children objects bounds 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 (cond-> autolayouts auto? (conj (:id parent)))])) @@ -258,28 +272,6 @@ (update id ctm/apply-structure-modifiers modifiers)))] (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 [modif-tree other-tree] (reduce (fn [modif-tree [id {:keys [modifiers]}]] @@ -287,41 +279,58 @@ modif-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 "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 + bounds (transform-bounds bounds objects modif-tree) sizing-auto-layouts (reverse sizing-auto-layouts)] (if-let [current (first sizing-auto-layouts)] (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 - {current {:modifiers (calc-auto-modifiers objects parent-base)}} + {current {:modifiers (calc-auto-modifiers objects bounds parent-base)}} tree-seq (resolve-tree-sequence #{current} objects) [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)] - (recur modif-tree (rest sizing-auto-layouts))) + (recur modif-tree bounds (rest sizing-auto-layouts))) modif-tree))) (defn set-objects-modifiers [modif-tree objects ignore-constraints snap-pixel?] (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) [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 - 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 (cond-> modif-tree diff --git a/common/src/app/common/geom/shapes/pixel_precision.cljc b/common/src/app/common/geom/shapes/pixel_precision.cljc index 8b5c8b053..7b4428064 100644 --- a/common/src/app/common/geom/shapes/pixel_precision.cljc +++ b/common/src/app/common/geom/shapes/pixel_precision.cljc @@ -37,7 +37,7 @@ (defn position-pixel-precision [modifiers _ points] - (let [bounds (gpr/points->rect points) + (let [bounds (gpr/bounds->rect points) corner (gpt/point bounds) target-corner (gpt/round corner) deltav (gpt/to-vec corner target-corner)] diff --git a/common/src/app/common/geom/shapes/points.cljc b/common/src/app/common/geom/shapes/points.cljc index 3bbfdeaa7..f8ea54369 100644 --- a/common/src/app/common/geom/shapes/points.cljc +++ b/common/src/app/common/geom/shapes/points.cljc @@ -7,7 +7,8 @@ (ns app.common.geom.shapes.points (:require [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 [points] @@ -70,45 +71,66 @@ (-> p2 (gpt/add right-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] - -#_(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)] - + (let [line-vec (gpt/to-vec start end) + pr-point (gsi/line-line-intersect point (gpt/add point other-axis-vec) start end)] (cond - (and (<= da db) (<= da dc) (<= da dd)) - [a b c d] + (not (mth/almost-zero? (:x line-vec))) + (/ (- (:x pr-point) (:x start)) (:x line-vec)) - (and (<= db da) (<= db dc) (<= db dd)) - [b c d a] - - (and (<= dc da) (<= dc db) (<= dc dd)) - [c d a b] + (not (mth/almost-zero? (:y line-vec))) + (/ (- (:y pr-point) (:y start)) (:y line-vec)) + ;; Vector is almost zero :else - [d a b c]))) + 0))) (defn parent-coords-bounds - [bounds [p1 p2 _ p4]] + [child-bounds [p1 p2 _ p4 :as parent-bounds]] - (let [[b1 b2 b3 b4] (closest-first bounds p1 p2) - hv (gpt/to-vec p1 p2) - vv (gpt/to-vec p1 p4) + (if (empty? child-bounds) + parent-bounds - 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])) + (let [rh [p1 p2] + rv [p1 p4] + + hv (gpt/to-vec p1 p2) + vv (gpt/to-vec p1 p4) + + ph #(gpt/add p1 (gpt/scale hv %)) + pv #(gpt/add p1 (gpt/scale vv %)) + + find-boundary-ts + (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)) diff --git a/common/src/app/common/geom/shapes/rect.cljc b/common/src/app/common/geom/shapes/rect.cljc index be8d5a5b5..672928a61 100644 --- a/common/src/app/common/geom/shapes/rect.cljc +++ b/common/src/app/common/geom/shapes/rect.cljc @@ -91,6 +91,15 @@ (when (d/num? minx miny maxx maxy) (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 [points] (when (d/not-empty? points) diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 43d8902e2..38a4ee7e6 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -463,18 +463,19 @@ (apply-modifiers modifiers))))) (defn apply-objects-modifiers - [objects modifiers] + ([objects modifiers] + (apply-objects-modifiers objects modifiers (keys modifiers))) - (loop [objects objects - entry (first modifiers) - modifiers (rest modifiers)] + ([objects modifiers ids] + (loop [objects objects + ids (seq ids)] + (if (empty? ids) + objects - (if (nil? entry) - objects - (let [[id modifier] entry] - (recur (d/update-when objects id transform-shape (:modifiers modifier)) - (first modifiers) - (rest modifiers)))))) + (let [id (first ids) + modifier (dm/get-in modifiers [id :modifiers])] + (recur (d/update-when objects id transform-shape modifier) + (rest ids))))))) (defn transform-bounds ([points modifiers] diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index c8b0e0c36..1fb0f4050 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -106,6 +106,12 @@ parent (get objects parent-id)] (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? "Check if the shape is inside a layout" [objects shape] diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index bc7c5bc27..4a5fad453 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -14,6 +14,7 @@ [app.common.types.shape-tree :as ctt] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.selection :as dws] + [app.main.data.workspace.shapes-update-layout :as dwul] [app.main.data.workspace.state-helpers :as wsh] [beicon.core :as rx] [potok.core :as ptk])) @@ -174,15 +175,22 @@ (cph/frame-shape? shape) (remove-frame-changes it page-id shape objects)))) + selected (wsh/lookup-selected state) changes-list (sequence (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)) :undo-changes (vec (mapcat :undo-changes changes-list)) :origin it}] - (rx/of (dch/commit-changes changes)))))) + (rx/of (dch/commit-changes changes) + (dwul/update-layout-positions parents)))))) (def mask-group (ptk/reify ::mask-group diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 96088ec36..14cac6d30 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -443,7 +443,8 @@ exclude-frames-siblings (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) fix-axis diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index e50758d55..30514cfe3 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -80,7 +80,8 @@ modifiers (mf/deref refs/workspace-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) ;; STATE @@ -423,11 +424,17 @@ :hover-top-frame-id @hover-top-frame-id :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) - [:& wvd/debug-layout {:selected-shapes selected-shapes - :objects objects-modified - :hover-top-frame-id @hover-top-frame-id - :zoom zoom}]) + [:& wvd/debug-layout-lines {:selected-shapes selected-shapes + :objects objects-modified + :hover-top-frame-id @hover-top-frame-id + :zoom zoom}]) (when (debug? :parent-bounds) [:& wvd/debug-parent-bounds {:selected-shapes selected-shapes diff --git a/frontend/src/app/main/ui/workspace/viewport/debug.cljs b/frontend/src/app/main/ui/workspace/viewport/debug.cljs index 14da00e68..5da0460f8 100644 --- a/frontend/src/app/main/ui/workspace/viewport/debug.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/debug.cljs @@ -19,7 +19,7 @@ [rumext.v2 :as mf])) ;; 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" {::mf/wrap-props false} [props] @@ -36,27 +36,12 @@ (when (and shape (:layout 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) - 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 +(mf/defc debug-layout-lines "Debug component to show the auto-layout drop areas" {::mf/wrap-props false} [props] diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index 18900f901..81ea45c60 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -74,6 +74,9 @@ ;; Display the layout lines :layout-lines + ;; Display the bounds for the hug content adjust + :layout-content-bounds + ;; Makes the pixel grid red so its more visibile :pixel-grid