@tldraw/editor 4.3.0 → 4.4.0-canary.1e3b436e33e4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist-cjs/index.d.ts +5 -0
- package/dist-cjs/index.js +3 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +54 -5
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +13 -21
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js +144 -0
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js.map +7 -0
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +181 -0
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +5 -0
- package/dist-esm/index.mjs +3 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +54 -5
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +13 -21
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs +114 -0
- package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs.map +7 -0
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +161 -0
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +10 -8
- package/src/index.ts +1 -0
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -5
- package/src/lib/editor/Editor.ts +74 -5
- package/src/lib/editor/derivations/notVisibleShapes.ts +15 -41
- package/src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts +144 -0
- package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +215 -0
- package/src/version.ts +3 -3
|
@@ -23,33 +23,25 @@ __export(notVisibleShapes_exports, {
|
|
|
23
23
|
module.exports = __toCommonJS(notVisibleShapes_exports);
|
|
24
24
|
var import_state = require("@tldraw/state");
|
|
25
25
|
function notVisibleShapes(editor) {
|
|
26
|
-
return (0, import_state.computed)("notVisibleShapes", function
|
|
27
|
-
const
|
|
28
|
-
const nextValue = /* @__PURE__ */ new Set();
|
|
26
|
+
return (0, import_state.computed)("notVisibleShapes", function(prevValue) {
|
|
27
|
+
const allShapeIds = editor.getCurrentPageShapeIds();
|
|
29
28
|
const viewportPageBounds = editor.getViewportPageBounds();
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
continue;
|
|
29
|
+
const visibleIds = editor.getShapeIdsInsideBounds(viewportPageBounds);
|
|
30
|
+
const nextValue = /* @__PURE__ */ new Set();
|
|
31
|
+
for (const id of allShapeIds) {
|
|
32
|
+
if (!visibleIds.has(id)) {
|
|
33
|
+
const shape = editor.getShape(id);
|
|
34
|
+
if (!shape) continue;
|
|
35
|
+
const canCull = editor.getShapeUtil(shape.type).canCull(shape);
|
|
36
|
+
if (!canCull) continue;
|
|
37
|
+
nextValue.add(id);
|
|
38
38
|
}
|
|
39
|
-
const shape = editor.getShape(id);
|
|
40
|
-
if (!shape) continue;
|
|
41
|
-
const canCull = editor.getShapeUtil(shape.type).canCull(shape);
|
|
42
|
-
if (!canCull) continue;
|
|
43
|
-
nextValue.add(id);
|
|
44
39
|
}
|
|
45
|
-
if ((0, import_state.isUninitialized)(prevValue)) {
|
|
40
|
+
if ((0, import_state.isUninitialized)(prevValue) || prevValue.size !== nextValue.size) {
|
|
46
41
|
return nextValue;
|
|
47
42
|
}
|
|
48
|
-
if (prevValue.size !== nextValue.size) return nextValue;
|
|
49
43
|
for (const prev of prevValue) {
|
|
50
|
-
if (!nextValue.has(prev))
|
|
51
|
-
return nextValue;
|
|
52
|
-
}
|
|
44
|
+
if (!nextValue.has(prev)) return nextValue;
|
|
53
45
|
}
|
|
54
46
|
return prevValue;
|
|
55
47
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/derivations/notVisibleShapes.ts"],
|
|
4
|
-
"sourcesContent": ["import { computed, isUninitialized } from '@tldraw/state'\nimport { TLShapeId } from '@tldraw/tlschema'\nimport { Editor } from '../Editor'\n\n/**\n * Non visible shapes are shapes outside of the viewport page bounds.\n *\n * @param editor - Instance of the tldraw Editor.\n * @returns Incremental derivation of non visible shapes.\n */\nexport function notVisibleShapes(editor: Editor) {\n\treturn computed<Set<TLShapeId>>('notVisibleShapes', function
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0C;AAUnC,SAAS,iBAAiB,QAAgB;AAChD,aAAO,uBAAyB,oBAAoB,
|
|
4
|
+
"sourcesContent": ["import { computed, isUninitialized } from '@tldraw/state'\nimport { TLShapeId } from '@tldraw/tlschema'\nimport { Editor } from '../Editor'\n\n/**\n * Non visible shapes are shapes outside of the viewport page bounds.\n *\n * @param editor - Instance of the tldraw Editor.\n * @returns Incremental derivation of non visible shapes.\n */\nexport function notVisibleShapes(editor: Editor) {\n\treturn computed<Set<TLShapeId>>('notVisibleShapes', function (prevValue) {\n\t\tconst allShapeIds = editor.getCurrentPageShapeIds()\n\t\tconst viewportPageBounds = editor.getViewportPageBounds()\n\t\tconst visibleIds = editor.getShapeIdsInsideBounds(viewportPageBounds)\n\n\t\tconst nextValue = new Set<TLShapeId>()\n\n\t\t// Non-visible shapes are all shapes minus visible shapes\n\t\tfor (const id of allShapeIds) {\n\t\t\tif (!visibleIds.has(id)) {\n\t\t\t\tconst shape = editor.getShape(id)\n\t\t\t\tif (!shape) continue\n\n\t\t\t\tconst canCull = editor.getShapeUtil(shape.type).canCull(shape)\n\t\t\t\tif (!canCull) continue\n\n\t\t\t\tnextValue.add(id)\n\t\t\t}\n\t\t}\n\n\t\tif (isUninitialized(prevValue) || prevValue.size !== nextValue.size) {\n\t\t\treturn nextValue\n\t\t}\n\n\t\tfor (const prev of prevValue) {\n\t\t\tif (!nextValue.has(prev)) return nextValue\n\t\t}\n\n\t\treturn prevValue\n\t})\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA0C;AAUnC,SAAS,iBAAiB,QAAgB;AAChD,aAAO,uBAAyB,oBAAoB,SAAU,WAAW;AACxE,UAAM,cAAc,OAAO,uBAAuB;AAClD,UAAM,qBAAqB,OAAO,sBAAsB;AACxD,UAAM,aAAa,OAAO,wBAAwB,kBAAkB;AAEpE,UAAM,YAAY,oBAAI,IAAe;AAGrC,eAAW,MAAM,aAAa;AAC7B,UAAI,CAAC,WAAW,IAAI,EAAE,GAAG;AACxB,cAAM,QAAQ,OAAO,SAAS,EAAE;AAChC,YAAI,CAAC,MAAO;AAEZ,cAAM,UAAU,OAAO,aAAa,MAAM,IAAI,EAAE,QAAQ,KAAK;AAC7D,YAAI,CAAC,QAAS;AAEd,kBAAU,IAAI,EAAE;AAAA,MACjB;AAAA,IACD;AAEA,YAAI,8BAAgB,SAAS,KAAK,UAAU,SAAS,UAAU,MAAM;AACpE,aAAO;AAAA,IACR;AAEA,eAAW,QAAQ,WAAW;AAC7B,UAAI,CAAC,UAAU,IAAI,IAAI,EAAG,QAAO;AAAA,IAClC;AAEA,WAAO;AAAA,EACR,CAAC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var RBushIndex_exports = {};
|
|
30
|
+
__export(RBushIndex_exports, {
|
|
31
|
+
RBushIndex: () => RBushIndex
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(RBushIndex_exports);
|
|
34
|
+
var import_rbush = __toESM(require("rbush"), 1);
|
|
35
|
+
var import_Box = require("../../../primitives/Box");
|
|
36
|
+
class TldrawRBush extends import_rbush.default {
|
|
37
|
+
}
|
|
38
|
+
class RBushIndex {
|
|
39
|
+
rBush;
|
|
40
|
+
elementsInTree;
|
|
41
|
+
constructor() {
|
|
42
|
+
this.rBush = new TldrawRBush();
|
|
43
|
+
this.elementsInTree = /* @__PURE__ */ new Map();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Search for shapes within the given bounds.
|
|
47
|
+
* Returns set of shape IDs that intersect with the bounds.
|
|
48
|
+
*/
|
|
49
|
+
search(bounds) {
|
|
50
|
+
const results = this.rBush.search({
|
|
51
|
+
minX: bounds.minX,
|
|
52
|
+
minY: bounds.minY,
|
|
53
|
+
maxX: bounds.maxX,
|
|
54
|
+
maxY: bounds.maxY
|
|
55
|
+
});
|
|
56
|
+
return new Set(results.map((e) => e.id));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Insert or update a shape in the spatial index.
|
|
60
|
+
* If the shape already exists, it will be removed first to prevent duplicates.
|
|
61
|
+
*/
|
|
62
|
+
upsert(id, bounds) {
|
|
63
|
+
const existing = this.elementsInTree.get(id);
|
|
64
|
+
if (existing) {
|
|
65
|
+
this.rBush.remove(existing);
|
|
66
|
+
}
|
|
67
|
+
const element = {
|
|
68
|
+
minX: bounds.minX,
|
|
69
|
+
minY: bounds.minY,
|
|
70
|
+
maxX: bounds.maxX,
|
|
71
|
+
maxY: bounds.maxY,
|
|
72
|
+
id
|
|
73
|
+
};
|
|
74
|
+
this.rBush.insert(element);
|
|
75
|
+
this.elementsInTree.set(id, element);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Remove a shape from the spatial index.
|
|
79
|
+
*/
|
|
80
|
+
remove(id) {
|
|
81
|
+
const element = this.elementsInTree.get(id);
|
|
82
|
+
if (element) {
|
|
83
|
+
this.rBush.remove(element);
|
|
84
|
+
this.elementsInTree.delete(id);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Bulk load elements into the spatial index.
|
|
89
|
+
* More efficient than individual inserts for initial loading.
|
|
90
|
+
*/
|
|
91
|
+
bulkLoad(elements) {
|
|
92
|
+
this.rBush.load(elements);
|
|
93
|
+
for (const element of elements) {
|
|
94
|
+
this.elementsInTree.set(element.id, element);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Clear all elements from the spatial index.
|
|
99
|
+
*/
|
|
100
|
+
clear() {
|
|
101
|
+
this.rBush.clear();
|
|
102
|
+
this.elementsInTree.clear();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if a shape is in the spatial index.
|
|
106
|
+
*/
|
|
107
|
+
has(id) {
|
|
108
|
+
return this.elementsInTree.has(id);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the number of elements in the spatial index.
|
|
112
|
+
*/
|
|
113
|
+
getSize() {
|
|
114
|
+
return this.elementsInTree.size;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get all shape IDs currently in the spatial index.
|
|
118
|
+
*/
|
|
119
|
+
getAllShapeIds() {
|
|
120
|
+
return Array.from(this.elementsInTree.keys());
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get the bounds currently stored in the spatial index for a shape.
|
|
124
|
+
* Returns undefined if the shape is not in the index.
|
|
125
|
+
*/
|
|
126
|
+
getBounds(id) {
|
|
127
|
+
const element = this.elementsInTree.get(id);
|
|
128
|
+
if (!element) return void 0;
|
|
129
|
+
return new import_Box.Box(
|
|
130
|
+
element.minX,
|
|
131
|
+
element.minY,
|
|
132
|
+
element.maxX - element.minX,
|
|
133
|
+
element.maxY - element.minY
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Dispose of the spatial index.
|
|
138
|
+
* Clears all data structures to prevent memory leaks.
|
|
139
|
+
*/
|
|
140
|
+
dispose() {
|
|
141
|
+
this.clear();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=RBushIndex.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts"],
|
|
4
|
+
"sourcesContent": ["import type { TLShapeId } from '@tldraw/tlschema'\nimport RBush from 'rbush'\nimport { Box } from '../../../primitives/Box'\n\n/**\n * Element stored in the R-tree spatial index.\n * Contains bounds (minX, minY, maxX, maxY) and shape ID.\n */\nexport interface SpatialElement {\n\tminX: number\n\tminY: number\n\tmaxX: number\n\tmaxY: number\n\tid: TLShapeId\n}\n\n/**\n * Custom RBush class for tldraw shapes.\n */\nclass TldrawRBush extends RBush<SpatialElement> {}\n\n/**\n * Wrapper around RBush R-tree for efficient spatial queries.\n * Maintains a map of elements currently in the tree for efficient updates.\n */\nexport class RBushIndex {\n\tprivate rBush: TldrawRBush\n\tprivate elementsInTree: Map<TLShapeId, SpatialElement>\n\n\tconstructor() {\n\t\tthis.rBush = new TldrawRBush()\n\t\tthis.elementsInTree = new Map()\n\t}\n\n\t/**\n\t * Search for shapes within the given bounds.\n\t * Returns set of shape IDs that intersect with the bounds.\n\t */\n\tsearch(bounds: Box): Set<TLShapeId> {\n\t\tconst results = this.rBush.search({\n\t\t\tminX: bounds.minX,\n\t\t\tminY: bounds.minY,\n\t\t\tmaxX: bounds.maxX,\n\t\t\tmaxY: bounds.maxY,\n\t\t})\n\t\treturn new Set(results.map((e: SpatialElement) => e.id))\n\t}\n\n\t/**\n\t * Insert or update a shape in the spatial index.\n\t * If the shape already exists, it will be removed first to prevent duplicates.\n\t */\n\tupsert(id: TLShapeId, bounds: Box): void {\n\t\t// Remove existing entry to prevent map-tree desync\n\t\tconst existing = this.elementsInTree.get(id)\n\t\tif (existing) {\n\t\t\tthis.rBush.remove(existing)\n\t\t}\n\n\t\tconst element: SpatialElement = {\n\t\t\tminX: bounds.minX,\n\t\t\tminY: bounds.minY,\n\t\t\tmaxX: bounds.maxX,\n\t\t\tmaxY: bounds.maxY,\n\t\t\tid,\n\t\t}\n\t\tthis.rBush.insert(element)\n\t\tthis.elementsInTree.set(id, element)\n\t}\n\n\t/**\n\t * Remove a shape from the spatial index.\n\t */\n\tremove(id: TLShapeId): void {\n\t\tconst element = this.elementsInTree.get(id)\n\t\tif (element) {\n\t\t\tthis.rBush.remove(element)\n\t\t\tthis.elementsInTree.delete(id)\n\t\t}\n\t}\n\n\t/**\n\t * Bulk load elements into the spatial index.\n\t * More efficient than individual inserts for initial loading.\n\t */\n\tbulkLoad(elements: SpatialElement[]): void {\n\t\tthis.rBush.load(elements)\n\t\tfor (const element of elements) {\n\t\t\tthis.elementsInTree.set(element.id, element)\n\t\t}\n\t}\n\n\t/**\n\t * Clear all elements from the spatial index.\n\t */\n\tclear(): void {\n\t\tthis.rBush.clear()\n\t\tthis.elementsInTree.clear()\n\t}\n\n\t/**\n\t * Check if a shape is in the spatial index.\n\t */\n\thas(id: TLShapeId): boolean {\n\t\treturn this.elementsInTree.has(id)\n\t}\n\n\t/**\n\t * Get the number of elements in the spatial index.\n\t */\n\tgetSize(): number {\n\t\treturn this.elementsInTree.size\n\t}\n\n\t/**\n\t * Get all shape IDs currently in the spatial index.\n\t */\n\tgetAllShapeIds(): TLShapeId[] {\n\t\treturn Array.from(this.elementsInTree.keys())\n\t}\n\n\t/**\n\t * Get the bounds currently stored in the spatial index for a shape.\n\t * Returns undefined if the shape is not in the index.\n\t */\n\tgetBounds(id: TLShapeId): Box | undefined {\n\t\tconst element = this.elementsInTree.get(id)\n\t\tif (!element) return undefined\n\t\treturn new Box(\n\t\t\telement.minX,\n\t\t\telement.minY,\n\t\t\telement.maxX - element.minX,\n\t\t\telement.maxY - element.minY\n\t\t)\n\t}\n\n\t/**\n\t * Dispose of the spatial index.\n\t * Clears all data structures to prevent memory leaks.\n\t */\n\tdispose(): void {\n\t\tthis.clear()\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAkB;AAClB,iBAAoB;AAiBpB,MAAM,oBAAoB,aAAAA,QAAsB;AAAC;AAM1C,MAAM,WAAW;AAAA,EACf;AAAA,EACA;AAAA,EAER,cAAc;AACb,SAAK,QAAQ,IAAI,YAAY;AAC7B,SAAK,iBAAiB,oBAAI,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,QAA6B;AACnC,UAAM,UAAU,KAAK,MAAM,OAAO;AAAA,MACjC,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,IACd,CAAC;AACD,WAAO,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAsB,EAAE,EAAE,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,IAAe,QAAmB;AAExC,UAAM,WAAW,KAAK,eAAe,IAAI,EAAE;AAC3C,QAAI,UAAU;AACb,WAAK,MAAM,OAAO,QAAQ;AAAA,IAC3B;AAEA,UAAM,UAA0B;AAAA,MAC/B,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb;AAAA,IACD;AACA,SAAK,MAAM,OAAO,OAAO;AACzB,SAAK,eAAe,IAAI,IAAI,OAAO;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,IAAqB;AAC3B,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE;AAC1C,QAAI,SAAS;AACZ,WAAK,MAAM,OAAO,OAAO;AACzB,WAAK,eAAe,OAAO,EAAE;AAAA,IAC9B;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,UAAkC;AAC1C,SAAK,MAAM,KAAK,QAAQ;AACxB,eAAW,WAAW,UAAU;AAC/B,WAAK,eAAe,IAAI,QAAQ,IAAI,OAAO;AAAA,IAC5C;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACb,SAAK,MAAM,MAAM;AACjB,SAAK,eAAe,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,IAAwB;AAC3B,WAAO,KAAK,eAAe,IAAI,EAAE;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAkB;AACjB,WAAO,KAAK,eAAe;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA8B;AAC7B,WAAO,MAAM,KAAK,KAAK,eAAe,KAAK,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,IAAgC;AACzC,UAAM,UAAU,KAAK,eAAe,IAAI,EAAE;AAC1C,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,IAAI;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ,OAAO,QAAQ;AAAA,MACvB,QAAQ,OAAO,QAAQ;AAAA,IACxB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAgB;AACf,SAAK,MAAM;AAAA,EACZ;AACD;",
|
|
6
|
+
"names": ["RBush"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var SpatialIndexManager_exports = {};
|
|
20
|
+
__export(SpatialIndexManager_exports, {
|
|
21
|
+
SpatialIndexManager: () => SpatialIndexManager
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(SpatialIndexManager_exports);
|
|
24
|
+
var import_state = require("@tldraw/state");
|
|
25
|
+
var import_tlschema = require("@tldraw/tlschema");
|
|
26
|
+
var import_utils = require("@tldraw/utils");
|
|
27
|
+
var import_Box = require("../../../primitives/Box");
|
|
28
|
+
var import_RBushIndex = require("./RBushIndex");
|
|
29
|
+
class SpatialIndexManager {
|
|
30
|
+
constructor(editor) {
|
|
31
|
+
this.editor = editor;
|
|
32
|
+
this.rbush = new import_RBushIndex.RBushIndex();
|
|
33
|
+
this.spatialIndexComputed = this.createSpatialIndexComputed();
|
|
34
|
+
}
|
|
35
|
+
rbush;
|
|
36
|
+
spatialIndexComputed;
|
|
37
|
+
lastPageId = null;
|
|
38
|
+
createSpatialIndexComputed() {
|
|
39
|
+
const shapeHistory = this.editor.store.query.filterHistory("shape");
|
|
40
|
+
return (0, import_state.computed)("spatialIndex", (_prevValue, lastComputedEpoch) => {
|
|
41
|
+
if ((0, import_state.isUninitialized)(_prevValue)) {
|
|
42
|
+
return this.buildFromScratch(lastComputedEpoch);
|
|
43
|
+
}
|
|
44
|
+
const shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch);
|
|
45
|
+
if (shapeDiff === import_state.RESET_VALUE) {
|
|
46
|
+
return this.buildFromScratch(lastComputedEpoch);
|
|
47
|
+
}
|
|
48
|
+
const currentPageId = this.editor.getCurrentPageId();
|
|
49
|
+
if (this.lastPageId !== currentPageId) {
|
|
50
|
+
return this.buildFromScratch(lastComputedEpoch);
|
|
51
|
+
}
|
|
52
|
+
if (shapeDiff.length === 0) {
|
|
53
|
+
return lastComputedEpoch;
|
|
54
|
+
}
|
|
55
|
+
this.processIncrementalUpdate(shapeDiff);
|
|
56
|
+
return lastComputedEpoch;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
buildFromScratch(epoch) {
|
|
60
|
+
this.rbush.clear();
|
|
61
|
+
this.lastPageId = this.editor.getCurrentPageId();
|
|
62
|
+
const shapes = this.editor.getCurrentPageShapes();
|
|
63
|
+
const elements = [];
|
|
64
|
+
for (const shape of shapes) {
|
|
65
|
+
const bounds = this.editor.getShapePageBounds(shape.id);
|
|
66
|
+
if (bounds) {
|
|
67
|
+
elements.push({
|
|
68
|
+
minX: bounds.minX,
|
|
69
|
+
minY: bounds.minY,
|
|
70
|
+
maxX: bounds.maxX,
|
|
71
|
+
maxY: bounds.maxY,
|
|
72
|
+
id: shape.id
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.rbush.bulkLoad(elements);
|
|
77
|
+
return epoch;
|
|
78
|
+
}
|
|
79
|
+
processIncrementalUpdate(shapeDiff) {
|
|
80
|
+
const processedShapeIds = /* @__PURE__ */ new Set();
|
|
81
|
+
for (const changes of shapeDiff) {
|
|
82
|
+
for (const shape of (0, import_utils.objectMapValues)(changes.added)) {
|
|
83
|
+
if ((0, import_tlschema.isShape)(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {
|
|
84
|
+
const bounds = this.editor.getShapePageBounds(shape.id);
|
|
85
|
+
if (bounds) {
|
|
86
|
+
this.rbush.upsert(shape.id, bounds);
|
|
87
|
+
}
|
|
88
|
+
processedShapeIds.add(shape.id);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const shape of (0, import_utils.objectMapValues)(changes.removed)) {
|
|
92
|
+
if ((0, import_tlschema.isShape)(shape)) {
|
|
93
|
+
this.rbush.remove(shape.id);
|
|
94
|
+
processedShapeIds.add(shape.id);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
for (const [from, to] of (0, import_utils.objectMapValues)(changes.updated)) {
|
|
98
|
+
if (!(0, import_tlschema.isShape)(to)) continue;
|
|
99
|
+
processedShapeIds.add(to.id);
|
|
100
|
+
const wasOnPage = this.editor.getAncestorPageId(from) === this.lastPageId;
|
|
101
|
+
const isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId;
|
|
102
|
+
if (isOnPage) {
|
|
103
|
+
const bounds = this.editor.getShapePageBounds(to.id);
|
|
104
|
+
if (bounds) {
|
|
105
|
+
this.rbush.upsert(to.id, bounds);
|
|
106
|
+
}
|
|
107
|
+
} else if (wasOnPage) {
|
|
108
|
+
this.rbush.remove(to.id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const allShapeIds = this.rbush.getAllShapeIds();
|
|
113
|
+
for (const shapeId of allShapeIds) {
|
|
114
|
+
if (processedShapeIds.has(shapeId)) continue;
|
|
115
|
+
const currentBounds = this.editor.getShapePageBounds(shapeId);
|
|
116
|
+
const indexedBounds = this.rbush.getBounds(shapeId);
|
|
117
|
+
if (!this.areBoundsEqual(currentBounds, indexedBounds)) {
|
|
118
|
+
if (currentBounds) {
|
|
119
|
+
this.rbush.upsert(shapeId, currentBounds);
|
|
120
|
+
} else {
|
|
121
|
+
this.rbush.remove(shapeId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
areBoundsEqual(a, b) {
|
|
127
|
+
if (!a && !b) return true;
|
|
128
|
+
if (!a || !b) return false;
|
|
129
|
+
return a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get shape IDs within the given bounds.
|
|
133
|
+
* Optimized for viewport culling queries.
|
|
134
|
+
*
|
|
135
|
+
* Note: Results are unordered. If you need z-order, combine with sorted shapes:
|
|
136
|
+
* ```ts
|
|
137
|
+
* const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)
|
|
138
|
+
* const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @param bounds - The bounds to search within
|
|
142
|
+
* @returns Unordered set of shape IDs within the bounds
|
|
143
|
+
*
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
146
|
+
getShapeIdsInsideBounds(bounds) {
|
|
147
|
+
this.spatialIndexComputed.get();
|
|
148
|
+
return this.rbush.search(bounds);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get shape IDs at a point (with optional margin).
|
|
152
|
+
* Creates a small bounding box around the point and searches the spatial index.
|
|
153
|
+
*
|
|
154
|
+
* Note: Results are unordered. If you need z-order, combine with sorted shapes:
|
|
155
|
+
* ```ts
|
|
156
|
+
* const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)
|
|
157
|
+
* const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @param point - The point to search at
|
|
161
|
+
* @param margin - The margin around the point to search (default: 0)
|
|
162
|
+
* @returns Unordered set of shape IDs that could potentially contain the point
|
|
163
|
+
*
|
|
164
|
+
* @public
|
|
165
|
+
*/
|
|
166
|
+
getShapeIdsAtPoint(point, margin = 0) {
|
|
167
|
+
this.spatialIndexComputed.get();
|
|
168
|
+
return this.rbush.search(new import_Box.Box(point.x - margin, point.y - margin, margin * 2, margin * 2));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Dispose of the spatial index manager.
|
|
172
|
+
* Clears the R-tree to prevent memory leaks.
|
|
173
|
+
*
|
|
174
|
+
* @public
|
|
175
|
+
*/
|
|
176
|
+
dispose() {
|
|
177
|
+
this.rbush.dispose();
|
|
178
|
+
this.lastPageId = null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=SpatialIndexManager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts"],
|
|
4
|
+
"sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport type { RecordsDiff } from '@tldraw/store'\nimport type { TLRecord } from '@tldraw/tlschema'\nimport { TLPageId, TLShape, TLShapeId, isShape } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport { Box } from '../../../primitives/Box'\nimport type { Editor } from '../../Editor'\nimport { RBushIndex, type SpatialElement } from './RBushIndex'\n\n/**\n * Manages spatial indexing for efficient shape location queries.\n *\n * Uses an R-tree (via RBush) to enable O(log n) spatial queries instead of O(n) iteration.\n * Handles shapes with computed bounds (arrows, groups, custom shapes) by checking all shapes'\n * bounds on each update using the reactive bounds cache.\n *\n * Key features:\n * - Incremental updates using filterHistory pattern\n * - Leverages existing bounds cache reactivity for dependency tracking\n * - Works with any custom shape type with computed bounds\n * - Per-page index (rebuilds on page change)\n * - Optimized for viewport culling queries\n *\n * @internal\n */\nexport class SpatialIndexManager {\n\tprivate rbush: RBushIndex\n\tprivate spatialIndexComputed: Computed<number>\n\tprivate lastPageId: TLPageId | null = null\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.rbush = new RBushIndex()\n\t\tthis.spatialIndexComputed = this.createSpatialIndexComputed()\n\t}\n\n\tprivate createSpatialIndexComputed() {\n\t\tconst shapeHistory = this.editor.store.query.filterHistory('shape')\n\n\t\treturn computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(_prevValue)) {\n\t\t\t\treturn this.buildFromScratch(lastComputedEpoch)\n\t\t\t}\n\n\t\t\tconst shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (shapeDiff === RESET_VALUE) {\n\t\t\t\treturn this.buildFromScratch(lastComputedEpoch)\n\t\t\t}\n\n\t\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\t\tif (this.lastPageId !== currentPageId) {\n\t\t\t\treturn this.buildFromScratch(lastComputedEpoch)\n\t\t\t}\n\n\t\t\t// No shape changes - index is already up to date\n\t\t\tif (shapeDiff.length === 0) {\n\t\t\t\treturn lastComputedEpoch\n\t\t\t}\n\n\t\t\t// Process incremental updates\n\t\t\tthis.processIncrementalUpdate(shapeDiff)\n\n\t\t\treturn lastComputedEpoch\n\t\t})\n\t}\n\n\tprivate buildFromScratch(epoch: number): number {\n\t\tthis.rbush.clear()\n\t\tthis.lastPageId = this.editor.getCurrentPageId()\n\n\t\tconst shapes = this.editor.getCurrentPageShapes()\n\t\tconst elements: SpatialElement[] = []\n\n\t\t// Collect all shape elements for bulk loading\n\t\tfor (const shape of shapes) {\n\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\tif (bounds) {\n\t\t\t\telements.push({\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tid: shape.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Bulk load for efficiency\n\t\tthis.rbush.bulkLoad(elements)\n\n\t\treturn epoch\n\t}\n\n\tprivate processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): void {\n\t\t// Track shapes we've already processed from the diff\n\t\tconst processedShapeIds = new Set<TLShapeId>()\n\n\t\t// 1. Process shape additions, removals, and updates from diff\n\t\tfor (const changes of shapeDiff) {\n\t\t\t// Handle additions (only for shapes on current page)\n\t\t\tfor (const shape of objectMapValues(changes.added) as TLShape[]) {\n\t\t\t\tif (isShape(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\t\t\tif (bounds) {\n\t\t\t\t\t\tthis.rbush.upsert(shape.id, bounds)\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle removals\n\t\t\tfor (const shape of objectMapValues(changes.removed) as TLShape[]) {\n\t\t\t\tif (isShape(shape)) {\n\t\t\t\t\tthis.rbush.remove(shape.id)\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle updated shapes: page changes and bounds updates\n\t\t\tfor (const [from, to] of objectMapValues(changes.updated) as [TLShape, TLShape][]) {\n\t\t\t\tif (!isShape(to)) continue\n\t\t\t\tprocessedShapeIds.add(to.id)\n\n\t\t\t\tconst wasOnPage = this.editor.getAncestorPageId(from) === this.lastPageId\n\t\t\t\tconst isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId\n\n\t\t\t\tif (isOnPage) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(to.id)\n\t\t\t\t\tif (bounds) {\n\t\t\t\t\t\tthis.rbush.upsert(to.id, bounds)\n\t\t\t\t\t}\n\t\t\t\t} else if (wasOnPage) {\n\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 2. Check remaining shapes in index for bounds changes\n\t\t// This handles shapes with computed bounds (arrows bound to moved shapes, groups with moved children, etc.)\n\t\tconst allShapeIds = this.rbush.getAllShapeIds()\n\n\t\tfor (const shapeId of allShapeIds) {\n\t\t\tif (processedShapeIds.has(shapeId)) continue\n\n\t\t\tconst currentBounds = this.editor.getShapePageBounds(shapeId)\n\t\t\tconst indexedBounds = this.rbush.getBounds(shapeId)\n\n\t\t\tif (!this.areBoundsEqual(currentBounds, indexedBounds)) {\n\t\t\t\tif (currentBounds) {\n\t\t\t\t\tthis.rbush.upsert(shapeId, currentBounds)\n\t\t\t\t} else {\n\t\t\t\t\tthis.rbush.remove(shapeId)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate areBoundsEqual(a: Box | undefined, b: Box | undefined): boolean {\n\t\tif (!a && !b) return true\n\t\tif (!a || !b) return false\n\t\treturn a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY\n\t}\n\n\t/**\n\t * Get shape IDs within the given bounds.\n\t * Optimized for viewport culling queries.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param bounds - The bounds to search within\n\t * @returns Unordered set of shape IDs within the bounds\n\t *\n\t * @public\n\t */\n\tgetShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(bounds)\n\t}\n\n\t/**\n\t * Get shape IDs at a point (with optional margin).\n\t * Creates a small bounding box around the point and searches the spatial index.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param point - The point to search at\n\t * @param margin - The margin around the point to search (default: 0)\n\t * @returns Unordered set of shape IDs that could potentially contain the point\n\t *\n\t * @public\n\t */\n\tgetShapeIdsAtPoint(point: { x: number; y: number }, margin = 0): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(new Box(point.x - margin, point.y - margin, margin * 2, margin * 2))\n\t}\n\n\t/**\n\t * Dispose of the spatial index manager.\n\t * Clears the R-tree to prevent memory leaks.\n\t *\n\t * @public\n\t */\n\tdispose(): void {\n\t\tthis.rbush.dispose()\n\t\tthis.lastPageId = null\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist-cjs/version.js
CHANGED
|
@@ -22,10 +22,10 @@ __export(version_exports, {
|
|
|
22
22
|
version: () => version
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(version_exports);
|
|
25
|
-
const version = "4.
|
|
25
|
+
const version = "4.4.0-canary.1e3b436e33e4";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2025-09-18T14:39:22.803Z",
|
|
28
|
-
minor: "2026-01-
|
|
29
|
-
patch: "2026-01-
|
|
28
|
+
minor: "2026-01-21T13:45:38.121Z",
|
|
29
|
+
patch: "2026-01-21T13:45:38.121Z"
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=version.js.map
|
package/dist-cjs/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '4.4.0-canary.1e3b436e33e4'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2026-01-21T13:45:38.121Z',\n\tpatch: '2026-01-21T13:45:38.121Z',\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/index.d.mts
CHANGED
|
@@ -1072,6 +1072,7 @@ export declare class Editor extends EventEmitter<TLEventMap> {
|
|
|
1072
1072
|
* @public
|
|
1073
1073
|
*/
|
|
1074
1074
|
readonly snaps: SnapManager;
|
|
1075
|
+
private readonly _spatialIndex;
|
|
1075
1076
|
/**
|
|
1076
1077
|
* A manager for the any asynchronous events and making sure they're
|
|
1077
1078
|
* cleaned up upon disposal.
|
|
@@ -2708,6 +2709,7 @@ export declare class Editor extends EventEmitter<TLEventMap> {
|
|
|
2708
2709
|
*/
|
|
2709
2710
|
getNotVisibleShapes(): Set<TLShapeId>;
|
|
2710
2711
|
private _notVisibleShapes;
|
|
2712
|
+
private _culledShapesCache;
|
|
2711
2713
|
/**
|
|
2712
2714
|
* Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
|
|
2713
2715
|
*
|
|
@@ -2757,6 +2759,7 @@ export declare class Editor extends EventEmitter<TLEventMap> {
|
|
|
2757
2759
|
hitInside?: boolean;
|
|
2758
2760
|
margin?: number;
|
|
2759
2761
|
}): TLShape[];
|
|
2762
|
+
/* Excluded from this release type: getShapeIdsInsideBounds */
|
|
2760
2763
|
/**
|
|
2761
2764
|
* Test whether a point (in the current page space) will will a shape. This method takes into account masks,
|
|
2762
2765
|
* such as when a shape is the child of a frame and is partially clipped by the frame.
|
|
@@ -6077,6 +6080,8 @@ export declare class SnapManager {
|
|
|
6077
6080
|
getCurrentCommonAncestor(): TLShapeId | undefined;
|
|
6078
6081
|
}
|
|
6079
6082
|
|
|
6083
|
+
/* Excluded from this release type: SpatialIndexManager */
|
|
6084
|
+
|
|
6080
6085
|
/** @public */
|
|
6081
6086
|
export declare class Stadium2d extends Geometry2d {
|
|
6082
6087
|
config: Omit<Geometry2dOptions, 'isClosed'> & {
|
package/dist-esm/index.mjs
CHANGED
|
@@ -110,6 +110,7 @@ import { HandleSnaps } from "./lib/editor/managers/SnapManager/HandleSnaps.mjs";
|
|
|
110
110
|
import {
|
|
111
111
|
SnapManager
|
|
112
112
|
} from "./lib/editor/managers/SnapManager/SnapManager.mjs";
|
|
113
|
+
import { SpatialIndexManager } from "./lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs";
|
|
113
114
|
import {
|
|
114
115
|
TextManager
|
|
115
116
|
} from "./lib/editor/managers/TextManager/TextManager.mjs";
|
|
@@ -304,7 +305,7 @@ import { uniq } from "./lib/utils/uniq.mjs";
|
|
|
304
305
|
import { openWindow } from "./lib/utils/window-open.mjs";
|
|
305
306
|
registerTldrawLibraryVersion(
|
|
306
307
|
"@tldraw/editor",
|
|
307
|
-
"4.
|
|
308
|
+
"4.4.0-canary.1e3b436e33e4",
|
|
308
309
|
"esm"
|
|
309
310
|
);
|
|
310
311
|
export {
|
|
@@ -382,6 +383,7 @@ export {
|
|
|
382
383
|
ShapeUtil,
|
|
383
384
|
SharedStyleMap,
|
|
384
385
|
SnapManager,
|
|
386
|
+
SpatialIndexManager,
|
|
385
387
|
Stadium2d,
|
|
386
388
|
StateNode,
|
|
387
389
|
TAB_ID,
|