Skip to content

Commit d19f15c

Browse files
committed
enhance snapping
- decouple from transform related actions - each tool provide base points through a multimethod - support snapping on element creation
1 parent 5f5d3bf commit d19f15c

File tree

15 files changed

+1445
-1245
lines changed

15 files changed

+1445
-1245
lines changed

.clj-kondo/metosin/malli-types-cljs/config.edn

+1,360-1,167
Large diffs are not rendered by default.

src/renderer/app/db.cljs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
[renderer.handle.db :refer [Handle]]
1111
[renderer.notification.db :refer [Notification]]
1212
[renderer.ruler.db :refer [Ruler]]
13-
[renderer.snap.db :refer [Snap]]
13+
[renderer.snap.db :refer [Snap NearestNeighbor]]
1414
[renderer.theme.db :refer [Theme]]
1515
[renderer.timeline.db :refer [Timeline]]
1616
[renderer.tool.db :refer [Tool State Cursor]]
@@ -40,6 +40,8 @@
4040
[:pointer-offset {:optional true} Vec2D]
4141
[:adjusted-pointer-pos {:default [0 0]} Vec2D]
4242
[:adjusted-pointer-offset {:optional true} Vec2D]
43+
[:nearest-neighbor-offset {:optional true} Vec2D]
44+
[:nearest-neighbor {:optional true} NearestNeighbor]
4345
[:drag {:optional true} boolean?]
4446
[:zoom-sensitivity {:default 0.75} [:and number? pos?]]
4547
[:event-time {:optional true} number?]

src/renderer/snap/db.cljs

-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,4 @@
2020
[:map {:default {} :closed true}
2121
[:active {:default true} boolean?]
2222
[:threshold {:default 15} number?]
23-
[:nearest-neighbor {:optional true} NearestNeighbor]
2423
[:options {:default (set snap-options)} SnapOptions]])

src/renderer/snap/handlers.cljs

+7-37
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55
[malli.core :as m]
66
[re-frame.core :as rf]
77
[renderer.app.db :refer [App]]
8-
[renderer.element.handlers :as element.h]
98
[renderer.snap.db :refer [SnapOption NearestNeighbor]]
109
[renderer.snap.subs :as-alias snap.s]
11-
[renderer.tool.handlers :as tool.h]
12-
[renderer.utils.bounds :as bounds]
13-
[renderer.utils.element :as element]
10+
[renderer.tool.hierarchy :as tool.hierarchy]
1411
[renderer.utils.math :refer [Vec2D]]))
1512

1613
(m/=> toggle-option [:-> App SnapOption App])
@@ -20,36 +17,14 @@
2017
(update-in db [:snap :options] disj option)
2118
(update-in db [:snap :options] conj option)))
2219

23-
(m/=> base-points [:-> App [:vector Vec2D]])
24-
(defn base-points
25-
[db]
26-
(let [elements (vals (element.h/entities db))
27-
selected (filter :selected elements)
28-
selected-visible (filter :visible selected)
29-
options (-> db :snap :options)]
30-
(when (-> db :snap :active)
31-
(cond
32-
(and (contains? #{:translate :clone} (:state db)) (seq selected))
33-
(reduce (fn [points el] (into points (element/snapping-points el options)))
34-
(if (seq (rest selected))
35-
(bounds/->snapping-points (element.h/bounds db) options)
36-
[])
37-
selected-visible)
38-
39-
(contains? #{:edit :scale} (:state db))
40-
[(mat/add [(-> db :clicked-element :x) (-> db :clicked-element :y)]
41-
(tool.h/pointer-delta db))]
42-
43-
:else
44-
[(:adjusted-pointer-pos db)]))))
45-
4620
(m/=> find-nearest-neighbors [:-> App [:sequential NearestNeighbor]])
4721
(defn find-nearest-neighbors
4822
[db]
4923
(let [tree @(rf/subscribe [::snap.s/in-viewport-tree])] ; FIXME: Subscription in event.
5024
(map #(let [nearest-neighbor (kdtree/nearest-neighbor tree %)]
5125
(when nearest-neighbor
52-
(assoc nearest-neighbor :base-point %))) (base-points db))))
26+
(assoc nearest-neighbor :base-point %)))
27+
(tool.hierarchy/snapping-bases db))))
5328

