Remove fill/auto when resizing

This commit is contained in:
alonso.torres 2022-11-03 15:11:29 +01:00
parent 7f0054959f
commit 4ecc166055
13 changed files with 479 additions and 349 deletions

View file

@ -138,20 +138,21 @@
(let [modifiers (get-in modif-tree [(:id parent) :modifiers]) (let [modifiers (get-in modif-tree [(:id parent) :modifiers])
transformed-parent (gtr/transform-shape parent modifiers) transformed-parent (gtr/transform-shape parent modifiers)
children (->> transformed-parent children (->> transformed-parent
:shapes :shapes
(map (comp apply-modifiers (d/getf objects)))) (map (comp apply-modifiers (d/getf objects))))
{auto-width :width auto-height :height} {auto-width :width auto-height :height}
(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? transformed-parent) (ctl/auto-width? transformed-parent)))
(gcl/layout-content-bounds parent children)) (gcl/layout-content-bounds transformed-parent children))
modifiers modifiers
(cond-> modifiers (cond-> modifiers
(and (some? auto-width) (ctl/auto-width? parent)) (and (some? auto-width) (ctl/auto-width? transformed-parent))
(set-parent-auto-width transformed-parent auto-width) (set-parent-auto-width transformed-parent auto-width)
(and (some? auto-height) (ctl/auto-height? parent)) (and (some? auto-height) (ctl/auto-height? transformed-parent))
(set-parent-auto-height transformed-parent auto-height))] (set-parent-auto-height transformed-parent auto-height))]
(assoc-in modif-tree [(:id parent) :modifiers] modifiers)))) (assoc-in modif-tree [(:id parent) :modifiers] modifiers))))

View file

