mirror of
https://github.com/penpot/penpot.git
synced 2025-07-30 05:18:33 +02:00
✨ Fixed angle with shift in paths
This commit is contained in:
parent
5c71601fcf
commit
89f0f24707
4 changed files with 107 additions and 78 deletions
|
@ -55,6 +55,11 @@
|
||||||
;;(assert (not (nil? y)))
|
;;(assert (not (nil? y)))
|
||||||
(Point. x y)))
|
(Point. x y)))
|
||||||
|
|
||||||
|
(defn angle->point [{:keys [x y]} angle distance]
|
||||||
|
(point
|
||||||
|
(+ x (* distance (mth/cos angle)))
|
||||||
|
(- y (* distance (mth/sin angle)))))
|
||||||
|
|
||||||
(defn add
|
(defn add
|
||||||
"Returns the addition of the supplied value to both
|
"Returns the addition of the supplied value to both
|
||||||
coordinates of the point as a new point."
|
coordinates of the point as a new point."
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
[app.common.data :as cd]
|
[app.common.data :as cd]
|
||||||
[app.util.geom.path :as ugp]
|
[app.util.geom.path :as ugp]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.drawing.common :as common]
|
[app.main.data.workspace.drawing.common :as common]
|
||||||
[app.common.geom.shapes.path :as gsp]))
|
[app.common.geom.shapes.path :as gsp]))
|
||||||
|
@ -67,7 +68,7 @@
|
||||||
|
|
||||||
;; CONSTANTS
|
;; CONSTANTS
|
||||||
(defonce enter-keycode 13)
|
(defonce enter-keycode 13)
|
||||||
(defonce drag-threshold 2)
|
(defonce drag-threshold 5)
|
||||||
|
|
||||||
;; PRIVATE METHODS
|
;; PRIVATE METHODS
|
||||||
|
|
||||||
|
@ -96,6 +97,25 @@
|
||||||
points (gsh/rect->points selrect)]
|
points (gsh/rect->points selrect)]
|
||||||
(assoc shape :points points :selrect selrect)))
|
(assoc shape :points points :selrect selrect)))
|
||||||
|
|
||||||
|
(defn closest-angle [angle]
|
||||||
|
(cond
|
||||||
|
(or (> angle 337.5) (<= angle 22.5)) 0
|
||||||
|
(and (> angle 22.5) (<= angle 67.5)) 45
|
||||||
|
(and (> angle 67.5) (<= angle 112.5)) 90
|
||||||
|
(and (> angle 112.5) (<= angle 157.5)) 135
|
||||||
|
(and (> angle 157.5) (<= angle 202.5)) 180
|
||||||
|
(and (> angle 202.5) (<= angle 247.5)) 225
|
||||||
|
(and (> angle 247.5) (<= angle 292.5)) 270
|
||||||
|
(and (> angle 292.5) (<= angle 337.5)) 315))
|
||||||
|
|
||||||
|
(defn position-fixed-angle [point from-point]
|
||||||
|
(if (and from-point point)
|
||||||
|
(let [angle (mod (+ 360 (- (gpt/angle point from-point))) 360)
|
||||||
|
to-angle (closest-angle angle)
|
||||||
|
distance (gpt/distance point from-point)]
|
||||||
|
(gpt/angle->point from-point (mth/radians to-angle) distance))
|
||||||
|
point))
|
||||||
|
|
||||||
(defn next-node
|
(defn next-node
|
||||||
"Calculates the next-node to be inserted."
|
"Calculates the next-node to be inserted."
|
||||||
[shape position prev-point prev-handler]
|
[shape position prev-point prev-handler]
|
||||||
|
@ -177,6 +197,21 @@
|
||||||
(fn [current]
|
(fn [current]
|
||||||
(>= (gpt/distance start current) (/ drag-threshold zoom))))
|
(>= (gpt/distance start current) (/ drag-threshold zoom))))
|
||||||
|
|
||||||
|
(defn drag-stream [to-stream]
|
||||||
|
(let [start @ms/mouse-position
|
||||||
|
zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||||
|
mouse-up (->> st/stream (rx/filter #(ms/mouse-up? %)))]
|
||||||
|
(->> ms/mouse-position
|
||||||
|
(rx/take-until mouse-up)
|
||||||
|
(rx/filter (dragging? start zoom))
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/merge-map (fn [] to-stream)))))
|
||||||
|
|
||||||
|
(defn position-stream []
|
||||||
|
(->> ms/mouse-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? %))))))
|
||||||
|
|
||||||
;; EVENTS
|
;; EVENTS
|
||||||
|
|
||||||
(defn init-path []
|
(defn init-path []
|
||||||
|
@ -190,24 +225,31 @@
|
||||||
(-> state
|
(-> state
|
||||||
(update-in [:workspace-local :edit-path id] clean-edit-state))))))
|
(update-in [:workspace-local :edit-path id] clean-edit-state))))))
|
||||||
|
|
||||||
(defn preview-next-point [{:keys [x y]}]
|
(defn preview-next-point [{:keys [x y shift?]}]
|
||||||
(ptk/reify ::preview-next-point
|
(ptk/reify ::preview-next-point
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (get-path-id state)
|
(let [id (get-path-id state)
|
||||||
position (gpt/point x y)
|
fix-angle? shift?
|
||||||
|
last-point (get-in state [:workspace-local :edit-path id :last-point])
|
||||||
|
position (cond-> (gpt/point x y)
|
||||||
|
fix-angle? (position-fixed-angle last-point))
|
||||||
|
|
||||||
shape (get-in state (get-path state))
|
shape (get-in state (get-path state))
|
||||||
{:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path id])
|
{:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path id])
|
||||||
command (next-node shape position last-point prev-handler)]
|
command (next-node shape position last-point prev-handler)]
|
||||||
(assoc-in state [:workspace-local :edit-path id :preview] command)))))
|
(assoc-in state [:workspace-local :edit-path id :preview] command)))))
|
||||||
|
|
||||||
(defn add-node [{:keys [x y]}]
|
(defn add-node [{:keys [x y shift?]}]
|
||||||
(ptk/reify ::add-node
|
(ptk/reify ::add-node
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (get-path-id state)
|
(let [id (get-path-id state)
|
||||||
position (gpt/point x y)
|
fix-angle? shift?
|
||||||
{:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path id])]
|
{:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path id])
|
||||||
|
position (cond-> (gpt/point x y)
|
||||||
|
fix-angle? (position-fixed-angle last-point))
|
||||||
|
]
|
||||||
(if-not (= last-point position)
|
(if-not (= last-point position)
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-local :edit-path id :last-point] position)
|
(assoc-in [:workspace-local :edit-path id :last-point] position)
|
||||||
|
@ -301,16 +343,12 @@
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (add-node position))
|
(rx/of (add-node position))
|
||||||
|
(drag-stream
|
||||||
(->> position-stream
|
(rx/concat
|
||||||
(rx/filter (dragging? start-position zoom))
|
(rx/of (start-drag-handler))
|
||||||
(rx/take 1)
|
drag-events-stream
|
||||||
(rx/merge-map
|
(rx/of (finish-drag))
|
||||||
#(rx/concat
|
(rx/of (close-path-drag-end))))
|
||||||
(rx/of (start-drag-handler))
|
|
||||||
drag-events-stream
|
|
||||||
(rx/of (finish-drag))
|
|
||||||
(rx/of (close-path-drag-end)))))
|
|
||||||
(rx/of (finish-path "close-path")))))))
|
(rx/of (finish-path "close-path")))))))
|
||||||
|
|
||||||
(defn close-path-drag-end []
|
(defn close-path-drag-end []
|
||||||
|
@ -338,16 +376,21 @@
|
||||||
(ptk/reify ::start-path-from-point
|
(ptk/reify ::start-path-from-point
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [mouse-up (->> stream (rx/filter #(or (end-path-event? %)
|
(let [start-point @ms/mouse-position
|
||||||
|
zoom (get-in state [:workspace-local :zoom])
|
||||||
|
mouse-up (->> stream (rx/filter #(or (end-path-event? %)
|
||||||
(ms/mouse-up? %))))
|
(ms/mouse-up? %))))
|
||||||
drag-events (->> ms/mouse-position
|
drag-events (->> ms/mouse-position
|
||||||
(rx/take-until mouse-up)
|
(rx/take-until mouse-up)
|
||||||
(rx/map #(drag-handler %)))]
|
(rx/map #(drag-handler %)))]
|
||||||
|
|
||||||
(rx/concat (rx/of (add-node position))
|
(rx/concat
|
||||||
(rx/of (start-drag-handler))
|
(rx/of (add-node position))
|
||||||
drag-events
|
(drag-stream
|
||||||
(rx/of (finish-drag)))))))
|
(rx/concat
|
||||||
|
(rx/of (start-drag-handler))
|
||||||
|
drag-events
|
||||||
|
(rx/of (finish-drag)))))))))
|
||||||
|
|
||||||
(defn make-corner []
|
(defn make-corner []
|
||||||
(ptk/reify ::make-corner
|
(ptk/reify ::make-corner
|
||||||
|
@ -389,14 +432,6 @@
|
||||||
|
|
||||||
;; EVENT STREAMS
|
;; EVENT STREAMS
|
||||||
|
|
||||||
(defn make-click-stream
|
|
||||||
[stream down-event]
|
|
||||||
(->> stream
|
|
||||||
(rx/filter ms/mouse-click?)
|
|
||||||
#_(rx/debounce 200)
|
|
||||||
(rx/first)
|
|
||||||
(rx/map #(add-node down-event))))
|
|
||||||
|
|
||||||
(defn make-drag-stream
|
(defn make-drag-stream
|
||||||
[stream down-event zoom]
|
[stream down-event zoom]
|
||||||
(let [mouse-up (->> stream (rx/filter #(or (end-path-event? %)
|
(let [mouse-up (->> stream (rx/filter #(or (end-path-event? %)
|
||||||
|
@ -407,16 +442,11 @@
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (add-node down-event))
|
(rx/of (add-node down-event))
|
||||||
|
(drag-stream
|
||||||
(->> ms/mouse-position
|
(rx/concat
|
||||||
(rx/take-until mouse-up)
|
(rx/of (start-drag-handler))
|
||||||
(rx/filter (dragging? (gpt/point down-event) zoom))
|
drag-events
|
||||||
(rx/take 1)
|
(rx/of (finish-drag)))))))
|
||||||
(rx/merge-map
|
|
||||||
#(rx/concat
|
|
||||||
(rx/of (start-drag-handler))
|
|
||||||
drag-events
|
|
||||||
(rx/of (finish-drag))))))))
|
|
||||||
|
|
||||||
(defn make-node-events-stream
|
(defn make-node-events-stream
|
||||||
[stream]
|
[stream]
|
||||||
|
@ -444,7 +474,7 @@
|
||||||
|
|
||||||
;; Mouse move preview
|
;; Mouse move preview
|
||||||
mousemove-events
|
mousemove-events
|
||||||
(->> ms/mouse-position
|
(->> (position-stream)
|
||||||
(rx/take-until end-path-events)
|
(rx/take-until end-path-events)
|
||||||
(rx/map #(preview-next-point %)))
|
(rx/map #(preview-next-point %)))
|
||||||
|
|
||||||
|
@ -452,7 +482,7 @@
|
||||||
mousedown-events
|
mousedown-events
|
||||||
(->> mouse-down
|
(->> mouse-down
|
||||||
(rx/take-until end-path-events)
|
(rx/take-until end-path-events)
|
||||||
(rx/with-latest merge ms/mouse-position)
|
(rx/with-latest merge (position-stream))
|
||||||
|
|
||||||
;; We change to the stream that emits the first event
|
;; We change to the stream that emits the first event
|
||||||
(rx/switch-map
|
(rx/switch-map
|
||||||
|
@ -608,22 +638,14 @@
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [start-position @ms/mouse-position
|
(let [start-position @ms/mouse-position
|
||||||
stopper (->> stream (rx/filter ms/mouse-up?))
|
stopper (->> stream (rx/filter ms/mouse-up?))
|
||||||
zoom (get-in state [:workspace-local :zoom])
|
zoom (get-in state [:workspace-local :zoom])]
|
||||||
|
|
||||||
move-point-stream
|
(drag-stream
|
||||||
(fn [] (rx/concat
|
(rx/concat
|
||||||
(->> ms/mouse-position
|
(->> ms/mouse-position
|
||||||
(rx/take-until stopper)
|
(rx/take-until stopper)
|
||||||
(rx/map #(move-path-point position %)))
|
(rx/map #(move-path-point position %)))
|
||||||
(rx/of (apply-content-modifiers))))]
|
(rx/of (apply-content-modifiers))))))))
|
||||||
|
|
||||||
(->> ms/mouse-position
|
|
||||||
(rx/take-until stopper)
|
|
||||||
(rx/filter (dragging? start-position zoom))
|
|
||||||
(rx/take 1)
|
|
||||||
(rx/merge-map #(move-point-stream)))
|
|
||||||
|
|
||||||
))))
|
|
||||||
|
|
||||||
(defn start-move-handler
|
(defn start-move-handler
|
||||||
[index prefix]
|
[index prefix]
|
||||||
|
@ -636,21 +658,21 @@
|
||||||
start-delta-x (get-in state [:workspace-local :edit-path id :content-modifiers index cx] 0)
|
start-delta-x (get-in state [:workspace-local :edit-path id :content-modifiers index cx] 0)
|
||||||
start-delta-y (get-in state [:workspace-local :edit-path id :content-modifiers index cy] 0)]
|
start-delta-y (get-in state [:workspace-local :edit-path id :content-modifiers index cy] 0)]
|
||||||
|
|
||||||
(rx/concat
|
(drag-stream
|
||||||
(->> ms/mouse-position
|
(rx/concat
|
||||||
(rx/take-until (->> stream (rx/filter ms/mouse-up?)))
|
(->> ms/mouse-position
|
||||||
(rx/with-latest vector ms/mouse-position-alt)
|
(rx/take-until (->> stream (rx/filter ms/mouse-up?)))
|
||||||
(rx/map
|
(rx/with-latest vector ms/mouse-position-alt)
|
||||||
(fn [[pos alt?]]
|
(rx/map
|
||||||
(modify-handler
|
(fn [[pos alt?]]
|
||||||
id
|
(modify-handler
|
||||||
index
|
id
|
||||||
prefix
|
index
|
||||||
(+ start-delta-x (- (:x pos) (:x start-point)))
|
prefix
|
||||||
(+ start-delta-y (- (:y pos) (:y start-point)))
|
(+ start-delta-x (- (:x pos) (:x start-point)))
|
||||||
(not alt?))))
|
(+ start-delta-y (- (:y pos) (:y start-point)))
|
||||||
)
|
(not alt?)))))
|
||||||
(rx/concat (rx/of (apply-content-modifiers))))))))
|
(rx/concat (rx/of (apply-content-modifiers)))))))))
|
||||||
|
|
||||||
(defn start-draw-mode []
|
(defn start-draw-mode []
|
||||||
(ptk/reify ::start-draw-mode
|
(ptk/reify ::start-draw-mode
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
{:alt (t locale "workspace.toolbar.path")
|
{:alt (t locale "workspace.toolbar.path")
|
||||||
:class (when (= selected-drawtool :path) "selected")
|
:class (when (= selected-drawtool :path) "selected")
|
||||||
:on-click (partial select-drawtool :path)}
|
:on-click (partial select-drawtool :path)}
|
||||||
i/curve]
|
i/pen]
|
||||||
|
|
||||||
[:li.tooltip.tooltip-right
|
[:li.tooltip.tooltip-right
|
||||||
{:alt (t locale "workspace.toolbar.comments")
|
{:alt (t locale "workspace.toolbar.comments")
|
||||||
|
|
|
@ -169,8 +169,7 @@
|
||||||
{:cx x
|
{:cx x
|
||||||
:cy y
|
:cy y
|
||||||
:r (if (or selected? hover?) (/ 3.5 zoom) (/ 3 zoom))
|
:r (if (or selected? hover?) (/ 3.5 zoom) (/ 3 zoom))
|
||||||
:style {:cursor (when (= edit-mode :draw) cur/pen-node)
|
:style {:stroke-width (/ 1 zoom)
|
||||||
:stroke-width (/ 1 zoom)
|
|
||||||
:stroke (cond (or selected? hover?) black-color
|
:stroke (cond (or selected? hover?) black-color
|
||||||
preview? secondary-color
|
preview? secondary-color
|
||||||
:else primary-color)
|
:else primary-color)
|
||||||
|
@ -183,7 +182,10 @@
|
||||||
:on-mouse-down on-mouse-down
|
:on-mouse-down on-mouse-down
|
||||||
:on-mouse-enter on-enter
|
:on-mouse-enter on-enter
|
||||||
:on-mouse-leave on-leave
|
:on-mouse-leave on-leave
|
||||||
:style {:fill "transparent"}}]]))
|
:style {:cursor (cond
|
||||||
|
(and (not last-p?) (= edit-mode :draw)) cur/pen-node
|
||||||
|
(= 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]}]
|
||||||
(when (and point handler)
|
(when (and point handler)
|
||||||
|
@ -227,8 +229,7 @@
|
||||||
:width (/ 6 zoom)
|
:width (/ 6 zoom)
|
||||||
:height (/ 6 zoom)
|
:height (/ 6 zoom)
|
||||||
|
|
||||||
:style {:cursor cur/pointer-move
|
:style {:stroke-width (/ 1 zoom)
|
||||||
:stroke-width (/ 1 zoom)
|
|
||||||
:stroke (cond (or selected? hover?) black-color
|
:stroke (cond (or selected? hover?) black-color
|
||||||
:else primary-color)
|
:else primary-color)
|
||||||
:fill (cond selected? primary-color
|
:fill (cond selected? primary-color
|
||||||
|
@ -240,7 +241,8 @@
|
||||||
:on-mouse-down on-mouse-down
|
:on-mouse-down on-mouse-down
|
||||||
:on-mouse-enter on-enter
|
:on-mouse-enter on-enter
|
||||||
:on-mouse-leave on-leave
|
:on-mouse-leave on-leave
|
||||||
:style {:fill "transparent"}}]])))
|
:style {:cursor (when (= edit-mode :move) cur/pointer-move)
|
||||||
|
:fill "transparent"}}]])))
|
||||||
|
|
||||||
(mf/defc path-preview [{:keys [zoom command from]}]
|
(mf/defc path-preview [{:keys [zoom command from]}]
|
||||||
[:g.preview {:style {:pointer-events "none"}}
|
[:g.preview {:style {:pointer-events "none"}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue