Path edition mode

This commit is contained in:
alonso.torres 2020-11-16 13:00:39 +01:00
parent 275f6e3dc2
commit 8db7078ce8
20 changed files with 436 additions and 108 deletions

View file

@ -32,6 +32,7 @@
[app.main.data.workspace.texts :as dwtxt]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.drawing :as dwd]
[app.main.data.workspace.drawing.path :as dwdp]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.streams :as ms]
@ -1629,6 +1630,8 @@
(def add-shape dwc/add-shape)
(def start-edition-mode dwc/start-edition-mode)
(defn start-path-edit [id] (dwdp/start-path-edit id))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts

View file

@ -99,6 +99,6 @@
(box/handle-drawing-box))))))
;; Export
(def close-drawing-path path/close-drawing-path)
#_(def close-drawing-path path/close-drawing-path)

View file

@ -17,7 +17,9 @@
[app.util.data :as d]
[app.util.geom.path :as ugp]
[app.main.streams :as ms]
[app.main.data.workspace.drawing.common :as common]))
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.drawing.common :as common]
[app.common.geom.shapes.path :as gsp]))
;;;;
@ -149,14 +151,14 @@
(update [_ state]
(-> state
(assoc-in [:workspace-drawing :object :initialized?] true)
(assoc-in [:workspace-drawing :object :last-point] nil)))))
(assoc-in [:workspace-local :edit-path :last-point] nil)))))
(defn finish-path []
(ptk/reify ::finish-path
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-drawing :object :last-point] nil)
(update :workspace-local dissoc :edit-path)
(update-in [:workspace-drawing :object] calculate-selrect)))))
(defn preview-next-point [{:keys [x y]}]
@ -164,9 +166,9 @@
ptk/UpdateEvent
(update [_ state]
(let [position (gpt/point x y)
{:keys [last-point prev-handler] :as shape} (get-in state [:workspace-drawing :object])
{:keys [last-point prev-handler] :as shape} (get-in state [:workspace-local :edit-path])
command (next-node shape position last-point prev-handler)]
(assoc-in state [:workspace-drawing :object :preview] command)))))
(assoc-in state [:workspace-local :edit-path :preview] command)))))
(defn add-node [{:keys [x y]}]
(ptk/reify ::add-node
@ -174,14 +176,11 @@
(update [_ state]
(let [position (gpt/point x y)
{:keys [last-point prev-handler]} (get-in state [:workspace-drawing :object])]
(update-in
state
[:workspace-drawing :object]
#(-> %
(append-node position last-point prev-handler)
(assoc :last-point position)
(dissoc :prev-handler)))))))
{:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path])]
(-> state
(assoc-in [:workspace-local :edit-path :last-point] position)
(update-in [:workspace-local :edit-path] dissoc :prev-handler)
(update-in [:workspace-drawing :object] append-node position last-point prev-handler))))))
(defn drag-handler [{:keys [x y]}]
(ptk/reify ::drag-handler
@ -193,16 +192,16 @@
index (dec (count (:content shape)))]
(-> state
(update-in [:workspace-drawing :object] move-handler index :next true position)
(assoc-in [:workspace-drawing :object :drag-handler] position))))))
(assoc-in [:workspace-local :edit-path :drag-handler] position))))))
(defn finish-drag []
(ptk/reify ::finish-drag
ptk/UpdateEvent
(update [_ state]
(let [handler (get-in state [:workspace-drawing :object :drag-handler])]
(let [handler (get-in state [:workspace-local :edit-path :drag-handler])]
(-> state
(update-in [:workspace-drawing :object] dissoc :drag-handler)
(assoc-in [:workspace-drawing :object :prev-handler] handler))))))
(update-in [:workspace-local :edit-path] dissoc :drag-handler)
(assoc-in [:workspace-local :edit-path :prev-handler] handler))))))
(defn make-click-stream
[stream down-event]
@ -238,7 +237,6 @@
ptk/WatchEvent
(watch [_ state stream]
;; clicks stream<[MouseEvent, Position]>
(let [mouse-down (->> stream (rx/filter ms/mouse-down?))
finish-events (->> stream (rx/filter finish-event?))
@ -266,10 +264,7 @@
(rx/merge mousemove-events
mousedown-events)
(rx/of (finish-path))
(rx/of common/handle-finish-drawing)))
)))
(rx/of common/handle-finish-drawing))))))
#_(def handle-drawing-path
(ptk/reify ::handle-drawing-path
@ -331,7 +326,7 @@
(rx/of finish-drawing-path
common/handle-finish-drawing)))))))
(defn close-drawing-path []
#_(defn close-drawing-path []
(ptk/reify ::close-drawing-path
ptk/UpdateEvent
(update [_ state]
@ -340,3 +335,121 @@
ptk/WatchEvent
(watch [_ state stream]
(rx/of ::end-path-drawing))))
(defn stop-path-edit []
(ptk/reify ::stop-path-edit
ptk/UpdateEvent
(update [_ state]
(update state :workspace-local dissoc :edit-path))))
(defn start-path-edit
[id]
(ptk/reify ::start-path-edit
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :edit-path] {}))
ptk/WatchEvent
(watch [_ state stream]
(->> stream
(rx/filter #(= % :interrupt))
(rx/take 1)
(rx/map #(stop-path-edit))))))
(defn modify-point [index dx dy]
(ptk/reify ::modify-point
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:workspace-local :edit-path :content-modifiers (inc index)] assoc
:c1x dx :c1y dy)
(update-in [:workspace-local :edit-path :content-modifiers index] assoc
:x dx :y dy :c2x dx :c2y dy)
))))
(defn modify-handler [index type dx dy]
(ptk/reify ::modify-point
ptk/UpdateEvent
(update [_ state]
(let [s1 (if (= type :prev) -1 1)
s2 (if (= type :prev) 1 -1)]
(-> state
(update-in [:workspace-local :edit-path :content-modifiers (inc index)] assoc
:c1x (* s1 dx) :c1y (* s1 dy))
(update-in [:workspace-local :edit-path :content-modifiers index] assoc
:c2x (* s2 dx) :c2y (* s2 dy) ))
))))
(defn apply-content-modifiers []
(ptk/reify ::apply-content-modifiers
;;ptk/UpdateEvent
;;(update [_ state]
;; (update-in state [:workspace-local :edit-path] dissoc :content-modifiers))
ptk/WatchEvent
(watch [_ state stream]
(let [id (get-in state [:workspace-local :edition])
page-id (:current-page-id state)
old-content (get-in state [:workspace-data :pages-index page-id :objects id :content])
old-selrect (get-in state [:workspace-data :pages-index page-id :objects id :selrect])
content-modifiers (get-in state [:workspace-local :edit-path :content-modifiers])
new-content (gsp/apply-content-modifiers old-content content-modifiers)
new-selrect (gsh/content->selrect new-content)
rch [{:type :mod-obj
:id id
:page-id page-id
:operations [{:type :set :attr :content :val new-content}
{:type :set :attr :selrect :val new-selrect}]}]
uch [{:type :mod-obj
:id id
:page-id page-id
:operations [{:type :set :attr :content :val old-content}
{:type :set :attr :selrect :val old-selrect}]}]]
(rx/of (dwc/commit-changes rch uch {:commit-local? true})
(fn [state] (update-in state [:workspace-local :edit-path] dissoc :content-modifiers)))))))
(defn start-move-path-point
[index]
(ptk/reify ::start-move-path-point
ptk/WatchEvent
(watch [_ state stream]
(let [start-point @ms/mouse-position
start-delta-x (get-in state [:workspace-local :edit-path :content-modifiers index :x] 0)
start-delta-y (get-in state [:workspace-local :edit-path :content-modifiers index :y] 0)]
(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)))
)))))
(defn start-move-handler
[index type]
(ptk/reify ::start-move-handler
ptk/WatchEvent
(watch [_ state stream]
(let [[cx cy] (if (= :prev type) [:c2x :c2y] [:c1x :c1y])
cidx (if (= :prev type) index (inc index))
start-point @ms/mouse-position
start-delta-x (get-in state [:workspace-local :edit-path :content-modifiers cidx cx] 0)
start-delta-y (get-in state [:workspace-local :edit-path :content-modifiers cidx 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/concat (rx/of (apply-content-modifiers))))))))

View file

@ -128,6 +128,14 @@
(def checkbox-checked (icon-xref :checkbox-checked))
(def checkbox-unchecked (icon-xref :checkbox-unchecked))
(def code (icon-xref :code))
(def nodes-add (icon-xref :nodes-add))
(def nodes-corner (icon-xref :nodes-corner))
(def nodes-curve (icon-xref :nodes-curve))
(def nodes-join (icon-xref :nodes-join))
(def nodes-merge (icon-xref :nodes-merge))
(def nodes-remove (icon-xref :nodes-remove))
(def nodes-separate (icon-xref :nodes-separate))
(def nodes-snap (icon-xref :nodes-snap))
(def loader-pencil
(mf/html

View file

@ -29,7 +29,7 @@
[app.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
[app.main.ui.workspace.scroll :as scroll]
[app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
[app.main.ui.workspace.viewport :refer [viewport coordinates]]
[app.main.ui.workspace.viewport :refer [viewport viewport-actions coordinates]]
[app.util.dom :as dom]
[beicon.core :as rx]
[cuerdas.core :as str]
@ -65,6 +65,7 @@
(when (contains? layout :rules)
[:& workspace-rules {:local local}])
[:& viewport-actions]
[:& viewport {:file file
:local local
:layout layout}]]]

View file

@ -31,7 +31,8 @@
[app.common.geom.matrix :as gmt]
[app.util.debug :refer [debug?]]
[app.main.ui.workspace.shapes.outline :refer [outline]]
[app.main.ui.measurements :as msr]))
[app.main.ui.measurements :as msr]
[app.main.ui.workspace.shapes.path :refer [path-editor]]))
(def rotation-handler-size 25)
(def resize-point-radius 4)
@ -366,8 +367,13 @@
[:& text-edition-selection-handlers {:shape shape
:zoom zoom
:color color}]
(and (or (= type :path)
(= type :curve))
(= (= type :path)
(= edition (:id shape)))
[:& path-editor {:zoom zoom
:shape shape}]
(and (= type :curve)
(= edition (:id shape)))
[:& path-edition-selection-handlers {:shape shape
:zoom zoom

View file

@ -23,6 +23,7 @@
(defn- on-mouse-down
[event {:keys [id type] :as shape}]
(let [selected @refs/selected-shapes
edition @refs/selected-edition
selected? (contains? selected id)
drawing? @refs/selected-drawing-tool
button (.-which (.-nativeEvent event))]
@ -35,9 +36,8 @@
nil
(= type :frame)
(when selected?
(dom/stop-propagation event)
(st/emit! (dw/start-move-selected)))
(do (dom/stop-propagation event)
(st/emit! (dw/start-move-selected)))
:else
(do
@ -50,7 +50,8 @@
(st/emit! (dw/deselect-all)))
(st/emit! (dw/select-shape id))))
(st/emit! (dw/start-move-selected)))))))
(when (not= edition id)
(st/emit! (dw/start-move-selected))))))))
(defn on-context-menu
[event shape]

View file

@ -10,22 +10,40 @@
(ns app.main.ui.workspace.shapes.path
(:require
[rumext.alpha :as mf]
[app.common.data :as d]
[okulary.core :as l]
[app.util.data :as d]
[app.util.dom :as dom]
[app.util.timers :as ts]
[app.main.refs :as refs]
[app.main.streams :as ms]
[app.main.constants :as c]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.data.workspace :as dw]
[app.main.data.workspace.drawing :as dr]
[app.main.data.workspace.drawing.path :as drp]
[app.main.ui.keyboard :as kbd]
[app.main.ui.shapes.path :as path]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.common :as common]
[app.util.geom.path :as ugp]
[app.common.geom.shapes.path :as gsp]))
[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]))
(def primary-color "#1FDEA7")
(def secondary-color "#DB00FF")
(def black-color "#000000")
(def white-color "#FFFFFF")
(def gray-color "#B1B2B5")
(def edit-path-ref
(l/derived :edit-path refs/workspace-local))
(def content-modifiers-ref
(l/derived :content-modifiers edit-path-ref))
(mf/defc path-wrapper
{::mf/wrap-props false}
@ -43,12 +61,15 @@
on-double-click (mf/use-callback
(mf/deps shape)
(fn [event]
(prn "?? PATH")
(when (and (not (::dr/initialized? shape)) (hover? (:id shape)))
(when (not (::dr/initialized? shape))
(do
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! (dw/start-edition-mode (:id shape)))))))]
(st/emit! (dw/start-edition-mode (:id shape))
(dw/start-path-edit (:id shape)))))))
content-modifiers (mf/deref content-modifiers-ref)
shape (update shape :content gsp/apply-content-modifiers content-modifiers)]
[:> shape-container {:shape shape
:on-double-click on-double-click
@ -57,75 +78,142 @@
[:& path/path-shape {:shape shape
:background? true}]]))
(mf/defc path-actions [{:keys [shape]}]
[:div.path-actions
[:div.viewport-actions-group
[:div.viewport-actions-entry i/nodes-add]
[:div.viewport-actions-entry i/nodes-remove]]
(mf/defc path-handler [{:keys [point handler zoom selected]}]
[:div.viewport-actions-group
[:div.viewport-actions-entry i/nodes-merge]
[:div.viewport-actions-entry i/nodes-join]
[:div.viewport-actions-entry i/nodes-separate]]
[:div.viewport-actions-group
[:div.viewport-actions-entry i/nodes-corner]
[:div.viewport-actions-entry i/nodes-curve]]
[:div.viewport-actions-group
[:div.viewport-actions-entry 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]}]
(let [{:keys [x y]} position
on-click (fn [event]
(dom/stop-propagation event)
(dom/prevent-default event))
on-mouse-down (fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! (drp/start-move-path-point index)))]
[:circle
{:cx x
:cy y
:r (/ 3 zoom)
:on-click on-click
:on-mouse-down on-mouse-down
:style {:cursor cur/resize-alt
:stroke-width (/ 1 zoom)
:stroke (or stroke-color black-color)
:fill (or fill-color white-color)}}]))
(mf/defc path-handler [{:keys [index point handler zoom selected type]}]
(when (and point handler)
(let [{:keys [x y]} handler]
[:g.handler
(let [{:keys [x y]} handler
on-click (fn [event]
(dom/stop-propagation event)
(dom/prevent-default event))
on-mouse-down (fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! (drp/start-move-handler index type)))]
[:g.handler {:class (name type)}
[:line
{:x1 (:x point)
:y1 (:y point)
:x2 x
:y2 y
:style {:stroke "#B1B2B5"
:style {:stroke gray-color
:stroke-width (/ 1 zoom)}}]
[:rect
{:x (- x (/ 3 zoom))
:y (- y (/ 3 zoom))
:width (/ 6 zoom)
:height (/ 6 zoom)
:style {:stroke-width (/ 1 zoom)
:stroke (if selected "#000000" "#1FDEA7")
:fill (if selected "#1FDEA7" "#FFFFFF")}}]])))
:on-click on-click
:on-mouse-down on-mouse-down
:style {:cursor cur/resize-alt
:stroke-width (/ 1 zoom)
:stroke (if selected black-color primary-color)
:fill (if selected primary-color white-color)}}]])))
(mf/defc path-editor
[{:keys [shape zoom]}]
(let [points (gsp/content->points (:content shape))
drag-handler (:drag-handler shape)
prev-handler (:prev-handler shape)
last-command (last (:content shape))
selected false
(let [{:keys [content]} shape
{:keys [drag-handler prev-handler preview content-modifiers]} (mf/deref edit-path-ref)
content (gsp/apply-content-modifiers content content-modifiers)
points (gsp/content->points content)
last-command (last content)
last-p (last points)
handlers (ugp/extract-handlers (:content shape))
handlers (if (and prev-handler (not drag-handler))
(conj handlers {:point last-p :prev prev-handler})
handlers)
]
selected false]
[:g.path-editor
(when (and (:preview shape) (not (:drag-handler shape)))
[:*
[:path {:style {:fill "transparent"
:stroke "#DB00FF"
:stroke-width (/ 1 zoom)}
:d (ugp/content->path [{:command :move-to
:params {:x (:x last-p)
:y (:y last-p)}}
(:preview shape)])}]
[:circle
{:cx (-> shape :preview :params :x)
:cy (-> shape :preview :params :y)
:r (/ 3 zoom)
:style {:stroke-width (/ 1 zoom)
:stroke "#DB00FF"
:fill "#FFFFFF"}}]])
(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}]])
(for [{:keys [point prev next]} handlers]
[:*
[:& path-handler {:point point
:handler prev
:zoom zoom
:type :prev
:selected false}]
[:& path-handler {:point point
:handler next
:zoom zoom
:type :next
:selected false}]])
(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 false}])
(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 false}])
(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 false}])
[:& path-point {:position point
:stroke-color (when-not selected primary-color)
:fill-color (when selected primary-color)
:index index
:zoom zoom}]]))
(when drag-handler
[:*
[:g.drag-handler
(when (not= :move-to (:command last-command))
[:& path-handler {:point last-p
:handler (ugp/opposite-handler last-p drag-handler)
@ -136,14 +224,4 @@
:handler drag-handler
:zoom zoom
:type :drag
:selected false}]])
(for [{:keys [x y] :as point} points]
[:circle
{:cx x
:cy y
:r (/ 3 zoom)
:style {:stroke-width (/ 1 zoom)
:stroke (if selected "#000000" "#1FDEA7")
:fill (if selected "#1FDEA7" "#FFFFFF")}
}])]))
:selected false}]])]))

