🚧 Experimenting with some performance improvements.

This commit is contained in:
Andrey Antukh 2019-09-20 17:30:03 +02:00
parent 31ffa73bda
commit 4cf7a48567
16 changed files with 299 additions and 267 deletions

View file

@ -10,8 +10,8 @@
funcool/beicon {:mvn/version "5.1.0"}
funcool/cuerdas {:mvn/version "2.2.0"}
funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"}
funcool/potok {:mvn/version "2.5.0"}
funcool/promesa {:mvn/version "3.0.0-SNAPSHOT"}
funcool/potok {:mvn/version "2.6.0"}
funcool/promesa {:mvn/version "4.0.0-SNAPSHOT"}
funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"}
}
:paths ["src" "vendor" "resources"]

View file

@ -27,10 +27,7 @@
(defn logged-in
[data]
(reify
ptk/EventType
(type [_] ::logged-in)
(ptk/reify ::logged-in
ptk/UpdateEvent
(update [this state]
(assoc state :auth data))

View file

@ -153,13 +153,10 @@
[id pages]
(s/assert ::us/uuid id)
(s/assert ::us/coll pages)
(reify
(ptk/reify ::page-fetched
IDeref
(-deref [_] (list id pages))
ptk/EventType
(type [_] ::page-fetched)
ptk/UpdateEvent
(update [_ state]
(let [get-order #(get-in % [:metadata :order])
@ -262,13 +259,10 @@
(defn page-persisted
[data]
(s/assert ::server-page data)
(reify
(ptk/reify ::page-persisted
cljs.core/IDeref
(-deref [_] data)
ptk/EventType
(type [_] ::page-persisted)
ptk/UpdateEvent
(update [_ state]
(let [{:keys [id version]} data]
@ -283,13 +277,9 @@
;; --- Persist Page
(defn persist-page
([id] (persist-page id identity))
([id on-success]
(assert (uuid? id))
(reify
ptk/EventType
(type [_] ::persist-page)
[id]
(s/assert ::us/uuid id)
(ptk/reify ::persist-page
ptk/WatchEvent
(watch [this state s]
(let [page (get-in state [:pages id])]
@ -299,8 +289,7 @@
(->> (rp/req :update/page page)
(rx/map :payload)
(rx/map page-persisted)
(rx/catch (fn [err] (rx/of ::page-persist-error)))))))))))
(rx/catch (fn [err] (rx/of ::page-persist-error))))))))))
(defn persist-page?
[v]
@ -314,10 +303,7 @@
(defn metadata-persisted
[{:keys [id] :as data}]
(s/assert ::metadata-persisted-params data)
(reify
ptk/EventType
(type [_] ::metadata-persisted)
(ptk/reify ::metadata-persisted
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:pages id :version] (:version data)))))
@ -335,8 +321,8 @@
(defn persist-metadata
[id]
{:pre [(uuid? id)]}
(reify
(s/assert ::us/uuid id)
(ptk/reify ::persist-metadata
ptk/WatchEvent
(watch [_ state stream]
(let [page (get-in state [:pages id])]
@ -349,7 +335,7 @@
(defn update-page-attrs
[{:keys [id] :as data}]
(s/assert ::page-entity data)
(reify
(ptk/reify
IPageUpdate
ptk/UpdateEvent
(update [_ state]
@ -446,6 +432,5 @@
(->> (rx/filter metadata-persisted? stream)
(rx/take 1)
(rx/ignore))))))
(rx/take-until stopper)
(rx/retry 10000))))))
(rx/take-until stopper))))))

View file

