🚧 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/beicon {:mvn/version "5.1.0"}
funcool/cuerdas {:mvn/version "2.2.0"} funcool/cuerdas {:mvn/version "2.2.0"}
funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"} funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"}
funcool/potok {:mvn/version "2.5.0"} funcool/potok {:mvn/version "2.6.0"}
funcool/promesa {:mvn/version "3.0.0-SNAPSHOT"} funcool/promesa {:mvn/version "4.0.0-SNAPSHOT"}
funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"} funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"}
} }
:paths ["src" "vendor" "resources"] :paths ["src" "vendor" "resources"]

View file

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

View file

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

View file

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

View file

@ -25,9 +25,13 @@
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.math :as mth] [uxbox.util.math :as mth]
[uxbox.util.spec :as us] [uxbox.util.spec :as us]
[uxbox.util.perf :as perf]
[uxbox.util.time :as dt] [uxbox.util.time :as dt]
[uxbox.util.uuid :as uuid])) [uxbox.util.uuid :as uuid]))
(s/def ::set-of-uuid
(s/every ::us/uuid :kind set?))
;; --- Expose inner functions ;; --- Expose inner functions
(def start-ruler nil) (def start-ruler nil)
@ -338,6 +342,8 @@
;; Shapes events ;; Shapes events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: add spec
(defn add-shape (defn add-shape
[data] [data]
(reify (reify
@ -352,14 +358,24 @@
(defn delete-shape (defn delete-shape
[id] [id]
{:pre [(uuid? id)]} (s/assert ::us/uuid id)
(reify (ptk/reify ::delete-shape
udp/IPageUpdate udp/IPageUpdate
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [shape (get-in state [:shapes id])] (let [shape (get-in state [:shapes id])]
(ds/dissoc-shape state shape))))) (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] (defrecord SelectShape [id]
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -385,10 +401,7 @@
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid] #(-> % (update-in state [:workspace pid] #(-> %
(assoc :selected #{}) (assoc :selected #{})
(dissoc :selected-canvas))))) (dissoc :selected-canvas))))))
ptk/WatchEvent
(watch [_ state stream]
(rx/just :interrupt)))
(defn deselect-all (defn deselect-all
"Clear all possible state of drawing, edition "Clear all possible state of drawing, edition
@ -398,22 +411,18 @@
;; --- Select First Shape ;; --- Select First Shape
(deftype SelectFirstShape [] (def select-first-shape
ptk/UpdateEvent (ptk/reify ::select-first-shape
(update [_ state] ptk/UpdateEvent
(let [pid (get-in state [:workspace :current]) (update [_ state]
sid (first (get-in state [:pages pid :shapes]))] (let [pid (get-in state [:workspace :current])
(assoc-in state [:workspace pid :selected] #{sid})))) 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.))
;; --- Select Shapes (By selrect) ;; --- Select Shapes (By selrect)
(def select-shapes-by-current-selrect (def select-shapes-by-current-selrect
(reify (ptk/reify ::select-shapes-by-current-selrect
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
@ -471,11 +480,9 @@
:fast (gpt/point (if align? (* 3 gx) 10) :fast (gpt/point (if align? (* 3 gx) 10)
(if align? (* 3 gy) 10))})) (if align? (* 3 gy) 10))}))
(declare initial-shape-align) (declare initial-selection-align)
(declare apply-displacement) (declare materialize-current-modifier-in-bulk)
(declare assoc-temporal-modifier) (declare apply-temporal-displacement-in-bulk)
(declare materialize-current-modifier)
(declare apply-temporal-displacement)
(s/def ::direction #{:up :down :right :left}) (s/def ::direction #{:up :down :right :left})
(s/def ::speed #{:std :fast}) (s/def ::speed #{:std :fast})
@ -497,11 +504,9 @@
displacement (get-displacement direction speed distance)] displacement (get-displacement direction speed distance)]
(rx/concat (rx/concat
(when align? (when align?
(rx/concat (rx/of (initial-selection-align selected)))
(rx/from-coll (map initial-shape-align selected)) (rx/of (apply-temporal-displacement-in-bulk selected displacement))
(rx/from-coll (map apply-displacement selected)))) (rx/of (materialize-current-modifier-in-bulk selected)))))))
(rx/from-coll (map #(apply-temporal-displacement % displacement) selected))
(rx/from-coll (map materialize-current-modifier selected)))))))
;; --- Move Selected Layer ;; --- Move Selected Layer
@ -539,8 +544,7 @@
(watch [_ state stream] (watch [_ state stream]
(let [id (get-in state [:workspace :current]) (let [id (get-in state [:workspace :current])
selected (get-in state [:workspace id :selected])] selected (get-in state [:workspace id :selected])]
(rx/from-coll (rx/of (delete-many-shapes selected))))))
(into [(deselect-all)] (map #(delete-shape %) selected)))))))
;; --- Rename Shape ;; --- Rename Shape
@ -584,56 +588,71 @@
canvas (vec (concat before [id] after))] canvas (vec (concat before [id] after))]
(assoc-in state [:pages page-id :canvas] canvas))))) (assoc-in state [:pages page-id :canvas] canvas)))))
;; --- Shape Transformations ;; --- Shape / Selection Alignment
(defn initial-shape-align (defn initial-selection-align
[id] "Align the selection of shapes."
{:pre [(uuid? id)]} [ids]
(reify (s/assert ::set-of-uuid ids)
ptk/WatchEvent (ptk/reify ::initialize-shapes-align-in-bulk
(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
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [prev (get-in state [:shapes id :modifier-mtx] (gmt/matrix)) (let [shapes-by-id (:shapes state)
curr (gmt/translate prev delta)] shapes (mapv #(get shapes-by-id %) ids)
(rx/of (assoc-temporal-modifier id curr)))))) 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))
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 ;; --- Modifiers
(defn assoc-temporal-modifier (defn assoc-temporal-modifier-in-bulk
[id xfmt] [ids xfmt]
{:pre [(uuid? id) (s/assert ::set-of-uuid ids)
(gmt/matrix? xfmt)]} (s/assert gmt/matrix? xfmt)
(reify (ptk/reify ::assoc-temporal-modifier-in-bulk
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (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 (defn materialize-current-modifier-in-bulk
[id] [ids]
{:pre [(uuid? id)]} (s/assert ::us/set ids)
(reify (letfn [(process-shape [state id]
ptk/WatchEvent (let [xfmt (get-in state [:shapes id :modifier-mtx])]
(watch [_ state stream] (if (gmt/matrix? xfmt)
(let [xfmt (get-in state [:shapes id :modifier-mtx])] (-> state
(when (gmt/matrix? xfmt) (update-in [:shapes id] geom/transform xfmt)
(rx/of #(update-in % [:shapes id] geom/transform xfmt) (update-in [:shapes id] dissoc :modifier-mtx))
#(update-in % [:shapes id] dissoc :modifier-mtx) state)))]
::udp/page-update)))))) (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 (defn rehash-shape-relationship
"Checks shape overlaping with existing canvas, if one or more "Checks shape overlaping with existing canvas, if one or more
@ -644,10 +663,7 @@
(let [shape1 (geom/shape->rect-shape canvas) (let [shape1 (geom/shape->rect-shape canvas)
shape2 (geom/shape->rect-shape shape)] shape2 (geom/shape->rect-shape shape)]
(geom/overlaps? shape1 shape2)))] (geom/overlaps? shape1 shape2)))]
(reify (ptk/reify ::rehash-shape-relationship
ptk/EventType
(type [_] ::rehash-shape-relationship)
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [shape (get-in state [:shapes id]) (let [shape (get-in state [:shapes id])
@ -945,38 +961,36 @@
;; Canvas Interactions ;; 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 (defn select-canvas
[id] [id]
(reify (s/assert ::us/uuid id)
(ptk/reify ::select-canvas
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid] assoc :selected-canvas id))))) (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 ;; Server Interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -456,31 +456,50 @@
(defn shape->rect-shape (defn shape->rect-shape
"Coerce shape to rect like shape." "Coerce shape to rect like shape."
([shape] (shape->rect-shape @st/state shape)) [{:keys [type] :as shape}]
([state {:keys [type] :as shape}] (case type
(case type :circle (circle->rect-shape shape)
:circle (circle->rect-shape state shape) :path (path->rect-shape shape)
:path (path->rect-shape state shape) :curve (path->rect-shape shape)
:curve (path->rect-shape state shape) shape))
shape)))
(defn shapes->rect-shape (defn shapes->rect-shape
([shapes] (shapes->rect-shape @st/state shapes)) [shapes]
([state [shape :as shapes]] (let [shapes (mapv shape->rect-shape shapes)
{:pre [(seq shapes)]} minx (apply js/Math.min (mapv :x1 shapes))
(let [shapes (map shape->rect-shape shapes) miny (apply js/Math.min (mapv :y1 shapes))
minx (apply min (map :x1 shapes)) maxx (apply js/Math.max (mapv :x2 shapes))
miny (apply min (map :y1 shapes)) maxy (apply js/Math.max (mapv :y2 shapes))]
maxx (apply max (map :x2 shapes)) {:x1 minx
maxy (apply max (map :y2 shapes))] :y1 miny
{:x1 minx :x2 maxx
:y1 miny :y2 maxy
:x2 maxx :type :rect}))
:y2 maxy
: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 (defn- path->rect-shape
[state {:keys [segments] :as shape}] [{:keys [segments] :as shape}]
(let [minx (apply min (map :x segments)) (let [minx (apply min (map :x segments))
miny (apply min (map :y segments)) miny (apply min (map :y segments))
maxx (apply max (map :x segments)) maxx (apply max (map :x segments))
@ -492,7 +511,7 @@
:y2 maxy))) :y2 maxy)))
(defn- circle->rect-shape (defn- circle->rect-shape
[state {:keys [cx cy rx ry] :as shape}] [{:keys [cx cy rx ry] :as shape}]
(let [width (* rx 2) (let [width (* rx 2)
height (* ry 2) height (* ry 2)
x1 (- cx rx) x1 (- cx rx)

View file

@ -20,6 +20,24 @@
(defonce store (ptk/store {:on-error #(*on-error* %)})) (defonce store (ptk/store {:on-error #(*on-error* %)}))
(defonce stream (ptk/input-stream store)) (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 (def auth-ref
(-> (l/key :auth) (-> (l/key :auth)
(l/derive state))) (l/derive state)))

View file

@ -30,12 +30,39 @@
:image [:& image/image-component {:shape shape}] :image [:& image/image-component {:shape shape}]
:circle [:& circle/circle-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 (mf/defc shape-component
{:wrap [mf/wrap-memo]} {:wrap [mf/wrap-memo]}
[{:keys [id] :as props}] [{:keys [id] :as props}]
(let [shape-iref (mf/use-memo {:deps #js [id] (uxbox.util.perf/with-measure ::foobar
:fn #(-> (l/in [:shapes id]) (let [shape-iref (mf/use-memo {:deps #js [id]
(l/derive st/state))})] :fn #(-> (l/in [:shapes id])
(when-let [shape (mf/deref shape-iref)] (l/derive st/state))})]
(when-not (:hidden shape) (when-let [shape (mf/deref shape-iref)]
(render-shape shape))))) (when-not (:hidden 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) ;; --- 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 (def start-move-selected
(reify (reify
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
selected (get-in state [:workspace pid :selected])] flags (get-in state [:workspace pid :flags])
(rx/from-coll (map start-move selected)))))) 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 (defn on-mouse-down
[event {:keys [id type] :as shape} selected] [event {:keys [id type] :as shape} selected]

View file

@ -55,9 +55,9 @@
props {:x x1 :y y1 props {:x x1 :y y1
:id (str "shape-" id) :id (str "shape-" id)
:class-name (classnames :move-cursor moving?) :className (classnames :move-cursor moving?)
:width width :width width
:height height :height height
:transform transform} :transform transform}
attrs (merge (attrs/extract-style-attrs shape) props)] 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 handle-finish-drawing)
(declare conditional-align) (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 (def ^:private minimal-shapes
[{:type :rect [{:type :rect
:name "Rect" :name "Rect"
@ -87,6 +66,27 @@
:name "Text" :name "Text"
:content "Type your text here"}]) :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 (defn- make-minimal-shape
[type] [type]
(let [tool (seek #(= type (:type %)) minimal-shapes)] (let [tool (seek #(= type (:type %)) minimal-shapes)]
@ -95,7 +95,7 @@
(defn handle-drawing (defn handle-drawing
[type] [type]
(reify (ptk/reify ::handle-drawing
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
@ -131,7 +131,7 @@
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing] resize-shape point lock?)))] (update-in state [:workspace pid :drawing] resize-shape point lock?)))]
(reify (ptk/reify ::handle-drawing-generic
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
@ -184,7 +184,7 @@
(remove-dangling-segmnet [state] (remove-dangling-segmnet [state]
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] #(vec (butlast %)))))] (update-in state [:workspace pid :drawing :segments] #(vec (butlast %)))))]
(reify (ptk/reify ::handle-drawing-path
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
@ -262,7 +262,8 @@
(simplify-drawing-path [state tolerance] (simplify-drawing-path [state tolerance]
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] path/simplify tolerance)))] (update-in state [:workspace pid :drawing :segments] path/simplify tolerance)))]
(reify
(ptk/reify ::handle-drawing-curve
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
@ -283,7 +284,7 @@
handle-finish-drawing))))))) handle-finish-drawing)))))))
(def handle-finish-drawing (def handle-finish-drawing
(reify (ptk/reify ::handle-finish-drawing
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [pid (get-in state [:workspace :current]) (let [pid (get-in state [:workspace :current])
@ -298,10 +299,10 @@
shape (dissoc shape ::initialized? :modifier-mtx)] shape (dissoc shape ::initialized? :modifier-mtx)]
;; Add & select the cred shape to the workspace ;; Add & select the cred shape to the workspace
(rx/of (dw/add-shape shape) (rx/of (dw/add-shape shape)
(dw/select-first-shape))))))))) dw/select-first-shape))))))))
(def close-drawing-path (def close-drawing-path
(reify (ptk/reify ::close-drawing-path
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]

View file

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

View file

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

View file

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

View file

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

View file

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