mirror of
https://github.com/penpot/penpot.git
synced 2025-07-11 01:27:18 +02:00
♻️ Reorder functions, for more clarity, and add some comments
This commit is contained in:
parent
741d3050ad
commit
56795f8d26
8 changed files with 560 additions and 529 deletions
|
@ -7,50 +7,12 @@
|
||||||
(ns app.common.geom.shapes
|
(ns app.common.geom.shapes
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.matrix :as gmt]
|
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes.common :as gco]
|
[app.common.geom.shapes.common :as gco]
|
||||||
[app.common.geom.shapes.intersect :as gin]
|
[app.common.geom.shapes.intersect :as gin]
|
||||||
[app.common.geom.shapes.path :as gsp]
|
[app.common.geom.shapes.path :as gsp]
|
||||||
[app.common.geom.shapes.rect :as gpr]
|
[app.common.geom.shapes.rect :as gpr]
|
||||||
[app.common.geom.shapes.transforms :as gtr]
|
[app.common.geom.shapes.transforms :as gtr]))
|
||||||
[app.common.spec :as us]))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Resize (Dimensions)
|
|
||||||
(defn resize-modifiers
|
|
||||||
[shape attr value]
|
|
||||||
(us/assert map? shape)
|
|
||||||
(us/assert #{:width :height} attr)
|
|
||||||
(us/assert number? value)
|
|
||||||
(let [{:keys [proportion proportion-lock]} shape
|
|
||||||
size (select-keys (:selrect shape) [:width :height])
|
|
||||||
new-size (if-not proportion-lock
|
|
||||||
(assoc size attr value)
|
|
||||||
(if (= attr :width)
|
|
||||||
(-> size
|
|
||||||
(assoc :width value)
|
|
||||||
(assoc :height (/ value proportion)))
|
|
||||||
(-> size
|
|
||||||
(assoc :height value)
|
|
||||||
(assoc :width (* value proportion)))))
|
|
||||||
width (:width new-size)
|
|
||||||
height (:height new-size)
|
|
||||||
|
|
||||||
shape-transform (:transform shape (gmt/matrix))
|
|
||||||
shape-transform-inv (:transform-inverse shape (gmt/matrix))
|
|
||||||
shape-center (gco/center-shape shape)
|
|
||||||
{sr-width :width sr-height :height} (:selrect shape)
|
|
||||||
|
|
||||||
origin (-> (gpt/point (:selrect shape))
|
|
||||||
(gtr/transform-point-center shape-center shape-transform))
|
|
||||||
|
|
||||||
scalev (gpt/divide (gpt/point width height)
|
|
||||||
(gpt/point sr-width sr-height))]
|
|
||||||
{:resize-vector scalev
|
|
||||||
:resize-origin origin
|
|
||||||
:resize-transform shape-transform
|
|
||||||
:resize-transform-inverse shape-transform-inv}))
|
|
||||||
|
|
||||||
;; --- Setup (Initialize)
|
;; --- Setup (Initialize)
|
||||||
;; FIXME: Is this the correct place for these functions?
|
;; FIXME: Is this the correct place for these functions?
|
||||||
|
@ -164,15 +126,6 @@
|
||||||
(assoc :selrect selrect
|
(assoc :selrect selrect
|
||||||
:points points))))
|
:points points))))
|
||||||
|
|
||||||
(defn rotation-modifiers
|
|
||||||
[shape center angle]
|
|
||||||
(let [displacement (let [shape-center (gco/center-shape shape)]
|
|
||||||
(-> (gmt/matrix)
|
|
||||||
(gmt/rotate angle center)
|
|
||||||
(gmt/rotate (- angle) shape-center)))]
|
|
||||||
{:rotation angle
|
|
||||||
:displacement displacement}))
|
|
||||||
|
|
||||||
|
|
||||||
;; EXPORTS
|
;; EXPORTS
|
||||||
(d/export gco/center-shape)
|
(d/export gco/center-shape)
|
||||||
|
@ -187,17 +140,20 @@
|
||||||
(d/export gpr/points->rect)
|
(d/export gpr/points->rect)
|
||||||
(d/export gpr/center->rect)
|
(d/export gpr/center->rect)
|
||||||
|
|
||||||
(d/export gtr/transform-shape)
|
(d/export gtr/move)
|
||||||
(d/export gtr/calc-child-modifiers)
|
(d/export gtr/absolute-move)
|
||||||
(d/export gtr/transform-matrix)
|
(d/export gtr/transform-matrix)
|
||||||
(d/export gtr/inverse-transform-matrix)
|
(d/export gtr/inverse-transform-matrix)
|
||||||
(d/export gtr/transform-point-center)
|
(d/export gtr/transform-point-center)
|
||||||
(d/export gtr/transform-rect)
|
|
||||||
(d/export gtr/update-group-selrect)
|
|
||||||
(d/export gtr/transform-points)
|
(d/export gtr/transform-points)
|
||||||
|
(d/export gtr/transform-rect)
|
||||||
(d/export gtr/calculate-adjust-matrix)
|
(d/export gtr/calculate-adjust-matrix)
|
||||||
(d/export gtr/move)
|
(d/export gtr/update-group-selrect)
|
||||||
(d/export gtr/absolute-move)
|
(d/export gtr/resize-modifiers)
|
||||||
|
(d/export gtr/rotation-modifiers)
|
||||||
|
(d/export gtr/merge-modifiers)
|
||||||
|
(d/export gtr/transform-shape)
|
||||||
|
(d/export gtr/calc-child-modifiers)
|
||||||
|
|
||||||
;; PATHS
|
;; PATHS
|
||||||
(d/export gsp/content->points)
|
(d/export gsp/content->points)
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
[app.common.geom.shapes.rect :as gpr]
|
[app.common.geom.shapes.rect :as gpr]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages.spec :as spec]
|
[app.common.pages.spec :as spec]
|
||||||
|
[app.common.spec :as us]
|
||||||
[app.common.text :as txt]))
|
[app.common.text :as txt]))
|
||||||
|
|
||||||
|
|
||||||
;; --- Relative Movement
|
;; --- Relative Movement
|
||||||
|
|
||||||
(defn move-selrect [selrect {dx :x dy :y}]
|
(defn- move-selrect [selrect {dx :x dy :y}]
|
||||||
(-> selrect
|
(-> selrect
|
||||||
(d/update-when :x + dx)
|
(d/update-when :x + dx)
|
||||||
(d/update-when :y + dy)
|
(d/update-when :y + dy)
|
||||||
|
@ -28,7 +30,7 @@
|
||||||
(d/update-when :x2 + dx)
|
(d/update-when :x2 + dx)
|
||||||
(d/update-when :y2 + dy)))
|
(d/update-when :y2 + dy)))
|
||||||
|
|
||||||
(defn move-points [points move-vec]
|
(defn- move-points [points move-vec]
|
||||||
(->> points
|
(->> points
|
||||||
(mapv #(gpt/add % move-vec))))
|
(mapv #(gpt/add % move-vec))))
|
||||||
|
|
||||||
|
@ -48,9 +50,8 @@
|
||||||
(cond-> (= :path (:type shape))
|
(cond-> (= :path (:type shape))
|
||||||
(update :content gpa/move-content move-vec)))))
|
(update :content gpa/move-content move-vec)))))
|
||||||
|
|
||||||
;; --- Absolute Movement
|
|
||||||
|
|
||||||
(declare absolute-move-rect)
|
;; --- Absolute Movement
|
||||||
|
|
||||||
(defn absolute-move
|
(defn absolute-move
|
||||||
"Move the shape to the exactly specified position."
|
"Move the shape to the exactly specified position."
|
||||||
|
@ -59,6 +60,68 @@
|
||||||
dy (- (d/check-num y) (-> shape :selrect :y))]
|
dy (- (d/check-num y) (-> shape :selrect :y))]
|
||||||
(move shape (gpt/point dx dy))))
|
(move shape (gpt/point dx dy))))
|
||||||
|
|
||||||
|
|
||||||
|
; ---- Geometric operations
|
||||||
|
|
||||||
|
(defn- normalize-scale
|
||||||
|
"We normalize the scale so it's not too close to 0"
|
||||||
|
[scale]
|
||||||
|
(cond
|
||||||
|
(and (< scale 0) (> scale -0.01)) -0.01
|
||||||
|
(and (>= scale 0) (< scale 0.01)) 0.01
|
||||||
|
:else scale))
|
||||||
|
|
||||||
|
(defn- calculate-skew-angle
|
||||||
|
"Calculates the skew angle of the paralelogram given by the points"
|
||||||
|
[[p1 _ p3 p4]]
|
||||||
|
(let [v1 (gpt/to-vec p3 p4)
|
||||||
|
v2 (gpt/to-vec p4 p1)]
|
||||||
|
;; If one of the vectors is zero it's a rectangle with 0 height or width
|
||||||
|
;; We don't skew these
|
||||||
|
(if (or (gpt/almost-zero? v1)
|
||||||
|
(gpt/almost-zero? v2))
|
||||||
|
0
|
||||||
|
(- 90 (gpt/angle-with-other v1 v2)))))
|
||||||
|
|
||||||
|
(defn- calculate-height
|
||||||
|
"Calculates the height of a paralelogram given by the points"
|
||||||
|
[[p1 _ _ p4]]
|
||||||
|
(-> (gpt/to-vec p4 p1)
|
||||||
|
(gpt/length)))
|
||||||
|
|
||||||
|
(defn- calculate-width
|
||||||
|
"Calculates the width of a paralelogram given by the points"
|
||||||
|
[[p1 p2 _ _]]
|
||||||
|
(-> (gpt/to-vec p1 p2)
|
||||||
|
(gpt/length)))
|
||||||
|
|
||||||
|
(defn- calculate-rotation
|
||||||
|
"Calculates the rotation between two shapes given the resize vector direction"
|
||||||
|
[center points-shape1 points-shape2 flip-x flip-y]
|
||||||
|
|
||||||
|
(let [idx-1 0
|
||||||
|
idx-2 (cond (and flip-x (not flip-y)) 1
|
||||||
|
(and flip-x flip-y) 2
|
||||||
|
(and (not flip-x) flip-y) 3
|
||||||
|
:else 0)
|
||||||
|
p1 (nth points-shape1 idx-1)
|
||||||
|
p2 (nth points-shape2 idx-2)
|
||||||
|
v1 (gpt/to-vec center p1)
|
||||||
|
v2 (gpt/to-vec center p2)
|
||||||
|
|
||||||
|
rot-angle (gpt/angle-with-other v1 v2)
|
||||||
|
rot-sign (gpt/angle-sign v1 v2)]
|
||||||
|
(* rot-sign rot-angle)))
|
||||||
|
|
||||||
|
(defn- calculate-dimensions
|
||||||
|
[[p1 p2 p3 _]]
|
||||||
|
(let [width (gpt/distance p1 p2)
|
||||||
|
height (gpt/distance p2 p3)]
|
||||||
|
{:width width :height height}))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Transformation matrix operations
|
||||||
|
|
||||||
(defn transform-matrix
|
(defn transform-matrix
|
||||||
"Returns a transformation matrix without changing the shape properties.
|
"Returns a transformation matrix without changing the shape properties.
|
||||||
The result should be used in a `transform` attribute in svg"
|
The result should be used in a `transform` attribute in svg"
|
||||||
|
@ -117,98 +180,6 @@
|
||||||
(transform-points matrix))]
|
(transform-points matrix))]
|
||||||
(gpr/points->rect points)))
|
(gpr/points->rect points)))
|
||||||
|
|
||||||
(defn normalize-scale
|
|
||||||
"We normalize the scale so it's not too close to 0"
|
|
||||||
[scale]
|
|
||||||
(cond
|
|
||||||
(and (< scale 0) (> scale -0.01)) -0.01
|
|
||||||
(and (>= scale 0) (< scale 0.01)) 0.01
|
|
||||||
:else scale))
|
|
||||||
|
|
||||||
(defn modifiers->transform
|
|
||||||
[center modifiers]
|
|
||||||
(let [ds-modifier (:displacement modifiers (gmt/matrix))
|
|
||||||
{res-x :x res-y :y} (:resize-vector modifiers (gpt/point 1 1))
|
|
||||||
|
|
||||||
;; Normalize x/y vector coordinates because scale by 0 is infinite
|
|
||||||
res-x (normalize-scale res-x)
|
|
||||||
res-y (normalize-scale res-y)
|
|
||||||
resize (gpt/point res-x res-y)
|
|
||||||
|
|
||||||
origin (:resize-origin modifiers (gpt/point 0 0))
|
|
||||||
|
|
||||||
resize-transform (:resize-transform modifiers (gmt/matrix))
|
|
||||||
resize-transform-inverse (:resize-transform-inverse modifiers (gmt/matrix))
|
|
||||||
rt-modif (or (:rotation modifiers) 0)
|
|
||||||
|
|
||||||
center (gpt/transform center ds-modifier)
|
|
||||||
|
|
||||||
transform (-> (gmt/matrix)
|
|
||||||
|
|
||||||
;; Applies the current resize transformation
|
|
||||||
(gmt/translate origin)
|
|
||||||
(gmt/multiply resize-transform)
|
|
||||||
(gmt/scale resize)
|
|
||||||
(gmt/multiply resize-transform-inverse)
|
|
||||||
(gmt/translate (gpt/negate origin))
|
|
||||||
|
|
||||||
;; Applies the stacked transformations
|
|
||||||
(gmt/translate center)
|
|
||||||
(gmt/multiply (gmt/rotate-matrix rt-modif))
|
|
||||||
(gmt/translate (gpt/negate center))
|
|
||||||
|
|
||||||
;; Displacement
|
|
||||||
(gmt/multiply ds-modifier))]
|
|
||||||
transform))
|
|
||||||
|
|
||||||
(defn- calculate-skew-angle
|
|
||||||
"Calculates the skew angle of the paralelogram given by the points"
|
|
||||||
[[p1 _ p3 p4]]
|
|
||||||
(let [v1 (gpt/to-vec p3 p4)
|
|
||||||
v2 (gpt/to-vec p4 p1)]
|
|
||||||
;; If one of the vectors is zero it's a rectangle with 0 height or width
|
|
||||||
;; We don't skew these
|
|
||||||
(if (or (gpt/almost-zero? v1)
|
|
||||||
(gpt/almost-zero? v2))
|
|
||||||
0
|
|
||||||
(- 90 (gpt/angle-with-other v1 v2)))))
|
|
||||||
|
|
||||||
(defn- calculate-height
|
|
||||||
"Calculates the height of a paralelogram given by the points"
|
|
||||||
[[p1 _ _ p4]]
|
|
||||||
(-> (gpt/to-vec p4 p1)
|
|
||||||
(gpt/length)))
|
|
||||||
|
|
||||||
(defn- calculate-width
|
|
||||||
"Calculates the width of a paralelogram given by the points"
|
|
||||||
[[p1 p2 _ _]]
|
|
||||||
(-> (gpt/to-vec p1 p2)
|
|
||||||
(gpt/length)))
|
|
||||||
|
|
||||||
(defn- calculate-rotation
|
|
||||||
"Calculates the rotation between two shapes given the resize vector direction"
|
|
||||||
[center points-shape1 points-shape2 flip-x flip-y]
|
|
||||||
|
|
||||||
(let [idx-1 0
|
|
||||||
idx-2 (cond (and flip-x (not flip-y)) 1
|
|
||||||
(and flip-x flip-y) 2
|
|
||||||
(and (not flip-x) flip-y) 3
|
|
||||||
:else 0)
|
|
||||||
p1 (nth points-shape1 idx-1)
|
|
||||||
p2 (nth points-shape2 idx-2)
|
|
||||||
v1 (gpt/to-vec center p1)
|
|
||||||
v2 (gpt/to-vec center p2)
|
|
||||||
|
|
||||||
rot-angle (gpt/angle-with-other v1 v2)
|
|
||||||
rot-sign (gpt/angle-sign v1 v2)]
|
|
||||||
(* rot-sign rot-angle)))
|
|
||||||
|
|
||||||
(defn- calculate-dimensions
|
|
||||||
[[p1 p2 p3 _]]
|
|
||||||
(let [width (gpt/distance p1 p2)
|
|
||||||
height (gpt/distance p2 p3)]
|
|
||||||
{:width width :height height}))
|
|
||||||
|
|
||||||
(defn calculate-adjust-matrix
|
(defn calculate-adjust-matrix
|
||||||
"Calculates a matrix that is a series of transformations we have to do to the transformed rectangle so that
|
"Calculates a matrix that is a series of transformations we have to do to the transformed rectangle so that
|
||||||
after applying them the end result is the `shape-pathn-temp`.
|
after applying them the end result is the `shape-pathn-temp`.
|
||||||
|
@ -257,7 +228,7 @@
|
||||||
(gmt/rotate (- rotation-angle)))]
|
(gmt/rotate (- rotation-angle)))]
|
||||||
[stretch-matrix stretch-matrix-inverse rotation-angle])))
|
[stretch-matrix stretch-matrix-inverse rotation-angle])))
|
||||||
|
|
||||||
(defn apply-transform
|
(defn- apply-transform
|
||||||
"Given a new set of points transformed, set up the rectangle so it keeps
|
"Given a new set of points transformed, set up the rectangle so it keeps
|
||||||
its properties. We adjust de x,y,width,height and create a custom transform"
|
its properties. We adjust de x,y,width,height and create a custom transform"
|
||||||
[shape transform round-coords?]
|
[shape transform round-coords?]
|
||||||
|
@ -310,7 +281,145 @@
|
||||||
(assoc $ :selrect (gpr/rect->selrect rect-shape))
|
(assoc $ :selrect (gpr/rect->selrect rect-shape))
|
||||||
(assoc $ :rotation (mod (+ base-rotation modif-rotation) 360)))))
|
(assoc $ :rotation (mod (+ base-rotation modif-rotation) 360)))))
|
||||||
|
|
||||||
(defn set-flip [shape modifiers]
|
(defn- update-group-viewbox
|
||||||
|
"Updates the viewbox for groups imported from SVG's"
|
||||||
|
[{:keys [selrect svg-viewbox] :as group} new-selrect]
|
||||||
|
(let [;; Gets deltas for the selrect to update the svg-viewbox (for svg-imports)
|
||||||
|
deltas {:x (- (:x new-selrect 0) (:x selrect 0))
|
||||||
|
:y (- (:y new-selrect 0) (:y selrect 0))
|
||||||
|
:width (- (:width new-selrect 1) (:width selrect 1))
|
||||||
|
:height (- (:height new-selrect 1) (:height selrect 1))}]
|
||||||
|
|
||||||
|
(cond-> group
|
||||||
|
(and (some? svg-viewbox) (some? selrect) (some? new-selrect))
|
||||||
|
(update :svg-viewbox
|
||||||
|
#(-> %
|
||||||
|
(update :x + (:x deltas))
|
||||||
|
(update :y + (:y deltas))
|
||||||
|
(update :width + (:width deltas))
|
||||||
|
(update :height + (:height deltas)))))))
|
||||||
|
|
||||||
|
(defn update-group-selrect [group children]
|
||||||
|
(let [shape-center (gco/center-shape group)
|
||||||
|
;; Points for every shape inside the group
|
||||||
|
points (->> children (mapcat :points))
|
||||||
|
|
||||||
|
;; Invert to get the points minus the transforms applied to the group
|
||||||
|
base-points (transform-points points shape-center (:transform-inverse group (gmt/matrix)))
|
||||||
|
|
||||||
|
;; Defines the new selection rect with its transformations
|
||||||
|
new-points (-> (gpr/points->selrect base-points)
|
||||||
|
(gpr/rect->points)
|
||||||
|
(transform-points shape-center (:transform group (gmt/matrix))))
|
||||||
|
|
||||||
|
;; Calculte the new selrect
|
||||||
|
new-selrect (gpr/points->selrect base-points)]
|
||||||
|
|
||||||
|
;; Updates the shape and the applytransform-rect will update the other properties
|
||||||
|
(-> group
|
||||||
|
(update-group-viewbox new-selrect)
|
||||||
|
(assoc :selrect new-selrect)
|
||||||
|
(assoc :points new-points)
|
||||||
|
|
||||||
|
;; We're regenerating the selrect from its children so we
|
||||||
|
;; need to remove the flip flags
|
||||||
|
(assoc :flip-x false)
|
||||||
|
(assoc :flip-y false)
|
||||||
|
(apply-transform (gmt/matrix) true))))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Modifiers
|
||||||
|
|
||||||
|
(defn resize-modifiers
|
||||||
|
[shape attr value]
|
||||||
|
(us/assert map? shape)
|
||||||
|
(us/assert #{:width :height} attr)
|
||||||
|
(us/assert number? value)
|
||||||
|
(let [{:keys [proportion proportion-lock]} shape
|
||||||
|
size (select-keys (:selrect shape) [:width :height])
|
||||||
|
new-size (if-not proportion-lock
|
||||||
|
(assoc size attr value)
|
||||||
|
(if (= attr :width)
|
||||||
|
(-> size
|
||||||
|
(assoc :width value)
|
||||||
|
(assoc :height (/ value proportion)))
|
||||||
|
(-> size
|
||||||
|
(assoc :height value)
|
||||||
|
(assoc :width (* value proportion)))))
|
||||||
|
width (:width new-size)
|
||||||
|
height (:height new-size)
|
||||||
|
|
||||||
|
shape-transform (:transform shape (gmt/matrix))
|
||||||
|
shape-transform-inv (:transform-inverse shape (gmt/matrix))
|
||||||
|
shape-center (gco/center-shape shape)
|
||||||
|
{sr-width :width sr-height :height} (:selrect shape)
|
||||||
|
|
||||||
|
origin (-> (gpt/point (:selrect shape))
|
||||||
|
(transform-point-center shape-center shape-transform))
|
||||||
|
|
||||||
|
scalev (gpt/divide (gpt/point width height)
|
||||||
|
(gpt/point sr-width sr-height))]
|
||||||
|
{:resize-vector scalev
|
||||||
|
:resize-origin origin
|
||||||
|
:resize-transform shape-transform
|
||||||
|
:resize-transform-inverse shape-transform-inv}))
|
||||||
|
|
||||||
|
(defn rotation-modifiers
|
||||||
|
[shape center angle]
|
||||||
|
(let [displacement (let [shape-center (gco/center-shape shape)]
|
||||||
|
(-> (gmt/matrix)
|
||||||
|
(gmt/rotate angle center)
|
||||||
|
(gmt/rotate (- angle) shape-center)))]
|
||||||
|
{:rotation angle
|
||||||
|
:displacement displacement}))
|
||||||
|
|
||||||
|
(defn merge-modifiers
|
||||||
|
[objects modifiers]
|
||||||
|
|
||||||
|
(let [set-modifier
|
||||||
|
(fn [objects [id modifiers]]
|
||||||
|
(-> objects
|
||||||
|
(d/update-when id merge modifiers)))]
|
||||||
|
(->> modifiers
|
||||||
|
(reduce set-modifier objects))))
|
||||||
|
|
||||||
|
(defn- modifiers->transform
|
||||||
|
[center modifiers]
|
||||||
|
(let [ds-modifier (:displacement modifiers (gmt/matrix))
|
||||||
|
{res-x :x res-y :y} (:resize-vector modifiers (gpt/point 1 1))
|
||||||
|
|
||||||
|
;; Normalize x/y vector coordinates because scale by 0 is infinite
|
||||||
|
res-x (normalize-scale res-x)
|
||||||
|
res-y (normalize-scale res-y)
|
||||||
|
resize (gpt/point res-x res-y)
|
||||||
|
|
||||||
|
origin (:resize-origin modifiers (gpt/point 0 0))
|
||||||
|
|
||||||
|
resize-transform (:resize-transform modifiers (gmt/matrix))
|
||||||
|
resize-transform-inverse (:resize-transform-inverse modifiers (gmt/matrix))
|
||||||
|
rt-modif (or (:rotation modifiers) 0)
|
||||||
|
|
||||||
|
center (gpt/transform center ds-modifier)
|
||||||
|
|
||||||
|
transform (-> (gmt/matrix)
|
||||||
|
|
||||||
|
;; Applies the current resize transformation
|
||||||
|
(gmt/translate origin)
|
||||||
|
(gmt/multiply resize-transform)
|
||||||
|
(gmt/scale resize)
|
||||||
|
(gmt/multiply resize-transform-inverse)
|
||||||
|
(gmt/translate (gpt/negate origin))
|
||||||
|
|
||||||
|
;; Applies the stacked transformations
|
||||||
|
(gmt/translate center)
|
||||||
|
(gmt/multiply (gmt/rotate-matrix rt-modif))
|
||||||
|
(gmt/translate (gpt/negate center))
|
||||||
|
|
||||||
|
;; Displacement
|
||||||
|
(gmt/multiply ds-modifier))]
|
||||||
|
transform))
|
||||||
|
|
||||||
|
(defn- set-flip [shape modifiers]
|
||||||
(let [rx (get-in modifiers [:resize-vector :x])
|
(let [rx (get-in modifiers [:resize-vector :x])
|
||||||
ry (get-in modifiers [:resize-vector :y])]
|
ry (get-in modifiers [:resize-vector :y])]
|
||||||
(cond-> shape
|
(cond-> shape
|
||||||
|
@ -319,7 +428,7 @@
|
||||||
(and ry (< ry 0)) (-> (update :flip-y not)
|
(and ry (< ry 0)) (-> (update :flip-y not)
|
||||||
(update :rotation -)))))
|
(update :rotation -)))))
|
||||||
|
|
||||||
(defn apply-displacement [shape]
|
(defn- apply-displacement [shape]
|
||||||
(let [modifiers (:modifiers shape)]
|
(let [modifiers (:modifiers shape)]
|
||||||
(if (contains? modifiers :displacement)
|
(if (contains? modifiers :displacement)
|
||||||
(let [mov-vec (-> (gpt/point 0 0)
|
(let [mov-vec (-> (gpt/point 0 0)
|
||||||
|
@ -332,9 +441,8 @@
|
||||||
(dissoc :modifiers))))
|
(dissoc :modifiers))))
|
||||||
shape)))
|
shape)))
|
||||||
|
|
||||||
;; TODO: looks like orig-shape is useless argument
|
(defn- apply-text-resize
|
||||||
(defn apply-text-resize
|
[shape modifiers]
|
||||||
[shape _orig-shape modifiers]
|
|
||||||
(if (and (= (:type shape) :text)
|
(if (and (= (:type shape) :text)
|
||||||
(:resize-scale-text modifiers))
|
(:resize-scale-text modifiers))
|
||||||
(let [merge-attrs (fn [attrs]
|
(let [merge-attrs (fn [attrs]
|
||||||
|
@ -364,7 +472,7 @@
|
||||||
(-> shape
|
(-> shape
|
||||||
(set-flip modifiers)
|
(set-flip modifiers)
|
||||||
(apply-transform transform round-coords?)
|
(apply-transform transform round-coords?)
|
||||||
(apply-text-resize shape modifiers)
|
(apply-text-resize modifiers)
|
||||||
(dissoc :modifiers)))
|
(dissoc :modifiers)))
|
||||||
shape))))
|
shape))))
|
||||||
|
|
||||||
|
@ -508,49 +616,3 @@
|
||||||
(assoc :resize-transform (:resize-transform parent-modifiers)
|
(assoc :resize-transform (:resize-transform parent-modifiers)
|
||||||
:resize-transform-inverse (:resize-transform-inverse parent-modifiers)))))
|
:resize-transform-inverse (:resize-transform-inverse parent-modifiers)))))
|
||||||
|
|
||||||
(defn update-group-viewbox
|
|
||||||
"Updates the viewbox for groups imported from SVG's"
|
|
||||||
[{:keys [selrect svg-viewbox] :as group} new-selrect]
|
|
||||||
(let [;; Gets deltas for the selrect to update the svg-viewbox (for svg-imports)
|
|
||||||
deltas {:x (- (:x new-selrect 0) (:x selrect 0))
|
|
||||||
:y (- (:y new-selrect 0) (:y selrect 0))
|
|
||||||
:width (- (:width new-selrect 1) (:width selrect 1))
|
|
||||||
:height (- (:height new-selrect 1) (:height selrect 1))}]
|
|
||||||
|
|
||||||
(cond-> group
|
|
||||||
(and (some? svg-viewbox) (some? selrect) (some? new-selrect))
|
|
||||||
(update :svg-viewbox
|
|
||||||
#(-> %
|
|
||||||
(update :x + (:x deltas))
|
|
||||||
(update :y + (:y deltas))
|
|
||||||
(update :width + (:width deltas))
|
|
||||||
(update :height + (:height deltas)))))))
|
|
||||||
|
|
||||||
(defn update-group-selrect [group children]
|
|
||||||
(let [shape-center (gco/center-shape group)
|
|
||||||
;; Points for every shape inside the group
|
|
||||||
points (->> children (mapcat :points))
|
|
||||||
|
|
||||||
;; Invert to get the points minus the transforms applied to the group
|
|
||||||
base-points (transform-points points shape-center (:transform-inverse group (gmt/matrix)))
|
|
||||||
|
|
||||||
;; Defines the new selection rect with its transformations
|
|
||||||
new-points (-> (gpr/points->selrect base-points)
|
|
||||||
(gpr/rect->points)
|
|
||||||
(transform-points shape-center (:transform group (gmt/matrix))))
|
|
||||||
|
|
||||||
;; Calculte the new selrect
|
|
||||||
new-selrect (gpr/points->selrect base-points)]
|
|
||||||
|
|
||||||
;; Updates the shape and the applytransform-rect will update the other properties
|
|
||||||
(-> group
|
|
||||||
(update-group-viewbox new-selrect)
|
|
||||||
(assoc :selrect new-selrect)
|
|
||||||
(assoc :points new-points)
|
|
||||||
|
|
||||||
;; We're regenerating the selrect from its children so we
|
|
||||||
;; need to remove the flip flags
|
|
||||||
(assoc :flip-x false)
|
|
||||||
(assoc :flip-y false)
|
|
||||||
(apply-transform (gmt/matrix) true))))
|
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,6 @@
|
||||||
(d/export helpers/merge-path-item)
|
(d/export helpers/merge-path-item)
|
||||||
(d/export helpers/compact-path)
|
(d/export helpers/compact-path)
|
||||||
(d/export helpers/compact-name)
|
(d/export helpers/compact-name)
|
||||||
(d/export helpers/merge-modifiers)
|
|
||||||
|
|
||||||
;; Indices
|
;; Indices
|
||||||
(d/export indices/calculate-z-index)
|
(d/export indices/calculate-z-index)
|
||||||
|
|
|
@ -465,12 +465,3 @@
|
||||||
(let [path-split (split-path path)]
|
(let [path-split (split-path path)]
|
||||||
(merge-path-item (first path-split) name)))
|
(merge-path-item (first path-split) name)))
|
||||||
|
|
||||||
(defn merge-modifiers
|
|
||||||
[objects modifiers]
|
|
||||||
|
|
||||||
(let [set-modifier
|
|
||||||
(fn [objects [id modifiers]]
|
|
||||||
(-> objects
|
|
||||||
(d/update-when id merge modifiers)))]
|
|
||||||
(->> modifiers
|
|
||||||
(reduce set-modifier objects))))
|
|
||||||
|
|
|
@ -1139,33 +1139,6 @@
|
||||||
(gpr/assign-proportions))))]
|
(gpr/assign-proportions))))]
|
||||||
(rx/of (dch/update-shapes [id] assign-proportions))))))
|
(rx/of (dch/update-shapes [id] assign-proportions))))))
|
||||||
|
|
||||||
;; --- Update Shape Position
|
|
||||||
|
|
||||||
(s/def ::x number?)
|
|
||||||
(s/def ::y number?)
|
|
||||||
(s/def ::position
|
|
||||||
(s/keys :opt-un [::x ::y]))
|
|
||||||
|
|
||||||
(defn update-position
|
|
||||||
[id position]
|
|
||||||
(us/verify ::us/uuid id)
|
|
||||||
(us/verify ::position position)
|
|
||||||
(ptk/reify ::update-position
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
shape (get objects id)
|
|
||||||
|
|
||||||
bbox (-> shape :points gsh/points->selrect)
|
|
||||||
|
|
||||||
cpos (gpt/point (:x bbox) (:y bbox))
|
|
||||||
pos (gpt/point (or (:x position) (:x bbox))
|
|
||||||
(or (:y position) (:y bbox)))
|
|
||||||
displ (gmt/translate-matrix (gpt/subtract pos cpos))]
|
|
||||||
(rx/of (dwt/set-modifiers [id] {:displacement displ})
|
|
||||||
(dwt/apply-modifiers [id]))))))
|
|
||||||
|
|
||||||
;; --- Update Shape Flags
|
;; --- Update Shape Flags
|
||||||
|
|
||||||
(defn update-shape-flags
|
(defn update-shape-flags
|
||||||
|
@ -1811,15 +1784,13 @@
|
||||||
|
|
||||||
;; Transform
|
;; Transform
|
||||||
|
|
||||||
(d/export dwt/start-rotate)
|
|
||||||
(d/export dwt/start-resize)
|
(d/export dwt/start-resize)
|
||||||
|
(d/export dwt/update-dimensions)
|
||||||
|
(d/export dwt/start-rotate)
|
||||||
|
(d/export dwt/increase-rotation)
|
||||||
(d/export dwt/start-move-selected)
|
(d/export dwt/start-move-selected)
|
||||||
(d/export dwt/move-selected)
|
(d/export dwt/move-selected)
|
||||||
(d/export dwt/set-rotation)
|
(d/export dwt/update-position)
|
||||||
(d/export dwt/increase-rotation)
|
|
||||||
(d/export dwt/set-modifiers)
|
|
||||||
(d/export dwt/apply-modifiers)
|
|
||||||
(d/export dwt/update-dimensions)
|
|
||||||
(d/export dwt/flip-horizontal-selected)
|
(d/export dwt/flip-horizontal-selected)
|
||||||
(d/export dwt/flip-vertical-selected)
|
(d/export dwt/flip-vertical-selected)
|
||||||
|
|
||||||
|
|
|
@ -24,18 +24,13 @@
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
;; -- Declarations
|
|
||||||
|
|
||||||
(declare set-modifiers)
|
;; -- Helpers --------------------------------------------------------
|
||||||
(declare set-rotation)
|
|
||||||
(declare apply-modifiers)
|
|
||||||
|
|
||||||
;; -- Helpers
|
;; For each of the 8 handlers gives the multiplier for resize
|
||||||
|
|
||||||
;; For each of the 8 handlers gives the modifier for resize
|
|
||||||
;; for example, right will only grow in the x coordinate and left
|
;; for example, right will only grow in the x coordinate and left
|
||||||
;; will grow in the inverse of the x coordinate
|
;; will grow in the inverse of the x coordinate
|
||||||
(def ^:private handler-modifiers
|
(def ^:private handler-multipliers
|
||||||
{:right [ 1 0]
|
{:right [ 1 0]
|
||||||
:bottom [ 0 1]
|
:bottom [ 0 1]
|
||||||
:left [-1 0]
|
:left [-1 0]
|
||||||
|
@ -45,13 +40,16 @@
|
||||||
:bottom-right [ 1 1]
|
:bottom-right [ 1 1]
|
||||||
:bottom-left [-1 1]})
|
:bottom-left [-1 1]})
|
||||||
|
|
||||||
;; Given a handler returns the coordinate origin for resizes
|
(defn- handler-resize-origin
|
||||||
;; this is the opposite of the handler so for right we want the
|
"Given a handler, return the coordinate origin for resizes.
|
||||||
;; left side as origin of the resize
|
This is the opposite of the handler so for right we want the
|
||||||
;; sx, sy => start x/y
|
left side as origin of the resize.
|
||||||
;; mx, my => middle x/y
|
|
||||||
;; ex, ey => end x/y
|
sx, sy => start x/y
|
||||||
(defn- handler-resize-origin [{sx :x sy :y :keys [width height]} handler]
|
mx, my => middle x/y
|
||||||
|
ex, ey => end x/y
|
||||||
|
"
|
||||||
|
[{sx :x sy :y :keys [width height]} handler]
|
||||||
(let [mx (+ sx (/ width 2))
|
(let [mx (+ sx (/ width 2))
|
||||||
my (+ sy (/ height 2))
|
my (+ sy (/ height 2))
|
||||||
ex (+ sx width)
|
ex (+ sx width)
|
||||||
|
@ -95,8 +93,193 @@
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update state :workspace-local dissoc :transform))))
|
(update state :workspace-local dissoc :transform))))
|
||||||
|
|
||||||
;; -- RESIZE
|
|
||||||
|
;; -- 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 set-modifiers-recursive)
|
||||||
|
(declare check-delta)
|
||||||
|
(declare set-local-displacement)
|
||||||
|
(declare clear-local-transform)
|
||||||
|
|
||||||
|
(defn- set-modifiers
|
||||||
|
([ids] (set-modifiers ids nil))
|
||||||
|
([ids modifiers]
|
||||||
|
(us/verify (s/coll-of uuid?) ids)
|
||||||
|
(ptk/reify ::set-modifiers
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {}))
|
||||||
|
page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
|
||||||
|
ids (->> ids (into #{} (remove #(get-in objects [% :blocked] false))))]
|
||||||
|
|
||||||
|
(reduce (fn [state id]
|
||||||
|
(update state :workspace-modifiers
|
||||||
|
#(set-modifiers-recursive %
|
||||||
|
objects
|
||||||
|
(get objects id)
|
||||||
|
modifiers
|
||||||
|
nil
|
||||||
|
nil)))
|
||||||
|
state
|
||||||
|
ids))))))
|
||||||
|
|
||||||
|
;; 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)
|
||||||
|
id->obj #(get objects %)
|
||||||
|
get-children (fn [shape] (map id->obj (cp/get-children (:id shape) objects)))
|
||||||
|
|
||||||
|
shapes (->> shapes (into [] (remove #(get % :blocked false))))
|
||||||
|
|
||||||
|
shapes (->> shapes (mapcat get-children) (concat shapes))
|
||||||
|
|
||||||
|
update-shape
|
||||||
|
(fn [modifiers shape]
|
||||||
|
(let [rotate-modifiers (gsh/rotation-modifiers shape center angle)]
|
||||||
|
(assoc-in modifiers [(:id shape) :modifiers] rotate-modifiers)))]
|
||||||
|
(-> state
|
||||||
|
(update :workspace-modifiers
|
||||||
|
#(reduce update-shape % shapes))))))))
|
||||||
|
|
||||||
|
(defn- apply-modifiers
|
||||||
|
[ids]
|
||||||
|
(us/verify (s/coll-of uuid?) ids)
|
||||||
|
(ptk/reify ::apply-modifiers
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
|
children-ids (->> ids (mapcat #(cp/get-children % objects)))
|
||||||
|
ids-with-children (d/concat [] children-ids ids)
|
||||||
|
object-modifiers (get state :workspace-modifiers)
|
||||||
|
ignore-tree (d/mapm #(get-in %2 [:modifiers :ignore-geometry?]) object-modifiers)]
|
||||||
|
|
||||||
|
(rx/of (dwu/start-undo-transaction)
|
||||||
|
(dch/update-shapes
|
||||||
|
ids-with-children
|
||||||
|
(fn [shape]
|
||||||
|
(-> shape
|
||||||
|
(merge (get object-modifiers (:id shape)))
|
||||||
|
(gsh/transform-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
|
||||||
|
:flip-x
|
||||||
|
:flip-y]})
|
||||||
|
(clear-local-transform)
|
||||||
|
(dwu/commit-undo-transaction))))))
|
||||||
|
|
||||||
|
(defn- set-modifiers-recursive
|
||||||
|
[modif-tree objects shape modifiers root transformed-root]
|
||||||
|
(let [children (->> (get shape :shapes [])
|
||||||
|
(map #(get objects %)))
|
||||||
|
|
||||||
|
transformed-shape (gsh/transform-shape (assoc shape :modifiers modifiers))
|
||||||
|
|
||||||
|
[root transformed-root ignore-geometry?]
|
||||||
|
(check-delta shape root transformed-shape transformed-root objects)
|
||||||
|
|
||||||
|
modifiers (assoc modifiers :ignore-geometry? ignore-geometry?)
|
||||||
|
|
||||||
|
set-child (fn [modif-tree child]
|
||||||
|
(let [child-modifiers (gsh/calc-child-modifiers shape
|
||||||
|
child
|
||||||
|
modifiers)]
|
||||||
|
(set-modifiers-recursive modif-tree
|
||||||
|
objects
|
||||||
|
child
|
||||||
|
child-modifiers
|
||||||
|
root
|
||||||
|
transformed-root)))]
|
||||||
|
(reduce set-child
|
||||||
|
(update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers))
|
||||||
|
children)))
|
||||||
|
|
||||||
|
(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]
|
||||||
|
(let [root (cond
|
||||||
|
(:component-root? shape)
|
||||||
|
shape
|
||||||
|
|
||||||
|
(nil? root)
|
||||||
|
(cp/get-root-shape shape objects)
|
||||||
|
|
||||||
|
:else root)
|
||||||
|
|
||||||
|
transformed-root (cond
|
||||||
|
(:component-root? transformed-shape)
|
||||||
|
transformed-shape
|
||||||
|
|
||||||
|
(nil? transformed-root)
|
||||||
|
(cp/get-root-shape transformed-shape objects)
|
||||||
|
|
||||||
|
:else transformed-root)
|
||||||
|
|
||||||
|
shape-delta (when root
|
||||||
|
(gpt/point (- (:x shape) (:x root))
|
||||||
|
(- (:y shape) (:y root))))
|
||||||
|
|
||||||
|
transformed-shape-delta (when transformed-root
|
||||||
|
(gpt/point (- (:x transformed-shape) (:x transformed-root))
|
||||||
|
(- (:y transformed-shape) (:y transformed-root))))
|
||||||
|
|
||||||
|
ignore-geometry? (= shape-delta transformed-shape-delta)]
|
||||||
|
|
||||||
|
[root transformed-root ignore-geometry?]))
|
||||||
|
|
||||||
|
(defn- set-local-displacement [point]
|
||||||
|
(ptk/reify ::start-local-displacement
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [mtx (gmt/translate-matrix point)]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-local :modifiers] {:displacement mtx}))))))
|
||||||
|
|
||||||
|
(defn- clear-local-transform []
|
||||||
|
(ptk/reify ::clear-local-transform
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(-> state
|
||||||
|
(dissoc :workspace-modifiers)
|
||||||
|
(update :workspace-local dissoc :modifiers :current-move-selected)))))
|
||||||
|
|
||||||
|
|
||||||
|
;; -- Resize --------------------------------------------------------
|
||||||
|
|
||||||
(defn start-resize
|
(defn start-resize
|
||||||
|
"Enter mouse resize mode, until mouse button is released."
|
||||||
[handler ids shape]
|
[handler ids shape]
|
||||||
(letfn [(resize [shape initial layout [point lock? point-snap]]
|
(letfn [(resize [shape initial layout [point lock? point-snap]]
|
||||||
(let [{:keys [width height]} (:selrect shape)
|
(let [{:keys [width height]} (:selrect shape)
|
||||||
|
@ -113,12 +296,12 @@
|
||||||
lock? (or lock? scale-text)
|
lock? (or lock? scale-text)
|
||||||
|
|
||||||
;; Vector modifiers depending on the handler
|
;; Vector modifiers depending on the handler
|
||||||
handler-modif (let [[x y] (handler-modifiers handler)] (gpt/point x y))
|
handler-mult (let [[x y] (handler-multipliers handler)] (gpt/point x y))
|
||||||
|
|
||||||
;; Difference between the origin point in the coordinate system of the rotation
|
;; Difference between the origin point in the coordinate system of the rotation
|
||||||
deltav (-> (gpt/to-vec initial (if (= rotation 0) point-snap point))
|
deltav (-> (gpt/to-vec initial (if (= rotation 0) point-snap point))
|
||||||
(gpt/transform (gmt/rotate-matrix (- rotation)))
|
(gpt/transform (gmt/rotate-matrix (- rotation)))
|
||||||
(gpt/multiply handler-modif))
|
(gpt/multiply handler-mult))
|
||||||
|
|
||||||
;; Resize vector
|
;; Resize vector
|
||||||
scalev (gpt/divide (gpt/add shapev deltav) shapev)
|
scalev (gpt/divide (gpt/add shapev deltav) shapev)
|
||||||
|
@ -185,8 +368,43 @@
|
||||||
(rx/of (apply-modifiers ids)
|
(rx/of (apply-modifiers ids)
|
||||||
(finish-transform))))))))
|
(finish-transform))))))))
|
||||||
|
|
||||||
|
(defn update-dimensions
|
||||||
|
"Change size of shapes, from the sideber options form."
|
||||||
|
[ids attr value]
|
||||||
|
(us/verify (s/coll-of ::us/uuid) ids)
|
||||||
|
(us/verify #{:width :height} attr)
|
||||||
|
(us/verify ::us/number value)
|
||||||
|
(ptk/reify ::update-dimensions
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (get-in state [:workspace-data :pages-index page-id :objects])]
|
||||||
|
|
||||||
|
(reduce (fn [state id]
|
||||||
|
(let [shape (get objects id)
|
||||||
|
modifiers (gsh/resize-modifiers shape attr value)]
|
||||||
|
(update state :workspace-modifiers
|
||||||
|
#(set-modifiers-recursive %
|
||||||
|
objects
|
||||||
|
shape
|
||||||
|
modifiers
|
||||||
|
nil
|
||||||
|
nil))))
|
||||||
|
state
|
||||||
|
ids)))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
ids (d/concat [] ids (mapcat #(cp/get-children % objects) ids))]
|
||||||
|
(rx/of (apply-modifiers ids))))))
|
||||||
|
|
||||||
|
|
||||||
|
;; -- Rotate --------------------------------------------------------
|
||||||
|
|
||||||
(defn start-rotate
|
(defn start-rotate
|
||||||
|
"Enter mouse rotate mode, until mouse button is released."
|
||||||
[shapes]
|
[shapes]
|
||||||
(ptk/reify ::start-rotate
|
(ptk/reify ::start-rotate
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
|
@ -218,19 +436,37 @@
|
||||||
(rx/with-latest vector ms/mouse-position-ctrl)
|
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||||
(rx/map (fn [[pos ctrl?]]
|
(rx/map (fn [[pos ctrl?]]
|
||||||
(let [delta-angle (calculate-angle pos ctrl?)]
|
(let [delta-angle (calculate-angle pos ctrl?)]
|
||||||
(set-rotation delta-angle shapes group-center))))
|
(set-rotation-modifiers delta-angle shapes group-center))))
|
||||||
(rx/take-until stoper))
|
(rx/take-until stoper))
|
||||||
(rx/of (apply-modifiers (map :id shapes))
|
(rx/of (apply-modifiers (map :id shapes))
|
||||||
(finish-transform)))))))
|
(finish-transform)))))))
|
||||||
|
|
||||||
;; -- MOVE
|
(defn increase-rotation
|
||||||
|
"Rotate shapes a fixed angle, from a keyboard action."
|
||||||
|
[ids rotation]
|
||||||
|
(ptk/reify ::increase-rotation
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
rotate-shape (fn [shape]
|
||||||
|
(let [delta (- rotation (:rotation shape))]
|
||||||
|
(set-rotation-modifiers delta [shape])))]
|
||||||
|
(rx/concat
|
||||||
|
(rx/from (->> ids (map #(get objects %)) (map rotate-shape)))
|
||||||
|
(rx/of (apply-modifiers ids)))))))
|
||||||
|
|
||||||
|
|
||||||
|
;; -- Move ----------------------------------------------------------
|
||||||
|
|
||||||
(declare start-move)
|
(declare start-move)
|
||||||
(declare start-move-duplicate)
|
(declare start-move-duplicate)
|
||||||
(declare set-local-displacement)
|
(declare calculate-frame-for-move)
|
||||||
(declare clear-local-transform)
|
(declare get-displacement)
|
||||||
|
|
||||||
(defn start-move-selected
|
(defn start-move-selected
|
||||||
|
"Enter mouse move mode, until mouse button is released."
|
||||||
[]
|
[]
|
||||||
(ptk/reify ::start-move-selected
|
(ptk/reify ::start-move-selected
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
|
@ -255,7 +491,8 @@
|
||||||
;; Otherwise just plain old move
|
;; Otherwise just plain old move
|
||||||
(rx/of (start-move initial selected)))))))))))
|
(rx/of (start-move initial selected)))))))))))
|
||||||
|
|
||||||
(defn start-move-duplicate [from-position]
|
(defn- start-move-duplicate
|
||||||
|
[from-position]
|
||||||
(ptk/reify ::start-move-selected
|
(ptk/reify ::start-move-selected
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ stream]
|
(watch [_ _ stream]
|
||||||
|
@ -264,45 +501,7 @@
|
||||||
(rx/first)
|
(rx/first)
|
||||||
(rx/map #(start-move from-position))))))
|
(rx/map #(start-move from-position))))))
|
||||||
|
|
||||||
(defn calculate-frame-for-move [ids]
|
(defn- start-move
|
||||||
(ptk/reify ::calculate-frame-for-move
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [it state _]
|
|
||||||
(let [position @ms/mouse-position
|
|
||||||
page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
frame-id (cp/frame-id-by-position objects position)
|
|
||||||
|
|
||||||
moving-shapes (->> ids
|
|
||||||
(cp/clean-loops objects)
|
|
||||||
(map #(get objects %))
|
|
||||||
(remove #(or (nil? %)
|
|
||||||
(= (:frame-id %) frame-id))))
|
|
||||||
|
|
||||||
rch [{:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id frame-id
|
|
||||||
:shapes (mapv :id moving-shapes)}]
|
|
||||||
|
|
||||||
|
|
||||||
uch (->> moving-shapes
|
|
||||||
(reverse)
|
|
||||||
(mapv (fn [shape]
|
|
||||||
{:type :mov-objects
|
|
||||||
:page-id page-id
|
|
||||||
:parent-id (:parent-id shape)
|
|
||||||
:index (cp/get-index-in-parent objects (:id shape))
|
|
||||||
:shapes [(:id shape)]})))]
|
|
||||||
|
|
||||||
(when-not (empty? uch)
|
|
||||||
(rx/of dwu/pop-undo-into-transaction
|
|
||||||
(dch/commit-changes {:redo-changes rch
|
|
||||||
:undo-changes uch
|
|
||||||
:origin it})
|
|
||||||
(dwu/commit-undo-transaction)
|
|
||||||
(dwc/expand-collapse frame-id)))))))
|
|
||||||
|
|
||||||
(defn start-move
|
|
||||||
([from-position] (start-move from-position nil))
|
([from-position] (start-move from-position nil))
|
||||||
([from-position ids]
|
([from-position ids]
|
||||||
(ptk/reify ::start-move
|
(ptk/reify ::start-move
|
||||||
|
@ -349,19 +548,10 @@
|
||||||
(calculate-frame-for-move ids)
|
(calculate-frame-for-move ids)
|
||||||
(finish-transform)))))))))
|
(finish-transform)))))))))
|
||||||
|
|
||||||
(defn- get-displacement
|
|
||||||
"Retrieve the correct displacement delta point for the
|
|
||||||
provided direction speed and distances thresholds."
|
|
||||||
[direction]
|
|
||||||
(case direction
|
|
||||||
:up (gpt/point 0 (- 1))
|
|
||||||
:down (gpt/point 0 1)
|
|
||||||
:left (gpt/point (- 1) 0)
|
|
||||||
:right (gpt/point 1 0)))
|
|
||||||
|
|
||||||
(s/def ::direction #{:up :down :right :left})
|
(s/def ::direction #{:up :down :right :left})
|
||||||
|
|
||||||
(defn move-selected
|
(defn move-selected
|
||||||
|
"Move shapes a fixed increment in one direction, from a keyboard action."
|
||||||
[direction shift?]
|
[direction shift?]
|
||||||
(us/verify ::direction direction)
|
(us/verify ::direction direction)
|
||||||
(us/verify boolean? shift?)
|
(us/verify boolean? shift?)
|
||||||
|
@ -405,209 +595,83 @@
|
||||||
(finish-transform))))
|
(finish-transform))))
|
||||||
(rx/empty))))))
|
(rx/empty))))))
|
||||||
|
|
||||||
|
(s/def ::x number?)
|
||||||
|
(s/def ::y number?)
|
||||||
|
(s/def ::position
|
||||||
|
(s/keys :opt-un [::x ::y]))
|
||||||
|
|
||||||
;; -- Apply modifiers
|
(defn update-position
|
||||||
|
"Move shapes to a new position, from the sidebar options form."
|
||||||
(defn- check-delta
|
[id position]
|
||||||
"If the shape is a component instance, check its relative position respect the
|
(us/verify ::us/uuid id)
|
||||||
root of the component, and see if it changes after applying a transformation."
|
(us/verify ::position position)
|
||||||
[shape root transformed-shape transformed-root objects]
|
(ptk/reify ::update-position
|
||||||
(let [root (cond
|
|
||||||
(:component-root? shape)
|
|
||||||
shape
|
|
||||||
|
|
||||||
(nil? root)
|
|
||||||
(cp/get-root-shape shape objects)
|
|
||||||
|
|
||||||
:else root)
|
|
||||||
|
|
||||||
transformed-root (cond
|
|
||||||
(:component-root? transformed-shape)
|
|
||||||
transformed-shape
|
|
||||||
|
|
||||||
(nil? transformed-root)
|
|
||||||
(cp/get-root-shape transformed-shape objects)
|
|
||||||
|
|
||||||
:else transformed-root)
|
|
||||||
|
|
||||||
shape-delta (when root
|
|
||||||
(gpt/point (- (:x shape) (:x root))
|
|
||||||
(- (:y shape) (:y root))))
|
|
||||||
|
|
||||||
transformed-shape-delta (when transformed-root
|
|
||||||
(gpt/point (- (:x transformed-shape) (:x transformed-root))
|
|
||||||
(- (:y transformed-shape) (:y transformed-root))))
|
|
||||||
|
|
||||||
ignore-geometry? (= shape-delta transformed-shape-delta)]
|
|
||||||
|
|
||||||
[root transformed-root ignore-geometry?]))
|
|
||||||
|
|
||||||
(defn- set-modifiers-recursive
|
|
||||||
"Apply the modifiers to one shape, and the corresponding ones to all children,
|
|
||||||
depending on the child constraints. The modifiers are not directly applied to
|
|
||||||
the objects tree, but to a separated structure (modif-tree), that may be
|
|
||||||
merged later with the real objects. This way, the objects are changed only
|
|
||||||
once, avoiding unnecesary redrawings."
|
|
||||||
[modif-tree objects shape modifiers root transformed-root]
|
|
||||||
(let [children (->> (get shape :shapes [])
|
|
||||||
(map #(get objects %)))
|
|
||||||
|
|
||||||
transformed-shape (gsh/transform-shape (assoc shape :modifiers modifiers))
|
|
||||||
|
|
||||||
[root transformed-root ignore-geometry?]
|
|
||||||
(check-delta shape root transformed-shape transformed-root objects)
|
|
||||||
|
|
||||||
modifiers (assoc modifiers :ignore-geometry? ignore-geometry?)
|
|
||||||
|
|
||||||
set-child (fn [modif-tree child]
|
|
||||||
(let [child-modifiers (gsh/calc-child-modifiers shape
|
|
||||||
child
|
|
||||||
modifiers)]
|
|
||||||
(set-modifiers-recursive modif-tree
|
|
||||||
objects
|
|
||||||
child
|
|
||||||
child-modifiers
|
|
||||||
root
|
|
||||||
transformed-root)))]
|
|
||||||
(reduce set-child
|
|
||||||
(update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers))
|
|
||||||
children)))
|
|
||||||
|
|
||||||
(defn set-modifiers
|
|
||||||
([ids] (set-modifiers ids nil))
|
|
||||||
([ids modifiers]
|
|
||||||
(us/verify (s/coll-of uuid?) ids)
|
|
||||||
(ptk/reify ::set-modifiers
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {}))
|
|
||||||
page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
|
|
||||||
ids (->> ids (into #{} (remove #(get-in objects [% :blocked] false))))]
|
|
||||||
|
|
||||||
(reduce (fn [state id]
|
|
||||||
(update state :workspace-modifiers
|
|
||||||
#(set-modifiers-recursive %
|
|
||||||
objects
|
|
||||||
(get objects id)
|
|
||||||
modifiers
|
|
||||||
nil
|
|
||||||
nil)))
|
|
||||||
state
|
|
||||||
ids))))))
|
|
||||||
|
|
||||||
;; Set-rotation is custom because applies different modifiers to each
|
|
||||||
;; shape adjusting their position.
|
|
||||||
|
|
||||||
(defn set-rotation
|
|
||||||
([angle shapes]
|
|
||||||
(set-rotation angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
|
|
||||||
|
|
||||||
([angle shapes center]
|
|
||||||
(ptk/reify ::set-rotation
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [objects (wsh/lookup-page-objects state)
|
|
||||||
id->obj #(get objects %)
|
|
||||||
get-children (fn [shape] (map id->obj (cp/get-children (:id shape) objects)))
|
|
||||||
|
|
||||||
shapes (->> shapes (into [] (remove #(get % :blocked false))))
|
|
||||||
|
|
||||||
shapes (->> shapes (mapcat get-children) (concat shapes))
|
|
||||||
|
|
||||||
update-shape
|
|
||||||
(fn [modifiers shape]
|
|
||||||
(let [rotate-modifiers (gsh/rotation-modifiers shape center angle)]
|
|
||||||
(assoc-in modifiers [(:id shape) :modifiers] rotate-modifiers)))]
|
|
||||||
(-> state
|
|
||||||
(update :workspace-modifiers
|
|
||||||
#(reduce update-shape % shapes))))))))
|
|
||||||
|
|
||||||
(defn increase-rotation [ids rotation]
|
|
||||||
(ptk/reify ::increase-rotation
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
|
|
||||||
(let [page-id (:current-page-id state)
|
(let [page-id (:current-page-id state)
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
rotate-shape (fn [shape]
|
shape (get objects id)
|
||||||
(let [delta (- rotation (:rotation shape))]
|
|
||||||
(set-rotation delta [shape])))]
|
|
||||||
(rx/concat
|
|
||||||
(rx/from (->> ids (map #(get objects %)) (map rotate-shape)))
|
|
||||||
(rx/of (apply-modifiers ids)))))))
|
|
||||||
|
|
||||||
(defn apply-modifiers
|
bbox (-> shape :points gsh/points->selrect)
|
||||||
|
|
||||||
|
cpos (gpt/point (:x bbox) (:y bbox))
|
||||||
|
pos (gpt/point (or (:x position) (:x bbox))
|
||||||
|
(or (:y position) (:y bbox)))
|
||||||
|
displ (gmt/translate-matrix (gpt/subtract pos cpos))]
|
||||||
|
(rx/of (set-modifiers [id] {:displacement displ})
|
||||||
|
(apply-modifiers [id]))))))
|
||||||
|
|
||||||
|
(defn- calculate-frame-for-move
|
||||||
[ids]
|
[ids]
|
||||||
(us/verify (s/coll-of uuid?) ids)
|
(ptk/reify ::calculate-frame-for-move
|
||||||
(ptk/reify ::apply-modifiers
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [it state _]
|
||||||
(let [objects (wsh/lookup-page-objects state)
|
(let [position @ms/mouse-position
|
||||||
children-ids (->> ids (mapcat #(cp/get-children % objects)))
|
page-id (:current-page-id state)
|
||||||
ids-with-children (d/concat [] children-ids ids)
|
|
||||||
object-modifiers (get state :workspace-modifiers)
|
|
||||||
ignore-tree (d/mapm #(get-in %2 [:modifiers :ignore-geometry?]) object-modifiers)]
|
|
||||||
|
|
||||||
(rx/of (dwu/start-undo-transaction)
|
|
||||||
(dch/update-shapes
|
|
||||||
ids-with-children
|
|
||||||
(fn [shape]
|
|
||||||
(-> shape
|
|
||||||
(merge (get object-modifiers (:id shape)))
|
|
||||||
(gsh/transform-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
|
|
||||||
:flip-x
|
|
||||||
:flip-y]})
|
|
||||||
(clear-local-transform)
|
|
||||||
(dwu/commit-undo-transaction))))))
|
|
||||||
|
|
||||||
;; --- Update Dimensions
|
|
||||||
|
|
||||||
;; Event mainly used for handling user modification of the size of the
|
|
||||||
;; object from workspace sidebar options inputs.
|
|
||||||
|
|
||||||
(defn update-dimensions
|
|
||||||
[ids attr value]
|
|
||||||
(us/verify (s/coll-of ::us/uuid) ids)
|
|
||||||
(us/verify #{:width :height} attr)
|
|
||||||
(us/verify ::us/number value)
|
|
||||||
(ptk/reify ::update-dimensions
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (get-in state [:workspace-data :pages-index page-id :objects])]
|
|
||||||
|
|
||||||
(reduce (fn [state id]
|
|
||||||
(let [shape (get objects id)
|
|
||||||
modifiers (gsh/resize-modifiers shape attr value)]
|
|
||||||
(update state :workspace-modifiers
|
|
||||||
#(set-modifiers-recursive %
|
|
||||||
objects
|
|
||||||
shape
|
|
||||||
modifiers
|
|
||||||
nil
|
|
||||||
nil))))
|
|
||||||
state
|
|
||||||
ids)))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
ids (d/concat [] ids (mapcat #(cp/get-children % objects) ids))]
|
frame-id (cp/frame-id-by-position objects position)
|
||||||
(rx/of (apply-modifiers ids))))))
|
|
||||||
|
moving-shapes (->> ids
|
||||||
|
(cp/clean-loops objects)
|
||||||
|
(map #(get objects %))
|
||||||
|
(remove #(or (nil? %)
|
||||||
|
(= (:frame-id %) frame-id))))
|
||||||
|
|
||||||
|
rch [{:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id frame-id
|
||||||
|
:shapes (mapv :id moving-shapes)}]
|
||||||
|
|
||||||
|
|
||||||
|
uch (->> moving-shapes
|
||||||
|
(reverse)
|
||||||
|
(mapv (fn [shape]
|
||||||
|
{:type :mov-objects
|
||||||
|
:page-id page-id
|
||||||
|
:parent-id (:parent-id shape)
|
||||||
|
:index (cp/get-index-in-parent objects (:id shape))
|
||||||
|
:shapes [(:id shape)]})))]
|
||||||
|
|
||||||
|
(when-not (empty? uch)
|
||||||
|
(rx/of dwu/pop-undo-into-transaction
|
||||||
|
(dch/commit-changes {:redo-changes rch
|
||||||
|
:undo-changes uch
|
||||||
|
:origin it})
|
||||||
|
(dwu/commit-undo-transaction)
|
||||||
|
(dwc/expand-collapse frame-id)))))))
|
||||||
|
|
||||||
|
(defn- get-displacement
|
||||||
|
"Retrieve the correct displacement delta point for the
|
||||||
|
provided direction speed and distances thresholds."
|
||||||
|
[direction]
|
||||||
|
(case direction
|
||||||
|
:up (gpt/point 0 (- 1))
|
||||||
|
:down (gpt/point 0 1)
|
||||||
|
:left (gpt/point (- 1) 0)
|
||||||
|
:right (gpt/point 1 0)))
|
||||||
|
|
||||||
|
|
||||||
|
;; -- Flip ----------------------------------------------------------
|
||||||
|
|
||||||
(defn flip-horizontal-selected []
|
(defn flip-horizontal-selected []
|
||||||
(ptk/reify ::flip-horizontal-selected
|
(ptk/reify ::flip-horizontal-selected
|
||||||
|
@ -641,19 +705,6 @@
|
||||||
:displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))})
|
:displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))})
|
||||||
(apply-modifiers selected))))))
|
(apply-modifiers selected))))))
|
||||||
|
|
||||||
(defn set-local-displacement [point]
|
|
||||||
(ptk/reify ::start-local-displacement
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [mtx (gmt/translate-matrix point)]
|
|
||||||
(-> state
|
|
||||||
(assoc-in [:workspace-local :modifiers] {:displacement mtx}))))))
|
|
||||||
|
|
||||||
(defn clear-local-transform []
|
;; -- Transform to path ---------------------------------------------
|
||||||
(ptk/reify ::clear-local-transform
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(-> state
|
|
||||||
(dissoc :workspace-modifiers)
|
|
||||||
(update :workspace-local dissoc :modifiers :current-move-selected)))))
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"A collection of derived refs."
|
"A collection of derived refs."
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
@ -239,7 +240,7 @@
|
||||||
modifiers (:workspace-modifiers state)
|
modifiers (:workspace-modifiers state)
|
||||||
objects (cond-> objects
|
objects (cond-> objects
|
||||||
with-modifiers?
|
with-modifiers?
|
||||||
(cp/merge-modifiers modifiers))
|
(gsh/merge-modifiers modifiers))
|
||||||
xform (comp (map #(get objects %))
|
xform (comp (map #(get objects %))
|
||||||
(remove nil?))]
|
(remove nil?))]
|
||||||
(into [] xform ids)))
|
(into [] xform ids)))
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
(ns app.main.ui.workspace.viewport
|
(ns app.main.ui.workspace.viewport
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.pages :as cp]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.measurements :as msr]
|
[app.main.ui.measurements :as msr]
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
object-modifiers (mf/deref refs/workspace-modifiers)
|
object-modifiers (mf/deref refs/workspace-modifiers)
|
||||||
objects (mf/use-memo
|
objects (mf/use-memo
|
||||||
(mf/deps objects object-modifiers)
|
(mf/deps objects object-modifiers)
|
||||||
#(cp/merge-modifiers objects object-modifiers))
|
#(gsh/merge-modifiers objects object-modifiers))
|
||||||
background (get options :background "#E8E9EA")
|
background (get options :background "#E8E9EA")
|
||||||
|
|
||||||
;; STATE
|
;; STATE
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue