mirror of
https://github.com/penpot/penpot.git
synced 2025-04-09 13:31:23 -05:00
🎉 Add (web)worker impl with selection index.
This commit is contained in:
parent
f7dce00c1f
commit
4030e43ac1
6 changed files with 203 additions and 94 deletions
frontend/src/uxbox
26
frontend/src/uxbox/main/worker.cljs
Normal file
26
frontend/src/uxbox/main/worker.cljs
Normal file
|
@ -0,0 +1,26 @@
|
|||
;; 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 uxbox.main.worker
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[uxbox.common.spec :as us]
|
||||
[uxbox.util.worker :as uw]))
|
||||
|
||||
(defn on-error
|
||||
[instance error]
|
||||
(js/console.log "error on worker" error))
|
||||
|
||||
(defonce instance
|
||||
(when (not= *target* "nodejs")
|
||||
(uw/init "js/worker.js" on-error)))
|
||||
|
||||
(defn ask!
|
||||
[message]
|
||||
(uw/ask! instance message))
|
53
frontend/src/uxbox/util/worker.cljs
Normal file
53
frontend/src/uxbox/util/worker.cljs
Normal file
|
@ -0,0 +1,53 @@
|
|||
;; 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 uxbox.util.worker
|
||||
"A lightweight layer on top of webworkers api."
|
||||
(:require
|
||||
[beicon.core :as rx]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
(declare handle-response)
|
||||
(defrecord Worker [instance stream])
|
||||
|
||||
(defn ask!
|
||||
[w message]
|
||||
(let [sender-id (uuid/next)
|
||||
data (t/encode {:payload message :sender-id sender-id})
|
||||
instance (:instance w)]
|
||||
(.postMessage instance data)
|
||||
(->> (:stream w)
|
||||
(rx/filter #(= (:reply-to %) sender-id))
|
||||
(rx/map handle-response)
|
||||
(rx/first))))
|
||||
|
||||
(defn init
|
||||
"Return a initialized webworker instance."
|
||||
[path on-error]
|
||||
(let [ins (js/Worker. path)
|
||||
bus (rx/subject)
|
||||
wrk (Worker. ins bus)]
|
||||
(.addEventListener ins "message"
|
||||
(fn [event]
|
||||
(let [data (.-data event)
|
||||
data (t/decode data)]
|
||||
(rx/push! bus data))))
|
||||
(.addEventListener ins "error"
|
||||
(fn [error]
|
||||
(on-error wrk error)))
|
||||
|
||||
wrk))
|
||||
|
||||
(defn- handle-response
|
||||
[{:keys [payload error] :as response}]
|
||||
(if-let [{:keys [data message]} error]
|
||||
(throw (ex-info message data))
|
||||
payload))
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
;; 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 uxbox.util.workers
|
||||
"A lightweight layer on top of Web Workers API."
|
||||
(:require [beicon.core :as rx]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
;; --- Implementation
|
||||
|
||||
(defprotocol IWorker
|
||||
(-ask [_ msg] "Send and receive message as rx stream.")
|
||||
(-send [_ msg] "Send message and forget."))
|
||||
|
||||
(deftype WebWorker [stream wrk]
|
||||
IWorker
|
||||
(-ask [this message]
|
||||
(let [sender (uuid/next)
|
||||
data (assoc message :sender sender)
|
||||
data (t/encode data)]
|
||||
(.postMessage wrk data)
|
||||
(->> stream
|
||||
(rx/filter #(= (:reply-to %) sender))
|
||||
(rx/take 1))))
|
||||
|
||||
(-send [this message]
|
||||
(let [sender (uuid/next)
|
||||
data (assoc message :sender sender)
|
||||
data (t/encode data)]
|
||||
(.postMessage wrk data)
|
||||
(->> stream
|
||||
(rx/filter #(= (:reply-to %) sender))))))
|
||||
|
||||
;; --- Public Api
|
||||
|
||||
(defn init
|
||||
"Return a initialized webworker instance."
|
||||
[path]
|
||||
(let [wrk (js/Worker. path)
|
||||
bus (rx/subject)]
|
||||
(.addEventListener wrk "message"
|
||||
(fn [event]
|
||||
(let [data (.-data event)
|
||||
data (t/decode data)]
|
||||
(rx/push! bus data))))
|
||||
(.addEventListener wrk "error"
|
||||
(fn [event]
|
||||
(rx/error! bus event)))
|
||||
|
||||
(WebWorker. (rx/map identity bus) wrk)))
|
||||
|
||||
(defn ask!
|
||||
[wrk message]
|
||||
(-ask wrk message))
|
||||
|
||||
(defn send!
|
||||
[wrk message]
|
||||
(-send wrk message))
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
[uxbox.common.spec :as us]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.worker.impl :as impl]
|
||||
[uxbox.worker.selection]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.util.worker :as w]))
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.worker.align
|
||||
"Workspace aligment indexes worker."
|
||||
(:require [beicon.core :as rx]
|
||||
[uxbox.util.kdtree :as kd]
|
||||
[uxbox.worker.impl :as impl]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
(defonce tree (kd/create))
|
||||
|
||||
(defmethod impl/handler :grid-init
|
||||
[{:keys [sender width height x-axis y-axis] :as opts}]
|
||||
(time
|
||||
(kd/setup! tree width height (or x-axis 10) (or y-axis 10)))
|
||||
(impl/reply! sender nil))
|
||||
|
||||
(defmethod impl/handler :grid-align
|
||||
[{:keys [sender point] :as message}]
|
||||
(let [point [(:x point) (:y point)]
|
||||
results (kd/nearest tree point 1)
|
||||
[x y] (ffirst results)
|
||||
result (gpt/point x y)]
|
||||
(impl/reply! sender {:point (gpt/point x y)})))
|
123
frontend/src/uxbox/worker/selection.cljs
Normal file
123
frontend/src/uxbox/worker/selection.cljs
Normal file
|
@ -0,0 +1,123 @@
|
|||
;; 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 uxbox.worker.selection
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[okulary.core :as l]
|
||||
[promesa.core :as p]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.spec :as us]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.worker.impl :as impl]
|
||||
[uxbox.util.quadtree :as qdt]))
|
||||
|
||||
(defonce state (l/atom {}))
|
||||
|
||||
(declare resolve-object)
|
||||
(declare index-object)
|
||||
(declare retrieve-toplevel-shapes)
|
||||
(declare calculate-bounds)
|
||||
(declare create-index)
|
||||
|
||||
(defmethod impl/handler :selection/create-index
|
||||
[{:keys [file-id pages] :as message}]
|
||||
(js/console.log :selection/create-index file-id)
|
||||
(letfn [(index-page [state page]
|
||||
(let [id (:id page)
|
||||
objects (get-in page [:data :objects])
|
||||
objects (reduce resolve-object {} (vals objects))
|
||||
index (create-index objects)]
|
||||
(assoc state id {:index index
|
||||
:objects objects})))
|
||||
(update-state [state]
|
||||
(reduce index-page state pages))]
|
||||
|
||||
(time
|
||||
(swap! state update-state))
|
||||
nil))
|
||||
|
||||
(defmethod impl/handler :selection/update-index
|
||||
[{:keys [page-id objects] :as message}]
|
||||
(js/console.log :selection/update-index page-id)
|
||||
(letfn [(update-page [_]
|
||||
(let [objects (reduce resolve-object {} (vals objects))
|
||||
index (create-index objects)]
|
||||
{:index index :objects objects}))]
|
||||
(time
|
||||
(swap! state update page-id update-page))
|
||||
nil))
|
||||
|
||||
(defmethod impl/handler :selection/query
|
||||
[{:keys [page-id rect] :as message}]
|
||||
(time
|
||||
(when-let [{:keys [index objects]} (get @state page-id)]
|
||||
(let [lookup #(get objects %)
|
||||
result (-> (qdt/search index (clj->js rect))
|
||||
(es6-iterator-seq))
|
||||
matches? #(geom/overlaps? % rect)]
|
||||
(into #{} (comp (map #(unchecked-get % "data"))
|
||||
(filter matches?)
|
||||
(map :id))
|
||||
result)))))
|
||||
|
||||
(defn- calculate-bounds
|
||||
[objects]
|
||||
#js {:x 0
|
||||
:y 0
|
||||
:width (::width objects)
|
||||
:height (::height objects)})
|
||||
|
||||
(defn- create-index
|
||||
[objects]
|
||||
(let [bounds (calculate-bounds objects)]
|
||||
(reduce index-object
|
||||
(qdt/create bounds)
|
||||
(->> (retrieve-toplevel-shapes objects)
|
||||
(map #(get objects %))))))
|
||||
|
||||
(defn- index-object
|
||||
[index {:keys [id x y width height] :as obj}]
|
||||
(let [rect #js {:x x :y y :width width :height height}]
|
||||
(qdt/insert index rect obj)))
|
||||
|
||||
(defn- resolve-object
|
||||
[state {:keys [id] :as item}]
|
||||
(let [item (-> item
|
||||
(geom/shape->rect-shape)
|
||||
(geom/resolve-rotation)
|
||||
(geom/shape->rect-shape))
|
||||
width (+ (:x item 0) (:width item 0))
|
||||
height (+ (:y item 0) (:height item 0))
|
||||
max (fnil max 0)]
|
||||
(-> state
|
||||
(assoc id item)
|
||||
(update ::width max width)
|
||||
(update ::height max height))))
|
||||
|
||||
(defn- retrieve-toplevel-shapes
|
||||
[objects]
|
||||
(let [lookup #(get objects %)
|
||||
root (lookup uuid/zero)
|
||||
childs (:shapes root)]
|
||||
(loop [id (first childs)
|
||||
ids (rest childs)
|
||||
res []]
|
||||
(if (nil? id)
|
||||
res
|
||||
(let [obj (lookup id)
|
||||
typ (:type obj)]
|
||||
(recur (first ids)
|
||||
(rest ids)
|
||||
(if (= :frame typ)
|
||||
(into res (:shapes obj))
|
||||
(conj res id))))))))
|
Loading…
Add table
Reference in a new issue