@ -188,31 +188,16 @@
(defn dissoc-from-index
"A function that dissoc shape from the indexed
data structure of shapes from the state."
[state {:keys [id type] :as shape}]
(if (= :group type)
(let [items (map #(get-in state [:shapes %]) (:items shape))]
(as-> state $
(update-in $ [:shapes] dissoc id)
(reduce dissoc-from-index $ items)))
(update-in state [:shapes] dissoc id)))
[state shape]
(update state :shapes dissoc (:id shape)))
(defn dissoc-from-page
"Given a shape, try to remove its reference from the
corresponding page."
[state {:keys [id page] :as shape}]
(as-> (get-in state [:pages page :shapes]) $
(into [] (remove #(= % id) $))
(assoc-in state [:pages page :shapes] $)))
(defn dissoc-from-group
"Given a shape, try to remove its reference from the
corresponding group (only if it belongs to one group)."
[state {:keys [id group] :as shape}]
(if-let [group' (get-in state [:shapes group])]
(as-> (:items group') $
(into [] (remove #(= % id) $))
(assoc-in state [:shapes group :items] $))
state))
;; TODO: handle canvas special case
(update-in state [:pages page :shapes]
(fn [items] (vec (remove #(= % id) items)))))
(declare dissoc-shape)
@ -234,11 +219,9 @@
(defn dissoc-shape
"Given a shape, removes it from the state."
[state shape]
(as-> state $
(dissoc-from-page $ shape)
(dissoc-from-group $ shape)
(dissoc-from-index $ shape)
(clear-empty-groups $ shape)))
(-> state
(dissoc-from-page shape)
(dissoc-from-index shape)))
;; --- Shape Movements
@ -265,8 +248,7 @@
:last (- (count shapes) 1))
state (-> state
(dissoc-from-page shape)
(dissoc-from-group shape))
(dissoc-from-page shape))
shapes (if group
(get-in state [:shapes group :items])
@ -293,8 +275,7 @@
source (get-in state [:shapes sid])
state (-> state
(dissoc-from-page source)
(dissoc-from-group source))
(dissoc-from-page source))
shapes (if group
(get-in state [:shapes group :items])
@ -323,8 +304,7 @@
{:pre [(not= tid sid)]}
(let [source (get-in state [:shapes sid])
state (-> state
(dissoc-from-page source)
(dissoc-from-group source))
(dissoc-from-page source))
shapes (get-in state [:shapes tid :items])]
(if (seq shapes)
(as-> state $

View file

@ -25,9 +25,13 @@
[uxbox.util.geom.point :as gpt]
[uxbox.util.math :as mth]
[uxbox.util.spec :as us]
[uxbox.util.perf :as perf]
[uxbox.util.time :as dt]
[uxbox.util.uuid :as uuid]))
(s/def ::set-of-uuid
(s/every ::us/uuid :kind set?))
;; --- Expose inner functions
(def start-ruler nil)
@ -338,6 +342,8 @@
;; Shapes events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: add spec
(defn add-shape
[data]
(reify
@ -352,14 +358,24 @@
(defn delete-shape
[id]
{:pre [(uuid? id)]}
(reify
(s/assert ::us/uuid id)
(ptk/reify ::delete-shape
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [shape (get-in state [:shapes id])]
(ds/dissoc-shape state shape)))))
(defn delete-many-shapes
[ids]
(s/assert ::us/set ids)
(ptk/reify ::delete-many-shapes
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(reduce ds/dissoc-shape state
(map #(get-in state [:shapes %]) ids)))))
(defrecord SelectShape [id]
ptk/UpdateEvent
(update [_ state]
@ -385,10 +401,7 @@
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid] #(-> %
(assoc :selected #{})
(dissoc :selected-canvas)))))
ptk/WatchEvent
(watch [_ state stream]
(rx/just :interrupt)))
(dissoc :selected-canvas))))))
(defn deselect-all
"Clear all possible state of drawing, edition
@ -398,22 +411,18 @@
;; --- Select First Shape
(deftype SelectFirstShape []
(def select-first-shape
(ptk/reify ::select-first-shape
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])
sid (first (get-in state [:pages pid :shapes]))]
(assoc-in state [:workspace pid :selected] #{sid}))))
(defn select-first-shape
"Mark a shape selected for drawing."
[]
(SelectFirstShape.))
(assoc-in state [:workspace pid :selected] #{sid})))))
;; --- Select Shapes (By selrect)
(def select-shapes-by-current-selrect
(reify
(ptk/reify ::select-shapes-by-current-selrect
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])
@ -471,11 +480,9 @@
:fast (gpt/point (if align? (* 3 gx) 10)
(if align? (* 3 gy) 10))}))
(declare initial-shape-align)
(declare apply-displacement)
(declare assoc-temporal-modifier)
(declare materialize-current-modifier)
(declare apply-temporal-displacement)
(declare initial-selection-align)
(declare materialize-current-modifier-in-bulk)
(declare apply-temporal-displacement-in-bulk)
(s/def ::direction #{:up :down :right :left})
(s/def ::speed #{:std :fast})
@ -497,11 +504,9 @@
displacement (get-displacement direction speed distance)]
(rx/concat
(when align?
(rx/concat
(rx/from-coll (map initial-shape-align selected))
(rx/from-coll (map apply-displacement selected))))
(rx/from-coll (map #(apply-temporal-displacement % displacement) selected))
(rx/from-coll (map materialize-current-modifier selected)))))))
(rx/of (initial-selection-align selected)))
(rx/of (apply-temporal-displacement-in-bulk selected displacement))
(rx/of (materialize-current-modifier-in-bulk selected)))))))
;; --- Move Selected Layer
@ -539,8 +544,7 @@
(watch [_ state stream]
(let [id (get-in state [:workspace :current])
selected (get-in state [:workspace id :selected])]
(rx/from-coll
(into [(deselect-all)] (map #(delete-shape %) selected)))))))
(rx/of (delete-many-shapes selected))))))
;; --- Rename Shape
@ -584,56 +588,71 @@
canvas (vec (concat before [id] after))]
(assoc-in state [:pages page-id :canvas] canvas)))))
;; --- Shape Transformations
;; --- Shape / Selection Alignment
(defn initial-shape-align
[id]
{:pre [(uuid? id)]}
(reify
ptk/WatchEvent
(watch [_ state s]
(let [{:keys [x1 y1] :as shape} (->> (get-in state [:shapes id])
(geom/shape->rect-shape state))
point (gpt/point x1 y1)]
(->> (uwrk/align-point point)
(rx/map (fn [{:keys [x y] :as pt}]
(apply-temporal-displacement id (gpt/subtract pt point)))))))))
;; --- Apply Temporal Displacement
(defn apply-temporal-displacement
[id delta]
{:pre [(uuid? id) (gpt/point? delta)]}
(reify
(defn initial-selection-align
"Align the selection of shapes."
[ids]
(s/assert ::set-of-uuid ids)
(ptk/reify ::initialize-shapes-align-in-bulk
ptk/WatchEvent
(watch [_ state stream]
(let [shapes-by-id (:shapes state)
shapes (mapv #(get shapes-by-id %) ids)
sshape (geom/shapes->rect-shape shapes)
point (gpt/point (:x1 sshape)
(:y1 sshape))]
(->> (uwrk/align-point point)
(rx/map (fn [{:keys [x y] :as pt}]
(apply-temporal-displacement-in-bulk ids (gpt/subtract pt point)))))))))
;; --- Temportal displacement for Shape / Selection
;; TODO: this can be done in more performant way using transients
(defn apply-temporal-displacement-in-bulk
"Apply the same displacement delta to all shapes identified by the
set if ids."
[ids delta]
(s/assert ::set-of-uuid ids)
(s/assert gpt/point? delta)
(letfn [(process-shape [state id]
(let [prev (get-in state [:shapes id :modifier-mtx] (gmt/matrix))
curr (gmt/translate prev delta)]
(rx/of (assoc-temporal-modifier id curr))))))
xfmt (gmt/translate prev delta)]
(assoc-in state [:shapes id :modifier-mtx] xfmt)))]
(ptk/reify ::apply-temporal-displacement-in-bulk
ptk/UpdateEvent
(update [_ state]
(perf/with-measure ::apply-temporal-displacement-in-bulk
(reduce process-shape state ids))))))
;; --- Modifiers
(defn assoc-temporal-modifier
[id xfmt]
{:pre [(uuid? id)
(gmt/matrix? xfmt)]}
(reify
(defn assoc-temporal-modifier-in-bulk
[ids xfmt]
(s/assert ::set-of-uuid ids)
(s/assert gmt/matrix? xfmt)
(ptk/reify ::assoc-temporal-modifier-in-bulk
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes id :modifier-mtx] xfmt))))
(reduce #(assoc-in %1 [:shapes %2 :modifier-mtx] xfmt) state ids))))
(defn materialize-current-modifier
[id]
{:pre [(uuid? id)]}
(reify
ptk/WatchEvent
(watch [_ state stream]
(defn materialize-current-modifier-in-bulk
[ids]
(s/assert ::us/set ids)
(letfn [(process-shape [state id]
(let [xfmt (get-in state [:shapes id :modifier-mtx])]
(when (gmt/matrix? xfmt)
(rx/of #(update-in % [:shapes id] geom/transform xfmt)
#(update-in % [:shapes id] dissoc :modifier-mtx)
::udp/page-update))))))
(if (gmt/matrix? xfmt)
(-> state
(update-in [:shapes id] geom/transform xfmt)
(update-in [:shapes id] dissoc :modifier-mtx))
state)))]
(ptk/reify ::materialize-current-modifier-in-bulk
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(perf/with-measure ::materialize-current-modifier-in-bulk
(reduce process-shape state ids))))))
(defn rehash-shape-relationship
"Checks shape overlaping with existing canvas, if one or more
@ -644,10 +663,7 @@
(let [shape1 (geom/shape->rect-shape canvas)
shape2 (geom/shape->rect-shape shape)]
(geom/overlaps? shape1 shape2)))]
(reify
ptk/EventType
(type [_] ::rehash-shape-relationship)
(ptk/reify ::rehash-shape-relationship
ptk/UpdateEvent
(update [_ state]
(let [shape (get-in state [:shapes id])
@ -945,38 +961,36 @@
;; Canvas Interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;; --- Group Collapsing
;; (deftype CollapseGroupShape [id]
;; udp/IPageUpdate
;; ptk/UpdateEvent
;; (update [_ state]
;; (update-in state [:shapes id] assoc :collapsed true)))
;; (defn collapse-shape
;; [id]
;; {:pre [(uuid? id)]}
;; (CollapseGroupShape. id))
;; (deftype UncollapseGroupShape [id]
;; udp/IPageUpdate
;; ptk/UpdateEvent
;; (update [_ state]
;; (update-in state [:shapes id] assoc :collapsed false)))
;; (defn uncollapse-shape
;; [id]
;; {:pre [(uuid? id)]}
;; (UncollapseGroupShape. id))
(defn select-canvas
[id]
(reify
(s/assert ::us/uuid id)
(ptk/reify ::select-canvas
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid] assoc :selected-canvas id)))))
;; (defn watch-page-changes
;; [id]
;; (s/assert ::us/uuid id)
;; (ptk/reify ::watch-page-changes
;; ptk/WatchEvent
;; (watch [_ state stream]
;; (let [stopper (rx/filter #(= % ::stop-page-watcher) stream)]
;; (->> (rx/merge
;; (->> stream
;; (rx/filter #(or (satisfies? IPageUpdate %)
;; (= ::page-update %)))
;; (rx/map #(rehash-shape-relationship
;; (->> stream
;; (rx/filter #(satisfies? IMetadataUpdate %))
;; (rx/debounce 1000)
;; (rx/mapcat #(rx/merge (rx/of (persist-metadata id))
;; (->> (rx/filter metadata-persisted? stream)
;; (rx/take 1)
;; (rx/ignore))))))
;; (rx/take-until stopper))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Server Interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -456,31 +456,50 @@
(defn shape->rect-shape
"Coerce shape to rect like shape."
([shape] (shape->rect-shape @st/state shape))
([state {:keys [type] :as shape}]
[{:keys [type] :as shape}]
(case type
:circle (circle->rect-shape state shape)
:path (path->rect-shape state shape)
:curve (path->rect-shape state shape)
shape)))
:circle (circle->rect-shape shape)
:path (path->rect-shape shape)
:curve (path->rect-shape shape)
shape))
(defn shapes->rect-shape
([shapes] (shapes->rect-shape @st/state shapes))
([state [shape :as shapes]]
{:pre [(seq shapes)]}
(let [shapes (map shape->rect-shape shapes)
minx (apply min (map :x1 shapes))
miny (apply min (map :y1 shapes))
maxx (apply max (map :x2 shapes))
maxy (apply max (map :y2 shapes))]
[shapes]
(let [shapes (mapv shape->rect-shape shapes)
minx (apply js/Math.min (mapv :x1 shapes))
miny (apply js/Math.min (mapv :y1 shapes))
maxx (apply js/Math.max (mapv :x2 shapes))
maxy (apply js/Math.max (mapv :y2 shapes))]
{:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:type :rect})))
:type :rect}))
(defn shapes->rect-shape'
[shapes]
(let [shapes (mapv shape->rect-shape shapes)
total (count shapes)]
(loop [idx (int 0)
minx js/Number.POSITIVE_INFINITY
miny js/Number.POSITIVE_INFINITY
maxx js/Number.NEGATIVE_INFINITY
maxy js/Number.NEGATIVE_INFINITY]
(if (> total idx)
(let [{:keys [x1 y1 x2 y2]} (nth shapes idx)]
(recur (inc idx)
(min minx x1)
(min miny y1)
(max maxx x2)
(max maxy y2)))
{:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:type :rect}))))
(defn- path->rect-shape
[state {:keys [segments] :as shape}]
[{:keys [segments] :as shape}]
(let [minx (apply min (map :x segments))
miny (apply min (map :y segments))
maxx (apply max (map :x segments))
@ -492,7 +511,7 @@
:y2 maxy)))
(defn- circle->rect-shape
[state {:keys [cx cy rx ry] :as shape}]
[{:keys [cx cy rx ry] :as shape}]
(let [width (* rx 2)
height (* ry 2)
x1 (- cx rx)

View file

@ -20,6 +20,24 @@
(defonce store (ptk/store {:on-error #(*on-error* %)}))
(defonce stream (ptk/input-stream store))
;; (defn repr-event
;; [event]
;; (cond
;; (satisfies? ptk/Event event)
;; (str "typ: " (pr-str (ptk/type event)))
;; (and (fn? event)
;; (pos? (count (.-name event))))
;; (str "fn: " (demunge (.-name event)))
;; :else
;; (str "unk: " (pr-str event))))
;; (defonce debug (as-> stream $
;; (rx/filter ptk/event? $)
;; (rx/subscribe $ (fn [event]
;; (println "[stream]: " (repr-event event))))))
(def auth-ref
(-> (l/key :auth)
(l/derive state)))

View file

@ -30,12 +30,39 @@
:image [:& image/image-component {:shape shape}]
:circle [:& circle/circle-component {:shape shape}])))
(mf/defc render-shape'
{:wrap [mf/wrap-memo]}
[{:keys [shape] :as props}]
(render-shape shape))
(mf/defc shape-component
{:wrap [mf/wrap-memo]}
[{:keys [id] :as props}]
(uxbox.util.perf/with-measure ::foobar
(let [shape-iref (mf/use-memo {:deps #js [id]
:fn #(-> (l/in [:shapes id])
(l/derive st/state))})]
(when-let [shape (mf/deref shape-iref)]
(when-not (:hidden shape)
(render-shape shape)))))
(mf/html
[:& render-shape' {:shape shape}]))))))
(mf/defc shape-component'
{:wrap [mf/wrap-memo]}
[{:keys [shape] :as props}]
(when (and shape (not (:hidden shape)))
[:& render-shape' {:shape shape}]))
(def ^:private shapes-iref
(-> (l/key :shapes)
(l/derive st/state)))
(mf/defc all-shapes
{:wrap [mf/wrap-memo]}
[{:keys [page] :as props}]
(let [shapes-by-id (mf/deref shapes-iref)
shapes (map #(get shapes-by-id %) (:shapes page))]
[:*
(for [item shapes]
[:& shape-component' {:shape item :key (:id item)}])]))

View file

@ -18,31 +18,21 @@
;; --- Shape Movement (by mouse)
(defn start-move
[id]
{:pre [(uuid? id)]}
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
flags (get-in state [:workspace pid :flags])
stoper (rx/filter uws/mouse-up? stream)]
(rx/concat
(when (refs/alignment-activated? flags)
(rx/of (dw/initial-shape-align id)))
(->> uws/mouse-position-deltas
(rx/map #(dw/apply-temporal-displacement id %))
(rx/take-until stoper))
(rx/of (dw/materialize-current-modifier id)
(dw/rehash-shape-relationship id)))))))
(def start-move-selected
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
selected (get-in state [:workspace pid :selected])]
(rx/from-coll (map start-move selected))))))
flags (get-in state [:workspace pid :flags])
selected (get-in state [:workspace pid :selected])
stoper (rx/filter uws/mouse-up? stream)]
(rx/concat
(when (refs/alignment-activated? flags)
(rx/of (dw/initial-selection-align selected)))
(->> uws/mouse-position-deltas
(rx/map #(dw/apply-temporal-displacement-in-bulk selected %))
(rx/take-until stoper))
(rx/of (dw/materialize-current-modifier-in-bulk selected)))))))
(defn on-mouse-down
[event {:keys [id type] :as shape} selected]

View file

@ -55,9 +55,9 @@
props {:x x1 :y y1
:id (str "shape-" id)
:class-name (classnames :move-cursor moving?)
:className (classnames :move-cursor moving?)
:width width
:height height
:transform transform}
attrs (merge (attrs/extract-style-attrs shape) props)]
[:> :rect (normalize-props attrs)]))
[:& "rect" attrs]))

View file

@ -36,27 +36,6 @@
(declare handle-finish-drawing)
(declare conditional-align)
(defn start-drawing
[type]
{:pre [(keyword? type)]}
(let [id (gensym "drawing")]
(reify
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace :drawing-lock] #(if (nil? %) id %)))
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
lock (get-in state [:workspace :drawing-lock])]
(if (= lock id)
(rx/merge
(->> (rx/filter #(= % handle-finish-drawing) stream)
(rx/take 1)
(rx/map (fn [_] #(update % :workspace dissoc :drawing-lock))))
(rx/of (handle-drawing type)))
(rx/empty)))))))
(def ^:private minimal-shapes
[{:type :rect
:name "Rect"
@ -87,6 +66,27 @@
:name "Text"
:content "Type your text here"}])
(defn start-drawing
[type]
{:pre [(keyword? type)]}
(let [id (gensym "drawing")]
(ptk/reify ::start-drawing
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace :drawing-lock] #(if (nil? %) id %)))
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
lock (get-in state [:workspace :drawing-lock])]
(if (= lock id)
(rx/merge
(->> (rx/filter #(= % handle-finish-drawing) stream)
(rx/take 1)
(rx/map (fn [_] #(update % :workspace dissoc :drawing-lock))))
(rx/of (handle-drawing type)))
(rx/empty)))))))
(defn- make-minimal-shape
[type]
(let [tool (seek #(= type (:type %)) minimal-shapes)]
@ -95,7 +95,7 @@
(defn handle-drawing
[type]
(reify
(ptk/reify ::handle-drawing
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])
@ -131,7 +131,7 @@
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing] resize-shape point lock?)))]
(reify
(ptk/reify ::handle-drawing-generic
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
@ -184,7 +184,7 @@
(remove-dangling-segmnet [state]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] #(vec (butlast %)))))]
(reify
(ptk/reify ::handle-drawing-path
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
@ -262,7 +262,8 @@
(simplify-drawing-path [state tolerance]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] path/simplify tolerance)))]
(reify
(ptk/reify ::handle-drawing-curve
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
@ -283,7 +284,7 @@
handle-finish-drawing)))))))
(def handle-finish-drawing
(reify
(ptk/reify ::handle-finish-drawing
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
@ -298,10 +299,10 @@
shape (dissoc shape ::initialized? :modifier-mtx)]
;; Add & select the cred shape to the workspace
(rx/of (dw/add-shape shape)
(dw/select-first-shape)))))))))
dw/select-first-shape))))))))
(def close-drawing-path
(reify
(ptk/reify ::close-drawing-path
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])]

