mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 21:31:37 +02:00
♻️ Refactor drawing actions
This commit is contained in:
parent
f170c7546b
commit
d25dbfb09b
9 changed files with 341 additions and 267 deletions
|
@ -1150,7 +1150,6 @@
|
||||||
(us/verify ::us/uuid id)
|
(us/verify ::us/uuid id)
|
||||||
(us/verify ::us/integer index)
|
(us/verify ::us/integer index)
|
||||||
(us/verify gpt/point? delta)
|
(us/verify gpt/point? delta)
|
||||||
(js/alert "TODO: broken")
|
|
||||||
#_(ptk/reify ::update-path
|
#_(ptk/reify ::update-path
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
|
|
|
@ -12,24 +12,14 @@
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[app.common.geom.point :as gpt]
|
|
||||||
[app.common.geom.shapes :as geom]
|
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.common.pages-helpers :as cph]
|
[app.main.data.workspace.drawing.common :as common]
|
||||||
[app.common.uuid :as uuid]
|
[app.main.data.workspace.drawing.path :as path]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace.drawing.curve :as curve]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.drawing.box :as box]))
|
||||||
[app.main.snap :as snap]
|
|
||||||
[app.main.streams :as ms]
|
|
||||||
[app.util.geom.path :as path]))
|
|
||||||
|
|
||||||
(declare handle-drawing)
|
(declare handle-drawing)
|
||||||
(declare handle-drawing-generic)
|
|
||||||
(declare handle-drawing-path)
|
|
||||||
(declare handle-drawing-curve)
|
|
||||||
(declare handle-finish-drawing)
|
|
||||||
(declare conditional-align)
|
|
||||||
|
|
||||||
;; NOTE/TODO: when an exception is raised in some point of drawing the
|
;; NOTE/TODO: when an exception is raised in some point of drawing the
|
||||||
;; draw lock is not released so the user need to refresh in order to
|
;; draw lock is not released so the user need to refresh in order to
|
||||||
|
@ -38,20 +28,22 @@
|
||||||
(defn start-drawing
|
(defn start-drawing
|
||||||
[type]
|
[type]
|
||||||
{:pre [(keyword? type)]}
|
{:pre [(keyword? type)]}
|
||||||
(let [id (uuid/next)]
|
(let [lock-id (uuid/next)]
|
||||||
(ptk/reify ::start-drawing
|
(ptk/reify ::start-drawing
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:workspace-drawing :lock] #(if (nil? %) id %)))
|
(update-in state [:workspace-drawing :lock] #(if (nil? %) lock-id %)))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [lock (get-in state [:workspace-drawing :lock])]
|
(let [lock (get-in state [:workspace-drawing :lock])]
|
||||||
(when (= lock id)
|
(when (= lock lock-id)
|
||||||
(rx/merge (->> (rx/filter #(= % handle-finish-drawing) stream)
|
(rx/merge
|
||||||
(rx/take 1)
|
(rx/of (handle-drawing type))
|
||||||
(rx/map (fn [_] #(update % :workspace-drawing dissoc :lock))))
|
(->> stream
|
||||||
(rx/of (handle-drawing type)))))))))
|
(rx/filter (ptk/type? ::common/handle-finish-drawing) )
|
||||||
|
(rx/first)
|
||||||
|
(rx/map #(fn [state] (update state :workspace-drawing dissoc :lock)))))))))))
|
||||||
|
|
||||||
(defn handle-drawing
|
(defn handle-drawing
|
||||||
[type]
|
[type]
|
||||||
|
@ -63,248 +55,13 @@
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(case type
|
(rx/of (case type
|
||||||
:path (rx/of handle-drawing-path)
|
:path
|
||||||
:curve (rx/of handle-drawing-curve)
|
path/handle-drawing-path
|
||||||
(rx/of handle-drawing-generic)))))
|
|
||||||
|
|
||||||
(def handle-drawing-generic
|
:curve
|
||||||
(letfn [(resize-shape [{:keys [x y width height] :as shape} point lock? point-snap]
|
curve/handle-drawing-curve
|
||||||
(let [;; The new shape behaves like a resize on the bottom-right corner
|
|
||||||
initial (gpt/point (+ x width) (+ y height))
|
|
||||||
shapev (gpt/point width height)
|
|
||||||
deltav (gpt/to-vec initial point-snap)
|
|
||||||
scalev (gpt/divide (gpt/add shapev deltav) shapev)
|
|
||||||
scalev (if lock?
|
|
||||||
(let [v (max (:x scalev) (:y scalev))]
|
|
||||||
(gpt/point v v))
|
|
||||||
scalev)]
|
|
||||||
(-> shape
|
|
||||||
(assoc ::click-draw? false)
|
|
||||||
(assoc-in [:modifiers :resize-vector] scalev)
|
|
||||||
(assoc-in [:modifiers :resize-origin] (gpt/point x y))
|
|
||||||
(assoc-in [:modifiers :resize-rotation] 0))))
|
|
||||||
|
|
||||||
(update-drawing [state point lock? point-snap]
|
;; default
|
||||||
(update-in state [:workspace-drawing :object] resize-shape point lock? point-snap))]
|
box/handle-drawing-box)))))
|
||||||
|
|
||||||
(ptk/reify ::handle-drawing-generic
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [{:keys [flags]} (:workspace-local state)
|
|
||||||
|
|
||||||
stoper? #(or (ms/mouse-up? %) (= % :interrupt))
|
|
||||||
stoper (rx/filter stoper? stream)
|
|
||||||
initial @ms/mouse-position
|
|
||||||
|
|
||||||
|
|
||||||
page-id (:current-page-id state)
|
|
||||||
objects (dwc/lookup-page-objects state page-id)
|
|
||||||
layout (get state :workspace-layout)
|
|
||||||
|
|
||||||
frames (cph/select-frames objects)
|
|
||||||
fid (or (->> frames
|
|
||||||
(filter #(geom/has-point? % initial))
|
|
||||||
first
|
|
||||||
:id)
|
|
||||||
uuid/zero)
|
|
||||||
|
|
||||||
shape (-> state
|
|
||||||
(get-in [:workspace-drawing :object])
|
|
||||||
(geom/setup {:x (:x initial) :y (:y initial) :width 1 :height 1})
|
|
||||||
(assoc :frame-id fid)
|
|
||||||
(assoc ::initialized? true)
|
|
||||||
(assoc ::click-draw? true))]
|
|
||||||
(rx/concat
|
|
||||||
;; Add shape to drawing state
|
|
||||||
(rx/of #(assoc-in state [:workspace-drawing :object] shape))
|
|
||||||
|
|
||||||
;; Initial SNAP
|
|
||||||
(->> (snap/closest-snap-point page-id [shape] layout initial)
|
|
||||||
(rx/map (fn [{:keys [x y]}]
|
|
||||||
#(update-in % [:workspace-drawing :object] assoc :x x :y y))))
|
|
||||||
|
|
||||||
(->> ms/mouse-position
|
|
||||||
(rx/filter #(> (gpt/distance % initial) 2))
|
|
||||||
(rx/with-latest vector ms/mouse-position-ctrl)
|
|
||||||
(rx/switch-map
|
|
||||||
(fn [[point :as current]]
|
|
||||||
(->> (snap/closest-snap-point page-id [shape] layout point)
|
|
||||||
(rx/map #(conj current %)))))
|
|
||||||
(rx/map
|
|
||||||
(fn [[pt ctrl? point-snap]]
|
|
||||||
#(update-drawing % pt ctrl? point-snap)))
|
|
||||||
|
|
||||||
(rx/take-until stoper))
|
|
||||||
(rx/of handle-finish-drawing)))))))
|
|
||||||
|
|
||||||
(def handle-drawing-path
|
|
||||||
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
|
||||||
(or (= event :path/end-path-drawing)
|
|
||||||
(= event :interrupt)
|
|
||||||
(and (ms/mouse-event? event)
|
|
||||||
(or (= type :double-click)
|
|
||||||
(= type :context-menu)))
|
|
||||||
(and (ms/keyboard-event? event)
|
|
||||||
(= type :down)
|
|
||||||
(= 13 (:key event)))))
|
|
||||||
|
|
||||||
(initialize-drawing [state point]
|
|
||||||
(-> state
|
|
||||||
(assoc-in [:workspace-drawing :object :segments] [point point])
|
|
||||||
(assoc-in [:workspace-drawing :object ::initialized?] true)))
|
|
||||||
|
|
||||||
(insert-point-segment [state point]
|
|
||||||
(-> state
|
|
||||||
(update-in [:workspace-drawing :object :segments] (fnil conj []) point)))
|
|
||||||
|
|
||||||
(update-point-segment [state index point]
|
|
||||||
(let [segments (count (get-in state [:workspace-drawing :object :segments]))
|
|
||||||
exists? (< -1 index segments)]
|
|
||||||
(cond-> state
|
|
||||||
exists? (assoc-in [:workspace-drawing :object :segments index] point))))
|
|
||||||
|
|
||||||
(finish-drawing-path [state]
|
|
||||||
(update-in
|
|
||||||
state [:workspace-drawing :object]
|
|
||||||
(fn [shape] (-> shape
|
|
||||||
(update :segments #(vec (butlast %)))
|
|
||||||
(geom/update-path-selrect)))))]
|
|
||||||
|
|
||||||
(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
|
|
||||||
handle-finish-drawing))))))))
|
|
||||||
|
|
||||||
(def simplify-tolerance 0.3)
|
|
||||||
|
|
||||||
(def handle-drawing-curve
|
|
||||||
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
|
||||||
(ms/mouse-event? event) (= type :up))
|
|
||||||
|
|
||||||
(initialize-drawing [state]
|
|
||||||
(assoc-in state [:workspace-drawing :object ::initialized?] true))
|
|
||||||
|
|
||||||
(insert-point-segment [state point]
|
|
||||||
(update-in state [:workspace-drawing :object :segments] (fnil conj []) point))
|
|
||||||
|
|
||||||
(finish-drawing-curve [state]
|
|
||||||
(update-in
|
|
||||||
state [:workspace-drawing :object]
|
|
||||||
(fn [shape]
|
|
||||||
(-> shape
|
|
||||||
(update :segments #(path/simplify % simplify-tolerance))
|
|
||||||
(geom/update-path-selrect)))))]
|
|
||||||
|
|
||||||
(ptk/reify ::handle-drawing-curve
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [{:keys [flags]} (:workspace-local state)
|
|
||||||
stoper (rx/filter stoper-event? stream)
|
|
||||||
mouse (rx/sample 10 ms/mouse-position)]
|
|
||||||
(rx/concat
|
|
||||||
(rx/of initialize-drawing)
|
|
||||||
(->> mouse
|
|
||||||
(rx/map (fn [pt] #(insert-point-segment % pt)))
|
|
||||||
(rx/take-until stoper))
|
|
||||||
(rx/of finish-drawing-curve
|
|
||||||
handle-finish-drawing)))))))
|
|
||||||
|
|
||||||
(def handle-finish-drawing
|
|
||||||
(ptk/reify ::handle-finish-drawing
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [shape (get-in state [:workspace-drawing :object])]
|
|
||||||
(rx/concat
|
|
||||||
(rx/of dw/clear-drawing)
|
|
||||||
(when (::initialized? shape)
|
|
||||||
(let [shape-click-width (case (:type shape)
|
|
||||||
:text 3
|
|
||||||
20)
|
|
||||||
shape-click-height (case (:type shape)
|
|
||||||
:text 16
|
|
||||||
20)
|
|
||||||
shape (if (::click-draw? shape)
|
|
||||||
(-> shape
|
|
||||||
(assoc-in [:modifiers :resize-vector]
|
|
||||||
(gpt/point shape-click-width shape-click-height))
|
|
||||||
(assoc-in [:modifiers :resize-origin]
|
|
||||||
(gpt/point (:x shape) (:y shape))))
|
|
||||||
shape)
|
|
||||||
|
|
||||||
shape (cond-> shape
|
|
||||||
(= (:type shape) :text) (assoc :grow-type
|
|
||||||
(if (::click-draw? shape) :auto-width :fixed)))
|
|
||||||
|
|
||||||
shape (-> shape
|
|
||||||
geom/transform-shape
|
|
||||||
(dissoc ::initialized? ::click-draw?))]
|
|
||||||
;; Add & select the created shape to the workspace
|
|
||||||
(rx/concat
|
|
||||||
(if (= :text (:type shape))
|
|
||||||
(rx/of dwc/start-undo-transaction)
|
|
||||||
(rx/empty))
|
|
||||||
|
|
||||||
(rx/of (dw/deselect-all)
|
|
||||||
(dw/add-shape shape))))))))))
|
|
||||||
|
|
||||||
(def close-drawing-path
|
|
||||||
(ptk/reify ::close-drawing-path
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc-in state [:workspace-drawing :object :close?] true))))
|
|
||||||
|
|
||||||
|
|
91
frontend/src/app/main/data/workspace/drawing/box.cljs
Normal file
91
frontend/src/app/main/data/workspace/drawing/box.cljs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.drawing.box
|
||||||
|
(:require
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.common.pages-helpers :as cph]
|
||||||
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.snap :as snap]
|
||||||
|
[app.main.streams :as ms]
|
||||||
|
[app.main.data.workspace.drawing.common :as common]))
|
||||||
|
|
||||||
|
(def handle-drawing-box
|
||||||
|
(letfn [(resize-shape [{:keys [x y width height] :as shape} point lock? point-snap]
|
||||||
|
(let [;; The new shape behaves like a resize on the bottom-right corner
|
||||||
|
initial (gpt/point (+ x width) (+ y height))
|
||||||
|
shapev (gpt/point width height)
|
||||||
|
deltav (gpt/to-vec initial point-snap)
|
||||||
|
scalev (gpt/divide (gpt/add shapev deltav) shapev)
|
||||||
|
scalev (if lock?
|
||||||
|
(let [v (max (:x scalev) (:y scalev))]
|
||||||
|
(gpt/point v v))
|
||||||
|
scalev)]
|
||||||
|
(-> shape
|
||||||
|
(assoc :click-draw? false)
|
||||||
|
(assoc-in [:modifiers :resize-vector] scalev)
|
||||||
|
(assoc-in [:modifiers :resize-origin] (gpt/point x y))
|
||||||
|
(assoc-in [:modifiers :resize-rotation] 0))))
|
||||||
|
|
||||||
|
(update-drawing [state point lock? point-snap]
|
||||||
|
(update-in state [:workspace-drawing :object] resize-shape point lock? point-snap))]
|
||||||
|
|
||||||
|
(ptk/reify ::handle-drawing-box
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [{:keys [flags]} (:workspace-local state)
|
||||||
|
|
||||||
|
stoper? #(or (ms/mouse-up? %) (= % :interrupt))
|
||||||
|
stoper (rx/filter stoper? stream)
|
||||||
|
initial @ms/mouse-position
|
||||||
|
|
||||||
|
|
||||||
|
page-id (:current-page-id state)
|
||||||
|
objects (dwc/lookup-page-objects state page-id)
|
||||||
|
layout (get state :workspace-layout)
|
||||||
|
|
||||||
|
frames (cph/select-frames objects)
|
||||||
|
fid (or (->> frames
|
||||||
|
(filter #(gsh/has-point? % initial))
|
||||||
|
first
|
||||||
|
:id)
|
||||||
|
uuid/zero)
|
||||||
|
|
||||||
|
shape (-> state
|
||||||
|
(get-in [:workspace-drawing :object])
|
||||||
|
(gsh/setup {:x (:x initial) :y (:y initial) :width 1 :height 1})
|
||||||
|
(assoc :frame-id fid)
|
||||||
|
(assoc :initialized? true)
|
||||||
|
(assoc :click-draw? true))]
|
||||||
|
(rx/concat
|
||||||
|
;; Add shape to drawing state
|
||||||
|
(rx/of #(assoc-in state [:workspace-drawing :object] shape))
|
||||||
|
|
||||||
|
;; Initial SNAP
|
||||||
|
(->> (snap/closest-snap-point page-id [shape] layout initial)
|
||||||
|
(rx/map (fn [{:keys [x y]}]
|
||||||
|
#(update-in % [:workspace-drawing :object] assoc :x x :y y))))
|
||||||
|
|
||||||
|
(->> ms/mouse-position
|
||||||
|
(rx/filter #(> (gpt/distance % initial) 2))
|
||||||
|
(rx/with-latest vector ms/mouse-position-ctrl)
|
||||||
|
(rx/switch-map
|
||||||
|
(fn [[point :as current]]
|
||||||
|
(->> (snap/closest-snap-point page-id [shape] layout point)
|
||||||
|
(rx/map #(conj current %)))))
|
||||||
|
(rx/map
|
||||||
|
(fn [[pt ctrl? point-snap]]
|
||||||
|
#(update-drawing % pt ctrl? point-snap)))
|
||||||
|
|
||||||
|
(rx/take-until stoper))
|
||||||
|
(rx/of common/handle-finish-drawing)))))))
|
56
frontend/src/app/main/data/workspace/drawing/common.cljs
Normal file
56
frontend/src/app/main/data/workspace/drawing/common.cljs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.drawing.common
|
||||||
|
(:require
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.streams :as ms]))
|
||||||
|
|
||||||
|
(def handle-finish-drawing
|
||||||
|
(ptk/reify ::handle-finish-drawing
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [shape (get-in state [:workspace-drawing :object])]
|
||||||
|
(rx/concat
|
||||||
|
(rx/of dw/clear-drawing)
|
||||||
|
(when (:initialized? shape)
|
||||||
|
(let [shape-click-width (case (:type shape)
|
||||||
|
:text 3
|
||||||
|
20)
|
||||||
|
shape-click-height (case (:type shape)
|
||||||
|
:text 16
|
||||||
|
20)
|
||||||
|
shape (if (:click-draw? shape)
|
||||||
|
(-> shape
|
||||||
|
(assoc-in [:modifiers :resize-vector]
|
||||||
|
(gpt/point shape-click-width shape-click-height))
|
||||||
|
(assoc-in [:modifiers :resize-origin]
|
||||||
|
(gpt/point (:x shape) (:y shape))))
|
||||||
|
shape)
|
||||||
|
|
||||||
|
shape (cond-> shape
|
||||||
|
(= (:type shape) :text) (assoc :grow-type
|
||||||
|
(if (:click-draw? shape) :auto-width :fixed)))
|
||||||
|
|
||||||
|
shape (-> shape
|
||||||
|
gsh/transform-shape
|
||||||
|
(dissoc :initialized? :click-draw?))]
|
||||||
|
;; Add & select the created shape to the workspace
|
||||||
|
(rx/concat
|
||||||
|
(if (= :text (:type shape))
|
||||||
|
(rx/of dwc/start-undo-transaction)
|
||||||
|
(rx/empty))
|
||||||
|
|
||||||
|
(rx/of (dw/deselect-all)
|
||||||
|
(dw/add-shape shape))))))))))
|
51
frontend/src/app/main/data/workspace/drawing/curve.cljs
Normal file
51
frontend/src/app/main/data/workspace/drawing/curve.cljs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.drawing.curve
|
||||||
|
(:require
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.main.streams :as ms]
|
||||||
|
[app.util.geom.path :as path]
|
||||||
|
[app.main.data.workspace.drawing.common :as common]))
|
||||||
|
|
||||||
|
(def simplify-tolerance 0.3)
|
||||||
|
|
||||||
|
(def handle-drawing-curve
|
||||||
|
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
||||||
|
(ms/mouse-event? event) (= type :up))
|
||||||
|
|
||||||
|
(initialize-drawing [state]
|
||||||
|
(assoc-in state [:workspace-drawing :object :initialized?] true))
|
||||||
|
|
||||||
|
(insert-point-segment [state point]
|
||||||
|
(update-in state [:workspace-drawing :object :segments] (fnil conj []) point))
|
||||||
|
|
||||||
|
(finish-drawing-curve [state]
|
||||||
|
(update-in
|
||||||
|
state [:workspace-drawing :object]
|
||||||
|
(fn [shape]
|
||||||
|
(-> shape
|
||||||
|
(update :segments #(path/simplify % simplify-tolerance))
|
||||||
|
(gsh/update-path-selrect)))))]
|
||||||
|
|
||||||
|
(ptk/reify ::handle-drawing-curve
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [{:keys [flags]} (:workspace-local state)
|
||||||
|
stoper (rx/filter stoper-event? stream)
|
||||||
|
mouse (rx/sample 10 ms/mouse-position)]
|
||||||
|
(rx/concat
|
||||||
|
(rx/of initialize-drawing)
|
||||||
|
(->> mouse
|
||||||
|
(rx/map (fn [pt] #(insert-point-segment % pt)))
|
||||||
|
(rx/take-until stoper))
|
||||||
|
(rx/of finish-drawing-curve
|
||||||
|
common/handle-finish-drawing)))))))
|
116
frontend/src/app/main/data/workspace/drawing/path.cljs
Normal file
116
frontend/src/app/main/data/workspace/drawing/path.cljs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
;; 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/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.data.workspace.drawing.path
|
||||||
|
(:require
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.main.streams :as ms]
|
||||||
|
[app.util.geom.path :as path]
|
||||||
|
[app.main.data.workspace.drawing.common :as common]))
|
||||||
|
|
||||||
|
(def handle-drawing-path
|
||||||
|
(letfn [(stoper-event? [{:keys [type shift] :as event}]
|
||||||
|
(or (= event :path/end-path-drawing)
|
||||||
|
(= event :interrupt)
|
||||||
|
(and (ms/mouse-event? event)
|
||||||
|
(or (= type :double-click)
|
||||||
|
(= type :context-menu)))
|
||||||
|
(and (ms/keyboard-event? event)
|
||||||
|
(= type :down)
|
||||||
|
(= 13 (:key event)))))
|
||||||
|
|
||||||
|
(initialize-drawing [state point]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-drawing :object :segments] [point point])
|
||||||
|
(assoc-in [:workspace-drawing :object :initialized?] true)))
|
||||||
|
|
||||||
|
(insert-point-segment [state point]
|
||||||
|
(-> state
|
||||||
|
(update-in [:workspace-drawing :object :segments] (fnil conj []) point)))
|
||||||
|
|
||||||
|
(update-point-segment [state index point]
|
||||||
|
(let [segments (count (get-in state [:workspace-drawing :object :segments]))
|
||||||
|
exists? (< -1 index segments)]
|
||||||
|
(cond-> state
|
||||||
|
exists? (assoc-in [:workspace-drawing :object :segments index] point))))
|
||||||
|
|
||||||
|
(finish-drawing-path [state]
|
||||||
|
(update-in
|
||||||
|
state [:workspace-drawing :object]
|
||||||
|
(fn [shape] (-> shape
|
||||||
|
(update :segments #(vec (butlast %)))
|
||||||
|
(gsh/update-path-selrect)))))]
|
||||||
|
|
||||||
|
(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))))))))
|
||||||
|
|
||||||
|
(def close-drawing-path
|
||||||
|
(ptk/reify ::close-drawing-path
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc-in state [:workspace-drawing :object :close?] true))))
|
|
@ -51,8 +51,8 @@
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(st/emit! (dw/assign-cursor-tooltip nil)
|
(st/emit! (dw/assign-cursor-tooltip nil)
|
||||||
dd/close-drawing-path
|
#_(dd/close-drawing-path)
|
||||||
:path/end-path-drawing))
|
#_:path/end-path-drawing))
|
||||||
|
|
||||||
on-mouse-enter
|
on-mouse-enter
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps (:id shape))
|
(mf/deps (:id shape))
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
(prn "?? FRAME")
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (dw/deselect-all)
|
(st/emit! (dw/deselect-all)
|
||||||
(dw/select-shape (:id shape)))))
|
(dw/select-shape (:id shape)))))
|
||||||
|
@ -131,7 +132,7 @@
|
||||||
(not (:hidden shape)))
|
(not (:hidden shape)))
|
||||||
[:g {:class (when selected? "selected")
|
[:g {:class (when selected? "selected")
|
||||||
:on-context-menu on-context-menu
|
:on-context-menu on-context-menu
|
||||||
:on-double-click on-double-click
|
;; :on-double-click on-double-click
|
||||||
:on-mouse-down on-mouse-down}
|
:on-mouse-down on-mouse-down}
|
||||||
|
|
||||||
[:& frame-title {:frame shape
|
[:& frame-title {:frame shape
|
||||||
|
|
|
@ -30,15 +30,18 @@
|
||||||
[props]
|
[props]
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
hover? (or (mf/deref refs/current-hover) #{})
|
hover? (or (mf/deref refs/current-hover) #{})
|
||||||
|
|
||||||
on-mouse-down (mf/use-callback
|
on-mouse-down (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(common/on-mouse-down % shape))
|
#(common/on-mouse-down % shape))
|
||||||
on-context-menu (mf/use-callback
|
on-context-menu (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(common/on-context-menu % shape))
|
#(common/on-context-menu % shape))
|
||||||
|
|
||||||
on-double-click (mf/use-callback
|
on-double-click (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
(prn "?? PATH")
|
||||||
(when (and (not (::dr/initialized? shape)) (hover? (:id shape)))
|
(when (and (not (::dr/initialized? shape)) (hover? (:id shape)))
|
||||||
(do
|
(do
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue