mirror of
https://github.com/penpot/penpot.git
synced 2025-05-20 23:06:10 +02:00
123 lines
3.7 KiB
Clojure
123 lines
3.7 KiB
Clojure
;; 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))))))))
|