From b9030fcc73b314e45efd281e3f6d7bc93b7d7de3 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 25 Sep 2025 09:56:39 +0200 Subject: [PATCH] :sparkles: Add better workspace file indexing strategy Improve file indexes initialization on workspace. Instead of initialize indexes for all pages only initialize indexes for the loaded page. --- common/src/app/common/files/indices.cljc | 56 +++-- frontend/src/app/main/data/changes.cljs | 2 +- frontend/src/app/main/data/workspace.cljs | 26 +-- .../main/data/workspace/drawing/common.cljs | 2 +- .../src/app/main/data/workspace/pages.cljs | 32 ++- .../app/main/data/workspace/selection.cljs | 2 +- frontend/src/app/main/snap.cljs | 6 +- .../app/main/ui/workspace/viewport/hooks.cljs | 2 +- frontend/src/app/worker.cljs | 2 - frontend/src/app/worker/index.cljs | 71 +++++-- frontend/src/app/worker/selection.cljs | 192 ++++++++++-------- .../{util/snap_data.cljs => worker/snap.cljs} | 2 +- frontend/src/app/worker/snaps.cljs | 40 ---- frontend/test/frontend_tests/runner.cljs | 16 +- ...p_data_test.cljs => worker_snap_test.cljs} | 160 +++++++-------- 15 files changed, 330 insertions(+), 281 deletions(-) rename frontend/src/app/{util/snap_data.cljs => worker/snap.cljs} (99%) delete mode 100644 frontend/src/app/worker/snaps.cljs rename frontend/test/frontend_tests/{util_snap_data_test.cljs => worker_snap_test.cljs} (71%) diff --git a/common/src/app/common/files/indices.cljc b/common/src/app/common/files/indices.cljc index 5dc13e3eaa..4e177f052c 100644 --- a/common/src/app/common/files/indices.cljc +++ b/common/src/app/common/files/indices.cljc @@ -6,14 +6,29 @@ (ns app.common.files.indices (:require + [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.uuid :as uuid])) +(defn- generate-index + "An optimized algorithm for calculate parents index that walk from top + to down starting from a provided shape-id. Usefull when you want to + create an index for the whole objects or subpart of the tree." + [index objects shape-id parents] + (let [shape (get objects shape-id) + index (assoc index shape-id parents) + parents (cons shape-id parents)] + (reduce (fn [index shape-id] + (generate-index index objects shape-id parents)) + index + (:shapes shape)))) + (defn generate-child-all-parents-index "Creates an index where the key is the shape id and the value is a set with all the parents" ([objects] - (generate-child-all-parents-index objects (vals objects))) + (generate-index {} objects uuid/zero [])) ([objects shapes] (let [shape->entry @@ -24,24 +39,25 @@ (defn create-clip-index "Retrieves the mask information for an object" [objects parents-index] - (let [retrieve-clips + (let [get-clip-parents + (fn [shape] + (let [shape-id (dm/get-prop shape :id)] + (cond-> [] + (or (and (cfh/frame-shape? shape) + (not (:show-content shape)) + (not= uuid/zero shape-id)) + (cfh/bool-shape? shape)) + (conj shape) + + (:masked-group shape) + (conj (get objects (->> shape :shapes first)))))) + + xform + (comp (map (d/getf objects)) + (mapcat get-clip-parents)) + + populate-with-clips (fn [parents] - (let [lookup-object (fn [id] (get objects id)) - get-clip-parents - (fn [shape] - (cond-> [] - (or (and (= :frame (:type shape)) - (not (:show-content shape)) - (not= uuid/zero (:id shape))) - (cfh/bool-shape? shape)) - (conj shape) + (into [] xform parents))] - (:masked-group shape) - (conj (get objects (->> shape :shapes first)))))] - - (into [] - (comp (map lookup-object) - (mapcat get-clip-parents)) - parents)))] - (-> parents-index - (update-vals retrieve-clips)))) + (d/update-vals parents-index populate-with-clips))) diff --git a/frontend/src/app/main/data/changes.cljs b/frontend/src/app/main/data/changes.cljs index e8fc97862c..6f7799d088 100644 --- a/frontend/src/app/main/data/changes.cljs +++ b/frontend/src/app/main/data/changes.cljs @@ -52,7 +52,7 @@ (->> (rx/from changes) (rx/merge-map (fn [[page-id changes]] (log/debug :hint "update-indexes" :page-id page-id :changes (count changes)) - (mw/ask! {:cmd :index/update-page-index + (mw/ask! {:cmd :index/update :page-id page-id :changes changes}))) (rx/catch (fn [cause] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index cf4921a8ce..8a19ef1871 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -21,7 +21,6 @@ [app.common.types.component :as ctc] [app.common.types.fills :as types.fills] [app.common.types.shape :as cts] - [app.common.types.shape-tree :as ctst] [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.changes :as dch] @@ -70,7 +69,6 @@ [app.main.features.pointer-map :as fpmap] [app.main.repo :as rp] [app.main.router :as rt] - [app.main.worker :as mw] [app.render-wasm :as wasm] [app.render-wasm.api :as api] [app.util.dom :as dom] @@ -84,7 +82,7 @@ [cuerdas.core :as str] [potok.v2.core :as ptk])) -(log/set-level! :debug) +(log/set-level! :trace) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Workspace Initialization @@ -158,18 +156,9 @@ (->> (fpmap/resolve-file file) (rx/map :data) (rx/map process-fills) - (rx/mapcat - (fn [{:keys [pages-index] :as data}] - (->> (rx/from (seq pages-index)) - (rx/mapcat - (fn [[id page]] - (let [page (update page :objects ctst/start-page-index)] - (->> (mw/ask! {:cmd :index/initialize-page-index :page page}) - (rx/map (fn [_] [id page])))))) - (rx/reduce conj {}) - (rx/map (fn [pages-index] - (let [data (assoc data :pages-index pages-index)] - (assoc file :data (d/removem (comp t/pointer? val) data)))))))))) + (rx/map + (fn [data] + (assoc file :data (d/removem (comp t/pointer? val) data)))))) (defn- check-libraries-synchronozation [file-id libraries] @@ -280,6 +269,8 @@ (ptk/reify ::fetch-bundle ptk/WatchEvent (watch [_ _ stream] + (log/debug :hint "fetch bundle" :file-id (dm/str file-id)) + (let [stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)] (->> (rx/zip (rp/cmd! :get-file {:id file-id :features features}) (get-file-object-thumbnails file-id)) @@ -288,6 +279,7 @@ (fn [[file thumbnails]] (->> (resolve-file file) (rx/map (fn [file] + (log/trace :hint "file resolved" :file-id file-id) {:file file :file-id file-id :features features @@ -357,6 +349,10 @@ (rx/map deref) (rx/mapcat (fn [{:keys [file]}] + (log/debug :hint "bundle fetched" + :team-id (dm/str team-id) + :file-id (dm/str file-id)) + (rx/of (dpj/initialize-project (:project-id file)) (dwn/initialize team-id file-id) (dwsl/initialize-shape-layout) diff --git a/frontend/src/app/main/data/workspace/drawing/common.cljs b/frontend/src/app/main/data/workspace/drawing/common.cljs index 70f826e593..ab0aed9480 100644 --- a/frontend/src/app/main/data/workspace/drawing/common.cljs +++ b/frontend/src/app/main/data/workspace/drawing/common.cljs @@ -83,7 +83,7 @@ (rx/of (dwsh/add-shape shape {:no-select? (= tool :curve)})) (if (cfh/frame-shape? shape) (rx/concat - (->> (mw/ask! {:cmd :selection/query + (->> (mw/ask! {:cmd :index/query-selection :page-id page-id :rect (:selrect shape) :include-frames? true diff --git a/frontend/src/app/main/data/workspace/pages.cljs b/frontend/src/app/main/data/workspace/pages.cljs index 145b77649b..a9c9d3354e 100644 --- a/frontend/src/app/main/data/workspace/pages.cljs +++ b/frontend/src/app/main/data/workspace/pages.cljs @@ -15,6 +15,7 @@ [app.common.types.components-list :as ctkl] [app.common.types.container :as ctn] [app.common.types.page :as ctp] + [app.common.types.shape-tree :as ctst] [app.common.uuid :as uuid] [app.config :as cf] [app.main.data.changes :as dch] @@ -29,6 +30,7 @@ [app.main.errors] [app.main.features :as features] [app.main.router :as rt] + [app.main.worker :as mw] [app.render-wasm.shape :as wasm.shape] [app.util.http :as http] [app.util.i18n :as i18n :refer [tr]] @@ -56,16 +58,21 @@ (some? metadata) (cf/resolve-file-media metadata) (some? fill-image) (cf/resolve-file-media fill-image)))))) +(defn- get-page-cache + [state file-id page-id] + (dm/get-in state [:workspace-cache [file-id page-id]])) (defn- initialize-page* "Second phase of page initialization, once we know the page is available in the state" - [file-id page-id page] + [file-id page-id] (ptk/reify ::initialize-page* ptk/UpdateEvent (update [_ state] - ;; selection; when user abandon the current page, the selection is lost - (let [local (dm/get-in state [:workspace-cache [file-id page-id]] default-workspace-local)] + (let [state (dsh/update-page state file-id page-id #(update % :objects ctst/start-page-index)) + page (dsh/lookup-page state file-id page-id) + local (or (get-page-cache state file-id page-id) default-workspace-local)] + (-> state (assoc :current-page-id page-id) (assoc :workspace-local (assoc local :selected (d/ordered-set))) @@ -75,11 +82,16 @@ (update :workspace-layout layout/load-layout-flags) (update :workspace-global layout/load-layout-state)))) - ptk/EffectEvent - (effect [_ _ _] - (let [uris (into #{} xf:collect-file-media (:objects page))] - (->> (rx/from uris) - (rx/subs! #(http/fetch-data-uri % false))))))) + ptk/WatchEvent + (watch [_ state _] + (let [page (dsh/lookup-page state file-id page-id) + uris (into #{} xf:collect-file-media (:objects page))] + (rx/merge + (->> (rx/from uris) + (rx/map #(http/fetch-data-uri % false)) + (rx/ignore)) + (->> (mw/ask! {:cmd :index/initialize :page page}) + (rx/ignore))))))) (defn initialize-page [file-id page-id] @@ -89,9 +101,9 @@ (ptk/reify ::initialize-page ptk/WatchEvent (watch [_ state _] - (if-let [page (dsh/lookup-page state file-id page-id)] + (if (dsh/lookup-page state file-id page-id) (rx/concat - (rx/of (initialize-page* file-id page-id page) + (rx/of (initialize-page* file-id page-id) (dwth/watch-state-changes file-id page-id) (dwl/watch-component-changes)) (let [profile (:profile state) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 498dbd2bad..0320573b15 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -344,7 +344,7 @@ (if (some? selrect) (->> (ask-worker - {:cmd :selection/query + {:cmd :index/query-selection :page-id page-id :rect selrect :include-frames? true diff --git a/frontend/src/app/main/snap.cljs b/frontend/src/app/main/snap.cljs index 090e624410..f485880536 100644 --- a/frontend/src/app/main/snap.cljs +++ b/frontend/src/app/main/snap.cljs @@ -84,7 +84,7 @@ (let [value (get point coord) vbox @refs/vbox ranges [[(- value (/ 0.5 zoom)) (+ value (/ 0.5 zoom))]]] - (->> (mw/ask! {:cmd :snaps/range-query + (->> (mw/ask! {:cmd :index/query-snap :page-id page-id :frame-id frame-id :axis coord @@ -101,7 +101,7 @@ (mapv #(vector (- % snap-accuracy) (+ % snap-accuracy)))) vbox @refs/vbox] - (->> (mw/ask! {:cmd :snaps/range-query + (->> (mw/ask! {:cmd :index/query-snap :page-id page-id :frame-id frame-id :axis coord @@ -217,7 +217,7 @@ (defn select-shapes-area [page-id frame-id selected objects area] - (->> (mw/ask! {:cmd :selection/query + (->> (mw/ask! {:cmd :index/query-selection :page-id page-id :frame-id frame-id :include-frames? true diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index b49699e7f0..f573e405bf 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -194,7 +194,7 @@ (if (mf/ref-val hover-disabled-ref) (rx/of nil) (->> (mw/ask-buffered! - {:cmd :selection/query + {:cmd :index/query-selection :page-id page-id :rect rect :include-frames? true diff --git a/frontend/src/app/worker.cljs b/frontend/src/app/worker.cljs index d5d5f18e44..89476d0408 100644 --- a/frontend/src/app/worker.cljs +++ b/frontend/src/app/worker.cljs @@ -16,8 +16,6 @@ [app.worker.import] [app.worker.index] [app.worker.messages :as wm] - [app.worker.selection] - [app.worker.snaps] [app.worker.thumbnails] [beicon.v2.core :as rx] [promesa.core :as p])) diff --git a/frontend/src/app/worker/index.cljs b/frontend/src/app/worker/index.cljs index 77c2b31f30..b89fad06ee 100644 --- a/frontend/src/app/worker/index.cljs +++ b/frontend/src/app/worker/index.cljs @@ -9,26 +9,69 @@ (:require [app.common.data.macros :as dm] [app.common.files.changes :as ch] + [app.common.geom.rect :as grc] + [app.common.logging :as log] + [app.common.time :as ct] [app.worker.impl :as impl] + [app.worker.selection :as selection] + [app.worker.snap :as snap] [okulary.core :as l])) +(log/set-level! :info) + (defonce state (l/atom {:pages-index {}})) -(defmethod impl/handler :index/initialize-page-index +(defmethod impl/handler :index/initialize [{:keys [page] :as message}] - (swap! state update :pages-index assoc (:id page) page) - (impl/handler (assoc message :cmd :selection/initialize-page-index)) - (impl/handler (assoc message :cmd :snaps/initialize-page-index))) + (let [tpoint (ct/tpoint-ms)] + (try + (swap! state update :pages-index assoc (:id page) page) + (swap! state update ::selection selection/add-page page) + (swap! state update ::snap snap/add-page page) -(defmethod impl/handler :index/update-page-index + (finally + (let [elapsed (tpoint)] + (log/dbg :hint "page indexed" :id (:id page) :elapsed elapsed ::log/sync? true)))) + nil)) + +(defmethod impl/handler :index/update [{:keys [page-id changes] :as message}] + (let [tpoint (ct/tpoint-ms)] + (try + (let [old-page (dm/get-in @state [:pages-index page-id]) + new-page (-> state + (swap! ch/process-changes changes false) + (dm/get-in [:pages-index page-id]))] - (let [old-page (dm/get-in @state [:pages-index page-id]) - new-page (-> state - (swap! ch/process-changes changes false) - (dm/get-in [:pages-index page-id])) - message (assoc message - :old-page old-page - :new-page new-page)] - (impl/handler (assoc message :cmd :selection/update-page-index)) - (impl/handler (assoc message :cmd :snaps/update-page-index)))) + (swap! state update ::snap snap/update-page old-page new-page) + (swap! state update ::selection selection/update-page old-page new-page)) + (finally + (let [elapsed (tpoint)] + (log/dbg :hint "page index updated" :id page-id :elapsed elapsed ::log/sync? true)))) + nil)) + +;; FIXME: schema + +(defmethod impl/handler :index/query-snap + [{:keys [page-id frame-id axis ranges bounds] :as message}] + (if-let [index (get @state ::snap)] + (let [match-bounds? + (fn [[_ data]] + (some #(or (= :guide (:type %)) + (= :layout (:type %)) + (grc/contains-point? bounds (:pt %))) data)) + + xform + (comp (mapcat #(snap/query index page-id frame-id axis %)) + (distinct) + (filter match-bounds?))] + (into [] xform ranges)) + [])) + +;; FIXME: schema + +(defmethod impl/handler :index/query-selection + [message] + (if-let [index (get @state ::selection)] + (selection/query index message) + [])) diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index f6a917b003..e48fbfc2c9 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -17,46 +17,62 @@ [app.common.types.modifiers :as ctm] [app.common.uuid :as uuid] [app.util.quadtree :as qdt] - [app.worker.impl :as impl] - [clojure.set :as set] - [okulary.core :as l])) + [clojure.set :as set])) -;; FIXME: performance shape & rect static props +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IMPL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:const padding-percent 0.10) -(defonce state (l/atom {})) +(defn- index-shape + "A reducing function that ads a shape to the index" + [objects parents-index clip-index index shape] + (let [bounds + (cond + (and ^boolean (cfh/text-shape? shape) + ^boolean (some? (:position-data shape)) + ^boolean (d/not-empty? (:position-data shape))) + (gst/shape->bounds shape) -(defn make-index-shape - [objects parents-index clip-parents-index] - (fn [index shape] - (let [{:keys [x y width height]} - (cond - (and ^boolean (cfh/text-shape? shape) - ^boolean (some? (:position-data shape)) - ^boolean (d/not-empty? (:position-data shape))) - (gst/shape->bounds shape) + :else + (grc/points->rect (:points shape))) - :else - (grc/points->rect (:points shape))) + bound + #js {:x (dm/get-prop bounds :x) + :y (dm/get-prop bounds :y) + :width (dm/get-prop bounds :width) + :height (dm/get-prop bounds :height)} - shape-bound #js {:x x :y y :width width :height height} + shape-id + (dm/get-prop shape :id) - parents (get parents-index (:id shape)) - clip-parents (get clip-parents-index (:id shape)) + frame-id + (dm/get-prop shape :frame-id) - frame (when (and (not= :frame (:type shape)) - (not= (:frame-id shape) uuid/zero)) - (get objects (:frame-id shape)))] - (qdt/insert index - (:id shape) - shape-bound - (assoc shape - :frame frame - :clip-parents clip-parents - :parents parents))))) + shape-type + (dm/get-prop shape :type) -(defn objects-bounds + parents + (get parents-index shape-id) + + clip-parents + (get clip-index shape-id) + + frame + (when (and (not= :frame shape-type) + (not= frame-id uuid/zero)) + (get objects frame-id))] + + (qdt/insert index + shape-id + bound + (assoc shape + :frame frame + :clip-parents clip-parents + :parents parents)))) + +(defn- objects-bounds "Calculates the bounds of the quadtree given a objects map." [objects] (-> objects @@ -64,7 +80,7 @@ vals gsh/shapes->rect)) -(defn add-padding-bounds +(defn- add-padding-bounds "Adds a padding to the bounds defined as a percent in the constant `padding-percent`. For a value of 0.1 will add a 20% width increase (2 x padding)" [bounds] @@ -81,41 +97,48 @@ (defn- create-index [objects] - (let [shapes (-> objects (dissoc uuid/zero) vals) - parents-index (cfi/generate-child-all-parents-index objects) - clip-parents-index (cfi/create-clip-index objects parents-index) - - root-shapes (cfh/get-immediate-children objects uuid/zero) - bounds (-> root-shapes gsh/shapes->rect add-padding-bounds) - - index-shape (make-index-shape objects parents-index clip-parents-index) - initial-quadtree (qdt/create (clj->js bounds)) - - index (reduce index-shape initial-quadtree shapes)] + (let [parents-index (cfi/generate-child-all-parents-index objects) + clip-index (cfi/create-clip-index objects parents-index) + root-shapes (cfh/get-immediate-children objects uuid/zero) + bounds (-> root-shapes gsh/shapes->rect add-padding-bounds) + index (reduce-kv #(index-shape objects parents-index clip-index %1 %3) + (qdt/create (clj->js bounds)) + (dissoc objects uuid/zero))] {:index index :bounds bounds})) +;; FIXME: optimize (defn- update-index [{index :index :as data} old-objects new-objects] - (let [changes? (fn [id] - (not= (get old-objects id) - (get new-objects id))) + (let [object-changed? + (fn [id] + (not= (get old-objects id) + (get new-objects id))) - changed-ids (into #{} - (comp (filter #(not= % uuid/zero)) - (filter changes?) - (mapcat #(into [%] (cfh/get-children-ids new-objects %)))) - (set/union (set (keys old-objects)) - (set (keys new-objects)))) + changed-ids + (into #{} + (comp (filter #(not= % uuid/zero)) + (filter object-changed?) + (mapcat #(into [%] (cfh/get-children-ids new-objects %)))) - shapes (->> changed-ids (mapv #(get new-objects %)) (filterv (comp not nil?))) - parents-index (cfi/generate-child-all-parents-index new-objects shapes) - clip-parents-index (cfi/create-clip-index new-objects parents-index) + (set/union (set (keys old-objects)) + (set (keys new-objects)))) - new-index (qdt/remove-all index changed-ids) + shapes + (->> changed-ids + (map #(get new-objects %)) + (filterv (comp not nil?))) - index-shape (make-index-shape new-objects parents-index clip-parents-index) - index (reduce index-shape new-index shapes)] + parents-index + (cfi/generate-child-all-parents-index new-objects shapes) + + clip-index + (cfi/create-clip-index new-objects parents-index) + + index + (reduce #(index-shape new-objects parents-index clip-index %1 %2) + (qdt/remove-all index changed-ids) + shapes)] (assoc data :index index))) @@ -231,35 +254,36 @@ (map :id)) result))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PUBLIC API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmethod impl/handler :selection/initialize-page-index - [{:keys [page] :as message}] - (letfn [(add-page [state {:keys [id objects] :as page}] - (assoc state id (create-index objects)))] - (swap! state add-page page) - nil)) +(defn add-page + "Add a page index to the state" + [state {:keys [id objects] :as page}] + (assoc state id (create-index objects))) -(defmethod impl/handler :selection/update-page-index - [{:keys [page-id old-page new-page] :as message}] - (swap! state update page-id - (fn [index] - (let [old-objects (:objects old-page) - new-objects (:objects new-page) - old-bounds (:bounds index) - new-bounds (objects-bounds new-objects)] +(defn update-page + "Update page index on the state" + [state old-page new-page] + (let [page-id (get old-page :id)] + (update state page-id + (fn [index] + (let [old-objects (:objects old-page) + new-objects (:objects new-page) + old-bounds (:bounds index) + new-bounds (objects-bounds new-objects)] - ;; If the new bounds are contained within the old bounds - ;; we can update the index. Otherwise we need to - ;; re-create it. - (if (and (some? index) - (grc/contains-rect? old-bounds new-bounds)) - (update-index index old-objects new-objects) - (create-index new-objects))))) - nil) + ;; If the new bounds are contained within the old bounds + ;; we can update the index. Otherwise we need to + ;; re-create it. + (if (and (some? index) + (grc/contains-rect? old-bounds new-bounds)) + (update-index index old-objects new-objects) + (create-index new-objects))))))) -(defmethod impl/handler :selection/query - [{:keys [page-id rect frame-id full-frame? include-frames? ignore-groups? clip-children? using-selrect?] - :or {full-frame? false include-frames? false clip-children? true using-selrect? false} - :as message}] - (when-let [index (get @state page-id)] +(defn query + [index {:keys [page-id rect frame-id full-frame? include-frames? ignore-groups? clip-children? using-selrect?] + :or {full-frame? false include-frames? false clip-children? true using-selrect? false}}] + (when-let [index (get index page-id)] (query-index index rect frame-id full-frame? include-frames? ignore-groups? clip-children? using-selrect?))) diff --git a/frontend/src/app/util/snap_data.cljs b/frontend/src/app/worker/snap.cljs similarity index 99% rename from frontend/src/app/util/snap_data.cljs rename to frontend/src/app/worker/snap.cljs index d8fc89cea9..494c34adff 100644 --- a/frontend/src/app/util/snap_data.cljs +++ b/frontend/src/app/worker/snap.cljs @@ -4,7 +4,7 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.util.snap-data +(ns app.worker.snap "Data structure that holds and retrieves the data to make the snaps. Internally is implemented with a balanced binary tree that queries by range. https://en.wikipedia.org/wiki/Range_tree" diff --git a/frontend/src/app/worker/snaps.cljs b/frontend/src/app/worker/snaps.cljs deleted file mode 100644 index 77bf5d8f5a..0000000000 --- a/frontend/src/app/worker/snaps.cljs +++ /dev/null @@ -1,40 +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) KALEIDOS INC - -(ns app.worker.snaps - (:require - [app.common.geom.rect :as grc] - [app.util.snap-data :as sd] - [app.worker.impl :as impl] - [okulary.core :as l])) - -(defonce state (l/atom {})) - -;; Public API -(defmethod impl/handler :snaps/initialize-page-index - [{:keys [page] :as message}] - (swap! state sd/add-page page) - nil) - -(defmethod impl/handler :snaps/update-page-index - [{:keys [old-page new-page] :as message}] - (swap! state sd/update-page old-page new-page) - nil) - -(defmethod impl/handler :snaps/range-query - [{:keys [page-id frame-id axis ranges bounds] :as message}] - (let [match-bounds? - (fn [[_ data]] - (some #(or (= :guide (:type %)) - (= :layout (:type %)) - (grc/contains-point? bounds (:pt %))) data))] - (->> (into [] - (comp (mapcat #(sd/query @state page-id frame-id axis %)) - (distinct)) - ranges) - (filter match-bounds?)))) - - diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 1b0f413364..11dc224dfa 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -18,7 +18,7 @@ [frontend-tests.tokens.token-form-test] [frontend-tests.util-range-tree-test] [frontend-tests.util-simple-math-test] - [frontend-tests.util-snap-data-test])) + [frontend-tests.worker-snap-test])) (enable-console-print!) @@ -30,6 +30,8 @@ (defn init [] (t/run-tests + 'frontend-tests.basic-shapes-test + 'frontend-tests.data.workspace-colors-test 'frontend-tests.helpers-shapes-test 'frontend-tests.logic.comp-remove-swap-slots-test 'frontend-tests.logic.components-and-tokens @@ -38,13 +40,11 @@ 'frontend-tests.logic.groups-test 'frontend-tests.logic.pasting-in-containers-test 'frontend-tests.plugins.context-shapes-test - 'frontend-tests.util-range-tree-test - 'frontend-tests.util-snap-data-test - 'frontend-tests.util-simple-math-test - 'frontend-tests.basic-shapes-test - 'frontend-tests.data.workspace-colors-test + 'frontend-tests.tokens.import-export-test 'frontend-tests.tokens.logic.token-actions-test 'frontend-tests.tokens.logic.token-data-test - 'frontend-tests.tokens.import-export-test 'frontend-tests.tokens.style-dictionary-test - 'frontend-tests.tokens.token-form-test)) + 'frontend-tests.tokens.token-form-test + 'frontend-tests.util-range-tree-test + 'frontend-tests.util-simple-math-test + 'frontend-tests.worker-snap-test)) diff --git a/frontend/test/frontend_tests/util_snap_data_test.cljs b/frontend/test/frontend_tests/worker_snap_test.cljs similarity index 71% rename from frontend/test/frontend_tests/util_snap_data_test.cljs rename to frontend/test/frontend_tests/worker_snap_test.cljs index 0ad546a672..280db50e56 100644 --- a/frontend/test/frontend_tests/util_snap_data_test.cljs +++ b/frontend/test/frontend_tests/worker_snap_test.cljs @@ -4,12 +4,12 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns frontend-tests.util-snap-data-test +(ns frontend-tests.worker-snap-test (:require [app.common.files.builder :as fb] [app.common.types.shape :as cts] [app.common.uuid :as uuid] - [app.util.snap-data :as sd] + [app.worker.snap :as snap] [cljs.pprint :refer [pprint]] [cljs.test :as t :include-macros true])) @@ -21,9 +21,9 @@ (fn [] (uuid/custom 123456789 (swap! counter inc))))) -(t/deftest test-create-index +(t/deftest create-index (t/testing "Create empty data" - (let [data (sd/make-snap-data)] + (let [data (snap/make-snap-data)] (t/is (some? data)))) (t/testing "Add empty page (only root-frame)" @@ -32,8 +32,8 @@ (fb/add-page {:name "Page 1"}) (fb/get-current-page)) - data (-> (sd/make-snap-data) - (sd/add-page page))] + data (-> (snap/make-snap-data) + (snap/add-page page))] (t/is (some? data)))) (t/testing "Create simple shape on root" @@ -48,10 +48,10 @@ :height 100})) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) - result-x (sd/query data (:id page) uuid/zero :x [0 100])] + result-x (snap/query data (:id page) uuid/zero :x [0 100])] (t/is (some? data)) @@ -82,11 +82,11 @@ page (fb/get-current-page state) ;; frame-id (::fb/last-id file) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100])] (t/is (some? data)) (t/is (= (count result-zero-x) 3)) @@ -116,11 +116,11 @@ page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100])] (t/is (some? data)) (t/is (= (count result-zero-x) 3)) @@ -137,13 +137,13 @@ frame-id (::fb/last-id state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-zero-y (sd/query data (:id page) uuid/zero :y [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100]) - result-frame-y (sd/query data (:id page) frame-id :y [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-zero-y (snap/query data (:id page) uuid/zero :y [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100]) + result-frame-y (snap/query data (:id page) frame-id :y [0 100])] (t/is (some? data)) ;; We can snap in the root @@ -168,13 +168,13 @@ page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-zero-y (sd/query data (:id page) uuid/zero :y [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100]) - result-frame-y (sd/query data (:id page) frame-id :y [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-zero-y (snap/query data (:id page) uuid/zero :y [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100]) + result-frame-y (snap/query data (:id page) frame-id :y [0 100])] (t/is (some? data)) ;; We can snap in the root @@ -185,7 +185,7 @@ (t/is (= (count result-frame-x) 1)) (t/is (= (count result-frame-y) 0))))) -(t/deftest test-update-index +(t/deftest update-index (t/testing "Create frame on root and then remove it." (let [state (-> (fb/create-state) (fb/add-file {:name "Test"}) @@ -200,17 +200,17 @@ shape-id (::fb/last-id state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) state (-> state (fb/delete-shape shape-id)) new-page (fb/get-current-page state) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-y (sd/query data (:id page) uuid/zero :y [0 100])] + result-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-y (snap/query data (:id page) uuid/zero :y [0 100])] (t/is (some? data)) (t/is (= (count result-x) 0)) @@ -231,16 +231,16 @@ page (fb/get-current-page state) ;; frame-id (::fb/last-id state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) state (fb/delete-shape state shape-id) new-page (fb/get-current-page state) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-y (sd/query data (:id page) uuid/zero :y [0 100])] + result-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-y (snap/query data (:id page) uuid/zero :y [0 100])] (t/is (some? data)) (t/is (= (count result-x) 0)) @@ -263,16 +263,16 @@ state (fb/close-board state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) state (fb/delete-shape state shape-id) new-page (fb/get-current-page state) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100])] (t/is (some? data)) (t/is (= (count result-zero-x) 3)) @@ -291,18 +291,18 @@ frame-id (::fb/last-id state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) new-page (-> (fb/delete-guide state guide-id) (fb/get-current-page)) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-zero-y (sd/query data (:id page) uuid/zero :y [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100]) - result-frame-y (sd/query data (:id page) frame-id :y [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-zero-y (snap/query data (:id page) uuid/zero :y [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100]) + result-frame-y (snap/query data (:id page) frame-id :y [0 100])] (t/is (some? data)) ;; We can snap in the root @@ -325,17 +325,17 @@ guide-id (::fb/last-id file) page (fb/get-current-page file) - data (-> (sd/make-snap-data) (sd/add-page page)) + data (-> (snap/make-snap-data) (snap/add-page page)) new-page (-> (fb/delete-guide file guide-id) (fb/get-current-page)) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-zero-x (sd/query data (:id page) uuid/zero :x [0 100]) - result-zero-y (sd/query data (:id page) uuid/zero :y [0 100]) - result-frame-x (sd/query data (:id page) frame-id :x [0 100]) - result-frame-y (sd/query data (:id page) frame-id :y [0 100])] + result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) + result-zero-y (snap/query data (:id page) uuid/zero :y [0 100]) + result-frame-x (snap/query data (:id page) frame-id :x [0 100]) + result-frame-y (snap/query data (:id page) frame-id :y [0 100])] (t/is (some? data)) ;; We can snap in the root (t/is (= (count result-zero-x) 0)) @@ -358,8 +358,8 @@ frame-id (::fb/last-id state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) state (fb/update-shape state frame-id (fn [shape] @@ -370,12 +370,12 @@ new-page (fb/get-current-page state) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100]) - result-frame-x-1 (sd/query data (:id page) frame-id :x [0 100]) - result-zero-x-2 (sd/query data (:id page) uuid/zero :x [200 300]) - result-frame-x-2 (sd/query data (:id page) frame-id :x [200 300])] + result-zero-x-1 (snap/query data (:id page) uuid/zero :x [0 100]) + result-frame-x-1 (snap/query data (:id page) frame-id :x [0 100]) + result-zero-x-2 (snap/query data (:id page) uuid/zero :x [200 300]) + result-frame-x-2 (snap/query data (:id page) frame-id :x [200 300])] (t/is (some? data)) (t/is (= (count result-zero-x-1) 0)) @@ -396,8 +396,8 @@ shape-id (::fb/last-id state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) - (sd/add-page page)) + data (-> (snap/make-snap-data) + (snap/add-page page)) state (fb/update-shape state shape-id (fn [shape] @@ -408,10 +408,10 @@ new-page (fb/get-current-page state) ;; FIXME: update - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100]) - result-zero-x-2 (sd/query data (:id page) uuid/zero :x [200 300])] + result-zero-x-1 (snap/query data (:id page) uuid/zero :x [0 100]) + result-zero-x-2 (snap/query data (:id page) uuid/zero :x [200 300])] (t/is (some? data)) (t/is (= (count result-zero-x-1) 0)) @@ -432,22 +432,22 @@ frame-id (::fb/last-id state) page (fb/get-current-page state) - data (-> (sd/make-snap-data) (sd/add-page page)) + data (-> (snap/make-snap-data) (snap/add-page page)) new-page (-> (fb/update-guide state (assoc guide :position 150)) (fb/get-current-page)) - data (sd/update-page data page new-page) + data (snap/update-page data page new-page) - result-zero-x-1 (sd/query data (:id page) uuid/zero :x [0 100]) - result-zero-y-1 (sd/query data (:id page) uuid/zero :y [0 100]) - result-frame-x-1 (sd/query data (:id page) frame-id :x [0 100]) - result-frame-y-1 (sd/query data (:id page) frame-id :y [0 100]) + result-zero-x-1 (snap/query data (:id page) uuid/zero :x [0 100]) + result-zero-y-1 (snap/query data (:id page) uuid/zero :y [0 100]) + result-frame-x-1 (snap/query data (:id page) frame-id :x [0 100]) + result-frame-y-1 (snap/query data (:id page) frame-id :y [0 100]) - result-zero-x-2 (sd/query data (:id page) uuid/zero :x [0 200]) - result-zero-y-2 (sd/query data (:id page) uuid/zero :y [0 200]) - result-frame-x-2 (sd/query data (:id page) frame-id :x [0 200]) - result-frame-y-2 (sd/query data (:id page) frame-id :y [0 200])] + result-zero-x-2 (snap/query data (:id page) uuid/zero :x [0 200]) + result-zero-y-2 (snap/query data (:id page) uuid/zero :y [0 200]) + result-frame-x-2 (snap/query data (:id page) frame-id :x [0 200]) + result-frame-y-2 (snap/query data (:id page) frame-id :y [0 200])] (t/is (some? data))