@ -71,9 +71,12 @@
(defn get-children-ids (defn get-children-ids
[objects id] [objects id]
(if-let [shapes (-> (get objects id) :shapes (some-> vec))] (letfn [(get-children-ids-rec
(into shapes (mapcat #(get-children-ids objects %)) shapes) [id processed]
[])) (when (not (contains? processed id))
(when-let [shapes (-> (get objects id) :shapes (some-> vec))]
(into shapes (mapcat #(get-children-ids-rec % (conj processed id))) shapes))))]
(get-children-ids-rec id #{})))
(defn get-children (defn get-children
[objects id] [objects id]

View file

@ -32,6 +32,7 @@
;; - structure-child: Structure recursive ;; - structure-child: Structure recursive
;; * scale-content ;; * scale-content
;; * rotation ;; * rotation
;; * change-properties
(def conjv (fnil conj [])) (def conjv (fnil conj []))
@ -118,6 +119,13 @@
(-> modifiers (-> modifiers
(update :structure-child conjv {:type :scale-content :value value}))) (update :structure-child conjv {:type :scale-content :value value})))
(defn set-change-property
[modifiers property value]
(-> modifiers
(update :structure-child conjv {:type :change-property
:property property
:value value})))
(defn add-modifiers (defn add-modifiers
[modifiers new-modifiers] [modifiers new-modifiers]
@ -376,7 +384,7 @@
(d/removev remove? shapes))) (d/removev remove? shapes)))
apply-modifier apply-modifier
(fn [shape {:keys [type value index rotation]}] (fn [shape {:keys [type property value index rotation]}]
(cond-> shape (cond-> shape
(= type :rotation) (= type :rotation)
(update :rotation #(mod (+ % rotation) 360)) (update :rotation #(mod (+ % rotation) 360))
@ -395,7 +403,10 @@
(update :shapes remove-children value) (update :shapes remove-children value)
(= type :scale-content) (= type :scale-content)
(apply-scale-content value)))] (apply-scale-content value)
(= type :change-property)
(assoc property value)))]
(as-> shape $ (as-> shape $
(reduce apply-modifier $ (:structure-parent modifiers)) (reduce apply-modifier $ (:structure-parent modifiers))

View file

@ -97,8 +97,16 @@
::layout-item-min-w ::layout-item-min-w
::layout-item-align-self])) ::layout-item-align-self]))
(defn layout? [shape] (defn layout?
(and (= :frame (:type shape)) (= :flex (:layout shape)))) ([objects id]
(layout? (get objects id)))
([shape]
(and (= :frame (:type shape)) (= :flex (:layout shape)))))
(defn layout-child? [objects shape]
(let [parent-id (:parent-id shape)
parent (get objects parent-id)]
(layout? parent)))
(defn wrap? [{:keys [layout-wrap-type]}] (defn wrap? [{:keys [layout-wrap-type]}]
(= layout-wrap-type :wrap)) (= layout-wrap-type :wrap))

View file

@ -0,0 +1,284 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.data.workspace.modifiers
"Events related with shapes transformations"
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.flex-layout :as gsl]
[app.common.math :as mth]
[app.common.pages.common :as cpc]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.modifiers :as ctm]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.comments :as-alias dwcm]
[app.main.data.workspace.guides :as-alias dwg]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[potok.core :as ptk]))
;; -- temporary modifiers -------------------------------------------
;; During an interactive transformation of shapes (e.g. when resizing or rotating
;; a group with the mouse), there are a lot of objects that need to be modified
;; (in this case, the group and all its children).
;;
;; To avoid updating the shapes theirselves, and forcing redraw of all components
;; that depend on the "objects" global state, we set a "modifiers" structure, with
;; the changes that need to be applied, and store it in :workspace-modifiers global
;; variable. The viewport reads this and merges it into the objects list it uses to
;; paint the viewport content, redrawing only the objects that have new modifiers.
;;
;; When the interaction is finished (e.g. user releases mouse button), the
;; apply-modifiers event is done, that consolidates all modifiers into the base
;; geometric attributes of the shapes.
(defn- check-delta
"If the shape is a component instance, check its relative position respect the
root of the component, and see if it changes after applying a transformation."
[shape root transformed-shape transformed-root objects modif-tree]
(let [root
(cond
(:component-root? shape)
shape
(nil? root)
(cph/get-root-shape objects shape)
:else root)
transformed-root
(cond
(:component-root? transformed-shape)
transformed-shape
(nil? transformed-root)
(as-> (cph/get-root-shape objects transformed-shape) $
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
:else transformed-root)
shape-delta
(when root
(gpt/point (- (gsh/left-bound shape) (gsh/left-bound root))
(- (gsh/top-bound shape) (gsh/top-bound root))))
transformed-shape-delta
(when transformed-root
(gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root))
(- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root))))
;; There are cases in that the coordinates change slightly (e.g. when
;; rounding to pixel, or when recalculating text positions in different
;; zoom levels). To take this into account, we ignore movements smaller
;; than 1 pixel.
distance (if (and shape-delta transformed-shape-delta)
(gpt/distance-vector shape-delta transformed-shape-delta)
(gpt/point 0 0))
ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))]
[root transformed-root ignore-geometry?]))
(defn- get-ignore-tree
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
([modif-tree objects shape]
(get-ignore-tree modif-tree objects shape nil nil {}))
([modif-tree objects shape root transformed-root ignore-tree]
(let [children (map (d/getf objects) (:shapes shape))
shape-id (:id shape)
transformed-shape (gsh/transform-shape shape (get modif-tree shape-id))
[root transformed-root ignore-geometry?]
(check-delta shape root transformed-shape transformed-root objects modif-tree)
ignore-tree (assoc ignore-tree shape-id ignore-geometry?)
set-child
(fn [ignore-tree child]
(get-ignore-tree modif-tree objects child root transformed-root ignore-tree))]
(reduce set-child ignore-tree children))))
(defn- update-grow-type
[shape old-shape]
(let [auto-width? (= :auto-width (:grow-type shape))
auto-height? (= :auto-height (:grow-type shape))
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
(and auto-height? changed-height?))]
(cond-> shape
change-to-fixed?
(assoc :grow-type :fixed))))
(defn- clear-local-transform []
(ptk/reify ::clear-local-transform
ptk/UpdateEvent
(update [_ state]
(-> state
(dissoc :workspace-modifiers)
(dissoc ::current-move-selected)))))
(defn create-modif-tree
[ids modifiers]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers modifiers})) ids))
(defn build-modif-tree
[ids objects get-modifier]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers (get-modifier (get objects %))})) ids))
(defn build-change-frame-modifiers
[modif-tree objects selected target-frame position]
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
layout? (get-in objects [target-frame :layout])
child-set (set (get-in objects [target-frame :shapes]))
drop-index (when layout? (gsl/get-drop-index target-frame objects position))
update-frame-modifiers
(fn [modif-tree [original-frame shapes]]
(let [shapes (->> shapes (d/removev #(= target-frame %)))
shapes (cond->> shapes
(and layout? (= original-frame target-frame))
;; When movining inside a layout frame remove the shapes that are not immediate children
(filterv #(contains? child-set %)))]
(cond-> modif-tree
(not= original-frame target-frame)
(-> (update-in [original-frame :modifiers] ctm/set-remove-children shapes)
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))
(and layout? (= original-frame target-frame))
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))))]
(reduce update-frame-modifiers modif-tree origin-frame-ids)))
(defn modif->js
[modif-tree objects]
(clj->js (into {}
(map (fn [[k v]]
[(get-in objects [k :name]) v]))
modif-tree)))
(defn set-modifiers
([modif-tree]
(set-modifiers modif-tree false))
([modif-tree ignore-constraints]
(set-modifiers modif-tree ignore-constraints false))
([modif-tree ignore-constraints ignore-snap-pixel]
(ptk/reify ::set-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects
(wsh/lookup-page-objects state)
snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
modif-tree
(gsh/set-objects-modifiers modif-tree objects ignore-constraints snap-pixel?)]
(assoc state :workspace-modifiers modif-tree))))))
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
(defn set-rotation-modifiers
([angle shapes]
(set-rotation-modifiers angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
([angle shapes center]
(ptk/reify ::set-rotation-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
ids
(->> shapes
(remove #(get % :blocked false))
(filter #((cpc/editable-attrs (:type %)) :rotation))
(map :id))
get-modifier
(fn [shape]
(ctm/rotation shape center angle))
modif-tree
(-> (build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false false))]
(assoc state :workspace-modifiers modif-tree))))))
(defn apply-modifiers
([]
(apply-modifiers nil))
([{:keys [undo-transation?] :or {undo-transation? true}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
object-modifiers (get state :workspace-modifiers)
ids (or (keys object-modifiers) [])
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
shapes (map (d/getf objects) ids)
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
(reduce merge {}))]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction))
(rx/empty))
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
(dch/update-shapes
ids
(fn [shape]
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
text-shape? (cph/text-shape? shape)]
(-> shape
(gsh/transform-shape modif)
(cond-> text-shape?
(update-grow-type shape)))))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points
:x
:y
:width
:height
:content
:transform
:transform-inverse
:rotation
:position-data
:flip-x
:flip-y
:grow-type
:layout-item-h-sizing
:layout-item-v-sizing
]})
(clear-local-transform))
(if undo-transation?
(rx/of (dwu/commit-undo-transaction))
(rx/empty))))))))

