diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index 1d7b4959ee..adfa661898 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -884,3 +884,13 @@ (extend-protocol ICloseable AutoCloseable (close! [this] (.close this)))) + +(defn take-until + "Returns a lazy sequence of successive items from coll until + (pred item) returns true, including that item" + ([pred] + (halt-when pred (fn [r h] (conj r h)))) + + ([pred coll] + (transduce (take-until pred) conj [] coll))) + diff --git a/common/src/app/common/geom/bounds_map.cljc b/common/src/app/common/geom/bounds_map.cljc index 6a653a17dc..aa8f11d170 100644 --- a/common/src/app/common/geom/bounds_map.cljc +++ b/common/src/app/common/geom/bounds_map.cljc @@ -11,8 +11,10 @@ [app.common.geom.shapes.common :as gco] [app.common.geom.shapes.points :as gpo] [app.common.geom.shapes.transforms :as gtr] + [app.common.math :as mth] [app.common.pages.helpers :as cph] - [app.common.types.modifiers :as ctm])) + [app.common.types.modifiers :as ctm] + [app.common.uuid :as uuid])) (defn objects->bounds-map [objects] @@ -20,51 +22,82 @@ (keys objects) #(gco/shape->points (get objects %)))) -(defn shape->bounds - "Retrieve the shape bounds" +(defn- create-bounds + "Create the bounds object for the current shape in this context" ([shape bounds-map objects] - (shape->bounds shape bounds-map objects nil)) + (create-bounds shape bounds-map objects nil nil)) - ([{:keys [id] :as shape} bounds-map objects modif-tree] - (let [shape-modifiers - (if modif-tree - (-> (dm/get-in modif-tree [id :modifiers]) - (ctm/select-geometry)) - (ctm/empty)) + ([shape bounds-map objects modif-tree] + (create-bounds shape bounds-map objects modif-tree nil)) - children (cph/get-immediate-children objects id)] + ([{:keys [id] :as shape} bounds-map objects modif-tree current-ref] + (cond + (and (cph/mask-shape? shape) (d/not-empty? (:shapes shape))) + (create-bounds (get objects (first (:shapes shape))) bounds-map objects modif-tree) - (cond - (and (cph/mask-shape? shape) (seq children)) - (shape->bounds (-> children first) bounds-map objects modif-tree) + (cph/group-shape? shape) + (let [modifiers (dm/get-in modif-tree [id :modifiers]) + children (cph/get-immediate-children objects id) + shape-bounds (if current-ref @current-ref @(get bounds-map id)) + current-bounds + (cond-> shape-bounds + (not (ctm/empty? modifiers)) + (gtr/transform-bounds modifiers)) - (cph/group-shape? shape) - (let [;; Transform here to then calculate the bounds relative to the transform - current-bounds - (cond-> @(get bounds-map id) - (not (ctm/empty? shape-modifiers)) - (gtr/transform-bounds shape-modifiers)) + children-bounds + (->> children + (mapv #(deref (get bounds-map (:id %)))))] + (gpo/merge-parent-coords-bounds children-bounds current-bounds)) - children-bounds - (->> children - (mapv #(shape->bounds % bounds-map objects modif-tree)))] - (gpo/merge-parent-coords-bounds children-bounds current-bounds)) - - :else - (cond-> @(get bounds-map id) - (not (ctm/empty? shape-modifiers)) - (gtr/transform-bounds shape-modifiers)))))) + :else + (let [modifiers (dm/get-in modif-tree [id :modifiers]) + shape-bounds (if current-ref @current-ref @(get bounds-map id))] + (cond-> shape-bounds + (not (ctm/empty? modifiers)) + (gtr/transform-bounds modifiers)))))) (defn transform-bounds-map - ([bounds-map objects modif-tree] - (transform-bounds-map bounds-map objects modif-tree (->> (keys modif-tree) (map #(get objects %))))) + [bounds-map objects modif-tree] + ;; We use the volatile in order to solve the dependencies problem. We want the groups to reference the new + ;; bounds instead of the old ones. The current as last parameter is to fix a possible infinite loop + ;; with self-references + (let [bm-holder (volatile! nil) - ([bounds-map objects modif-tree tree-seq] - (->> tree-seq - reverse - (reduce - (fn [bounds-map shape] - (assoc bounds-map - (:id shape) - (delay (shape->bounds shape bounds-map objects modif-tree)))) - bounds-map)))) + ;; These are the new bounds calculated. Are the "modified" plus any groups they belong to + ids (keys modif-tree) + ids (into (set ids) + (mapcat #(->> (cph/get-parent-ids-seq objects %) + (take-while (partial cph/group-like-shape? objects)))) + ids) + + new-bounds-map + (->> ids + (reduce + (fn [tr-bounds-map shape-id] + (cond-> tr-bounds-map + (not= uuid/zero shape-id) + (assoc! shape-id + (delay (create-bounds (get objects shape-id) + @bm-holder + objects + modif-tree + (get bounds-map shape-id)))))) + (transient bounds-map)) + (persistent!))] + (vreset! bm-holder new-bounds-map) + new-bounds-map)) + +;; Tool for debugging +(defn bounds-map + [objects bounds-map] + (letfn [(parse-bound [[id bounds*]] + (let [bounds (deref bounds*) + shape (get objects id)] + (when (and shape bounds) + [(:name shape) + {:x (mth/round (:x (gpo/origin bounds)) 2) + :y (mth/round (:y (gpo/origin bounds)) 2) + :width (mth/round (gpo/width-points bounds) 2) + :height (mth/round (gpo/height-points bounds) 2)}])))] + + (into {} (keep parse-bound) bounds-map))) diff --git a/common/src/app/common/geom/modif_tree.cljc b/common/src/app/common/geom/modif_tree.cljc index 70ce9c0aab..709012d7d3 100644 --- a/common/src/app/common/geom/modif_tree.cljc +++ b/common/src/app/common/geom/modif_tree.cljc @@ -12,6 +12,7 @@ [app.common.types.modifiers :as ctm])) (defn add-modifiers + "Add the given modifiers to the map of modifiers." [modif-tree id modifiers] (if (ctm/empty? modifiers) modif-tree @@ -27,6 +28,7 @@ (assoc-in [id :modifiers] new-modifiers))))) (defn merge-modif-tree + "Merge two maps of modifiers into a single one" [modif-tree other-tree] (reduce (fn [modif-tree [id {:keys [modifiers]}]] @@ -35,6 +37,7 @@ other-tree)) (defn apply-structure-modifiers + "Only applies the structure modifiers to the objects tree map" [objects modif-tree] (letfn [(update-children-structure-modifiers [objects ids modifiers] diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index 9b3e9d0e19..995d975413 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -280,11 +280,11 @@ (/ (gpo/height-points child-bb-before) (max 0.01 (gpo/height-points child-bb-after)))) resize-vector (gpt/point scale-x scale-y) - resize-origin (gpo/origin transformed-child-bounds) + resize-origin (gpo/origin child-bb-after) - center (gco/points->center transformed-child-bounds) - selrect (gtr/calculate-selrect transformed-child-bounds center) - transform (gtr/calculate-transform transformed-child-bounds center selrect) + center (gco/points->center child-bb-after) + selrect (gtr/calculate-selrect child-bb-after center) + transform (gtr/calculate-transform child-bb-after center selrect) transform-inverse (when (some? transform) (gmt/inverse transform))] (ctm/resize modifiers resize-vector resize-origin transform transform-inverse))) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 8a9c1c5312..a9c888f828 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -43,36 +43,28 @@ modif-tree (ctm/only-move? modifiers) - (loop [modif-tree modif-tree - children (seq children)] - (if-let [current (first children)] - (recur (cgt/add-modifiers modif-tree current modifiers) - (rest children)) - modif-tree)) + (reduce #(cgt/add-modifiers %1 %2 modifiers) modif-tree children) ;; Check the constraints, then resize :else (let [parent-id (:id parent) parent-bounds (gtr/transform-bounds @(get bounds parent-id) (ctm/select-parent modifiers))] - (loop [modif-tree modif-tree - children (seq children)] - (if (empty? children) - modif-tree - (let [child-id (first children) - child (get objects child-id)] - (if (some? child) - (let [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 (cgt/add-modifiers modif-tree child-id child-modifiers) - (rest children))) - (recur modif-tree (rest children)))))))))) + (->> children + (reduce + (fn [modif-tree child-id] + (if-let [child (get objects child-id)] + (let [child-bounds @(get bounds child-id) + child-modifiers + (gct/calc-child-modifiers parent child modifiers ignore-constraints child-bounds parent-bounds transformed-parent-bounds)] + (cgt/add-modifiers modif-tree child-id child-modifiers)) + modif-tree)) + modif-tree)))))) (defn- set-flex-layout-modifiers [modif-tree children objects bounds parent transformed-parent-bounds] - (letfn [(apply-modifiers [child] - [(-> (cgb/shape->bounds child bounds objects modif-tree) + (letfn [(apply-modifiers [bounds child] + [(-> @(get bounds (:id child)) (gpo/parent-coords-bounds @transformed-parent-bounds)) child]) @@ -81,12 +73,14 @@ (gcfl/layout-child-modifiers parent transformed-parent-bounds child child-bounds layout-line)] [layout-line (cgt/add-modifiers modif-tree (:id child) modifiers)]))] - (let [children + (let [bounds (cgb/transform-bounds-map bounds objects modif-tree) + + children (->> children (keep (d/getf objects)) (remove :hidden) (remove gco/invalid-geometry?) - (map apply-modifiers)) + (map (partial apply-modifiers bounds))) layout-data (gcfl/calc-layout-data parent @transformed-parent-bounds children bounds objects) children (into [] (cond-> children (not (:reverse? layout-data)) reverse)) @@ -109,8 +103,8 @@ (defn- set-grid-layout-modifiers [modif-tree objects bounds parent transformed-parent-bounds] - (letfn [(apply-modifiers [child] - [(-> (cgb/shape->bounds child bounds objects modif-tree) + (letfn [(apply-modifiers [bounds child] + [(-> @(get bounds (:id child)) (gpo/parent-coords-bounds @transformed-parent-bounds)) child]) @@ -119,9 +113,11 @@ (gcgl/child-modifiers parent transformed-parent-bounds child child-bounds grid-data cell-data)] (cgt/add-modifiers modif-tree (:id child) modifiers)))] - (let [children + (let [bounds (cgb/transform-bounds-map bounds objects modif-tree) + + children (->> (cph/get-immediate-children objects (:id parent) {:remove-hidden true}) - (map apply-modifiers)) + (map (partial apply-modifiers bounds))) grid-data (gcgl/calc-layout-data parent @transformed-parent-bounds children bounds objects)] (loop [modif-tree modif-tree bound+child (first children) @@ -134,7 +130,7 @@ (recur modif-tree (first pending) (rest pending))) modif-tree))))) -(defn- propagate-modifiers-constraints +(defn- set-modifiers-constraints "Propagate modifiers to its children" [objects bounds ignore-constraints modif-tree parent] (let [parent-id (:id parent) @@ -150,36 +146,34 @@ (and has-modifiers? parent? (not root?)) (set-children-modifiers children objects bounds parent transformed-parent-bounds ignore-constraints)))) -(defn- propagate-modifiers-layout +(defn- set-modifiers-layout "Propagate modifiers to its children" - [objects bounds ignore-constraints [modif-tree autolayouts] parent] + ([objects bounds ignore-constraints parent] + (set-modifiers-layout objects bounds ignore-constraints {} parent)) + ([objects bounds ignore-constraints modif-tree 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) + flex-layout? (ctl/flex-layout? parent) + grid-layout? (ctl/grid-layout? parent) + parent? (or (cph/group-like-shape? parent) (cph/frame-shape? 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) - flex-layout? (ctl/flex-layout? parent) - grid-layout? (ctl/grid-layout? parent) - auto? (ctl/auto? parent) - fill-with-grid? (and (ctl/grid-layout? objects (:parent-id parent)) - (ctl/fill? parent)) - parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent)) + transformed-parent-bounds (delay (gtr/transform-bounds @(get bounds parent-id) modifiers)) - transformed-parent-bounds (delay (gtr/transform-bounds @(get bounds parent-id) modifiers)) + children-modifiers + (if (or flex-layout? grid-layout?) + (->> (:shapes parent) + (filter #(ctl/layout-absolute? objects %))) + (:shapes parent)) - children-modifiers - (if (or flex-layout? grid-layout?) - (->> (:shapes parent) - (filter #(ctl/layout-absolute? objects %))) - (:shapes parent)) + children-layout + (when (or flex-layout? grid-layout?) + (->> (:shapes parent) + (remove #(ctl/layout-absolute? objects %))))] - children-layout - (when (or flex-layout? grid-layout?) - (->> (:shapes parent) - (remove #(ctl/layout-absolute? objects %))))] - - [(cond-> modif-tree + (cond-> modif-tree (and has-modifiers? parent? (not root?)) (set-children-modifiers children-modifiers objects bounds parent transformed-parent-bounds ignore-constraints) @@ -187,17 +181,19 @@ (set-flex-layout-modifiers children-layout objects bounds parent transformed-parent-bounds) grid-layout? - (set-grid-layout-modifiers objects bounds parent transformed-parent-bounds)) - - ;; Auto-width/height can change the positions in the parent so we need to recalculate - ;; also if the child is fill width/height inside a grid layout - (when autolayouts - (cond-> autolayouts (or auto? fill-with-grid?) (conj (:id parent))))])) - - - + (set-grid-layout-modifiers objects bounds parent transformed-parent-bounds))))) +(defn propagate-modifiers-constraints + ([objects bounds ignore-constraints shapes] + (propagate-modifiers-constraints objects bounds ignore-constraints {} shapes)) + ([objects bounds ignore-constraints modif-tree shapes] + (reduce #(set-modifiers-constraints objects bounds ignore-constraints %1 %2) modif-tree shapes))) +(defn propagate-modifiers-layouts + ([objects bounds ignore-constraints shapes] + (propagate-modifiers-layouts objects bounds ignore-constraints {} shapes)) + ([objects bounds ignore-constraints modif-tree shapes] + (reduce #(set-modifiers-layout objects bounds ignore-constraints %1 %2) modif-tree shapes))) (defn- calc-auto-modifiers "Calculates the modifiers to adjust the bounds for auto-width/auto-height shapes" @@ -247,46 +243,51 @@ (and (some? auto-height) (ctl/auto-height? parent)) (set-parent-auto-height auto-height)))) -(defn reflow-layout - [objects old-modif-tree bounds ignore-constraints id] +(defn find-auto-layouts + [objects shapes] - (let [tree-seq (cgst/get-children-seq id objects) - - [modif-tree _] - (reduce - #(propagate-modifiers-layout objects bounds ignore-constraints %1 %2) [{id {:modifiers (ctm/reflow-modifiers)}} #{}] - tree-seq) - - bounds - (cgb/transform-bounds-map bounds objects modif-tree) - - modif-tree (cgt/merge-modif-tree old-modif-tree modif-tree)] - [modif-tree bounds])) + (letfn [(mk-check-auto-layout [objects] + (fn [shape] + ;; Auto-width/height can change the positions in the parent so we need to recalculate + ;; also if the child is fill width/height inside a grid layout + (when (or (ctl/auto? shape) + (and (ctl/grid-layout? objects (:parent-id shape)) (ctl/fill? shape))) + (:id shape))))] + (into (d/ordered-set) + (keep (mk-check-auto-layout objects)) + shapes))) (defn sizing-auto-modifiers "Recalculates the layouts to adjust the sizing: auto new sizes" [modif-tree sizing-auto-layouts objects bounds ignore-constraints] - (let [[modif-tree _] - (->> sizing-auto-layouts - reverse - (reduce - (fn [[modif-tree bounds] layout-id] - (let [layout (get objects layout-id) - auto-modifiers (calc-auto-modifiers objects bounds layout)] + (let [calculate-modifiers + (fn [[modif-tree bounds] layout-id] + (let [layout (get objects layout-id) + auto-modifiers (calc-auto-modifiers objects bounds layout)] - (if (and (ctm/empty? auto-modifiers) (not (ctl/grid-layout? layout))) - [modif-tree bounds] + (if (and (ctm/empty? auto-modifiers) (not (ctl/grid-layout? layout))) + [modif-tree bounds] - (let [[auto-modif-tree _] - (->> (cgst/resolve-tree #{layout-id} objects) - (reduce #(propagate-modifiers-layout objects bounds ignore-constraints %1 %2) [{layout-id {:modifiers auto-modifiers}} nil])) + (let [from-layout + (->> (cph/get-parent-ids objects layout-id) + (d/seek sizing-auto-layouts)) - bounds (cgb/transform-bounds-map bounds objects auto-modif-tree) - modif-tree (cgt/merge-modif-tree modif-tree auto-modif-tree)] - [modif-tree bounds])))) - [modif-tree bounds]))] - modif-tree)) + shapes + (if from-layout + (cgst/resolve-subtree from-layout layout-id objects) + (cgst/resolve-tree #{layout-id} objects)) + + auto-modif-tree {layout-id {:modifiers auto-modifiers}} + auto-modif-tree (propagate-modifiers-layouts objects bounds ignore-constraints auto-modif-tree shapes) + + bounds (cgb/transform-bounds-map bounds objects auto-modif-tree) + modif-tree (cgt/merge-modif-tree modif-tree auto-modif-tree)] + [modif-tree bounds]))))] + (->> sizing-auto-layouts + (reverse) + (reduce calculate-modifiers [modif-tree bounds]) + (first)))) (defn set-objects-modifiers "Applies recursively the modifiers and calculate the layouts and constraints for all the items to be placed correctly" @@ -310,38 +311,43 @@ (cgt/apply-structure-modifiers old-modif-tree)) (cgt/apply-structure-modifiers modif-tree)) + ;; Creates the sequence of shapes with the shapes that are modified + shapes-tree + (cgst/resolve-tree (-> modif-tree keys set) objects) + + bounds-map + (cond-> (cgb/objects->bounds-map objects) + (some? old-modif-tree) + (cgb/transform-bounds-map objects old-modif-tree)) + ;; Round the transforms if the snap-to-pixel is active modif-tree (cond-> modif-tree snap-pixel? (gpp/adjust-pixel-precision objects snap-precision snap-ignore-axis)) - bounds - (cond-> (cgb/objects->bounds-map objects) - (some? old-modif-tree) - (cgb/transform-bounds-map objects old-modif-tree)) - - shapes-tree (cgst/resolve-tree (-> modif-tree keys set) objects) - - ;; Calculate the input transformation and constraints + ;; Propagates the modifiers to the normal shapes with constraints modif-tree - (->> shapes-tree - (reduce #(propagate-modifiers-constraints objects bounds ignore-constraints %1 %2) modif-tree)) + (propagate-modifiers-constraints objects bounds-map ignore-constraints modif-tree shapes-tree) - bounds - (cgb/transform-bounds-map bounds objects modif-tree) + bounds-map + (cgb/transform-bounds-map bounds-map objects modif-tree) - [modif-tree-layout sizing-auto-layouts] - (->> shapes-tree - (reduce #(propagate-modifiers-layout objects bounds ignore-constraints %1 %2) [{} (d/ordered-set)])) + modif-tree-layout + (propagate-modifiers-layouts objects bounds-map ignore-constraints shapes-tree) - modif-tree (cgt/merge-modif-tree modif-tree modif-tree-layout) + modif-tree + (cgt/merge-modif-tree modif-tree modif-tree-layout) ;; Calculate hug layouts positions - bounds (cgb/transform-bounds-map bounds objects modif-tree-layout) + bounds-map + (cgb/transform-bounds-map bounds-map objects modif-tree-layout) + + ;; Find layouts with auto width/height + sizing-auto-layouts (find-auto-layouts objects shapes-tree) modif-tree - (sizing-auto-modifiers modif-tree sizing-auto-layouts objects bounds ignore-constraints) + (sizing-auto-modifiers modif-tree sizing-auto-layouts objects bounds-map ignore-constraints) modif-tree (if old-modif-tree diff --git a/common/src/app/common/geom/shapes/points.cljc b/common/src/app/common/geom/shapes/points.cljc index 8783d76f31..83c110bb7b 100644 --- a/common/src/app/common/geom/shapes/points.cljc +++ b/common/src/app/common/geom/shapes/points.cljc @@ -116,45 +116,47 @@ (if (empty? child-bounds) parent-bounds - (let [rh [p1 p2] - rv [p1 p4] + (if (and (axis-aligned? child-bounds) (axis-aligned? parent-bounds)) + child-bounds - hv (gpt/to-vec p1 p2) - vv (gpt/to-vec p1 p4) + (let [rh [p1 p2] + rv [p1 p4] - ph #(gpt/add p1 (gpt/scale hv %)) - pv #(gpt/add p1 (gpt/scale vv %)) + hv (gpt/to-vec p1 p2) + vv (gpt/to-vec p1 p4) - 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)] - [(mth/min th-min cth) - (mth/max th-max cth) - (mth/min tv-min ctv) - (mth/max tv-max ctv)])) + ph #(gpt/add p1 (gpt/scale hv %)) + pv #(gpt/add p1 (gpt/scale vv %)) - [th-min th-max tv-min tv-max] - (->> child-bounds - (filter #(and (d/num? (:x %)) (d/num? (:y %)))) - (reduce find-boundary-ts [##Inf ##-Inf ##Inf ##-Inf])) + 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)] + [(mth/min th-min cth) + (mth/max th-max cth) + (mth/min tv-min ctv) + (mth/max tv-max ctv)])) - minv-start (pv tv-min) - minv-end (gpt/add minv-start hv) - minh-start (ph th-min) - minh-end (gpt/add minh-start vv) + [th-min th-max tv-min tv-max] + (->> child-bounds + (filter #(and (d/num? (:x %)) (d/num? (:y %)))) + (reduce find-boundary-ts [##Inf ##-Inf ##Inf ##-Inf])) - maxv-start (pv tv-max) - maxv-end (gpt/add maxv-start hv) - maxh-start (ph th-max) - maxh-end (gpt/add maxh-start vv) + minv-start (pv tv-min) + minv-end (gpt/add minv-start hv) + minh-start (ph th-min) + minh-end (gpt/add minh-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)] + 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 i2 i3 i4]))) + 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] diff --git a/common/src/app/common/geom/shapes/tree_seq.cljc b/common/src/app/common/geom/shapes/tree_seq.cljc index 34de51b8bf..77932154ba 100644 --- a/common/src/app/common/geom/shapes/tree_seq.cljc +++ b/common/src/app/common/geom/shapes/tree_seq.cljc @@ -85,3 +85,9 @@ (if (contains? ids uuid/zero) (cons (get objects uuid/zero) child-seq) child-seq))) + +(defn resolve-subtree + "Resolves the subtree but only partialy from-to the parameters" + [from-id to-id objects] + (->> (get-children-seq from-id objects) + (d/take-until #(= (:id %) to-id)))) diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index b8e9eee429..be402f5f15 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -68,9 +68,11 @@ (= :bool (dm/get-prop shape :type)))) (defn group-like-shape? - [shape] - (or ^boolean (group-shape? shape) - ^boolean (bool-shape? shape))) + ([objects id] + (group-like-shape? (get objects id))) + ([shape] + (or ^boolean (group-shape? shape) + ^boolean (bool-shape? shape)))) (defn text-shape? [shape] @@ -160,6 +162,13 @@ (recur (conj result parent-id) parent-id) result)))) +(defn get-parent-ids-seq + "Returns a vector of parents of the specified shape." + [objects shape-id] + (let [parent-id (get-parent-id objects shape-id)] + (when (and (some? parent-id) (not= parent-id shape-id)) + (lazy-seq (cons parent-id (get-parent-ids-seq objects parent-id)))))) + (defn get-parents "Returns a vector of parents of the specified shape." [objects shape-id] @@ -169,6 +178,17 @@ (recur (conj result (get objects parent-id)) parent-id) result)))) +(defn get-parent-seq + "Returns a vector of parents of the specified shape." + ([objects shape-id] + (get-parent-seq objects (get objects shape-id) shape-id)) + + ([objects shape shape-id] + (let [parent-id (dm/get-prop shape :parent-id) + parent (get objects parent-id)] + (when (and (some? parent) (not= parent-id shape-id)) + (lazy-seq (cons parent (get-parent-seq objects parent parent-id))))))) + (defn get-parents-with-self [objects id] (let [lookup (d/getf objects)]