From f396ef4fa0c86f2b790d2bd7b96503fb9117ee72 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 13 Apr 2021 17:44:26 +0200 Subject: [PATCH] :sparkles: Snap for moving path nodes and handlers --- .../src/app/main/data/workspace/path.cljs | 2 +- .../app/main/data/workspace/path/edition.cljs | 20 ++--- .../main/data/workspace/path/selection.cljs | 10 --- .../app/main/data/workspace/path/streams.cljs | 41 +++++----- frontend/src/app/main/snap.cljs | 6 +- .../main/ui/workspace/shapes/path/editor.cljs | 77 +++++++++++-------- .../main/ui/workspace/viewport/actions.cljs | 2 +- 7 files changed, 83 insertions(+), 75 deletions(-) diff --git a/frontend/src/app/main/data/workspace/path.cljs b/frontend/src/app/main/data/workspace/path.cljs index 2a02ebe7f..e4c04a5a7 100644 --- a/frontend/src/app/main/data/workspace/path.cljs +++ b/frontend/src/app/main/data/workspace/path.cljs @@ -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) diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index ff14d5d00..e5add8011 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -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) diff --git a/frontend/src/app/main/data/workspace/path/selection.cljs b/frontend/src/app/main/data/workspace/path/selection.cljs index 99ea74b17..c9ef72394 100644 --- a/frontend/src/app/main/data/workspace/path/selection.cljs +++ b/frontend/src/app/main/data/workspace/path/selection.cljs @@ -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 diff --git a/frontend/src/app/main/data/workspace/path/streams.cljs b/frontend/src/app/main/data/workspace/path/streams.cljs index 4f0b2a5ef..7251096e6 100644 --- a/frontend/src/app/main/data/workspace/path/streams.cljs +++ b/frontend/src/app/main/data/workspace/path/streams.cljs @@ -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? %))))))) diff --git a/frontend/src/app/main/snap.cljs b/frontend/src/app/main/snap.cljs index 414cc0a6a..3e7ebd870 100644 --- a/frontend/src/app/main/snap.cljs +++ b/frontend/src/app/main/snap.cljs @@ -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)) diff --git a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs index 90a754787..406d4bd58 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs @@ -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}]])])) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 2cea72660..6b886dbe9 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -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))