View file

@ -21,6 +21,7 @@
[app.main.data.modal :as md] [app.main.data.modal :as md]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.collapse :as dwc] [app.main.data.workspace.collapse :as dwc]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.thumbnails :as dwt]
[app.main.data.workspace.zoom :as dwz] [app.main.data.workspace.zoom :as dwz]
@ -552,8 +553,11 @@
(filter #(= :frame (get-in % [:obj :type]))) (filter #(= :frame (get-in % [:obj :type])))
(map #(vector (:old-id %) (get-in % [:obj :id])))) (map #(vector (:old-id %) (get-in % [:obj :id]))))
id-duplicated (first new-selected)] id-duplicated (first new-selected)
frames (into #{}
(map #(get-in objects [% :frame-id]))
selected)]
(rx/concat (rx/concat
(->> (rx/from dup-frames) (->> (rx/from dup-frames)
(rx/map (fn [[old-id new-id]] (dwt/duplicate-thumbnail old-id new-id)))) (rx/map (fn [[old-id new-id]] (dwt/duplicate-thumbnail old-id new-id))))
@ -561,6 +565,7 @@
;; Warning: This order is important for the focus mode. ;; Warning: This order is important for the focus mode.
(rx/of (dch/commit-changes changes) (rx/of (dch/commit-changes changes)
(select-shapes new-selected) (select-shapes new-selected)
(dwsl/update-layout-positions frames)
(memorize-duplicated id-original id-duplicated)))))))))) (memorize-duplicated id-original id-duplicated))))))))))
(defn change-hover-state (defn change-hover-state

View file

@ -11,8 +11,8 @@
[app.common.types.modifiers :as ctm] [app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.workspace.changes :as dwc] [app.main.data.workspace.changes :as dwc]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.transforms :as dwt]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -50,11 +50,11 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [objects (wsh/lookup-page-objects state) (let [objects (wsh/lookup-page-objects state)
ids (->> ids (filter #(get-in objects [% :layout])))] ids (->> ids (filter (partial ctl/layout? objects)))]
(if (d/not-empty? ids) (if (d/not-empty? ids)
(let [modif-tree (dwt/create-modif-tree ids (ctm/reflow))] (let [modif-tree (dwm/create-modif-tree ids (ctm/reflow))]
(rx/of (dwt/set-modifiers modif-tree) (rx/of (dwm/set-modifiers modif-tree)
(dwt/apply-modifiers))) (dwm/apply-modifiers)))
(rx/empty)))))) (rx/empty))))))
;; TODO LAYOUT: Remove constraints from children ;; TODO LAYOUT: Remove constraints from children

View file