View file

@ -1,4 +1,4 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
@ -52,7 +52,8 @@
[goog.events :as events]
[potok.core :as ptk]
[promesa.core :as p]
[rumext.alpha :as mf])
[rumext.alpha :as mf]
[app.main.ui.workspace.shapes.path :refer [path-actions]])
(:import goog.events.EventType))
;; --- Coordinates Widget
@ -222,7 +223,6 @@
drawing-obj (:object drawing)
zoom (or zoom 1)
on-mouse-down
(mf/use-callback
(mf/deps drawing-tool edition)
@ -234,15 +234,13 @@
alt? (kbd/alt? event)]
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt?))
(cond
(and (= 1 (.-which event)))
(and (= 1 (.-which event)) (not edition))
(if drawing-tool
(when (not (#{:comments :path} drawing-tool))
(st/emit! (dd/start-drawing drawing-tool)))
(st/emit! dw/handle-selection))
(and (not edition)
(= 2 (.-which event)))
(and (= 2 (.-which event)))
(handle-viewport-positioning viewport-ref)))))
on-context-menu
@ -294,12 +292,16 @@
on-double-click
(mf/use-callback
(mf/deps edition)
(fn [event]
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)]
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?)))))
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?))
(if edition
(st/emit! dw/clear-edition-mode)))))
on-key-down
(mf/use-callback
@ -610,3 +612,13 @@
(when (= options-mode :prototype)
[:& interactions {:selected selected}])]]))
(mf/defc viewport-actions []
(let [edition (mf/deref refs/selected-edition)
selected (mf/deref refs/selected-objects)
shape (-> selected first)]
(when (and (= (count selected) 1)
(= (:id shape) edition)
(= :path (:type shape)))
[:div.viewport-actions
[:& path-actions {:shape shape}]])))

View file

@ -213,9 +213,3 @@
opposite (gpt/add point (gpt/negate phv))]
opposite))
(defn extract-handlers [content]
(let [extract (fn [{param1 :params :as cmd1} {param2 :params :as cmd2}]
{:point (gpt/point (:x param1) (:y param1))
:prev (when (:c2x param1) (gpt/point (:c2x param1) (:c2y param1)))
:next (when (:c1x param2) (gpt/point (:c1x param2) (:c1y param2)))})]
(map extract content (d/concat [] (rest content) [nil]))))