5429
(m/=> find-nearest-neighbor [:-> App [:maybe NearestNeighbor]])
5530
(defn find-nearest-neighbor
@@ -74,25 +49,20 @@
7449
(let [nearest-neighbor (find-nearest-neighbor db)]
7550
(cond-> db
7651
:always
77-
(update :snap dissoc :nearest-neighbor)
52+
(dissoc :nearest-neighbor)
7853

7954
(and (-> db :snap :active) nearest-neighbor)
80-
(assoc-in [:snap :nearest-neighbor] nearest-neighbor))))
81-
82-
(m/=> nearest-neighbor [:-> App [:maybe NearestNeighbor]])
83-
(defn nearest-neighbor
84-
[db]
85-
(get-in db [:snap :nearest-neighbor]))
55+
(assoc :nearest-neighbor nearest-neighbor))))
8656

8757
(m/=> nearest-delta [:-> App Vec2D])
8858
(defn nearest-delta
8959
[db]
90-
(let [{:keys [point base-point]} (nearest-neighbor db)]
60+
(let [{:keys [point base-point]} (:nearest-neighbor db)]
9161
(mat/sub point base-point)))
9262

9363
(defn snap-with
9464
[db f & more]
9565
(let [db (update-nearest-neighbor db)]
96-
(if (nearest-neighbor db)
66+
(if (:nearest-neighbor db)
9767
(apply f db (nearest-delta db) more)
9868
db)))

src/renderer/snap/subs.cljs

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
(rf/reg-sub
2424
::nearest-neighbor
25-
:<- [::snap]
2625
:-> :nearest-neighbor)
2726

2827
(rf/reg-sub

src/renderer/tool/handlers.cljs

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
[renderer.element.handlers :as element.h]
99
[renderer.frame.handlers :as frame.h]
1010
[renderer.history.handlers :as history.h]
11+
[renderer.snap.handlers :as snap.h]
1112
[renderer.tool.db :refer [Tool State Cursor]]
1213
[renderer.tool.effects :as-alias fx]
1314
[renderer.tool.hierarchy :as hierarchy]
@@ -83,7 +84,7 @@
8384
(m/=> pointer-handler [:-> App PointerEvent number? App])
8485
(defn pointer-handler
8586
[db e now]
86-
(let [{:keys [pointer-offset tool dom-rect drag primary-tool drag-threshold]} db
87+
(let [{:keys [pointer-offset tool dom-rect drag primary-tool drag-threshold nearest-neighbor]} db
8788
{:keys [button buttons pointer-pos]} e
8889
adjusted-pointer-pos (frame.h/adjust-pointer-pos db pointer-pos)]
8990
(case (:type e)
@@ -103,6 +104,7 @@
103104
(hierarchy/drag e))
104105
db)
105106
(hierarchy/pointer-move db e))
107+
(snap.h/update-nearest-neighbor)
106108
(assoc :pointer-pos pointer-pos
107109
:adjusted-pointer-pos adjusted-pointer-pos))
108110

@@ -114,7 +116,8 @@
114116

115117
(not= buttons :right)
116118
(assoc :pointer-offset pointer-pos
117-
:adjusted-pointer-offset adjusted-pointer-pos)
119+
:adjusted-pointer-offset adjusted-pointer-pos
120+
:nearest-neighbor-offset (:point nearest-neighbor))
118121

119122
:always
120123
(hierarchy/pointer-down e))

src/renderer/tool/hierarchy.cljs

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
(defmulti drag-end (fn [db _e] (:tool db)))
1010
(defmulti key-up (fn [db _e] (:tool db)))
1111
(defmulti key-down (fn [db _e] (:tool db)))
12+
(defmulti snapping-bases (fn [db _e] (:tool db)))
1213
(defmulti activate :tool)
1314
(defmulti deactivate :tool)
1415
(defmulti properties "Returns the properties of the tool." keyword)
@@ -28,6 +29,7 @@
2829
(defmethod activate :default [db] (assoc db :cursor "default"))
2930
(defmethod deactivate :default [db] (assoc db :cursor "default"))
3031
(defmethod properties :default [])
32+
(defmethod snapping-bases :default [])
3133
(defmethod help :default [_tool _state] "")
3234

3335

src/renderer/tool/impl/base/edit.cljs

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
(ns renderer.tool.impl.base.edit
22
(:require
3+
[clojure.core.matrix :as mat]
34
[renderer.element.handlers :as element.h]
45
[renderer.element.hierarchy :as element.hierarchy]
56
[renderer.history.handlers :as history.h]
@@ -77,3 +78,9 @@
7778
(h/set-state :idle)
7879
(dissoc :clicked-element)
7980
(h/explain "Edit")))
81+
82+
(defmethod hierarchy/snapping-bases :edit
83+
[db]
84+
(when (contains? #{:edit :scale} (:state db))
85+
[(mat/add [(-> db :clicked-element :x) (-> db :clicked-element :y)]
86+
(mat/sub (:adjusted-pointer-pos db) (:adjusted-pointer-offset db)))]))

src/renderer/tool/impl/base/transform.cljs

+18
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,21 @@
315315
(h/set-state :idle)
316316
(element.h/clear-hovered)
317317
(dissoc :clicked-element :pivot-point)))
318+
319+
(defmethod hierarchy/snapping-bases :transform
320+
[db]
321+
(let [elements (vals (element.h/entities db))
322+
selected (filter :selected elements)
323+
selected-visible (filter :visible selected)
324+
options (-> db :snap :options)]
325+
(cond
326+
(and (contains? #{:translate :clone} (:state db)) (seq selected))
327+
(reduce (fn [points el] (into points (element/snapping-points el options)))
328+
(if (seq (rest selected))
329+
(bounds/->snapping-points (element.h/bounds db) options)
330+
[])
331+
selected-visible)
332+
333+
(= (:state db) :scale)
334+
[(mat/add [(-> db :clicked-element :x) (-> db :clicked-element :y)]
335+
(mat/sub (:adjusted-pointer-pos db) (:adjusted-pointer-offset db)))])))

src/renderer/tool/impl/element/circle.cljs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
(defmethod hierarchy/drag :circle
1616
[db]
17-
(let [offset (:adjusted-pointer-offset db)
18-
position (:adjusted-pointer-pos db)
17+
(let [offset (or (:nearest-neighbor-offset db) (:adjusted-pointer-offset db))
18+
position (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))
1919
[x y] offset
2020
radius (mat/distance position offset)
2121
attrs {:cx x

src/renderer/tool/impl/element/core.cljs

+4
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,7 @@
3737
(h/activate :transform)
3838
(h/set-state :idle)
3939
(h/explain (str "Create " (name (:tag (h/temp db)))))))
40+
41+
(defmethod hierarchy/snapping-bases ::hierarchy/element
42+
[db]
43+
[(:adjusted-pointer-pos db)])

src/renderer/tool/impl/element/line.cljs

+5-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
(defn create-line
1515
[db]
16-
(let [[offset-x offset-y] (:adjusted-pointer-offset db)
17-
[x y] (:adjusted-pointer-pos db)
16+
(let [[offset-x offset-y] (or (:nearest-neighbor-offset db) (:adjusted-pointer-offset db))
17+
[x y] (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))
1818
attrs {:x1 offset-x
1919
:y1 offset-y
2020
:x2 x
@@ -26,7 +26,7 @@
2626

2727
(defn update-line-end
2828
[db]
29-
(let [[x y] (:adjusted-pointer-pos db)
29+
(let [[x y] (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))
3030
temp (-> (h/temp db)
3131
(assoc-in [:attrs :x2] x)
3232
(assoc-in [:attrs :y2] y))]
@@ -35,7 +35,8 @@
3535
(defmethod tool.hierarchy/pointer-move :line
3636
[db]
3737
(cond-> db
38-
(h/temp db) (update-line-end)))
38+
(h/temp db)
39+
(update-line-end)))
3940

4041
(defmethod tool.hierarchy/pointer-up :line
4142
[db _e]

src/renderer/tool/impl/element/polyshape.cljs

+15-13
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@
3636

3737
(defmethod hierarchy/pointer-up ::hierarchy/polyshape
3838
[db]
39-
(if (h/temp db)
40-
(add-point db (:adjusted-pointer-pos db))
41-
(-> db
42-
(h/set-state :create)
43-
(create-polyline (:adjusted-pointer-pos db)))))
39+
(let [point (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))]
40+
(if (h/temp db)
41+
(add-point db point)
42+
(-> db
43+
(h/set-state :create)
44+
(create-polyline point)))))
4445

4546
(defmethod hierarchy/drag-end ::hierarchy/polyshape
4647
[db]
@@ -52,14 +53,15 @@
5253

5354
(defmethod hierarchy/pointer-move ::hierarchy/polyshape
5455
[db]
55-
(if-let [points (get-in db (points-path db))]
56-
(let [point-vector (attr/points->vec points)]
57-
(assoc-in db
58-
(points-path db)
59-
(str/join " " (concat (apply concat (if (second point-vector)
60-
(drop-last point-vector)
61-
point-vector))
62-
(:adjusted-pointer-pos db))))) db))
56+
(let [point (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))]
57+
(if-let [points (get-in db (points-path db))]
58+
(let [point-vector (attr/points->vec points)]
59+
(assoc-in db
60+
(points-path db)
61+
(str/join " " (concat (apply concat (if (second point-vector)
62+
(drop-last point-vector)
63+
point-vector))
64+
point)))) db)))
6365

6466
(defmethod hierarchy/double-click ::hierarchy/polyshape
6567
[db _e]

src/renderer/tool/impl/element/rect.cljs

+9-11
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,15 @@
1919

2020
(defmethod hierarchy/drag :rect
2121
[db e]
22-
(let [[offset-x offset-y] (:adjusted-pointer-offset db)
23-
[x y] (:adjusted-pointer-pos db)
24-
lock-ratio (pointer/ctrl? e)
22+
(let [[offset-x offset-y] (or (:nearest-neighbor-offset db) (:adjusted-pointer-offset db))
23+
[x y] (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))
2524
width (abs (- x offset-x))
26-
height (abs (- y offset-y))
27-
attrs {:x (min x offset-x)
28-
:y (min y offset-y)
29-
:width (if lock-ratio (min width height) width)
30-
:height (if lock-ratio (min width height) height)
31-
:fill (document.h/attr db :fill)
32-
:stroke (document.h/attr db :stroke)}]
25+
height (abs (- y offset-y))]
3326
(h/set-temp db {:type :element
3427
:tag :rect
35-
:attrs attrs})))
28+
:attrs {:x (min x offset-x)
29+
:y (min y offset-y)
30+
:width (if (pointer/ctrl? e) (min width height) width)
31+
:height (if (pointer/ctrl? e) (min width height) height)
32+
:fill (document.h/attr db :fill)
33+
:stroke (document.h/attr db :stroke)}})))

src/renderer/tool/impl/misc/measure.cljs

+8-6
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,17 @@
2222
[db]
2323
(h/dissoc-temp db))
2424

25-
(defmethod hierarchy/drag-end :measure
26-
[db] db)
25+
(defmethod hierarchy/drag-end :measure [db] db)
26+
27+
(defmethod hierarchy/snapping-bases :measure
28+
[db]
29+
[(:adjusted-pointer-pos db)])
2730

2831
(defmethod hierarchy/drag :measure
2932
[db]
30-
(let [{:keys [adjusted-pointer-offset adjusted-pointer-pos]} db
31-
[offset-x offset-y] adjusted-pointer-offset
32-
[x y] adjusted-pointer-pos
33-
[adjacent opposite] (mat/sub adjusted-pointer-offset adjusted-pointer-pos)
33+
(let [[offset-x offset-y] (or (:nearest-neighbor-offset db) (:adjusted-pointer-offset db))
34+
[x y] (or (:point (:nearest-neighbor db)) (:adjusted-pointer-pos db))
35+
[adjacent opposite] (mat/sub [offset-x offset-y] [x y])
3436
hypotenuse (Math/hypot adjacent opposite)]
3537
(h/set-temp db {:type :element
3638
:tag :measure

0 commit comments

Comments
 (0)