View file

@ -39,7 +39,7 @@
(let [result (geom/resize-shape vid shape point lock?)
scale (geom/calculate-scale-ratio shape result)
mtx (geom/generate-resize-matrix vid shape scale)]
(apply rx/of (map #(dw/assoc-temporal-modifier % mtx) ids))))
(rx/of (dw/assoc-temporal-modifier-in-bulk ids mtx))))
;; Unifies the instantaneous proportion lock modifier
;; activated by Ctrl key and the shapes own proportion
@ -72,9 +72,7 @@
(rx/map normalize-proportion-lock)
(rx/mapcat (partial resize shape))
(rx/take-until stoper))
(rx/from-coll (map dw/materialize-current-modifier ids))))))))
;; (rx/subscribe stream (partial on-resize shape) nil on-end))))
(rx/of (dw/materialize-current-modifier-in-bulk ids))))))))
;; --- Controls (Component)
@ -188,14 +186,16 @@
:stroke "#28c4d4"
:style {:cursor "pointer"}}])])))
;; TODO: add specs for clarity
(mf/defc multiple-selection-handlers
[{:keys [shapes zoom] :as props}]
[{:keys [shapes selected zoom] :as props}]
(let [shape (->> shapes
(map #(geom/selection-rect %))
(geom/shapes->rect-shape)
(geom/selection-rect))
on-click #(do (dom/stop-propagation %2)
(st/emit! (start-resize %1 (mapv :id shapes) shape)))]
(st/emit! (start-resize %1 selected shape)))]
[:& controls {:shape shape
:zoom zoom
:on-click on-click}]))
@ -239,6 +239,7 @@
(> num 1)
[:& multiple-selection-handlers {:shapes shapes
:selected (:selected wst)
:zoom zoom}]
(and (= type :text)

View file

@ -85,10 +85,11 @@
(clear-state [state]
(let [id (get-in state [:workspace :current])]
(update-in state [:workspace id] dissoc :selrect)))]
(reify
(ptk/reify ::handle-selrect
ptk/WatchEvent
(watch [_ state stream]
(let [stoper (rx/filter #(or (dw/interrupt? %) (uws/mouse-up? %)) stream)]
(let [stoper (->> (rx/filter #(or (dw/interrupt? %) (uws/mouse-up? %)) stream)
(rx/pr-log "handle-selrect|stoper:"))]
(rx/concat
(rx/of (dw/deselect-all))
(->> uws/mouse-position
@ -118,7 +119,7 @@
cy (.-scrollTop dom)]
(set! (.-scrollLeft dom) (- cx x))
(set! (.-scrollTop dom) (- cy y))))]
(reify
(ptk/reify ::handle-viewport-positioning
ptk/EffectEvent
(effect [_ state stream]
(let [stoper (rx/filter #(= ::finish-positioning %) stream)
@ -145,7 +146,7 @@
(when (not edition)
(if drawing-tool
(st/emit! (start-drawing drawing-tool))
(st/emit! :interrupt handle-selrect))))
(st/emit! handle-selrect))))
(on-context-menu [event]
(dom/prevent-default event)
@ -249,8 +250,9 @@
(for [id (reverse (:canvas page))]
[:& uus/shape-component {:id id :key id}])
(for [id (reverse (:shapes page))]
#_(for [id (reverse (:shapes page))]
[:& uus/shape-component {:id id :key id}])
[:& uus/all-shapes {:page page}]
(when (seq (:selected wst))
[:& selection-handlers {:wst wst}])

View file

@ -56,10 +56,7 @@
(defn show
[data]
(reify
ptk/EventType
(type [_] ::show)
(ptk/reify ::show
ptk/UpdateEvent
(update [_ state]
(let [message (assoc data :state :visible)]

View file

@ -54,6 +54,7 @@
(s/def ::inst inst?)
(s/def ::keyword keyword?)
(s/def ::fn fn?)
(s/def ::set set?)
(s/def ::coll coll?)
(s/def ::not-empty-string

View file

@ -23,6 +23,6 @@
[{:keys [sender point] :as message}]
(let [point [(:x point) (:y point)]
results (kd/nearest tree point 1)
[[x y] d] (first results)
[x y] (ffirst results)
result (gpt/point x y)]
(impl/reply! sender {:point (gpt/point x y)})))