From 6331dff484f60c00680ce4ddf226add184180461 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 21 Apr 2021 17:39:47 +0200 Subject: [PATCH] :sparkles: Snapping 180 angle with opposites handlers --- common/app/common/geom/point.cljc | 2 + common/app/common/geom/shapes/transforms.cljc | 2 +- .../app/main/data/workspace/path/drawing.cljs | 2 +- .../app/main/data/workspace/path/edition.cljs | 5 +- .../app/main/data/workspace/path/helpers.cljs | 35 ++--------- .../app/main/data/workspace/path/streams.cljs | 34 ++++++++--- .../main/ui/workspace/shapes/path/editor.cljs | 60 +++++++++++++------ frontend/src/app/util/path/commands.cljs | 24 ++++++++ 8 files changed, 108 insertions(+), 56 deletions(-) diff --git a/common/app/common/geom/point.cljc b/common/app/common/geom/point.cljc index 6d78d283a..0d3feeb06 100644 --- a/common/app/common/geom/point.cljc +++ b/common/app/common/geom/point.cljc @@ -162,6 +162,8 @@ (mth/precision 6))] (if (mth/nan? d) 0 d))))) +(defn angle-sign [v1 v2] + (if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1)) (defn update-angle "Update the angle of the point." diff --git a/common/app/common/geom/shapes/transforms.cljc b/common/app/common/geom/shapes/transforms.cljc index 2f80ffb95..78f78dc88 100644 --- a/common/app/common/geom/shapes/transforms.cljc +++ b/common/app/common/geom/shapes/transforms.cljc @@ -161,7 +161,7 @@ v2 (gpt/to-vec center p2) rot-angle (gpt/angle-with-other v1 v2) - rot-sign (if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1)] + rot-sign (gpt/angle-sign v1 v2)] (* rot-sign rot-angle))) (defn- calculate-dimensions diff --git a/frontend/src/app/main/data/workspace/path/drawing.cljs b/frontend/src/app/main/data/workspace/path/drawing.cljs index 80ed8631d..1a3d79d27 100644 --- a/frontend/src/app/main/data/workspace/path/drawing.cljs +++ b/frontend/src/app/main/data/workspace/path/drawing.cljs @@ -73,7 +73,7 @@ prefix (or prefix :c1) position (or position (upc/command->point (nth content (dec index)))) - old-handler (helpers/handler->point content index prefix) + old-handler (upc/handler->point content index prefix) handler-position (cond-> (gpt/point x y) shift? (helpers/position-fixed-angle position)) diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index bea694816..95c5cddf5 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -163,11 +163,14 @@ point (-> content (get (if (= prefix :c1) (dec index) index)) (upc/command->point)) handler (-> content (get index) (upc/get-handler prefix)) + [op-idx op-prefix] (upc/opposite-index content index prefix) + opposite (upc/handler->point content op-idx op-prefix) + snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled])] (streams/drag-stream (rx/concat - (->> (streams/move-handler-stream snap-toggled start-point handler points) + (->> (streams/move-handler-stream snap-toggled start-point point handler opposite points) (rx/take-until (->> stream (rx/filter ms/mouse-up?))) (rx/map (fn [{:keys [x y alt? shift?]}] diff --git a/frontend/src/app/main/data/workspace/path/helpers.cljs b/frontend/src/app/main/data/workspace/path/helpers.cljs index 85d0c5870..17c4edb01 100644 --- a/frontend/src/app/main/data/workspace/path/helpers.cljs +++ b/frontend/src/app/main/data/workspace/path/helpers.cljs @@ -107,29 +107,6 @@ (update :content (fnil conj []) command) (update-selrect)))) -(defn prefix->coords [prefix] - (case prefix - :c1 [:c1x :c1y] - :c2 [:c2x :c2y] - nil)) - -(defn handler->point [content index prefix] - (when (and (some? index) - (some? prefix) - (contains? content index)) - (let [[cx cy :as coords] (prefix->coords prefix)] - (if (= :curve-to (get-in content [index :command])) - (gpt/point (get-in content [index :params cx]) - (get-in content [index :params cy])) - - (gpt/point (get-in content [index :params :x]) - (get-in content [index :params :y])))))) - -(defn handler->node [content index prefix] - (if (= prefix :c1) - (upc/command->point (get content (dec index))) - (upc/command->point (get content index)))) - (defn angle-points [common p1 p2] (mth/abs (gpt/angle-with-other @@ -153,7 +130,7 @@ v2 (gpt/to-vec node new-handler) delta-angle (gpt/angle-with-other v1 v2) - delta-sign (if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1) + delta-sign (gpt/angle-sign v1 v2) distance-scale (/ (gpt/distance node handler) (gpt/distance node new-handler)) @@ -170,14 +147,14 @@ (defn move-handler-modifiers [content index prefix match-distance? match-angle? dx dy] - (let [[cx cy] (prefix->coords prefix) + (let [[cx cy] (upc/prefix->coords prefix) [op-idx op-prefix] (upc/opposite-index content index prefix) - node (handler->node content index prefix) - handler (handler->point content index prefix) - opposite (handler->point content op-idx op-prefix) + node (upc/handler->node content index prefix) + handler (upc/handler->point content index prefix) + opposite (upc/handler->point content op-idx op-prefix) - [ocx ocy] (prefix->coords op-prefix) + [ocx ocy] (upc/prefix->coords op-prefix) [odx ody] (calculate-opposite-delta node handler opposite match-angle? match-distance? dx dy) hnv (if (some? handler) diff --git a/frontend/src/app/main/data/workspace/path/streams.cljs b/frontend/src/app/main/data/workspace/path/streams.cljs index 8912a1141..4dbe0e846 100644 --- a/frontend/src/app/main/data/workspace/path/streams.cljs +++ b/frontend/src/app/main/data/workspace/path/streams.cljs @@ -73,24 +73,45 @@ (rx/map check-path-snap)))) (defn move-handler-stream - [snap-toggled start-point handler points] + [snap-toggled start-point node handler opposite points] (let [zoom (get-in @st/state [:workspace-local :zoom] 1) ranges (snap/create-ranges points) d-pos (/ snap/snap-path-accuracy zoom) + initial-angle (gpt/angle-with-other (gpt/to-vec node handler) + (gpt/to-vec node opposite)) + check-path-snap (fn [position] (if snap-toggled (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)) + handler (gpt/add handler delta) + + v1 (gpt/to-vec node opposite) + v2 (gpt/to-vec node handler) + + rot-angle (gpt/angle-with-other v1 v2) + rot-sign (gpt/angle-sign v1 v2) + + snap-opposite-angle? + (and (or (:alt? position) (> (- 180 initial-angle) 0.1)) + (<= (- 180 rot-angle) 5))] + + (cond + snap-opposite-angle? + (let [rot-handler (gpt/rotate handler node (- 180 (* rot-sign rot-angle))) + snap (gpt/to-vec handler rot-handler)] + (merge position (gpt/add position snap))) + + :else + (let [snap (snap/get-snap-delta [handler] ranges d-pos)] + (merge position (gpt/add position snap))))) position))] (->> ms/mouse-position - (rx/map check-path-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? %))))))) + (rx/with-latest merge (->> ms/mouse-position-alt (rx/map #(hash-map :alt? %)))) + (rx/map check-path-snap)))) (defn position-stream [snap-toggled points] @@ -115,6 +136,5 @@ (let [snap (snap/get-snap-delta [position] ranges d-pos)] (gpt/add position snap)) position))) - (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/ui/workspace/shapes/path/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs index 7457e35d7..0d2e805cd 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs @@ -80,7 +80,7 @@ (= edit-mode :move) cur/pointer-node) :fill "transparent"}}]])) -(mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode]}] +(mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode snap-angle?]}] (when (and point handler) (let [{:keys [x y]} handler on-enter @@ -108,6 +108,16 @@ :y2 y :style {:stroke (if hover? pc/black-color pc/gray-color) :stroke-width (/ 1 zoom)}}] + + (when snap-angle? + [:line + {:x1 (:x point) + :y1 (:y point) + :x2 x + :y2 y + :style {:stroke pc/secondary-color + :stroke-width (/ 1 zoom)}}]) + [:rect {:x (- x (/ 3 zoom)) :y (- y (/ 3 zoom)) @@ -157,6 +167,18 @@ :style {:stroke pc/secondary-color :stroke-width (/ 1 zoom)}}])])) +(defn matching-handler? [content node handlers] + (when (= 2 (count handlers)) + (let [[[i1 p1] [i2 p2]] handlers + p1 (upc/handler->point content i1 p1) + p2 (upc/handler->point content i2 p2) + + v1 (gpt/to-vec node p1) + v2 (gpt/to-vec node p2) + + angle (gpt/angle-with-other v1 v2)] + (<= (- 180 angle) 0.1)))) + (mf/defc path-editor [{:keys [shape zoom]}] @@ -258,20 +280,24 @@ [:g.path-node [:g.point-handlers {:pointer-events (when (= edit-mode :draw) "none")} - (for [[index prefix] (get handlers position)] - (let [command (get content index) - 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-hover? (contains? hover-handlers [index prefix])] - (when (not= position handler-position) - [:& path-handler {:point position - :handler handler-position - :index index - :prefix prefix - :zoom zoom - :hover? handler-hover? - :edit-mode edit-mode}])))] + (let [pos-handlers (get handlers position)] + (for [[index prefix] pos-handlers] + (let [command (get content index) + 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-hover? (contains? hover-handlers [index prefix]) + moving-handler? (= handler-position moving-handler) + matching-handler? (matching-handler? content position pos-handlers)] + (when (not= position handler-position) + [:& path-handler {:point position + :handler handler-position + :index index + :prefix prefix + :zoom zoom + :hover? handler-hover? + :snap-angle? (and moving-handler? matching-handler?) + :edit-mode edit-mode}]))))] [:& path-point {:position position :zoom zoom :edit-mode edit-mode @@ -289,6 +315,6 @@ (when show-snap? [:g.path-snap {:pointer-events "none"} [:& path-snap {:selected snap-selected - :points snap-points - :zoom zoom}]])])) + :points snap-points + :zoom zoom}]])])) diff --git a/frontend/src/app/util/path/commands.cljs b/frontend/src/app/util/path/commands.cljs index 9800b7772..f284a457c 100644 --- a/frontend/src/app/util/path/commands.cljs +++ b/frontend/src/app/util/path/commands.cljs @@ -180,3 +180,27 @@ [content point] (->> (d/enumerate content) (filterv (fn [[idx cmd]] (= (command->point cmd) point))))) + + +(defn prefix->coords [prefix] + (case prefix + :c1 [:c1x :c1y] + :c2 [:c2x :c2y] + nil)) + +(defn handler->point [content index prefix] + (when (and (some? index) + (some? prefix) + (contains? content index)) + (let [[cx cy :as coords] (prefix->coords prefix)] + (if (= :curve-to (get-in content [index :command])) + (gpt/point (get-in content [index :params cx]) + (get-in content [index :params cy])) + + (gpt/point (get-in content [index :params :x]) + (get-in content [index :params :y])))))) + +(defn handler->node [content index prefix] + (if (= prefix :c1) + (command->point (get content (dec index))) + (command->point (get content index))))