mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 00:58:26 -05:00
✨ Snap for moving path nodes and handlers
This commit is contained in:
parent
de8207c5a6
commit
f396ef4fa0
7 changed files with 83 additions and 75 deletions
|
@ -24,8 +24,8 @@
|
|||
(d/export edition/start-path-edit)
|
||||
|
||||
;; Selection
|
||||
(d/export selection/select-handler)
|
||||
(d/export selection/handle-selection)
|
||||
(d/export selection/select-node)
|
||||
(d/export selection/path-handler-enter)
|
||||
(d/export selection/path-handler-leave)
|
||||
(d/export selection/path-pointer-enter)
|
||||
|
|
|
@ -35,17 +35,20 @@
|
|||
:x dx :y dy :c2x dx :c2y dy))))))
|
||||
|
||||
(defn modify-handler [id index prefix dx dy match-opposite?]
|
||||
(ptk/reify ::modify-point
|
||||
(ptk/reify ::modify-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [content (get-in state (st/get-path state :content))
|
||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
||||
[ocx ocy] (if (= prefix :c1) [:c2x :c2y] [:c1x :c1y])
|
||||
point (gpt/point (+ (get-in content [index :params cx]) dx)
|
||||
(+ (get-in content [index :params cy]) dy))
|
||||
opposite-index (ugp/opposite-index content index prefix)]
|
||||
(cond-> state
|
||||
:always
|
||||
(update-in [:workspace-local :edit-path id :content-modifiers index] assoc
|
||||
cx dx cy dy)
|
||||
(-> (update-in [:workspace-local :edit-path id :content-modifiers index] assoc
|
||||
cx dx cy dy)
|
||||
(assoc-in [:workspace-local :edit-path id :moving-handler] point))
|
||||
|
||||
(and match-opposite? opposite-index)
|
||||
(update-in [:workspace-local :edit-path id :content-modifiers opposite-index] assoc
|
||||
|
@ -63,7 +66,7 @@
|
|||
[rch uch] (changes/generate-path-changes page-id shape (:content shape) new-content)]
|
||||
|
||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true})
|
||||
(fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers)))))))
|
||||
(fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler)))))))
|
||||
|
||||
(defn move-selected-path-point [from-point to-point]
|
||||
(letfn [(modify-content-point [content {dx :x dy :y} modifiers point]
|
||||
|
@ -101,7 +104,9 @@
|
|||
modifiers (->> points
|
||||
(reduce modifiers-reducer {}))]
|
||||
|
||||
(assoc-in state [:workspace-local :edit-path id :content-modifiers] modifiers))))))
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :moving-nodes] true)
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers)))))))
|
||||
|
||||
(defn start-move-path-point
|
||||
[position shift?]
|
||||
|
@ -120,11 +125,6 @@
|
|||
|
||||
mouse-drag-stream
|
||||
(rx/concat
|
||||
;; If we're dragging a selected item we don't change the selection
|
||||
(if selected?
|
||||
(rx/empty)
|
||||
(rx/of (selection/select-node position shift?)))
|
||||
|
||||
;; This stream checks the consecutive mouse positions to do the draging
|
||||
(->> points
|
||||
(streams/move-points-stream start-position selected-points)
|
||||
|
|
|
@ -28,15 +28,6 @@
|
|||
(let [id (st/get-path-id state)]
|
||||
(update-in state [:workspace-local :edit-path id :hover-points] disj position)))))
|
||||
|
||||
(defn select-handler [index type]
|
||||
(ptk/reify ::select-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :selected-handlers] (fnil conj #{}) [index type]))))))
|
||||
|
||||
|
||||
(defn path-handler-enter [index prefix]
|
||||
(ptk/reify ::path-handler-enter
|
||||
ptk/UpdateEvent
|
||||
|
@ -115,7 +106,6 @@
|
|||
(update [_ state]
|
||||
(let [id (st/get-path-id state)]
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :selected-handlers] #{})
|
||||
(assoc-in [:workspace-local :edit-path id :selected-points] #{}))))))
|
||||
|
||||
(defn update-area-selection
|
||||
|
|
|
@ -58,36 +58,39 @@
|
|||
[start-point selected-points points]
|
||||
|
||||
(let [zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
ranges (snap/create-ranges selected-points points)
|
||||
d-pos (/ snap/snap-accuracy zoom)]
|
||||
ranges (snap/create-ranges points selected-points)
|
||||
d-pos (/ snap/snap-path-accuracy zoom)
|
||||
|
||||
check-path-snap
|
||||
(fn [position]
|
||||
(let [delta (gpt/subtract position start-point)
|
||||
moved-points (->> selected-points (mapv #(gpt/add % delta)))
|
||||
snap (snap/get-snap-delta moved-points ranges d-pos)]
|
||||
(gpt/add position snap)))]
|
||||
(->> ms/mouse-position
|
||||
(rx/map (fn [position]
|
||||
(let [delta (gpt/subtract position start-point)
|
||||
moved-points (->> selected-points (mapv #(gpt/add % delta)))]
|
||||
(gpt/add
|
||||
position
|
||||
(snap/get-snap-delta moved-points ranges d-pos)))))))
|
||||
)
|
||||
(rx/map check-path-snap))))
|
||||
|
||||
(defn move-handler-stream
|
||||
[start-point handler points]
|
||||
|
||||
(let [zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
ranges (snap/create-ranges points)
|
||||
d-pos (/ snap/snap-accuracy zoom)]
|
||||
d-pos (/ snap/snap-path-accuracy zoom)
|
||||
|
||||
check-path-snap
|
||||
(fn [position]
|
||||
(let [delta (gpt/subtract position start-point)
|
||||
handler-position (gpt/add handler delta)
|
||||
snap (snap/get-snap-delta [handler-position] ranges d-pos)]
|
||||
(gpt/add position snap)))]
|
||||
(->> ms/mouse-position
|
||||
(rx/map (fn [position]
|
||||
(let [delta (gpt/subtract position start-point)
|
||||
handler-position (gpt/add handler delta)]
|
||||
(gpt/add
|
||||
position
|
||||
(snap/get-snap-delta [handler-position] ranges d-pos))))))))
|
||||
(rx/map check-path-snap))))
|
||||
|
||||
(defn position-stream
|
||||
[points]
|
||||
(let [zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
;; ranges (snap/create-ranges points)
|
||||
d-pos (/ snap/snap-accuracy zoom)
|
||||
d-pos (/ snap/snap-path-accuracy zoom)
|
||||
get-content (fn [state] (get-in state (state/get-path state :content)))
|
||||
|
||||
content-stream
|
||||
|
@ -103,9 +106,7 @@
|
|||
(rx/with-latest vector ranges-stream)
|
||||
(rx/map (fn [[position ranges]]
|
||||
(let [snap (snap/get-snap-delta [position] ranges d-pos)]
|
||||
#_(prn ">>>" snap)
|
||||
(gpt/add position snap))
|
||||
))
|
||||
(gpt/add position snap))))
|
||||
|
||||
(rx/with-latest merge (->> ms/mouse-position-shift (rx/map #(hash-map :shift? %))))
|
||||
(rx/with-latest merge (->> ms/mouse-position-alt (rx/map #(hash-map :alt? %)))))))
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
[clojure.set :as set]))
|
||||
|
||||
(defonce ^:private snap-accuracy 5)
|
||||
(defonce ^:private snap-path-accuracy 10)
|
||||
(defonce ^:private snap-distance-accuracy 10)
|
||||
|
||||
(defn- remove-from-snap-points
|
||||
|
@ -272,9 +273,8 @@
|
|||
(->> (rt/range-query (get ranges coord) (- pval precision) (+ pval precision))
|
||||
;; We save the distance to the point and add the matching point to the points
|
||||
(mapv (fn [[value points]]
|
||||
[(mth/abs (- value pval))
|
||||
[(- value pval)
|
||||
(->> points (mapv #(vector point %)))])))))]
|
||||
|
||||
{:x (query-coord point :x)
|
||||
:y (query-coord point :y)}))
|
||||
|
||||
|
@ -301,7 +301,7 @@
|
|||
[default matches]
|
||||
(let [get-min
|
||||
(fn [[cur-val :as current] [other-val :as other]]
|
||||
(if (< cur-val other-val)
|
||||
(if (< (mth/abs cur-val) (mth/abs other-val))
|
||||
current
|
||||
other))
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
[app.util.dom :as dom]
|
||||
[app.util.geom.path :as ugp]
|
||||
[app.util.keyboard :as kbd]
|
||||
[clojure.set :refer [map-invert]]
|
||||
[goog.events :as events]
|
||||
[rumext.alpha :as mf])
|
||||
(:import goog.events.EventType))
|
||||
|
@ -42,7 +43,10 @@
|
|||
(let [shift? (kbd/shift? event)]
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(st/emit! (drp/start-move-path-point position shift?))
|
||||
;; If we're dragging a selected item we don't change the selection
|
||||
(do (when (not selected?)
|
||||
(st/emit! (drp/select-node position shift?)))
|
||||
(st/emit! (drp/start-move-path-point position shift?)))
|
||||
|
||||
(and (= edit-mode :draw) start-path?)
|
||||
(st/emit! (drp/start-path-from-point position))
|
||||
|
@ -84,14 +88,6 @@
|
|||
(fn [event]
|
||||
(st/emit! (drp/path-handler-leave index prefix)))
|
||||
|
||||
on-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(drp/select-handler index prefix)))
|
||||
|
||||
on-mouse-down
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
|
@ -123,7 +119,6 @@
|
|||
[:circle {:cx x
|
||||
:cy y
|
||||
:r (/ 10 zoom)
|
||||
:on-click on-click
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-mouse-enter on-enter
|
||||
:on-mouse-leave on-leave
|
||||
|
@ -145,7 +140,7 @@
|
|||
:preview? true
|
||||
:zoom zoom}]])
|
||||
|
||||
(mf/defc snap-points [{:keys [selected points zoom]}]
|
||||
(mf/defc path-snap [{:keys [selected points zoom]}]
|
||||
(let [ranges (mf/use-memo (mf/deps selected points) #(snap/create-ranges points selected))
|
||||
snap-matches (snap/get-snap-delta-match selected ranges (/ 1 zoom))
|
||||
matches (d/concat [] (second (:x snap-matches)) (second (:y snap-matches)))]
|
||||
|
@ -172,19 +167,41 @@
|
|||
preview
|
||||
content-modifiers
|
||||
last-point
|
||||
selected-handlers
|
||||
selected-points
|
||||
moving-nodes
|
||||
moving-handler
|
||||
hover-handlers
|
||||
hover-points]
|
||||
:as edit-path} (mf/deref edit-path-ref)
|
||||
|
||||
{base-content :content} shape
|
||||
selected-points (or selected-points #{})
|
||||
|
||||
base-content (:content shape)
|
||||
base-points (mf/use-memo (mf/deps base-content) #(->> base-content ugp/content->points))
|
||||
|
||||
content (ugp/apply-content-modifiers base-content content-modifiers)
|
||||
points (mf/use-memo (mf/deps content) #(->> content ugp/content->points (into #{})))
|
||||
content-points (mf/use-memo (mf/deps content) #(->> content ugp/content->points))
|
||||
|
||||
point->base (->> (map hash-map content-points base-points) (reduce merge))
|
||||
base->point (map-invert point->base)
|
||||
|
||||
points (into #{} content-points)
|
||||
|
||||
last-command (last content)
|
||||
last-p (->> content last ugp/command->point)
|
||||
handlers (ugp/content->handlers content)
|
||||
|
||||
[snap-selected snap-points]
|
||||
(cond
|
||||
(some? drag-handler) [#{drag-handler} points]
|
||||
(some? preview) [#{(ugp/command->point preview)} points]
|
||||
(some? moving-handler) [#{moving-handler} points]
|
||||
:else
|
||||
[(->> selected-points (map base->point) (into #{}))
|
||||
(->> points (remove selected-points) (into #{}))])
|
||||
|
||||
show-snap? (or (some? drag-handler) (some? preview) (some? moving-handler) moving-nodes)
|
||||
|
||||
handle-double-click-outside
|
||||
(fn [event]
|
||||
(when (= edit-mode :move)
|
||||
|
@ -199,13 +216,14 @@
|
|||
|
||||
[:g.path-editor {:ref editor-ref}
|
||||
(when (and preview (not drag-handler))
|
||||
[:*
|
||||
[:& snap-points {:selected #{(ugp/command->point preview)}
|
||||
:points points
|
||||
:zoom zoom}]
|
||||
[:& path-preview {:command preview
|
||||
:from last-p
|
||||
:zoom zoom}])
|
||||
|
||||
[:& path-preview {:command preview
|
||||
:from last-p
|
||||
(when drag-handler
|
||||
[:g.drag-handler {:pointer-events "none"}
|
||||
[:& path-handler {:point last-p
|
||||
:handler drag-handler
|
||||
:zoom zoom}]])
|
||||
|
||||
(when @hover-point
|
||||
|
@ -214,10 +232,11 @@
|
|||
:zoom zoom}]])
|
||||
|
||||
(for [position points]
|
||||
(let [point-selected? (contains? selected-points position)
|
||||
point-hover? (contains? hover-points position)
|
||||
last-p? (= last-point position)
|
||||
(let [point-selected? (contains? selected-points (get point->base position))
|
||||
point-hover? (contains? hover-points (get point->base position))
|
||||
last-p? (= last-point (get point->base position))
|
||||
start-p? (not (some? last-point))]
|
||||
|
||||
[:g.path-node
|
||||
[:g.point-handlers {:pointer-events (when (= edit-mode :draw) "none")}
|
||||
(for [[index prefix] (get handlers position)]
|
||||
|
@ -225,7 +244,6 @@
|
|||
x (get-in command [:params (d/prefix-keyword prefix :x)])
|
||||
y (get-in command [:params (d/prefix-keyword prefix :y)])
|
||||
handler-position (gpt/point x y)
|
||||
handler-selected? (contains? selected-handlers [index prefix])
|
||||
handler-hover? (contains? hover-handlers [index prefix])]
|
||||
(when (not= position handler-position)
|
||||
[:& path-handler {:point position
|
||||
|
@ -233,7 +251,6 @@
|
|||
:index index
|
||||
:prefix prefix
|
||||
:zoom zoom
|
||||
:selected? handler-selected?
|
||||
:hover? handler-hover?
|
||||
:edit-mode edit-mode}])))]
|
||||
[:& path-point {:position position
|
||||
|
@ -250,9 +267,9 @@
|
|||
:handler prev-handler
|
||||
:zoom zoom}]])
|
||||
|
||||
(when drag-handler
|
||||
[:g.drag-handler {:pointer-events "none"}
|
||||
[:& path-handler {:point last-p
|
||||
:handler drag-handler
|
||||
:zoom zoom}]])]))
|
||||
(when show-snap?
|
||||
[:g.path-snap {:pointer-events "none"}
|
||||
[:& path-snap {:selected snap-selected
|
||||
:points snap-points
|
||||
:zoom zoom}]])]))
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
(when (and (not= edition id) text-editing?)
|
||||
(st/emit! dw/clear-edition-mode))
|
||||
|
||||
(when (and (or (not edition) (not= edition id))
|
||||
(when (and (not text-editing?)
|
||||
(not blocked)
|
||||
(not hidden)
|
||||
(not (#{:comments :path} drawing-tool))
|
||||
|
|
Loading…
Add table
Reference in a new issue