mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 09:08:31 -05:00
✨ Close paths and internals refactor
This commit is contained in:
parent
f339f1ee98
commit
b66b0cb431
5 changed files with 431 additions and 294 deletions
|
@ -17,9 +17,6 @@
|
|||
[app.common.math :as mth]
|
||||
[app.common.data :as d]))
|
||||
|
||||
(defn segments->points [segments]
|
||||
segments)
|
||||
|
||||
(defn content->points [content]
|
||||
(->> content
|
||||
(map #(when (-> % :params :x) (gpt/point (-> % :params :x) (-> % :params :y))))
|
||||
|
@ -147,21 +144,6 @@
|
|||
|
||||
(mapv #(update % :params transform-params) content)))
|
||||
|
||||
(defn apply-content-modifiers [content modifiers]
|
||||
(let [red-fn (fn [content [index params]]
|
||||
(if (contains? content index)
|
||||
(cond-> content
|
||||
(:x params) (update-in [index :params :x] + (:x params))
|
||||
(:y params) (update-in [index :params :y] + (:y params))
|
||||
|
||||
(:c1x params) (update-in [index :params :c1x] + (:c1x params))
|
||||
(:c1y params) (update-in [index :params :c1y] + (:c1y params))
|
||||
|
||||
(:c2x params) (update-in [index :params :c2x] + (:c2x params))
|
||||
(:c2y params) (update-in [index :params :c2y] + (:c2y params)))
|
||||
content))]
|
||||
(reduce red-fn content modifiers)))
|
||||
|
||||
(defn segments->content
|
||||
([segments]
|
||||
(segments->content segments false))
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]
|
||||
[app.common.math :as mth]
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.util.data :as ud]
|
||||
|
@ -28,11 +29,16 @@
|
|||
|
||||
;; PRIVATE METHODS
|
||||
|
||||
(defn get-path-id [state]
|
||||
(defn get-path-id
|
||||
"Retrieves the currently editing path id"
|
||||
[state]
|
||||
(or (get-in state [:workspace-local :edition])
|
||||
(get-in state [:workspace-drawing :object :id])))
|
||||
|
||||
(defn get-path [state & path]
|
||||
(defn get-path
|
||||
"Retrieves the location of the path object and additionaly can pass
|
||||
the arguments. This location can be used in get-in, assoc-in... functions"
|
||||
[state & path]
|
||||
(let [edit-id (get-in state [:workspace-local :edition])
|
||||
page-id (:current-page-id state)]
|
||||
(cd/concat
|
||||
|
@ -41,13 +47,9 @@
|
|||
[:workspace-drawing :object])
|
||||
path)))
|
||||
|
||||
(defn last-start-path [content]
|
||||
(->> content
|
||||
reverse
|
||||
(cd/seek (fn [{cmd :command}] (= cmd :move-to)))
|
||||
:params))
|
||||
|
||||
(defn update-selrect [shape]
|
||||
(defn update-selrect
|
||||
"Updates the selrect and points for a path"
|
||||
[shape]
|
||||
(let [selrect (gsh/content->selrect (:content shape))
|
||||
points (gsh/rect->points selrect)]
|
||||
(assoc shape :points points :selrect selrect)))
|
||||
|
@ -56,8 +58,6 @@
|
|||
"Calculates the next-node to be inserted."
|
||||
[shape position prev-point prev-handler]
|
||||
(let [last-command (-> shape :content last :command)
|
||||
start-point (-> shape :content last-start-path)
|
||||
|
||||
add-line? (and prev-point (not prev-handler) (not= last-command :close-path))
|
||||
add-curve? (and prev-point prev-handler (not= last-command :close-path))]
|
||||
(cond
|
||||
|
@ -76,53 +76,17 @@
|
|||
(update :content (fnil conj []) command)
|
||||
(update-selrect))))
|
||||
|
||||
(defn suffix-keyword
|
||||
[kw suffix]
|
||||
(let [strkw (if kw (name kw) "")]
|
||||
(keyword (str strkw suffix))))
|
||||
(defn move-handler-modifiers [content index prefix match-opposite? dx dy]
|
||||
(let [[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
||||
[ocx ocy] (if (= prefix :c1) [:c2x :c2y] [:c1x :c1y])
|
||||
opposite-index (ugp/opposite-index content index prefix)]
|
||||
|
||||
(defn move-handler
|
||||
[shape index handler-type match-opposite? position]
|
||||
(let [content (:content shape)
|
||||
[command next-command] (-> (ud/with-next content) (nth index))
|
||||
(cond-> {}
|
||||
:always
|
||||
(update index assoc cx dx cy dy)
|
||||
|
||||
update-command
|
||||
(fn [{cmd :command params :params :as command} param-prefix prev-command]
|
||||
(if (#{:line-to :curve-to} cmd)
|
||||
(let [command (if (= cmd :line-to)
|
||||
{:command :curve-to
|
||||
:params (ugp/make-curve-params params (:params prev-command))}
|
||||
command)]
|
||||
(-> command
|
||||
(update :params assoc
|
||||
(suffix-keyword param-prefix "x") (:x position)
|
||||
(suffix-keyword param-prefix "y") (:y position))))
|
||||
command))
|
||||
|
||||
update-content
|
||||
(fn [shape index prefix]
|
||||
(if (contains? (:content shape) index)
|
||||
(let [prev-command (get-in shape [:content (dec index)])
|
||||
content (-> shape :content (update index update-command prefix prev-command))]
|
||||
(-> shape
|
||||
(assoc :content content)
|
||||
(update-selrect)))
|
||||
shape))]
|
||||
|
||||
(cond-> shape
|
||||
(= :prev handler-type)
|
||||
(update-content index :c2)
|
||||
|
||||
(and (= :next handler-type) next-command)
|
||||
(update-content (inc index) :c1)
|
||||
|
||||
match-opposite?
|
||||
(move-handler
|
||||
index
|
||||
(if (= handler-type :prev) :next :prev)
|
||||
false
|
||||
(ugp/opposite-handler (gpt/point (:params command))
|
||||
(gpt/point position))))))
|
||||
(and match-opposite? opposite-index)
|
||||
(update opposite-index assoc ocx (- dx) ocy (- dy)))))
|
||||
|
||||
(defn end-path-event? [{:keys [type shift] :as event}]
|
||||
(or (= event ::end-path)
|
||||
|
@ -175,29 +139,58 @@
|
|||
(update-in [:workspace-local :edit-path id] dissoc :prev-handler)
|
||||
(update-in (get-path state) append-node position last-point prev-handler))))))
|
||||
|
||||
(defn start-drag-handler []
|
||||
(ptk/reify ::start-drag-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [content (get-in state (get-path state :content))
|
||||
index (dec (count content))
|
||||
command (get-in state (get-path state :content index :command))
|
||||
|
||||
make-curve
|
||||
(fn [command]
|
||||
(let [params (ugp/make-curve-params
|
||||
(get-in content [index :params])
|
||||
(get-in content [(dec index) :params]))]
|
||||
(-> command
|
||||
(assoc :command :curve-to :params params))))]
|
||||
|
||||
(cond-> state
|
||||
(= command :line-to)
|
||||
(update-in (get-path state :content index) make-curve))))))
|
||||
|
||||
(defn drag-handler [{:keys [x y]}]
|
||||
(ptk/reify ::drag-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
|
||||
(let [id (get-path-id state)
|
||||
position (gpt/point x y)
|
||||
handler-position (gpt/point x y)
|
||||
shape (get-in state (get-path state))
|
||||
index (dec (count (:content shape)))]
|
||||
content (:content shape)
|
||||
index (dec (count content))
|
||||
node-position (ugp/command->point (nth content index))
|
||||
{dx :x dy :y} (gpt/subtract handler-position node-position)
|
||||
match-opposite? true
|
||||
modifiers (move-handler-modifiers content (inc index) :c1 match-opposite? dx dy)]
|
||||
(-> state
|
||||
(update-in (get-path state) move-handler index :next true position)
|
||||
(assoc-in [:workspace-local :edit-path id :prev-handler] position)
|
||||
(assoc-in [:workspace-local :edit-path id :drag-handler] position))))))
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers)
|
||||
(assoc-in [:workspace-local :edit-path id :prev-handler] handler-position)
|
||||
(assoc-in [:workspace-local :edit-path id :drag-handler] handler-position))))))
|
||||
|
||||
(defn finish-drag []
|
||||
(ptk/reify ::finish-drag
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-path-id state)
|
||||
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers])
|
||||
handler (get-in state [:workspace-local :edit-path id :drag-handler])]
|
||||
(-> state
|
||||
(update-in (get-path state :content) ugp/apply-content-modifiers modifiers)
|
||||
(update-in [:workspace-local :edit-path id] dissoc :drag-handler)
|
||||
(assoc-in [:workspace-local :edit-path id :prev-handler] handler))))
|
||||
(update-in [:workspace-local :edit-path id] dissoc :content-modifiers)
|
||||
(assoc-in [:workspace-local :edit-path id :prev-handler] handler)
|
||||
(update-in (get-path state) update-selrect))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
@ -206,6 +199,76 @@
|
|||
;; Update the preview because can be outdated after the dragging
|
||||
(rx/of (preview-next-point handler))))))
|
||||
|
||||
(defn close-path [position]
|
||||
(ptk/reify ::close-path
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (add-node position)
|
||||
::end-path))))
|
||||
|
||||
(defn close-path-drag-start [position]
|
||||
(ptk/reify ::close-path-drag-start
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [zoom (get-in state [:workspace-local :zoom])
|
||||
threshold (/ 5 zoom)
|
||||
check-if-dragging
|
||||
(fn [current-position]
|
||||
(let [start (gpt/point position)
|
||||
current (gpt/point current-position)]
|
||||
(>= (gpt/distance start current) 100)))
|
||||
|
||||
stop-stream
|
||||
(->> stream (rx/filter #(or (end-path-event? %)
|
||||
(ms/mouse-up? %))))
|
||||
|
||||
position-stream
|
||||
(->> ms/mouse-position
|
||||
(rx/take-until stop-stream)
|
||||
(rx/throttle 50))
|
||||
|
||||
drag-events-stream
|
||||
(->> position-stream
|
||||
(rx/map #(drag-handler %)))]
|
||||
|
||||
|
||||
(rx/concat
|
||||
(rx/of (close-path position))
|
||||
|
||||
(->> position-stream
|
||||
(rx/filter check-if-dragging)
|
||||
(rx/take 1)
|
||||
(rx/merge-map
|
||||
#(rx/concat
|
||||
(rx/of (start-drag-handler))
|
||||
drag-events-stream
|
||||
(rx/of (finish-drag))))))))))
|
||||
|
||||
(defn close-path-drag-end [position]
|
||||
(ptk/reify ::close-path-drag-end))
|
||||
|
||||
(defn path-pointer-enter [position]
|
||||
(ptk/reify ::path-pointer-enter))
|
||||
|
||||
(defn path-pointer-leave [position]
|
||||
(ptk/reify ::path-pointer-leave))
|
||||
|
||||
(defn start-path-from-point [position]
|
||||
(ptk/reify ::start-path-from-point
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [mouse-up (->> stream (rx/filter #(or (end-path-event? %)
|
||||
(ms/mouse-up? %))))
|
||||
drag-events (->> ms/mouse-position
|
||||
(rx/take-until mouse-up)
|
||||
(rx/map #(drag-handler %)))]
|
||||
|
||||
(rx/concat (rx/of (add-node position))
|
||||
(rx/of (start-drag-handler))
|
||||
drag-events
|
||||
(rx/of (finish-drag))))
|
||||
)))
|
||||
|
||||
;; EVENT STREAMS
|
||||
|
||||
(defn make-click-stream
|
||||
|
@ -218,13 +281,15 @@
|
|||
|
||||
(defn make-drag-stream
|
||||
[stream down-event]
|
||||
(let [mouse-up (->> stream (rx/filter ms/mouse-up?))
|
||||
(let [mouse-up (->> stream (rx/filter #(or (end-path-event? %)
|
||||
(ms/mouse-up? %))))
|
||||
drag-events (->> ms/mouse-position
|
||||
(rx/take-until mouse-up)
|
||||
(rx/map #(drag-handler %)))]
|
||||
(->> (rx/timer 400)
|
||||
(rx/merge-map #(rx/concat
|
||||
(rx/of (add-node down-event))
|
||||
(rx/of (start-drag-handler))
|
||||
drag-events
|
||||
(rx/of (finish-drag)))))))
|
||||
|
||||
|
@ -237,14 +302,27 @@
|
|||
#(rx/of (add-node down-event)
|
||||
::end-path))))
|
||||
|
||||
(defn make-node-events-stream
|
||||
[stream]
|
||||
(->> (rx/merge
|
||||
(->> stream (rx/filter (ptk/type? ::close-path)))
|
||||
(->> stream (rx/filter (ptk/type? ::close-path-drag-start))))
|
||||
(rx/take 1)
|
||||
(rx/merge-map #(rx/empty))))
|
||||
|
||||
;; MAIN ENTRIES
|
||||
|
||||
(defn handle-drawing-path
|
||||
[id]
|
||||
(ptk/reify ::handle-drawing-path
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-path-id state)]
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :edit-mode] :draw))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
||||
(let [mouse-down (->> stream (rx/filter ms/mouse-down?))
|
||||
end-path-events (->> stream (rx/filter end-path-event?))
|
||||
|
||||
|
@ -264,7 +342,8 @@
|
|||
|
||||
;; We change to the stream that emits the first event
|
||||
(rx/switch-map
|
||||
#(rx/race (make-click-stream stream %)
|
||||
#(rx/race (make-node-events-stream stream)
|
||||
(make-click-stream stream %)
|
||||
(make-drag-stream stream %)
|
||||
(make-dbl-click-stream stream %))))]
|
||||
|
||||
|
@ -276,67 +355,6 @@
|
|||
|
||||
|
||||
|
||||
#_(def handle-drawing-path
|
||||
(ptk/reify ::handle-drawing-path
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [{:keys [flags]} (:workspace-local state)
|
||||
|
||||
last-point (volatile! @ms/mouse-position)
|
||||
|
||||
stoper (->> (rx/filter stoper-event? stream)
|
||||
(rx/share))
|
||||
|
||||
mouse (rx/sample 10 ms/mouse-position)
|
||||
|
||||
points (->> stream
|
||||
(rx/filter ms/mouse-click?)
|
||||
(rx/filter #(false? (:shift %)))
|
||||
(rx/with-latest vector mouse)
|
||||
(rx/map second))
|
||||
|
||||
counter (rx/merge (rx/scan #(inc %) 1 points) (rx/of 1))
|
||||
|
||||
stream' (->> mouse
|
||||
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||
(rx/with-latest vector counter)
|
||||
(rx/map flatten))
|
||||
|
||||
imm-transform #(vector (- % 7) (+ % 7) %)
|
||||
immanted-zones (vec (concat
|
||||
(map imm-transform (range 0 181 15))
|
||||
(map (comp imm-transform -) (range 0 181 15))))
|
||||
|
||||
align-position (fn [angle pos]
|
||||
(reduce (fn [pos [a1 a2 v]]
|
||||
(if (< a1 angle a2)
|
||||
(reduced (gpt/update-angle pos v))
|
||||
pos))
|
||||
pos
|
||||
immanted-zones))]
|
||||
|
||||
(rx/merge
|
||||
(rx/of #(initialize-drawing % @last-point))
|
||||
|
||||
(->> points
|
||||
(rx/take-until stoper)
|
||||
(rx/map (fn [pt] #(insert-point-segment % pt))))
|
||||
|
||||
(rx/concat
|
||||
(->> stream'
|
||||
(rx/take-until stoper)
|
||||
(rx/map (fn [[point ctrl? index :as xxx]]
|
||||
(let [point (if ctrl?
|
||||
(as-> point $
|
||||
(gpt/subtract $ @last-point)
|
||||
(align-position (gpt/angle $) $)
|
||||
(gpt/add $ @last-point))
|
||||
point)]
|
||||
#(update-point-segment % index point)))))
|
||||
(rx/of finish-drawing-path
|
||||
common/handle-finish-drawing)))))))
|
||||
|
||||
|
||||
(defn stop-path-edit []
|
||||
(ptk/reify ::stop-path-edit
|
||||
ptk/UpdateEvent
|
||||
|
@ -363,12 +381,12 @@
|
|||
(rx/take 1)
|
||||
(rx/map #(stop-path-edit))))))
|
||||
|
||||
(defn modify-point [index dx dy]
|
||||
(defn modify-point [index prefix dx dy]
|
||||
(ptk/reify ::modify-point
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(let [id (get-in state [:workspace-local :edition])
|
||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :content-modifiers (inc index)] assoc
|
||||
:c1x dx :c1y dy)
|
||||
|
@ -376,19 +394,22 @@
|
|||
:x dx :y dy :c2x dx :c2y dy)
|
||||
)))))
|
||||
|
||||
(defn modify-handler [index type dx dy]
|
||||
(defn modify-handler [id index prefix dx dy match-opposite?]
|
||||
(ptk/reify ::modify-point
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(let [s1 (if (= type :prev) -1 1)
|
||||
s2 (if (= type :prev) 1 -1)]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :content-modifiers (inc index)] assoc
|
||||
:c1x (* s1 dx) :c1y (* s1 dy))
|
||||
(update-in [:workspace-local :edit-path id :content-modifiers index] assoc
|
||||
:c2x (* s2 dx) :c2y (* s2 dy) ))
|
||||
)))))
|
||||
(let [content (get-in state (get-path state :content))
|
||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
||||
[ocx ocy] (if (= prefix :c1) [:c2x :c2y] [:c1x :c1y])
|
||||
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)
|
||||
|
||||
(and match-opposite? opposite-index)
|
||||
(update-in [:workspace-local :edit-path id :content-modifiers opposite-index] assoc
|
||||
ocx (- dx) ocy (- dy)))))))
|
||||
|
||||
(defn apply-content-modifiers []
|
||||
(ptk/reify ::apply-content-modifiers
|
||||
|
@ -400,7 +421,7 @@
|
|||
old-selrect (get-in state [:workspace-data :pages-index page-id :objects id :selrect])
|
||||
old-points (get-in state [:workspace-data :pages-index page-id :objects id :points])
|
||||
content-modifiers (get-in state [:workspace-local :edit-path id :content-modifiers])
|
||||
new-content (gsp/apply-content-modifiers old-content content-modifiers)
|
||||
new-content (ugp/apply-content-modifiers old-content content-modifiers)
|
||||
new-selrect (gsh/content->selrect new-content)
|
||||
new-points (gsh/rect->points new-selrect)
|
||||
rch [{:type :mod-obj
|
||||
|
@ -478,46 +499,82 @@
|
|||
(= mode :draw) (rx/of :interrupt)
|
||||
:else (rx/of (finish-path id)))))))
|
||||
|
||||
(defn move-path-point [start-point end-point]
|
||||
(ptk/reify ::move-point
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-path-id state)
|
||||
content (get-in state (get-path state :content))
|
||||
|
||||
{dx :x dy :y} (gpt/subtract end-point start-point)
|
||||
|
||||
handler-indices (-> (ugp/content->handlers content)
|
||||
(get start-point))
|
||||
|
||||
command-for-point (fn [[index command]]
|
||||
(let [point (ugp/command->point command)]
|
||||
(= point start-point)))
|
||||
|
||||
point-indices (->> (d/enumerate content)
|
||||
(filter command-for-point)
|
||||
(map first))
|
||||
|
||||
|
||||
point-reducer (fn [modifiers index]
|
||||
(-> modifiers
|
||||
(assoc-in [index :x] dx)
|
||||
(assoc-in [index :y] dy)))
|
||||
|
||||
handler-reducer (fn [modifiers [index prefix]]
|
||||
(let [cx (ud/prefix-keyword prefix :x)
|
||||
cy (ud/prefix-keyword prefix :y)]
|
||||
(-> modifiers
|
||||
(assoc-in [index cx] dx)
|
||||
(assoc-in [index cy] dy))))
|
||||
|
||||
modifiers (as-> (get-in state [:workspace-local :edit-path id :content-modifiers] {}) $
|
||||
(reduce point-reducer $ point-indices)
|
||||
(reduce handler-reducer $ handler-indices))]
|
||||
|
||||
(assoc-in state [:workspace-local :edit-path id :content-modifiers] modifiers)))))
|
||||
|
||||
(defn start-move-path-point
|
||||
[index]
|
||||
[position]
|
||||
(ptk/reify ::start-move-path-point
|
||||
ptk/WatchEvent
|
||||
;; TODO REWRITE
|
||||
(watch [_ state stream]
|
||||
(let [id (get-in state [:workspace-local :edition])
|
||||
start-point @ms/mouse-position
|
||||
start-delta-x (get-in state [:workspace-local :edit-path id :content-modifiers index :x] 0)
|
||||
start-delta-y (get-in state [:workspace-local :edit-path id :content-modifiers index :y] 0)]
|
||||
(let [stopper (->> stream (rx/filter ms/mouse-up?))]
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
(rx/take-until (->> stream (rx/filter ms/mouse-up?)))
|
||||
(rx/map #(modify-point
|
||||
index
|
||||
(+ start-delta-x (- (:x %) (:x start-point)))
|
||||
(+ start-delta-y (- (:y %) (:y start-point))))))
|
||||
(rx/concat (rx/of (apply-content-modifiers)))
|
||||
)))))
|
||||
(rx/take-until stopper)
|
||||
(rx/map #(move-path-point position %)))
|
||||
(rx/of (apply-content-modifiers)))))))
|
||||
|
||||
(defn start-move-handler
|
||||
[index type]
|
||||
[index prefix]
|
||||
(ptk/reify ::start-move-handler
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [id (get-in state [:workspace-local :edition])
|
||||
[cx cy] (if (= :prev type) [:c2x :c2y] [:c1x :c1y])
|
||||
cidx (if (= :prev type) index (inc index))
|
||||
|
||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
||||
start-point @ms/mouse-position
|
||||
start-delta-x (get-in state [:workspace-local :edit-path id :content-modifiers cidx cx] 0)
|
||||
start-delta-y (get-in state [:workspace-local :edit-path id :content-modifiers cidx cy] 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)]
|
||||
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
(rx/take-until (->> stream (rx/filter ms/mouse-up?)))
|
||||
(rx/map #(modify-handler
|
||||
index
|
||||
type
|
||||
(+ start-delta-x (- (:x %) (:x start-point)))
|
||||
(+ start-delta-y (- (:y %) (:y start-point)))))
|
||||
(rx/with-latest vector ms/mouse-position-alt)
|
||||
(rx/map
|
||||
(fn [[pos alt?]]
|
||||
(modify-handler
|
||||
id
|
||||
index
|
||||
prefix
|
||||
(+ start-delta-x (- (:x pos) (:x start-point)))
|
||||
(+ start-delta-y (- (:y pos) (:y start-point)))
|
||||
(not alt?))))
|
||||
)
|
||||
(rx/concat (rx/of (apply-content-modifiers))))))))
|
||||
|
||||
|
@ -568,13 +625,21 @@
|
|||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :selected] (fnil conj #{}) [index type]))))))
|
||||
|
||||
(defn select-node [index]
|
||||
(defn select-node [position]
|
||||
(ptk/reify ::select-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :selected] (fnil conj #{}) index))))))
|
||||
(update-in [:workspace-local :edit-path id :selected-node] (fnil conj #{}) position))))))
|
||||
|
||||
(defn deselect-node [position]
|
||||
(ptk/reify ::deselect-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(-> state
|
||||
(update-in [:workspace-local :edit-path id :selected-node] (fnil disj #{}) position))))))
|
||||
|
||||
(defn add-to-selection-handler [index type]
|
||||
(ptk/reify ::add-to-selection-handler
|
||||
|
@ -629,5 +694,4 @@
|
|||
(rx/filter (ptk/type? ::finish-path))
|
||||
(rx/take 1)
|
||||
(rx/observe-on :async)
|
||||
(rx/map #(handle-new-shape-result shape-id)))
|
||||
)))))
|
||||
(rx/map #(handle-new-shape-result shape-id))))))))
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
[app.main.ui.workspace.shapes.common :as common]
|
||||
[app.util.geom.path :as ugp]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.icons :as i]))
|
||||
|
||||
|
@ -39,8 +38,6 @@
|
|||
(def white-color "#FFFFFF")
|
||||
(def gray-color "#B1B2B5")
|
||||
|
||||
|
||||
|
||||
(def current-edit-path-ref
|
||||
(let [selfn (fn [local]
|
||||
(let [id (:edition local)]
|
||||
|
@ -85,7 +82,7 @@
|
|||
content-modifiers (mf/deref content-modifiers-ref)
|
||||
editing-id (mf/deref refs/selected-edition)
|
||||
editing? (= editing-id (:id shape))
|
||||
shape (update shape :content gsp/apply-content-modifiers content-modifiers)]
|
||||
shape (update shape :content ugp/apply-content-modifiers content-modifiers)]
|
||||
|
||||
[:> shape-container {:shape shape
|
||||
:pointer-events (when editing? "none")
|
||||
|
@ -122,69 +119,83 @@
|
|||
[:div.viewport-actions-entry {:class (when snap-toggled "is-toggled")} i/nodes-snap]]]))
|
||||
|
||||
|
||||
(mf/defc path-preview [{:keys [zoom command from]}]
|
||||
(when (not= :move-to (:command command))
|
||||
[:path {:style {:fill "transparent"
|
||||
:stroke secondary-color
|
||||
:stroke-width (/ 1 zoom)}
|
||||
:d (ugp/content->path [{:command :move-to
|
||||
:params {:x (:x from)
|
||||
:y (:y from)}}
|
||||
command])}]))
|
||||
|
||||
(mf/defc path-point [{:keys [index position stroke-color fill-color zoom edit-mode selected]}]
|
||||
(mf/defc path-point [{:keys [position zoom edit-mode hover? selected? preview? start-path?]}]
|
||||
(let [{:keys [x y]} position
|
||||
on-click (fn [event]
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (drp/select-node index)))))
|
||||
|
||||
on-mouse-down (fn [event]
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (drp/start-move-path-point index)))))]
|
||||
on-enter
|
||||
(fn [event]
|
||||
(st/emit! (drp/path-pointer-enter position)))
|
||||
|
||||
on-leave
|
||||
(fn [event]
|
||||
(st/emit! (drp/path-pointer-leave position)))
|
||||
|
||||
on-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
|
||||
(cond
|
||||
(and (= edit-mode :move) (not selected?))
|
||||
(st/emit! (drp/select-node position))
|
||||
|
||||
(and (= edit-mode :move) selected?)
|
||||
(st/emit! (drp/deselect-node position))))
|
||||
|
||||
on-mouse-down
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(st/emit! (drp/start-move-path-point position))
|
||||
|
||||
(and (= edit-mode :draw) start-path?)
|
||||
(st/emit! (drp/start-path-from-point position))
|
||||
|
||||
(and (= edit-mode :draw) (not start-path?))
|
||||
(st/emit! (drp/close-path-drag-start position))))]
|
||||
[:g.path-point
|
||||
[:circle.path-point
|
||||
{:cx x
|
||||
:cy y
|
||||
:r (/ 3 zoom)
|
||||
:style { ;; :cursor cur/resize-alt
|
||||
:style {:cursor (when (= edit-mode :draw) cur/pen-node)
|
||||
:stroke-width (/ 1 zoom)
|
||||
:stroke (or stroke-color black-color)
|
||||
:fill (or fill-color white-color)}}]
|
||||
:stroke (cond (or selected? hover?) black-color
|
||||
preview? secondary-color
|
||||
:else primary-color)
|
||||
:fill (cond selected? primary-color
|
||||
:else white-color)}}]
|
||||
[:circle {:cx x
|
||||
:cy y
|
||||
:r (/ 10 zoom)
|
||||
:on-click on-click
|
||||
:on-mouse-down on-mouse-down
|
||||
:style {:fill "transparent"}}]]
|
||||
))
|
||||
:style {:fill "transparent"}}]]))
|
||||
|
||||
(mf/defc path-handler [{:keys [index point handler zoom selected type edit-mode]}]
|
||||
(mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode]}]
|
||||
(when (and point handler)
|
||||
(let [{:keys [x y]} handler
|
||||
on-click (fn [event]
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(drp/select-handler index type))))
|
||||
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]
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (drp/start-move-handler index type)))))]
|
||||
[:g.handler {:class (name type)}
|
||||
on-mouse-down
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
|
||||
(cond
|
||||
(= edit-mode :move)
|
||||
(st/emit! (drp/start-move-handler index prefix))))]
|
||||
|
||||
[:g.handler {:pointer-events (when (= edit-mode :draw))}
|
||||
[:line
|
||||
{:x1 (:x point)
|
||||
:y1 (:y point)
|
||||
|
@ -198,11 +209,12 @@
|
|||
:width (/ 6 zoom)
|
||||
:height (/ 6 zoom)
|
||||
|
||||
:style {;; :cursor cur/resize-alt
|
||||
:style {:cursor cur/pointer-move
|
||||
:stroke-width (/ 1 zoom)
|
||||
:stroke (if selected black-color primary-color)
|
||||
:fill (if selected primary-color white-color)}}]
|
||||
|
||||
:stroke (cond (or selected? hover?) black-color
|
||||
:else primary-color)
|
||||
:fill (cond selected? primary-color
|
||||
:else white-color)}}]
|
||||
[:circle {:cx x
|
||||
:cy y
|
||||
:r (/ 10 zoom)
|
||||
|
@ -210,76 +222,78 @@
|
|||
:on-mouse-down on-mouse-down
|
||||
:style {:fill "transparent"}}]])))
|
||||
|
||||
(mf/defc path-preview [{:keys [zoom command from]}]
|
||||
[:g.preview {:style {:pointer-events "none"}}
|
||||
(when (not= :move-to (:command command))
|
||||
[:path {:style {:fill "transparent"
|
||||
:stroke secondary-color
|
||||
:stroke-width (/ 1 zoom)}
|
||||
:d (ugp/content->path [{:command :move-to
|
||||
:params {:x (:x from)
|
||||
:y (:y from)}}
|
||||
command])}])
|
||||
[:& path-point {:position (:params command)
|
||||
:preview? true
|
||||
:zoom zoom}]])
|
||||
|
||||
(mf/defc path-editor
|
||||
[{:keys [shape zoom]}]
|
||||
|
||||
(let [{:keys [content]} shape
|
||||
edit-path-ref (make-edit-path-ref (:id shape))
|
||||
{:keys [edit-mode selected drag-handler prev-handler preview content-modifiers]} (mf/deref edit-path-ref)
|
||||
(let [edit-path-ref (make-edit-path-ref (:id shape))
|
||||
{:keys [edit-mode selected drag-handler prev-handler preview content-modifiers last-point]} (mf/deref edit-path-ref)
|
||||
{:keys [content]} shape
|
||||
selected (or selected #{})
|
||||
content (gsp/apply-content-modifiers content content-modifiers)
|
||||
points (gsp/content->points content)
|
||||
content (ugp/apply-content-modifiers content content-modifiers)
|
||||
points (->> content ugp/content->points (into #{}))
|
||||
last-command (last content)
|
||||
last-p (last points)]
|
||||
last-p (->> content last ugp/command->point)
|
||||
handlers (ugp/content->handlers content)]
|
||||
|
||||
[:g.path-editor
|
||||
(when (and preview (not drag-handler))
|
||||
[:g.preview {:style {:pointer-events "none"}}
|
||||
[:& path-preview {:command preview
|
||||
:from last-p
|
||||
:zoom zoom}]
|
||||
[:& path-point {:position (:params preview)
|
||||
:fill-color secondary-color
|
||||
:zoom zoom}]])
|
||||
[:& path-preview {:command preview
|
||||
:from last-p
|
||||
:zoom zoom}])
|
||||
|
||||
(for [[index [cmd next]] (d/enumerate (d/with-next content))]
|
||||
(let [point (gpt/point (:params cmd))]
|
||||
[:g.path-node
|
||||
(when (= :curve-to (:command cmd))
|
||||
[:& path-handler {:point point
|
||||
:handler (gpt/point (-> cmd :params :c2x) (-> cmd :params :c2y))
|
||||
:zoom zoom
|
||||
:type :prev
|
||||
:index index
|
||||
:selected (selected [index :prev])
|
||||
:edit-mode edit-mode}])
|
||||
(for [position points]
|
||||
[:g.path-node
|
||||
[:& path-point {:position position
|
||||
:selected? false
|
||||
:zoom zoom
|
||||
:edit-mode edit-mode
|
||||
:start-path? (nil? last-point)}]
|
||||
|
||||
(when (= :curve-to (:command next))
|
||||
[:& path-handler {:point point
|
||||
:handler (gpt/point (-> next :params :c1x) (-> next :params :c1y))
|
||||
:zoom zoom
|
||||
:type :next
|
||||
:index index
|
||||
:selected (selected [index :next])
|
||||
:edit-mode edit-mode}])
|
||||
[: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)]
|
||||
[:& path-handler {:point position
|
||||
:handler handler-position
|
||||
:index index
|
||||
:prefix prefix
|
||||
:zoom zoom
|
||||
:selected? false
|
||||
:hover? false
|
||||
:preview? false
|
||||
:edit-mode edit-mode}]))]])
|
||||
|
||||
(when (and (= index (dec (count content)))
|
||||
prev-handler (not drag-handler))
|
||||
[:& path-handler {:point point
|
||||
:handler prev-handler
|
||||
:zoom zoom
|
||||
:type :prev
|
||||
:index index
|
||||
:selected (selected index)
|
||||
:edit-mode edit-mode}])
|
||||
|
||||
[:& path-point {:position point
|
||||
:stroke-color (when-not (selected index) primary-color)
|
||||
:fill-color (when (selected index) primary-color)
|
||||
:index index
|
||||
(when prev-handler
|
||||
[:g.prev-handler {:pointer-events "none"}
|
||||
[:& path-handler {:point last-p
|
||||
:handler prev-handler
|
||||
:zoom zoom
|
||||
:edit-mode edit-mode}]]))
|
||||
:selected false}]])
|
||||
|
||||
(when drag-handler
|
||||
[:g.drag-handler
|
||||
[:g.drag-handler {:pointer-events "none"}
|
||||
(when (not= :move-to (:command last-command))
|
||||
[:& path-handler {:point last-p
|
||||
:handler (ugp/opposite-handler last-p drag-handler)
|
||||
:zoom zoom
|
||||
:type :drag-opposite
|
||||
:selected false}])
|
||||
[:& path-handler {:point last-p
|
||||
:handler drag-handler
|
||||
:zoom zoom
|
||||
:type :drag
|
||||
:selected false}]])]))
|
||||
|
|
|
@ -248,4 +248,7 @@
|
|||
;; nil
|
||||
;; (throw e#)))))))
|
||||
|
||||
|
||||
(defn prefix-keyword [prefix kw]
|
||||
(let [prefix (if (keyword? prefix) (name prefix) prefix)
|
||||
kw (if (keyword? kw) (name kw) kw)]
|
||||
(keyword (str prefix kw))))
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
(ns app.util.geom.path
|
||||
(:require
|
||||
[cuerdas.core :as str]
|
||||
[app.common.data :as d]
|
||||
[app.util.data :as d]
|
||||
[app.common.data :as cd]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.geom.path-impl-simplify :as impl-simplify]))
|
||||
|
||||
|
@ -208,8 +209,81 @@
|
|||
:c2y (:y h2)}))
|
||||
|
||||
(defn opposite-handler
|
||||
"Calculates the coordinates of the opposite handler"
|
||||
[point handler]
|
||||
(let [phv (gpt/to-vec point handler)
|
||||
opposite (gpt/add point (gpt/negate phv))]
|
||||
opposite))
|
||||
(let [phv (gpt/to-vec point handler)]
|
||||
(gpt/add point (gpt/negate phv))))
|
||||
|
||||
(defn opposite-handler-keep-distance
|
||||
"Calculates the coordinates of the opposite handler but keeping the old distance"
|
||||
[point handler old-opposite]
|
||||
(let [old-distance (gpt/distance point old-opposite)
|
||||
phv (gpt/to-vec point handler)
|
||||
phv2 (gpt/multiply
|
||||
(gpt/unit (gpt/negate phv))
|
||||
(gpt/point old-distance))]
|
||||
(gpt/add point phv2)))
|
||||
|
||||
(defn apply-content-modifiers [content modifiers]
|
||||
(letfn [(apply-to-index [content [index params]]
|
||||
(if (contains? content index)
|
||||
(cond-> content
|
||||
(and
|
||||
(or (:c1x params) (:c1y params) (:c2x params) (:c2y params))
|
||||
(= :line-to (get-in content [index :params :command])))
|
||||
(-> (assoc-in [index :command] :curve-to)
|
||||
(assoc-in [index :params] :curve-to) (make-curve-params
|
||||
(get-in content [index :params])
|
||||
(get-in content [(dec index) :params])))
|
||||
|
||||
(:x params) (update-in [index :params :x] + (:x params))
|
||||
(:y params) (update-in [index :params :y] + (:y params))
|
||||
|
||||
(:c1x params) (update-in [index :params :c1x] + (:c1x params))
|
||||
(:c1y params) (update-in [index :params :c1y] + (:c1y params))
|
||||
|
||||
(:c2x params) (update-in [index :params :c2x] + (:c2x params))
|
||||
(:c2y params) (update-in [index :params :c2y] + (:c2y params)))
|
||||
content))]
|
||||
(reduce apply-to-index content modifiers)))
|
||||
|
||||
(defn command->point [{{:keys [x y]} :params}]
|
||||
(gpt/point x y))
|
||||
|
||||
(defn content->points [content]
|
||||
(->> content
|
||||
(map #(when (-> % :params :x) (gpt/point (-> % :params :x) (-> % :params :y))))
|
||||
(remove nil?)
|
||||
(into [])))
|
||||
|
||||
(defn content->handlers [content]
|
||||
(->> (d/with-prev content) ;; [cmd, prev]
|
||||
(d/enumerate) ;; [idx [cmd, prev]]
|
||||
|
||||
(mapcat (fn [[index [cur-cmd prev-cmd]]]
|
||||
(if (and prev-cmd
|
||||
(= :curve-to (:command cur-cmd)))
|
||||
(let [cur-pos (command->point cur-cmd)
|
||||
pre-pos (command->point prev-cmd)]
|
||||
[[pre-pos [index :c1]]
|
||||
[cur-pos [index :c2]]])
|
||||
[])))
|
||||
|
||||
(group-by first)
|
||||
(cd/mapm #(mapv second %2))))
|
||||
|
||||
(defn opposite-index [content index prefix]
|
||||
(let [point (if (= prefix :c2)
|
||||
(command->point (nth content index))
|
||||
(command->point (nth content (dec index))))
|
||||
|
||||
handlers (-> (content->handlers content)
|
||||
(get point))
|
||||
|
||||
opposite-prefix (if (= prefix :c1) :c2 :c1)
|
||||
|
||||
result (when (<= (count handlers) 2)
|
||||
(->> handlers
|
||||
(d/seek (fn [[index prefix]] (= prefix opposite-prefix)))
|
||||
(first)))]
|
||||
result))
|
||||
|
|
Loading…
Add table
Reference in a new issue