mirror of
https://github.com/penpot/penpot.git
synced 2025-03-15 01:01:30 -05:00
✨ Improved snap to grids
This commit is contained in:
parent
b5e965cf1a
commit
aec68c52ab
15 changed files with 220 additions and 119 deletions
|
@ -9,7 +9,7 @@
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth :refer [close?]]
|
||||||
[app.common.pages :refer [make-minimal-shape]]
|
[app.common.pages :refer [make-minimal-shape]]
|
||||||
[clojure.test :as t]))
|
[clojure.test :as t]))
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.common.geom-test
|
(ns app.common.geom-test
|
||||||
(:require
|
(:require
|
||||||
[clojure.test :as t]
|
[clojure.test :as t]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.matrix :as gmt]))
|
[app.common.geom.matrix :as gmt]))
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@
|
||||||
p2 (gpt/point 10 10)
|
p2 (gpt/point 10 10)
|
||||||
angle (gpt/angle-with-other p1 p2)]
|
angle (gpt/angle-with-other p1 p2)]
|
||||||
(t/is (number? angle))
|
(t/is (number? angle))
|
||||||
(t/is (= angle 45.0)))))
|
(t/is (mth/close? angle 45.0)))))
|
||||||
|
|
||||||
(t/deftest matrix-constructors-test
|
(t/deftest matrix-constructors-test
|
||||||
(let [m (gmt/matrix)]
|
(let [m (gmt/matrix)]
|
||||||
|
|
|
@ -678,7 +678,9 @@
|
||||||
(rx/of (finish-transform))
|
(rx/of (finish-transform))
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(->> position
|
(->> position
|
||||||
|
;; We ask for the snap position but we continue even if the result is not available
|
||||||
(rx/with-latest vector snap-delta)
|
(rx/with-latest vector snap-delta)
|
||||||
|
;; We try to use the previous snap so we don't have to wait for the result of the new
|
||||||
(rx/map snap/correct-snap-point)
|
(rx/map snap/correct-snap-point)
|
||||||
(rx/map #(hash-map :displacement (gmt/translate-matrix %)))
|
(rx/map #(hash-map :displacement (gmt/translate-matrix %)))
|
||||||
(rx/map (partial set-modifiers ids))
|
(rx/map (partial set-modifiers ids))
|
||||||
|
|
|
@ -55,10 +55,6 @@
|
||||||
(and (d/not-empty? focus)
|
(and (d/not-empty? focus)
|
||||||
(not (cp/is-in-focus? objects focus id)))))))
|
(not (cp/is-in-focus? objects focus id)))))))
|
||||||
|
|
||||||
(defn- flatten-to-points
|
|
||||||
[query-result]
|
|
||||||
(mapcat (fn [[_ data]] (map :pt data)) query-result))
|
|
||||||
|
|
||||||
(defn- calculate-distance [query-result point coord]
|
(defn- calculate-distance [query-result point coord]
|
||||||
(->> query-result
|
(->> query-result
|
||||||
(map (fn [[value _]] [(mth/abs (- value (coord point))) [(coord point) value]]))))
|
(map (fn [[value _]] [(mth/abs (- value (coord point))) [(coord point) value]]))))
|
||||||
|
@ -90,8 +86,7 @@
|
||||||
:axis coord
|
:axis coord
|
||||||
:ranges [[(- value (/ 0.5 zoom)) (+ value (/ 0.5 zoom))]]})
|
:ranges [[(- value (/ 0.5 zoom)) (+ value (/ 0.5 zoom))]]})
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map (remove-from-snap-points remove-snap?))
|
(rx/map (remove-from-snap-points remove-snap?)))))
|
||||||
(rx/map flatten-to-points))))
|
|
||||||
|
|
||||||
(defn- search-snap
|
(defn- search-snap
|
||||||
[page-id frame-id points coord remove-snap? zoom]
|
[page-id frame-id points coord remove-snap? zoom]
|
||||||
|
@ -369,7 +364,12 @@
|
||||||
0)
|
0)
|
||||||
dy (if (not= 0 (:y snap-delta))
|
dy (if (not= 0 (:y snap-delta))
|
||||||
(- (+ (:y snap-pos) (:y snap-delta)) (:y position))
|
(- (+ (:y snap-pos) (:y snap-delta)) (:y position))
|
||||||
0)]
|
0)
|
||||||
|
|
||||||
|
;; If the deltas (dx,dy) are bigger than the snap-accuracy means the stored snap
|
||||||
|
;; is not valid, so we change to 0
|
||||||
|
dx (if (> (mth/abs dx) snap-accuracy) 0 dx)
|
||||||
|
dy (if (> (mth/abs dy) snap-accuracy) 0 dy)]
|
||||||
(-> position
|
(-> position
|
||||||
(update :x + dx)
|
(update :x + dx)
|
||||||
(update :y + dy)))
|
(update :y + dy)))
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
on-blur (obj/get props "onBlur")
|
on-blur (obj/get props "onBlur")
|
||||||
title (obj/get props "title")
|
title (obj/get props "title")
|
||||||
default-val (obj/get props "default")
|
default-val (obj/get props "default")
|
||||||
|
nillable (obj/get props "nillable")
|
||||||
|
|
||||||
;; We need a ref pointing to the input dom element, but the user
|
;; We need a ref pointing to the input dom element, but the user
|
||||||
;; of this component may provide one (that is forwarded here).
|
;; of this component may provide one (that is forwarded here).
|
||||||
|
@ -71,12 +72,13 @@
|
||||||
|
|
||||||
parse-value
|
parse-value
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps ref min-val max-val value)
|
(mf/deps ref min-val max-val value nillable default-val)
|
||||||
(fn []
|
(fn []
|
||||||
(let [input-node (mf/ref-val ref)
|
(let [input-node (mf/ref-val ref)
|
||||||
new-value (-> (dom/get-value input-node)
|
new-value (-> (dom/get-value input-node)
|
||||||
(sm/expr-eval value))]
|
(sm/expr-eval value))]
|
||||||
(when (d/num? new-value)
|
(cond
|
||||||
|
(d/num? new-value)
|
||||||
(-> new-value
|
(-> new-value
|
||||||
(cljs.core/max us/min-safe-int)
|
(cljs.core/max us/min-safe-int)
|
||||||
(cljs.core/min us/max-safe-int)
|
(cljs.core/min us/max-safe-int)
|
||||||
|
@ -85,7 +87,12 @@
|
||||||
(cljs.core/max min-val)
|
(cljs.core/max min-val)
|
||||||
|
|
||||||
(d/num? max-val)
|
(d/num? max-val)
|
||||||
(cljs.core/min max-val)))))))
|
(cljs.core/min max-val)))
|
||||||
|
|
||||||
|
nillable
|
||||||
|
default-val
|
||||||
|
|
||||||
|
:else value))))
|
||||||
|
|
||||||
update-input
|
update-input
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
|
@ -109,8 +116,14 @@
|
||||||
(fn [event up? down?]
|
(fn [event up? down?]
|
||||||
(let [current-value (parse-value)]
|
(let [current-value (parse-value)]
|
||||||
(when current-value
|
(when current-value
|
||||||
(let [increment (if (kbd/shift? event)
|
(let [increment (cond
|
||||||
|
(kbd/shift? event)
|
||||||
(if up? (* step-val 10) (* step-val -10))
|
(if up? (* step-val 10) (* step-val -10))
|
||||||
|
|
||||||
|
(kbd/alt? event)
|
||||||
|
(if up? (* step-val 0.1) (* step-val -0.1))
|
||||||
|
|
||||||
|
:else
|
||||||
(if up? step-val (- step-val)))
|
(if up? step-val (- step-val)))
|
||||||
|
|
||||||
new-value (+ current-value increment)
|
new-value (+ current-value increment)
|
||||||
|
@ -154,14 +167,19 @@
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps set-delta)
|
(mf/deps set-delta)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(set-delta event (< (.-deltaY event) 0) (> (.-deltaY event) 0))))
|
(let [input-node (mf/ref-val ref)]
|
||||||
|
(when (dom/active? input-node)
|
||||||
|
(let [event (.getBrowserEvent ^js event)]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(set-delta event (< (.-deltaY event) 0) (> (.-deltaY event) 0)))))))
|
||||||
|
|
||||||
handle-blur
|
handle-blur
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps parse-value apply-value update-input on-blur)
|
(mf/deps parse-value apply-value update-input on-blur)
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(let [new-value (or (parse-value) default-val)]
|
(let [new-value (or (parse-value) default-val)]
|
||||||
(if new-value
|
(if (or nillable new-value)
|
||||||
(apply-value new-value)
|
(apply-value new-value)
|
||||||
(update-input new-value)))
|
(update-input new-value)))
|
||||||
(when on-blur (on-blur))))
|
(when on-blur (on-blur))))
|
||||||
|
@ -176,13 +194,12 @@
|
||||||
(dom/blur! current)))))))
|
(dom/blur! current)))))))
|
||||||
|
|
||||||
props (-> props
|
props (-> props
|
||||||
(obj/without ["value" "onChange"])
|
(obj/without ["value" "onChange" "nillable"])
|
||||||
(obj/set! "className" "input-text")
|
(obj/set! "className" "input-text")
|
||||||
(obj/set! "type" "text")
|
(obj/set! "type" "text")
|
||||||
(obj/set! "ref" ref)
|
(obj/set! "ref" ref)
|
||||||
(obj/set! "defaultValue" (fmt/format-number value))
|
(obj/set! "defaultValue" (fmt/format-number value))
|
||||||
(obj/set! "title" title)
|
(obj/set! "title" title)
|
||||||
(obj/set! "onWheel" handle-mouse-wheel)
|
|
||||||
(obj/set! "onKeyDown" handle-key-down)
|
(obj/set! "onKeyDown" handle-key-down)
|
||||||
(obj/set! "onBlur" handle-blur))]
|
(obj/set! "onBlur" handle-blur))]
|
||||||
|
|
||||||
|
@ -203,6 +220,13 @@
|
||||||
(let [handle-blur (:fn (mf/ref-val handle-blur-ref))]
|
(let [handle-blur (:fn (mf/ref-val handle-blur-ref))]
|
||||||
(handle-blur)))))
|
(handle-blur)))))
|
||||||
|
|
||||||
|
(mf/use-layout-effect
|
||||||
|
(mf/deps handle-mouse-wheel)
|
||||||
|
(fn []
|
||||||
|
(let [keys [(events/listen (mf/ref-val ref) EventType.WHEEL handle-mouse-wheel #js {:pasive false})]]
|
||||||
|
#(doseq [key keys]
|
||||||
|
(events/unlistenByKey key)))))
|
||||||
|
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(fn []
|
(fn []
|
||||||
(let [keys [(events/listen globals/window EventType.POINTERDOWN on-click)
|
(let [keys [(events/listen globals/window EventType.POINTERDOWN on-click)
|
||||||
|
@ -212,4 +236,3 @@
|
||||||
(events/unlistenByKey key)))))
|
(events/unlistenByKey key)))))
|
||||||
|
|
||||||
[:> :input props]))
|
[:> :input props]))
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,7 @@
|
||||||
[:> numeric-input
|
[:> numeric-input
|
||||||
{:placeholder "Auto"
|
{:placeholder "Auto"
|
||||||
:value (or (:item-length params) "")
|
:value (or (:item-length params) "")
|
||||||
:default nil
|
:nillable true
|
||||||
:on-change handle-change-item-length}]])
|
:on-change handle-change-item-length}]])
|
||||||
|
|
||||||
(when (#{:row :column} type)
|
(when (#{:row :column} type)
|
||||||
|
@ -214,11 +214,15 @@
|
||||||
:class "pixels"
|
:class "pixels"
|
||||||
:value (:gutter params)
|
:value (:gutter params)
|
||||||
:min 0
|
:min 0
|
||||||
|
:nillable true
|
||||||
|
:default 0
|
||||||
:placeholder "0"
|
:placeholder "0"
|
||||||
:on-change (handle-change :params :gutter)}]
|
:on-change (handle-change :params :gutter)}]
|
||||||
[:& input-row {:label (tr "workspace.options.grid.params.margin")
|
[:& input-row {:label (tr "workspace.options.grid.params.margin")
|
||||||
:class "pixels"
|
:class "pixels"
|
||||||
:min 0
|
:min 0
|
||||||
|
:nillable true
|
||||||
|
:default 0
|
||||||
:placeholder "0"
|
:placeholder "0"
|
||||||
:value (:margin params)
|
:value (:margin params)
|
||||||
:on-change (handle-change :params :margin)}]])
|
:on-change (handle-change :params :margin)}]])
|
||||||
|
|
|
@ -41,12 +41,6 @@
|
||||||
:svg-raw #{:size :position :rotation}
|
:svg-raw #{:size :position :rotation}
|
||||||
:text #{:size :position :rotation}})
|
:text #{:size :position :rotation}})
|
||||||
|
|
||||||
(defn- attr->string [attr values]
|
|
||||||
(let [value (attr values)]
|
|
||||||
(if (= value :multiple)
|
|
||||||
""
|
|
||||||
(str (-> value (d/coalesce 0))))))
|
|
||||||
|
|
||||||
(declare +size-presets+)
|
(declare +size-presets+)
|
||||||
|
|
||||||
;; -- User/drawing coords
|
;; -- User/drawing coords
|
||||||
|
@ -241,7 +235,7 @@
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-width-change
|
:on-change on-width-change
|
||||||
:value (attr->string :width values)}]]
|
:value (:width values)}]]
|
||||||
|
|
||||||
[:div.input-element.height {:title (tr "workspace.options.height")}
|
[:div.input-element.height {:title (tr "workspace.options.height")}
|
||||||
[:> numeric-input {:min 0.01
|
[:> numeric-input {:min 0.01
|
||||||
|
@ -249,7 +243,7 @@
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-height-change
|
:on-change on-height-change
|
||||||
:value (attr->string :height values)}]]
|
:value (:height values)}]]
|
||||||
|
|
||||||
[:div.lock-size {:class (dom/classnames
|
[:div.lock-size {:class (dom/classnames
|
||||||
:selected (true? proportion-lock)
|
:selected (true? proportion-lock)
|
||||||
|
@ -268,13 +262,13 @@
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-pos-x-change
|
:on-change on-pos-x-change
|
||||||
:value (attr->string :x values)}]]
|
:value (:x values)}]]
|
||||||
[:div.input-element.Yaxis {:title (tr "workspace.options.y")}
|
[:div.input-element.Yaxis {:title (tr "workspace.options.y")}
|
||||||
[:> numeric-input {:no-validate true
|
[:> numeric-input {:no-validate true
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-pos-y-change
|
:on-change on-pos-y-change
|
||||||
:value (attr->string :y values)}]]])
|
:value (:y values)}]]])
|
||||||
|
|
||||||
;; ROTATION
|
;; ROTATION
|
||||||
(when (options :rotation)
|
(when (options :rotation)
|
||||||
|
@ -285,19 +279,12 @@
|
||||||
{:no-validate true
|
{:no-validate true
|
||||||
:min 0
|
:min 0
|
||||||
:max 359
|
:max 359
|
||||||
|
:default 0
|
||||||
:data-wrap true
|
:data-wrap true
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-rotation-change
|
:on-change on-rotation-change
|
||||||
:value (attr->string :rotation values)}]]
|
:value (:rotation values)}]]])
|
||||||
#_[:input.slidebar
|
|
||||||
{:type "range"
|
|
||||||
:min "0"
|
|
||||||
:max "359"
|
|
||||||
:step "10"
|
|
||||||
:no-validate true
|
|
||||||
:on-change on-rotation-change
|
|
||||||
:value (attr->string :rotation values)}]])
|
|
||||||
|
|
||||||
;; RADIUS
|
;; RADIUS
|
||||||
(when (options :radius)
|
(when (options :radius)
|
||||||
|
@ -325,13 +312,14 @@
|
||||||
:min 0
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-1-change
|
:on-change on-radius-1-change
|
||||||
:value (attr->string :rx values)}]]
|
:value (:rx values)}]]
|
||||||
|
|
||||||
@radius-multi?
|
@radius-multi?
|
||||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "number"
|
{:type "number"
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-multi-change
|
:on-change on-radius-multi-change
|
||||||
:value ""}]]
|
:value ""}]]
|
||||||
|
@ -344,7 +332,7 @@
|
||||||
:min 0
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-r1-change
|
:on-change on-radius-r1-change
|
||||||
:value (attr->string :r1 values)}]]
|
:value (:r1 values)}]]
|
||||||
|
|
||||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||||
[:> numeric-input
|
[:> numeric-input
|
||||||
|
@ -352,7 +340,7 @@
|
||||||
:min 0
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-r2-change
|
:on-change on-radius-r2-change
|
||||||
:value (attr->string :r2 values)}]]
|
:value (:r2 values)}]]
|
||||||
|
|
||||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||||
[:> numeric-input
|
[:> numeric-input
|
||||||
|
@ -360,7 +348,7 @@
|
||||||
:min 0
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-r3-change
|
:on-change on-radius-r3-change
|
||||||
:value (attr->string :r3 values)}]]
|
:value (:r3 values)}]]
|
||||||
|
|
||||||
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
[:div.input-element.mini {:title (tr "workspace.options.radius")}
|
||||||
[:> numeric-input
|
[:> numeric-input
|
||||||
|
@ -368,7 +356,7 @@
|
||||||
:min 0
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-r4-change
|
:on-change on-radius-r4-change
|
||||||
:value (attr->string :r4 values)}]]])])]]]))
|
:value (:r4 values)}]]])])]]]))
|
||||||
|
|
||||||
(def +size-presets+
|
(def +size-presets+
|
||||||
[{:name "APPLE"}
|
[{:name "APPLE"}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(mf/defc input-row [{:keys [label options value class min max on-change type placeholder]}]
|
(mf/defc input-row [{:keys [label options value class min max on-change type placeholder default nillable]}]
|
||||||
[:div.row-flex.input-row
|
[:div.row-flex.input-row
|
||||||
[:span.element-set-subtitle label]
|
[:span.element-set-subtitle label]
|
||||||
[:div.input-element {:class class}
|
[:div.input-element {:class class}
|
||||||
|
@ -43,6 +43,8 @@
|
||||||
{:placeholder placeholder
|
{:placeholder placeholder
|
||||||
:min min
|
:min min
|
||||||
:max max
|
:max max
|
||||||
|
:default default
|
||||||
|
:nillable nillable
|
||||||
:on-change on-change
|
:on-change on-change
|
||||||
:value (or value "")}])]])
|
:value (or value "")}])]])
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.viewport.frame-grid
|
(ns app.main.ui.workspace.viewport.frame-grid
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.util.geom.grid :as gg]
|
[app.util.geom.grid :as gg]
|
||||||
|
@ -41,27 +43,71 @@
|
||||||
:fill (str "url(#" grid-id ")")}]]))
|
:fill (str "url(#" grid-id ")")}]]))
|
||||||
|
|
||||||
(mf/defc layout-grid
|
(mf/defc layout-grid
|
||||||
[{:keys [key frame grid]}]
|
[{:keys [key frame grid zoom]}]
|
||||||
(let [{color-value :color color-opacity :opacity} (-> grid :params :color)
|
(let [{color-value :color color-opacity :opacity} (-> grid :params :color)
|
||||||
;; Support for old color format
|
;; Support for old color format
|
||||||
color-value (or color-value (:value (get-in grid [:params :color :value])))
|
color-value (or color-value (:value (get-in grid [:params :color :value])))
|
||||||
gutter (-> grid :params :gutter)
|
gutter (gg/grid-gutter frame grid)
|
||||||
gutter? (and (not (nil? gutter)) (not= gutter 0))
|
gutter? (and (not (nil? gutter)) (not (mth/almost-zero? gutter)))]
|
||||||
|
|
||||||
style (if gutter?
|
|
||||||
#js {:fill color-value
|
|
||||||
:opacity color-opacity}
|
|
||||||
#js {:stroke color-value
|
|
||||||
:strokeOpacity color-opacity
|
|
||||||
:fill "none"})]
|
|
||||||
[:g.grid
|
[:g.grid
|
||||||
(for [{:keys [x y width height] :as area} (gg/grid-areas frame grid)]
|
(for [[idx {:keys [x y width height] :as area}] (d/enumerate (gg/grid-areas frame grid))]
|
||||||
|
(cond
|
||||||
|
gutter?
|
||||||
[:rect {:key (str key "-" x "-" y)
|
[:rect {:key (str key "-" x "-" y)
|
||||||
:x x
|
:x x
|
||||||
:y y
|
:y y
|
||||||
:width (- (+ x width) x)
|
:width (- (+ x width) x)
|
||||||
:height (- (+ y height) y)
|
:height (- (+ y height) y)
|
||||||
:style style}])]))
|
:style {:fill color-value
|
||||||
|
:stroke-width 0
|
||||||
|
:opacity color-opacity}}]
|
||||||
|
|
||||||
|
(and (not gutter?) (= :column (:type grid)))
|
||||||
|
[:*
|
||||||
|
(when (= idx 0)
|
||||||
|
[:line {:key (str key "-" x "-" y "-start")
|
||||||
|
:x1 x
|
||||||
|
:y1 y
|
||||||
|
:x2 x
|
||||||
|
:y2 (+ y height)
|
||||||
|
:style {:stroke color-value
|
||||||
|
:stroke-width (/ 1 zoom)
|
||||||
|
:strokeOpacity color-opacity
|
||||||
|
:fill "none"}}])
|
||||||
|
|
||||||
|
[:line {:key (str key "-" x "-" y "-end")
|
||||||
|
:x1 (+ x width)
|
||||||
|
:y1 y
|
||||||
|
:x2 (+ x width)
|
||||||
|
:y2 (+ y height)
|
||||||
|
:style {:stroke color-value
|
||||||
|
:stroke-width (/ 1 zoom)
|
||||||
|
:strokeOpacity color-opacity
|
||||||
|
:fill "none"}}]]
|
||||||
|
|
||||||
|
(and (not gutter?) (= :row (:type grid)))
|
||||||
|
[:*
|
||||||
|
(when (= idx 0)
|
||||||
|
[:line {:key (str key "-" x "-" y "-start")
|
||||||
|
:x1 x
|
||||||
|
:y1 y
|
||||||
|
:x2 (+ x width)
|
||||||
|
:y2 y
|
||||||
|
:style {:stroke color-value
|
||||||
|
:stroke-width (/ 1 zoom)
|
||||||
|
:strokeOpacity color-opacity
|
||||||
|
:fill "none"}}])
|
||||||
|
|
||||||
|
[:line {:key (str key "-" x "-" y "-end")
|
||||||
|
:x1 x
|
||||||
|
:y1 (+ y height)
|
||||||
|
:x2 (+ x width)
|
||||||
|
:y2 (+ y height)
|
||||||
|
:style {:stroke color-value
|
||||||
|
:stroke-width (/ 1 zoom)
|
||||||
|
:strokeOpacity color-opacity
|
||||||
|
:fill "none"}}]]))]))
|
||||||
|
|
||||||
(mf/defc grid-display-frame
|
(mf/defc grid-display-frame
|
||||||
[{:keys [frame zoom]}]
|
[{:keys [frame zoom]}]
|
||||||
|
|
|
@ -69,6 +69,8 @@
|
||||||
(rx/flat-map
|
(rx/flat-map
|
||||||
(fn [[frame-id point]]
|
(fn [[frame-id point]]
|
||||||
(->> (snap/get-snap-points page-id frame-id remove-snap? zoom point coord)
|
(->> (snap/get-snap-points page-id frame-id remove-snap? zoom point coord)
|
||||||
|
(rx/map #(mapcat second %))
|
||||||
|
(rx/map #(map :pt %))
|
||||||
(rx/map #(vector point % coord)))))
|
(rx/map #(vector point % coord)))))
|
||||||
(rx/reduce conj []))))
|
(rx/reduce conj []))))
|
||||||
|
|
||||||
|
@ -157,22 +159,29 @@
|
||||||
|
|
||||||
(mf/defc snap-points
|
(mf/defc snap-points
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [layout zoom objects selected page-id drawing transform modifiers focus] :as props}]
|
[{:keys [layout zoom objects selected page-id drawing modifiers focus] :as props}]
|
||||||
(us/assert set? selected)
|
(us/assert set? selected)
|
||||||
(let [shapes (into [] (keep (d/getf objects)) selected)
|
(let [shapes (into [] (keep (d/getf objects)) selected)
|
||||||
|
|
||||||
filter-shapes
|
filter-shapes
|
||||||
(into selected (mapcat #(cph/get-children-ids objects %)) selected)
|
(into selected (mapcat #(cph/get-children-ids objects %)) selected)
|
||||||
|
|
||||||
remove-snap?
|
remove-snap-base?
|
||||||
(mf/with-memo [layout filter-shapes objects focus]
|
(mf/with-memo [layout filter-shapes objects focus]
|
||||||
(snap/make-remove-snap layout filter-shapes objects focus))
|
(snap/make-remove-snap layout filter-shapes objects focus))
|
||||||
|
|
||||||
|
remove-snap?
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps remove-snap-base?)
|
||||||
|
(fn [{:keys [type grid] :as snap}]
|
||||||
|
(or (remove-snap-base? snap)
|
||||||
|
(and (= type :layout) (= grid :square))
|
||||||
|
(= type :guide))))
|
||||||
|
|
||||||
shapes (if drawing [drawing] shapes)]
|
shapes (if drawing [drawing] shapes)]
|
||||||
(when (or drawing transform)
|
|
||||||
[:& snap-feedback {:shapes shapes
|
[:& snap-feedback {:shapes shapes
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:remove-snap? remove-snap?
|
:remove-snap? remove-snap?
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:modifiers modifiers}])))
|
:modifiers modifiers}]))
|
||||||
|
|
||||||
|
|
|
@ -168,3 +168,10 @@
|
||||||
nm (str/trim (str/lower name))]
|
nm (str/trim (str/lower name))]
|
||||||
(str/includes? nm st))))
|
(str/includes? nm st))))
|
||||||
|
|
||||||
|
(defn tap
|
||||||
|
"Works like rx/tap but for normal collections"
|
||||||
|
;; Signature for transducer use
|
||||||
|
([f]
|
||||||
|
(map #(do (f %) %)))
|
||||||
|
([f coll]
|
||||||
|
(map #(do (f %) %) coll)))
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
next-v (fn [cur-val]
|
next-v (fn [cur-val]
|
||||||
(+ offset v (* (+ width' gutter) cur-val)))]
|
(+ offset v (* (+ width' gutter) cur-val)))]
|
||||||
|
|
||||||
[size width' next-v]))
|
[size width' next-v gutter]))
|
||||||
|
|
||||||
(defn- calculate-column-grid
|
(defn- calculate-column-grid
|
||||||
[{:keys [width height x y] :as frame} params]
|
[{:keys [width height x y] :as frame} params]
|
||||||
|
@ -70,6 +70,20 @@
|
||||||
|
|
||||||
[(* col-size row-size) size size next-x next-y]))
|
[(* col-size row-size) size size next-x next-y]))
|
||||||
|
|
||||||
|
(defn grid-gutter
|
||||||
|
[{:keys [x y width height]} {:keys [type params] :as grid}]
|
||||||
|
|
||||||
|
(case type
|
||||||
|
:column
|
||||||
|
(let [[_ _ _ gutter] (calculate-generic-grid x width params)]
|
||||||
|
gutter)
|
||||||
|
|
||||||
|
:row
|
||||||
|
(let [[_ _ _ gutter] (calculate-generic-grid y height params)]
|
||||||
|
gutter)
|
||||||
|
|
||||||
|
nil))
|
||||||
|
|
||||||
(defn grid-areas
|
(defn grid-areas
|
||||||
"Given a frame and the grid parameters returns the areas defined on the grid"
|
"Given a frame and the grid parameters returns the areas defined on the grid"
|
||||||
[frame grid]
|
[frame grid]
|
||||||
|
@ -93,10 +107,7 @@
|
||||||
|
|
||||||
(defn grid-snap-points
|
(defn grid-snap-points
|
||||||
"Returns the snap points for a given grid"
|
"Returns the snap points for a given grid"
|
||||||
([shape coord]
|
[shape {:keys [type params] :as grid} coord]
|
||||||
(mapcat #(grid-snap-points shape % coord) (:grids shape)))
|
|
||||||
|
|
||||||
([shape {:keys [type params] :as grid} coord]
|
|
||||||
(when (:display grid)
|
(when (:display grid)
|
||||||
(case type
|
(case type
|
||||||
:square
|
:square
|
||||||
|
@ -117,4 +128,4 @@
|
||||||
:row
|
:row
|
||||||
(when (= coord :y)
|
(when (= coord :y)
|
||||||
(->> (grid-areas shape grid)
|
(->> (grid-areas shape grid)
|
||||||
(mapcat grid-area-points)))))))
|
(mapcat grid-area-points))))))
|
||||||
|
|
|
@ -9,25 +9,28 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]))
|
[app.common.geom.shapes :as gsh]))
|
||||||
|
|
||||||
(defn selrect-snap-points [{:keys [x y width height]}]
|
(defn selrect-snap-points [{:keys [x y width height] :as selrect}]
|
||||||
#{(gpt/point x y)
|
#{(gpt/point x y)
|
||||||
(gpt/point (+ x width) y)
|
(gpt/point (+ x width) y)
|
||||||
(gpt/point (+ x width) (+ y height))
|
(gpt/point (+ x width) (+ y height))
|
||||||
(gpt/point x (+ y height))})
|
(gpt/point x (+ y height))
|
||||||
|
(gsh/center-selrect selrect)})
|
||||||
|
|
||||||
(defn frame-snap-points [{:keys [x y width height] :as selrect}]
|
(defn frame-snap-points [{:keys [x y width height blocked hidden] :as selrect}]
|
||||||
|
(when (and (not blocked) (not hidden))
|
||||||
(into (selrect-snap-points selrect)
|
(into (selrect-snap-points selrect)
|
||||||
#{(gpt/point (+ x (/ width 2)) y)
|
#{(gpt/point (+ x (/ width 2)) y)
|
||||||
(gpt/point (+ x width) (+ y (/ height 2)))
|
(gpt/point (+ x width) (+ y (/ height 2)))
|
||||||
(gpt/point (+ x (/ width 2)) (+ y height))
|
(gpt/point (+ x (/ width 2)) (+ y height))
|
||||||
(gpt/point x (+ y (/ height 2)))}))
|
(gpt/point x (+ y (/ height 2)))})))
|
||||||
|
|
||||||
(defn shape-snap-points
|
(defn shape-snap-points
|
||||||
[shape]
|
[{:keys [hidden blocked] :as shape}]
|
||||||
|
(when (and (not blocked) (not hidden))
|
||||||
(let [shape (gsh/transform-shape shape)]
|
(let [shape (gsh/transform-shape shape)]
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:frame (-> shape :selrect frame-snap-points)
|
:frame (-> shape :selrect frame-snap-points)
|
||||||
(into #{(gsh/center-shape shape)} (:points shape)))))
|
(into #{(gsh/center-shape shape)} (:points shape))))))
|
||||||
|
|
||||||
(defn guide-snap-points
|
(defn guide-snap-points
|
||||||
[guide]
|
[guide]
|
||||||
|
|
|
@ -53,6 +53,19 @@
|
||||||
(assoc-in [frame-id :x] (rt/make-tree))
|
(assoc-in [frame-id :x] (rt/make-tree))
|
||||||
(assoc-in [frame-id :y] (rt/make-tree)))))
|
(assoc-in [frame-id :y] (rt/make-tree)))))
|
||||||
|
|
||||||
|
(defn get-grids-snap-points
|
||||||
|
[frame coord]
|
||||||
|
(let [grid->snap (fn [[grid-type position]]
|
||||||
|
{:type :layout
|
||||||
|
:id (:id frame)
|
||||||
|
:grid grid-type
|
||||||
|
:pt position})]
|
||||||
|
(->> (:grids frame)
|
||||||
|
(mapcat (fn [grid]
|
||||||
|
(->> (gg/grid-snap-points frame grid coord)
|
||||||
|
(mapv #(vector (:type grid) %)))))
|
||||||
|
(mapv grid->snap))))
|
||||||
|
|
||||||
(defn- add-frame
|
(defn- add-frame
|
||||||
[page-data frame]
|
[page-data frame]
|
||||||
(let [frame-id (:id frame)
|
(let [frame-id (:id frame)
|
||||||
|
@ -61,17 +74,8 @@
|
||||||
(mapv #(array-map :type :shape
|
(mapv #(array-map :type :shape
|
||||||
:id frame-id
|
:id frame-id
|
||||||
:pt %)))
|
:pt %)))
|
||||||
|
grid-x-data (get-grids-snap-points frame :x)
|
||||||
grid-x-data (->> (gg/grid-snap-points frame :x)
|
grid-y-data (get-grids-snap-points frame :y)]
|
||||||
(mapv #(array-map :type :layout
|
|
||||||
:id frame-id
|
|
||||||
:pt %)))
|
|
||||||
|
|
||||||
grid-y-data (->> (gg/grid-snap-points frame :y)
|
|
||||||
(mapv #(array-map :type :layout
|
|
||||||
:id frame-id
|
|
||||||
:pt %)))]
|
|
||||||
|
|
||||||
(-> page-data
|
(-> page-data
|
||||||
;; Update root frame information
|
;; Update root frame information
|
||||||
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
|
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
|
||||||
|
@ -107,6 +111,7 @@
|
||||||
(mapv #(array-map
|
(mapv #(array-map
|
||||||
:type :guide
|
:type :guide
|
||||||
:id (:id guide)
|
:id (:id guide)
|
||||||
|
:axis (:axis guide)
|
||||||
:frame-id (:frame-id guide)
|
:frame-id (:frame-id guide)
|
||||||
:pt %)))]
|
:pt %)))]
|
||||||
(if-let [frame-id (:frame-id guide)]
|
(if-let [frame-id (:frame-id guide)]
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
(let [page (current-page state)
|
(let [page (current-page state)
|
||||||
frame (cph/get-frame (:objects page))
|
frame (cph/get-frame (:objects page))
|
||||||
shape (-> (cp/make-minimal-shape type)
|
shape (-> (cp/make-minimal-shape type)
|
||||||
(gsh/setup {:x 0 :y 0 :width 1 :height 1})
|
(cp/setup-shape {:x 0 :y 0 :width 1 :height 1})
|
||||||
(merge props))]
|
(merge props))]
|
||||||
(swap! idmap assoc label (:id shape))
|
(swap! idmap assoc label (:id shape))
|
||||||
(update state :workspace-data
|
(update state :workspace-data
|
||||||
|
|
Loading…
Add table
Reference in a new issue