@ -14,15 +14,14 @@
[app.common.geom.shapes.flex-layout :as gsl] [app.common.geom.shapes.flex-layout :as gsl]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.pages.changes-builder :as pcb] [app.common.pages.changes-builder :as pcb]
[app.common.pages.common :as cpc]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.modifiers :as ctm] [app.common.types.modifiers :as ctm]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.collapse :as dwc] [app.main.data.workspace.collapse :as dwc]
[app.main.data.workspace.comments :as-alias dwcm] [app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.guides :as-alias dwg]
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
@ -97,269 +96,12 @@
(update state :workspace-local dissoc :transform)))) (update state :workspace-local dissoc :transform))))
;; -- Temporary modifiers -------------------------------------------
;; During an interactive transformation of shapes (e.g. when resizing or rotating
;; a group with the mouse), there are a lot of objects that need to be modified
;; (in this case, the group and all its children).
;;
;; To avoid updating the shapes theirselves, and forcing redraw of all components
;; that depend on the "objects" global state, we set a "modifiers" structure, with
;; the changes that need to be applied, and store it in :workspace-modifiers global
;; variable. The viewport reads this and merges it into the objects list it uses to
;; paint the viewport content, redrawing only the objects that have new modifiers.
;;
;; When the interaction is finished (e.g. user releases mouse button), the
;; apply-modifiers event is done, that consolidates all modifiers into the base
;; geometric attributes of the shapes.
(declare clear-local-transform)
(declare get-ignore-tree)
(defn create-modif-tree
[ids modifiers]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers modifiers})) ids))
(defn build-modif-tree
[ids objects get-modifier]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers (get-modifier (get objects %))})) ids))
(defn build-change-frame-modifiers
[modif-tree objects selected target-frame position]
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
layout? (get-in objects [target-frame :layout])
child-set (set (get-in objects [target-frame :shapes]))
drop-index (when layout? (gsl/get-drop-index target-frame objects position))
update-frame-modifiers
(fn [modif-tree [original-frame shapes]]
(let [shapes (->> shapes (d/removev #(= target-frame %)))
shapes (cond->> shapes
(and layout? (= original-frame target-frame))
;; When movining inside a layout frame remove the shapes that are not immediate children
(filterv #(contains? child-set %)))]
(cond-> modif-tree
(not= original-frame target-frame)
(-> (update-in [original-frame :modifiers] ctm/set-remove-children shapes)
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))
(and layout? (= original-frame target-frame))
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))))]
(reduce update-frame-modifiers modif-tree origin-frame-ids)))
(defn modif->js
[modif-tree objects]
(clj->js (into {}
(map (fn [[k v]]
[(get-in objects [k :name]) v]))
modif-tree)))
(defn set-modifiers
([modif-tree]
(set-modifiers modif-tree false))
([modif-tree ignore-constraints]
(set-modifiers modif-tree ignore-constraints false))
([modif-tree ignore-constraints ignore-snap-pixel]
(ptk/reify ::set-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects
(wsh/lookup-page-objects state)
snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
modif-tree
(gsh/set-objects-modifiers modif-tree objects ignore-constraints snap-pixel?)]
(assoc state :workspace-modifiers modif-tree))))))
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
(defn- set-rotation-modifiers
([angle shapes]
(set-rotation-modifiers angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
([angle shapes center]
(ptk/reify ::set-rotation-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
ids
(->> shapes
(remove #(get % :blocked false))
(filter #((cpc/editable-attrs (:type %)) :rotation))
(map :id))
get-modifier
(fn [shape]
(ctm/rotation shape center angle))
modif-tree
(-> (build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false false))]
(assoc state :workspace-modifiers modif-tree))))))
(defn- update-grow-type
[shape old-shape]
(let [auto-width? (= :auto-width (:grow-type shape))
auto-height? (= :auto-height (:grow-type shape))
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
(and auto-height? changed-height?))]
(cond-> shape
change-to-fixed?
(assoc :grow-type :fixed))))
(defn apply-modifiers
([]
(apply-modifiers nil))
([{:keys [undo-transation?] :or {undo-transation? true}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
object-modifiers (get state :workspace-modifiers)
ids (or (keys object-modifiers) [])
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
shapes (map (d/getf objects) ids)
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
(reduce merge {}))]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction))
(rx/empty))
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
(dch/update-shapes
ids
(fn [shape]
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
text-shape? (cph/text-shape? shape)]
(-> shape
(gsh/transform-shape modif)
(cond-> text-shape?
(update-grow-type shape)))))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points
:x
:y
:width
:height
:content
:transform
:transform-inverse
:rotation
:position-data
:flip-x
:flip-y
:grow-type]})
(clear-local-transform))
(if undo-transation?
(rx/of (dwu/commit-undo-transaction))
(rx/empty))))))))
(defn- check-delta
"If the shape is a component instance, check its relative position respect the
root of the component, and see if it changes after applying a transformation."
[shape root transformed-shape transformed-root objects modif-tree]
(let [root
(cond
(:component-root? shape)
shape
(nil? root)
(cph/get-root-shape objects shape)
:else root)
transformed-root
(cond
(:component-root? transformed-shape)
transformed-shape
(nil? transformed-root)
(as-> (cph/get-root-shape objects transformed-shape) $
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
:else transformed-root)
shape-delta
(when root
(gpt/point (- (gsh/left-bound shape) (gsh/left-bound root))
(- (gsh/top-bound shape) (gsh/top-bound root))))
transformed-shape-delta
(when transformed-root
(gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root))
(- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root))))
;; There are cases in that the coordinates change slightly (e.g. when
;; rounding to pixel, or when recalculating text positions in different
;; zoom levels). To take this into account, we ignore movements smaller
;; than 1 pixel.
distance (if (and shape-delta transformed-shape-delta)
(gpt/distance-vector shape-delta transformed-shape-delta)
(gpt/point 0 0))
ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))]
[root transformed-root ignore-geometry?]))
(defn- get-ignore-tree
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
([modif-tree objects shape]
(get-ignore-tree modif-tree objects shape nil nil {}))
([modif-tree objects shape root transformed-root ignore-tree]
(let [children (map (d/getf objects) (:shapes shape))
shape-id (:id shape)
transformed-shape (gsh/transform-shape shape (get modif-tree shape-id))
[root transformed-root ignore-geometry?]
(check-delta shape root transformed-shape transformed-root objects modif-tree)
ignore-tree (assoc ignore-tree shape-id ignore-geometry?)
set-child
(fn [ignore-tree child]
(get-ignore-tree modif-tree objects child root transformed-root ignore-tree))]
(reduce set-child ignore-tree children))))
(defn- clear-local-transform []
(ptk/reify ::clear-local-transform
ptk/UpdateEvent
(update [_ state]
(-> state
(dissoc :workspace-modifiers)
(dissoc ::current-move-selected)))))
;; -- Resize -------------------------------------------------------- ;; -- Resize --------------------------------------------------------
(defn start-resize (defn start-resize
"Enter mouse resize mode, until mouse button is released." "Enter mouse resize mode, until mouse button is released."
[handler ids shape] [handler ids shape]
(letfn [(resize [shape initial layout [point lock? center? point-snap]] (letfn [(resize [shape objects initial layout [point lock? center? point-snap]]
(let [{:keys [width height]} (:selrect shape) (let [{:keys [width height]} (:selrect shape)
{:keys [rotation]} shape {:keys [rotation]} shape
@ -423,18 +165,42 @@
(some? displacement) (some? displacement)
(gpt/add displacement)) (gpt/add displacement))
;; When the horizontal/vertical scale a flex children with auto/fill
;; we change it too fixed
layout? (ctl/layout? shape)
layout-child? (ctl/layout-child? objects shape)
auto-width? (ctl/auto-width? shape)
fill-width? (ctl/fill-width? shape)
auto-height? (ctl/auto-height? shape)
fill-height? (ctl/fill-height? shape)
set-fix-width?
(and (not (mth/close? (:x scalev) 1))
(or (and (or layout? layout-child?) auto-width?)
(and layout-child? fill-width?)))
set-fix-height?
(and (not (mth/close? (:y scalev) 1))
(or (and (or layout? layout-child?) auto-height?)
(and layout-child? fill-height?)))
modifiers modifiers
(-> (ctm/empty-modifiers) (-> (ctm/empty-modifiers)
(cond-> displacement (cond-> displacement
(ctm/set-move displacement)) (ctm/set-move displacement))
(ctm/set-resize scalev resize-origin shape-transform shape-transform-inverse) (ctm/set-resize scalev resize-origin shape-transform shape-transform-inverse)
(cond-> set-fix-width?
(ctm/set-change-property :layout-item-h-sizing :fix))
(cond-> set-fix-height?
(ctm/set-change-property :layout-item-v-sizing :fix))
(cond-> scale-text (cond-> scale-text
(ctm/set-scale-content (:x scalev)))) (ctm/set-scale-content (:x scalev))))
modif-tree (create-modif-tree ids modifiers)] modif-tree (dwm/create-modif-tree ids modifiers)]
(rx/of (dwm/set-modifiers modif-tree))))
(rx/of (set-modifiers modif-tree))))
;; Unifies the instantaneous proportion lock modifier ;; Unifies the instantaneous proportion lock modifier
;; activated by Shift key and the shapes own proportion ;; activated by Shift key and the shapes own proportion
@ -458,6 +224,7 @@
zoom (get-in state [:workspace-local :zoom] 1) zoom (get-in state [:workspace-local :zoom] 1)
objects (wsh/lookup-page-objects state page-id) objects (wsh/lookup-page-objects state page-id)
resizing-shapes (map #(get objects %) ids)] resizing-shapes (map #(get objects %) ids)]
(rx/concat (rx/concat
(->> ms/mouse-position (->> ms/mouse-position
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-alt) (rx/with-latest-from ms/mouse-position-shift ms/mouse-position-alt)
@ -465,9 +232,9 @@
(rx/switch-map (fn [[point _ _ :as current]] (rx/switch-map (fn [[point _ _ :as current]]
(->> (snap/closest-snap-point page-id resizing-shapes objects layout zoom focus point) (->> (snap/closest-snap-point page-id resizing-shapes objects layout zoom focus point)
(rx/map #(conj current %))))) (rx/map #(conj current %)))))
(rx/mapcat (partial resize shape initial-position layout)) (rx/mapcat (partial resize shape objects initial-position layout))
(rx/take-until stoper)) (rx/take-until stoper))
(rx/of (apply-modifiers) (rx/of (dwm/apply-modifiers)
(finish-transform)))))))) (finish-transform))))))))
(defn update-dimensions (defn update-dimensions
@ -487,14 +254,14 @@
(fn [shape] (ctm/change-dimensions shape attr value)) (fn [shape] (ctm/change-dimensions shape attr value))
modif-tree modif-tree
(-> (build-modif-tree ids objects get-modifier) (-> (dwm/build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false snap-pixel?))] (gsh/set-objects-modifiers objects false snap-pixel?))]
(assoc state :workspace-modifiers modif-tree))) (assoc state :workspace-modifiers modif-tree)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(rx/of (apply-modifiers))))) (rx/of (dwm/apply-modifiers)))))
(defn change-orientation (defn change-orientation
"Change orientation of shapes, from the sidebar options form. "Change orientation of shapes, from the sidebar options form.
@ -512,14 +279,14 @@
(fn [shape] (ctm/change-orientation-modifiers shape orientation)) (fn [shape] (ctm/change-orientation-modifiers shape orientation))
modif-tree modif-tree
(-> (build-modif-tree ids objects get-modifier) (-> (dwm/build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false snap-pixel?))] (gsh/set-objects-modifiers objects false snap-pixel?))]
(assoc state :workspace-modifiers modif-tree))) (assoc state :workspace-modifiers modif-tree)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(rx/of (apply-modifiers))))) (rx/of (dwm/apply-modifiers)))))
;; -- Rotate -------------------------------------------------------- ;; -- Rotate --------------------------------------------------------
@ -560,9 +327,9 @@
(rx/map (rx/map
(fn [[[pos mod?] shift?]] (fn [[[pos mod?] shift?]]
(let [delta-angle (calculate-angle pos mod? shift?)] (let [delta-angle (calculate-angle pos mod? shift?)]
(set-rotation-modifiers delta-angle shapes group-center)))) (dwm/set-rotation-modifiers delta-angle shapes group-center))))
(rx/take-until stoper)) (rx/take-until stoper))
(rx/of (apply-modifiers) (rx/of (dwm/apply-modifiers)
(finish-transform))))))) (finish-transform)))))))
(defn increase-rotation (defn increase-rotation
@ -576,10 +343,10 @@
objects (wsh/lookup-page-objects state page-id) objects (wsh/lookup-page-objects state page-id)
rotate-shape (fn [shape] rotate-shape (fn [shape]
(let [delta (- rotation (:rotation shape))] (let [delta (- rotation (:rotation shape))]
(set-rotation-modifiers delta [shape])))] (dwm/set-rotation-modifiers delta [shape])))]
(rx/concat (rx/concat
(rx/from (->> ids (map #(get objects %)) (map rotate-shape))) (rx/from (->> ids (map #(get objects %)) (map rotate-shape)))
(rx/of (apply-modifiers))))))) (rx/of (dwm/apply-modifiers)))))))
;; -- Move ---------------------------------------------------------- ;; -- Move ----------------------------------------------------------
@ -705,15 +472,15 @@
(fn [move-vector] (fn [move-vector]
(let [position (gpt/add from-position move-vector) (let [position (gpt/add from-position move-vector)
target-frame (ctst/top-nested-frame objects position)] target-frame (ctst/top-nested-frame objects position)]
(-> (create-modif-tree ids (ctm/move move-vector)) (-> (dwm/create-modif-tree ids (ctm/move move-vector))
(build-change-frame-modifiers objects selected target-frame position) (dwm/build-change-frame-modifiers objects selected target-frame position)
(set-modifiers))))) (dwm/set-modifiers)))))
(rx/take-until stopper))) (rx/take-until stopper)))
(rx/of (dwu/start-undo-transaction) (rx/of (dwu/start-undo-transaction)
(calculate-frame-for-move ids) (calculate-frame-for-move ids)
(apply-modifiers {:undo-transation? false}) (dwm/apply-modifiers {:undo-transation? false})
(finish-transform) (finish-transform)
(dwu/commit-undo-transaction))))))))) (dwu/commit-undo-transaction)))))))))
@ -756,12 +523,12 @@
(rx/merge (rx/merge
(->> move-events (->> move-events
(rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0)) (rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0))
(rx/map #(create-modif-tree selected (ctm/move %))) (rx/map #(dwm/create-modif-tree selected (ctm/move %)))
(rx/map (partial set-modifiers)) (rx/map (partial dwm/set-modifiers))
(rx/take-until stopper)) (rx/take-until stopper))
(rx/of (move-selected direction shift?))) (rx/of (move-selected direction shift?)))
(rx/of (apply-modifiers) (rx/of (dwm/apply-modifiers)
(finish-transform)))) (finish-transform))))
(rx/empty)))))) (rx/empty))))))
@ -789,10 +556,10 @@
(or (:y position) (:y bbox))) (or (:y position) (:y bbox)))
delta (gpt/subtract pos cpos) delta (gpt/subtract pos cpos)
modif-tree (create-modif-tree [id] (ctm/move delta))] modif-tree (dwm/create-modif-tree [id] (ctm/move delta))]
(rx/of (set-modifiers modif-tree) (rx/of (dwm/set-modifiers modif-tree)
(apply-modifiers)))))) (dwm/apply-modifiers))))))
(defn- calculate-frame-for-move (defn- calculate-frame-for-move
[ids] [ids]
@ -851,14 +618,14 @@
selrect (gsh/selection-rect shapes) selrect (gsh/selection-rect shapes)
origin (gpt/point (:x selrect) (+ (:y selrect) (/ (:height selrect) 2))) origin (gpt/point (:x selrect) (+ (:y selrect) (/ (:height selrect) 2)))
modif-tree (create-modif-tree modif-tree (dwm/create-modif-tree
selected selected
(-> (ctm/empty-modifiers) (-> (ctm/empty-modifiers)
(ctm/set-resize (gpt/point -1.0 1.0) origin) (ctm/set-resize (gpt/point -1.0 1.0) origin)
(ctm/move (gpt/point (:width selrect) 0))))] (ctm/move (gpt/point (:width selrect) 0))))]
(rx/of (set-modifiers modif-tree true) (rx/of (dwm/set-modifiers modif-tree true)
(apply-modifiers)))))) (dwm/apply-modifiers))))))
(defn flip-vertical-selected [] (defn flip-vertical-selected []
(ptk/reify ::flip-vertical-selected (ptk/reify ::flip-vertical-selected
@ -870,11 +637,11 @@
selrect (gsh/selection-rect shapes) selrect (gsh/selection-rect shapes)
origin (gpt/point (+ (:x selrect) (/ (:width selrect) 2)) (:y selrect)) origin (gpt/point (+ (:x selrect) (/ (:width selrect) 2)) (:y selrect))
modif-tree (create-modif-tree modif-tree (dwm/create-modif-tree
selected selected
(-> (ctm/empty-modifiers) (-> (ctm/empty-modifiers)
(ctm/set-resize (gpt/point 1.0 -1.0) origin) (ctm/set-resize (gpt/point 1.0 -1.0) origin)
(ctm/move (gpt/point 0 (:height selrect)))))] (ctm/move (gpt/point 0 (:height selrect)))))]
(rx/of (set-modifiers modif-tree true) (rx/of (dwm/set-modifiers modif-tree true)
(apply-modifiers)))))) (dwm/apply-modifiers))))))

View file

@ -443,7 +443,8 @@
(l/derived (l/derived
(fn [objects] (fn [objects]
(->> ids (->> ids
(some #(-> (cph/get-parent objects %) ctl/layout?)))) (map (d/getf objects))
(some (partial ctl/layout-child? objects))))
workspace-page-objects)) workspace-page-objects))
(defn get-flex-child-viewer? (defn get-flex-child-viewer?
@ -452,8 +453,8 @@
(fn [state] (fn [state]
(let [objects (wsh/lookup-viewer-objects state page-id)] (let [objects (wsh/lookup-viewer-objects state page-id)]
(into [] (into []
(comp (filter #(= :flex (:layout (cph/get-parent objects %)))) (comp (filter (partial ctl/layout-child? objects))
(map #(get objects %))) (map (d/getf objects)))
ids))) ids)))
st/state =)) st/state =))

View file

@ -87,6 +87,12 @@
flex-child? (->> selection-parents (some ctl/layout?)) flex-child? (->> selection-parents (some ctl/layout?))
flex-container? (ctl/layout? shape)
flex-auto-width? (ctl/auto-width? shape)
flex-fill-width? (ctl/fill-width? shape)
flex-auto-height? (ctl/auto-height? shape)
flex-fill-height? (ctl/fill-height? shape)
;; To show interactively the measures while the user is manipulating ;; To show interactively the measures while the user is manipulating
;; the shape with the mouse, generate a copy of the shapes applying ;; the shape with the mouse, generate a copy of the shapes applying
;; the transient transformations. ;; the transient transformations.
@ -306,6 +312,7 @@
:placeholder "--" :placeholder "--"
:on-click select-all :on-click select-all
:on-change on-width-change :on-change on-width-change
:disabled (and (or flex-child? flex-container?) (or flex-auto-width? flex-fill-width?))
:value (:width values)}]] :value (:width values)}]]
[:div.input-element.height {:title (tr "workspace.options.height")} [:div.input-element.height {:title (tr "workspace.options.height")}
@ -314,6 +321,7 @@
:placeholder "--" :placeholder "--"
:on-click select-all :on-click select-all
:on-change on-height-change :on-change on-height-change
:disabled (and (or flex-child? flex-container?) (or flex-auto-height? flex-fill-height?))
:value (:height values)}]] :value (:height values)}]]
[:div.lock-size {:class (dom/classnames [:div.lock-size {:class (dom/classnames

View file

@ -286,7 +286,7 @@
(when show-frame-outline? (when show-frame-outline?
[:& outline/shape-outlines [:& outline/shape-outlines
{:objects base-objects {:objects objects-modified
:hover #{(->> @hover-ids :hover #{(->> @hover-ids
(filter #(cph/frame-shape? (get base-objects %))) (filter #(cph/frame-shape? (get base-objects %)))
(remove selected) (remove selected)

View file

@ -13,6 +13,44 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
;; Helper to debug the bounds when set the "hug" content property
#_(mf/defc debug-bounds
"Debug component to show the auto-layout drop areas"
{::mf/wrap-props false}
[props]
(let [objects (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))
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)
{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
"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}

View file

@ -13,6 +13,7 @@
[app.common.pages.diff :as diff] [app.common.pages.diff :as diff]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.util.geom.grid :as gg] [app.util.geom.grid :as gg]
[app.util.geom.snap-points :as snap] [app.util.geom.snap-points :as snap]
@ -70,7 +71,7 @@
(mapv grid->snap))))) (mapv grid->snap)))))
(defn- add-frame (defn- add-frame
[page-data frame] [objects page-data frame]
(let [frame-id (:id frame) (let [frame-id (:id frame)
parent-id (:parent-id frame) parent-id (:parent-id frame)
frame-data (->> (snap/shape-snap-points frame) frame-data (->> (snap/shape-snap-points frame)
@ -79,8 +80,11 @@
:pt %))) :pt %)))
grid-x-data (get-grids-snap-points frame :x) grid-x-data (get-grids-snap-points frame :x)
grid-y-data (get-grids-snap-points frame :y)] grid-y-data (get-grids-snap-points frame :y)]
(-> page-data
;; Update root frame information (cond-> page-data
(not (ctl/layout-child? objects frame))
(-> ;; Update root frame information
(assoc-in [uuid/zero :objects-data frame-id] frame-data) (assoc-in [uuid/zero :objects-data frame-id] frame-data)
(update-in [parent-id :x] (make-insert-tree-data frame-data :x)) (update-in [parent-id :x] (make-insert-tree-data frame-data :x))
(update-in [parent-id :y] (make-insert-tree-data frame-data :y)) (update-in [parent-id :y] (make-insert-tree-data frame-data :y))
@ -90,10 +94,10 @@
(update-in [frame-id :x] #(or % (rt/make-tree))) (update-in [frame-id :x] #(or % (rt/make-tree)))
(update-in [frame-id :y] #(or % (rt/make-tree))) (update-in [frame-id :y] #(or % (rt/make-tree)))
(update-in [frame-id :x] (make-insert-tree-data (d/concat-vec frame-data grid-x-data) :x)) (update-in [frame-id :x] (make-insert-tree-data (d/concat-vec frame-data grid-x-data) :x))
(update-in [frame-id :y] (make-insert-tree-data (d/concat-vec frame-data grid-y-data) :y))))) (update-in [frame-id :y] (make-insert-tree-data (d/concat-vec frame-data grid-y-data) :y))))))
(defn- add-shape (defn- add-shape
[page-data shape] [objects page-data shape]
(let [frame-id (:frame-id shape) (let [frame-id (:frame-id shape)
snap-points (snap/shape-snap-points shape) snap-points (snap/shape-snap-points shape)
shape-data (->> snap-points shape-data (->> snap-points
@ -101,11 +105,11 @@
:type :shape :type :shape
:id (:id shape) :id (:id shape)
:pt %)))] :pt %)))]
(-> page-data (cond-> page-data
(assoc-in [frame-id :objects-data (:id shape)] shape-data) (not (ctl/layout-child? objects shape))
(-> (assoc-in [frame-id :objects-data (:id shape)] shape-data)
(update-in [frame-id :x] (make-insert-tree-data shape-data :x)) (update-in [frame-id :x] (make-insert-tree-data shape-data :x))
(update-in [frame-id :y] (make-insert-tree-data shape-data :y))))) (update-in [frame-id :y] (make-insert-tree-data shape-data :y))))))
(defn- add-guide (defn- add-guide
[objects page-data guide] [objects page-data guide]
@ -164,22 +168,22 @@
(update-in [:guides (:axis guide)] (make-delete-tree-data guide-data (:axis guide))))))) (update-in [:guides (:axis guide)] (make-delete-tree-data guide-data (:axis guide)))))))
(defn- update-frame (defn- update-frame
[page-data [_ new-frame]] [objects page-data [_ new-frame]]
(let [frame-id (:id new-frame) (let [frame-id (:id new-frame)
root-data (get-in page-data [uuid/zero :objects-data frame-id]) root-data (get-in page-data [uuid/zero :objects-data frame-id])
frame-data (get-in page-data [frame-id :objects-data frame-id])] frame-data (get-in page-data [frame-id :objects-data frame-id])]
(-> page-data (as-> page-data $
(update-in [uuid/zero :x] (make-delete-tree-data root-data :x)) (update-in $ [uuid/zero :x] (make-delete-tree-data root-data :x))
(update-in [uuid/zero :y] (make-delete-tree-data root-data :y)) (update-in $ [uuid/zero :y] (make-delete-tree-data root-data :y))
(update-in [frame-id :x] (make-delete-tree-data frame-data :x)) (update-in $ [frame-id :x] (make-delete-tree-data frame-data :x))
(update-in [frame-id :y] (make-delete-tree-data frame-data :y)) (update-in $ [frame-id :y] (make-delete-tree-data frame-data :y))
(add-frame new-frame)))) (add-frame objects $ new-frame))))
(defn- update-shape (defn- update-shape
[page-data [old-shape new-shape]] [objects page-data [old-shape new-shape]]
(-> page-data (as-> page-data $
(remove-shape old-shape) (remove-shape $ old-shape)
(add-shape new-shape))) (add-shape objects $ new-shape)))
(defn- update-guide (defn- update-guide
[objects page-data [old-guide new-guide]] [objects page-data [old-guide new-guide]]
@ -205,8 +209,8 @@
page-data page-data
(as-> {} $ (as-> {} $
(add-root-frame $) (add-root-frame $)
(reduce add-frame $ frames) (reduce (partial add-frame objects) $ frames)
(reduce add-shape $ shapes) (reduce (partial add-shape objects) $ shapes)
(reduce (partial add-guide objects) $ guides))] (reduce (partial add-guide objects) $ guides))]
(assoc snap-data (:id page) page-data))) (assoc snap-data (:id page) page-data)))
@ -233,16 +237,16 @@
(diff/calculate-page-diff old-page page snap-attrs)] (diff/calculate-page-diff old-page page snap-attrs)]
(as-> page-data $ (as-> page-data $
(reduce update-shape $ change-frame-shapes) (reduce (partial update-shape objects) $ change-frame-shapes)
(reduce remove-frame $ removed-frames) (reduce remove-frame $ removed-frames)
(reduce remove-shape $ removed-shapes) (reduce remove-shape $ removed-shapes)
(reduce update-frame $ updated-frames) (reduce (partial update-frame objects) $ updated-frames)
(reduce update-shape $ updated-shapes) (reduce (partial update-shape objects) $ updated-shapes)
(reduce add-frame $ new-frames) (reduce (partial add-frame objects) $ new-frames)
(reduce add-shape $ new-shapes) (reduce (partial add-shape objects) $ new-shapes)
(reduce remove-guide $ removed-guides)
;; Guides functions. Need objects to get its frame data ;; Guides functions. Need objects to get its frame data
(reduce remove-guide $ removed-guides)
(reduce (partial update-guide objects) $ change-frame-guides) (reduce (partial update-guide objects) $ change-frame-guides)
(reduce (partial update-guide objects) $ updated-guides) (reduce (partial update-guide objects) $ updated-guides)
(reduce (partial add-guide objects) $ new-guides))))) (reduce (partial add-guide objects) $ new-guides)))))