@react-three-dom/core 0.1.0 → 0.2.0
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/dist/index.cjs +610 -193
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +272 -4
- package/dist/index.d.ts +272 -4
- package/dist/index.js +603 -194
- package/dist/index.js.map +1 -1
- package/package.json +11 -9
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,25 @@ var fiber = require('@react-three/fiber');
|
|
|
6
6
|
|
|
7
7
|
// src/version.ts
|
|
8
8
|
var version = "0.1.0";
|
|
9
|
+
|
|
10
|
+
// src/debug.ts
|
|
11
|
+
var _enabled = false;
|
|
12
|
+
function enableDebug(on = true) {
|
|
13
|
+
_enabled = on;
|
|
14
|
+
}
|
|
15
|
+
function isDebugEnabled() {
|
|
16
|
+
return _enabled || typeof window !== "undefined" && !!window.__R3F_DOM_DEBUG__;
|
|
17
|
+
}
|
|
18
|
+
function r3fLog(area, msg, data) {
|
|
19
|
+
if (!_enabled && !(typeof window !== "undefined" && window.__R3F_DOM_DEBUG__)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (data !== void 0) {
|
|
23
|
+
console.log(`[r3f-dom:${area}]`, msg, data);
|
|
24
|
+
} else {
|
|
25
|
+
console.log(`[r3f-dom:${area}]`, msg);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
9
28
|
function extractMetadata(obj) {
|
|
10
29
|
const meta = {
|
|
11
30
|
uuid: obj.uuid,
|
|
@@ -20,31 +39,42 @@ function extractMetadata(obj) {
|
|
|
20
39
|
childrenUuids: obj.children.map((c) => c.uuid),
|
|
21
40
|
boundsDirty: true
|
|
22
41
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
try {
|
|
43
|
+
if ("geometry" in obj) {
|
|
44
|
+
const geom = obj.geometry;
|
|
45
|
+
if (geom instanceof three.BufferGeometry) {
|
|
46
|
+
meta.geometryType = geom.type;
|
|
47
|
+
const posAttr = geom.getAttribute("position");
|
|
48
|
+
if (posAttr) {
|
|
49
|
+
meta.vertexCount = posAttr.count;
|
|
50
|
+
if (geom.index) {
|
|
51
|
+
meta.triangleCount = Math.floor(geom.index.count / 3);
|
|
52
|
+
} else {
|
|
53
|
+
meta.triangleCount = Math.floor(posAttr.count / 3);
|
|
54
|
+
}
|
|
34
55
|
}
|
|
35
56
|
}
|
|
36
57
|
}
|
|
58
|
+
} catch {
|
|
59
|
+
r3fLog("store", `extractMetadata: geometry access failed for "${obj.name || obj.uuid}"`);
|
|
37
60
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
61
|
+
try {
|
|
62
|
+
if ("material" in obj) {
|
|
63
|
+
const mat = obj.material;
|
|
64
|
+
if (mat instanceof three.Material) {
|
|
65
|
+
meta.materialType = mat.type;
|
|
66
|
+
} else if (Array.isArray(mat) && mat.length > 0) {
|
|
67
|
+
meta.materialType = mat[0].type + (mat.length > 1 ? ` (+${mat.length - 1})` : "");
|
|
68
|
+
}
|
|
44
69
|
}
|
|
70
|
+
} catch {
|
|
71
|
+
r3fLog("store", `extractMetadata: material access failed for "${obj.name || obj.uuid}"`);
|
|
45
72
|
}
|
|
46
|
-
|
|
47
|
-
|
|
73
|
+
try {
|
|
74
|
+
if (obj instanceof three.InstancedMesh) {
|
|
75
|
+
meta.instanceCount = obj.count;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
48
78
|
}
|
|
49
79
|
return meta;
|
|
50
80
|
}
|
|
@@ -53,81 +83,101 @@ function hasChanged(prev, curr) {
|
|
|
53
83
|
}
|
|
54
84
|
var _box3 = new three.Box3();
|
|
55
85
|
function inspectObject(obj, metadata) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
let worldMatrix = Array(16).fill(0);
|
|
87
|
+
let boundsMin = [0, 0, 0];
|
|
88
|
+
let boundsMax = [0, 0, 0];
|
|
89
|
+
try {
|
|
90
|
+
obj.updateWorldMatrix(true, false);
|
|
91
|
+
worldMatrix = Array.from(obj.matrixWorld.elements);
|
|
92
|
+
_box3.setFromObject(obj);
|
|
93
|
+
boundsMin = [_box3.min.x, _box3.min.y, _box3.min.z];
|
|
94
|
+
boundsMax = [_box3.max.x, _box3.max.y, _box3.max.z];
|
|
95
|
+
} catch {
|
|
96
|
+
r3fLog("store", `inspectObject: world matrix / bounds failed for "${obj.name || obj.uuid}"`);
|
|
97
|
+
}
|
|
61
98
|
const inspection = {
|
|
62
99
|
metadata,
|
|
63
100
|
worldMatrix,
|
|
64
101
|
bounds: { min: boundsMin, max: boundsMax },
|
|
65
|
-
userData: {
|
|
102
|
+
userData: {}
|
|
66
103
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
try {
|
|
105
|
+
inspection.userData = { ...obj.userData };
|
|
106
|
+
} catch {
|
|
107
|
+
r3fLog("store", `inspectObject: userData copy failed for "${obj.name || obj.uuid}"`);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
if ("geometry" in obj) {
|
|
111
|
+
const geom = obj.geometry;
|
|
112
|
+
if (geom instanceof three.BufferGeometry) {
|
|
113
|
+
const geoInspection = {
|
|
114
|
+
type: geom.type,
|
|
115
|
+
attributes: {}
|
|
78
116
|
};
|
|
117
|
+
for (const [name, attr] of Object.entries(geom.attributes)) {
|
|
118
|
+
geoInspection.attributes[name] = {
|
|
119
|
+
itemSize: attr.itemSize,
|
|
120
|
+
count: attr.count
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (geom.index) {
|
|
124
|
+
geoInspection.index = { count: geom.index.count };
|
|
125
|
+
}
|
|
126
|
+
geom.computeBoundingSphere();
|
|
127
|
+
const sphere = geom.boundingSphere;
|
|
128
|
+
if (sphere) {
|
|
129
|
+
geoInspection.boundingSphere = {
|
|
130
|
+
center: [sphere.center.x, sphere.center.y, sphere.center.z],
|
|
131
|
+
radius: sphere.radius
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
inspection.geometry = geoInspection;
|
|
79
135
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
r3fLog("store", `inspectObject: geometry inspection failed for "${obj.name || obj.uuid}"`);
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
if ("material" in obj) {
|
|
142
|
+
const rawMat = obj.material;
|
|
143
|
+
const mat = Array.isArray(rawMat) ? rawMat[0] : rawMat;
|
|
144
|
+
if (mat instanceof three.Material) {
|
|
145
|
+
const matInspection = {
|
|
146
|
+
type: mat.type,
|
|
147
|
+
transparent: mat.transparent,
|
|
148
|
+
opacity: mat.opacity,
|
|
149
|
+
side: mat.side
|
|
89
150
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if ("material" in obj) {
|
|
95
|
-
const rawMat = obj.material;
|
|
96
|
-
const mat = Array.isArray(rawMat) ? rawMat[0] : rawMat;
|
|
97
|
-
if (mat instanceof three.Material) {
|
|
98
|
-
const matInspection = {
|
|
99
|
-
type: mat.type,
|
|
100
|
-
transparent: mat.transparent,
|
|
101
|
-
opacity: mat.opacity,
|
|
102
|
-
side: mat.side
|
|
103
|
-
};
|
|
104
|
-
if ("color" in mat && mat.color instanceof three.Color) {
|
|
105
|
-
matInspection.color = "#" + mat.color.getHexString();
|
|
106
|
-
}
|
|
107
|
-
if ("map" in mat) {
|
|
108
|
-
const map = mat.map;
|
|
109
|
-
if (map) {
|
|
110
|
-
matInspection.map = map.name || map.uuid || "unnamed";
|
|
151
|
+
if ("color" in mat && mat.color instanceof three.Color) {
|
|
152
|
+
matInspection.color = "#" + mat.color.getHexString();
|
|
111
153
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
154
|
+
if ("map" in mat) {
|
|
155
|
+
const map = mat.map;
|
|
156
|
+
if (map) {
|
|
157
|
+
matInspection.map = map.name || map.uuid || "unnamed";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if ("uniforms" in mat) {
|
|
161
|
+
const uniforms = mat.uniforms;
|
|
162
|
+
matInspection.uniforms = {};
|
|
163
|
+
for (const [key, uniform] of Object.entries(uniforms)) {
|
|
164
|
+
const val = uniform.value;
|
|
165
|
+
if (val === null || val === void 0) {
|
|
166
|
+
matInspection.uniforms[key] = val;
|
|
167
|
+
} else if (typeof val === "number" || typeof val === "boolean" || typeof val === "string") {
|
|
168
|
+
matInspection.uniforms[key] = val;
|
|
169
|
+
} else if (typeof val === "object" && "toArray" in val) {
|
|
170
|
+
matInspection.uniforms[key] = val.toArray();
|
|
171
|
+
} else {
|
|
172
|
+
matInspection.uniforms[key] = `[${typeof val}]`;
|
|
173
|
+
}
|
|
126
174
|
}
|
|
127
175
|
}
|
|
176
|
+
inspection.material = matInspection;
|
|
128
177
|
}
|
|
129
|
-
inspection.material = matInspection;
|
|
130
178
|
}
|
|
179
|
+
} catch {
|
|
180
|
+
r3fLog("store", `inspectObject: material inspection failed for "${obj.name || obj.uuid}"`);
|
|
131
181
|
}
|
|
132
182
|
return inspection;
|
|
133
183
|
}
|
|
@@ -177,15 +227,25 @@ var ObjectStore = class {
|
|
|
177
227
|
nameSet.add(obj);
|
|
178
228
|
}
|
|
179
229
|
this._emit({ type: "add", object: obj, metadata: meta });
|
|
230
|
+
if (meta.testId) {
|
|
231
|
+
r3fLog("store", `Registered "${meta.testId}" (${meta.type})`);
|
|
232
|
+
}
|
|
180
233
|
return meta;
|
|
181
234
|
}
|
|
182
235
|
/**
|
|
183
236
|
* Register an entire subtree (object + all descendants).
|
|
237
|
+
* Individual objects that fail to register are skipped (logged when debug
|
|
238
|
+
* is enabled) so that one bad object doesn't prevent the rest from being
|
|
239
|
+
* tracked.
|
|
184
240
|
*/
|
|
185
241
|
registerTree(root) {
|
|
186
242
|
this._trackedRoots.add(root);
|
|
187
243
|
root.traverse((obj) => {
|
|
188
|
-
|
|
244
|
+
try {
|
|
245
|
+
this.register(obj);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
r3fLog("store", `registerTree: failed to register "${obj.name || obj.uuid}"`, err);
|
|
248
|
+
}
|
|
189
249
|
});
|
|
190
250
|
}
|
|
191
251
|
/**
|
|
@@ -210,6 +270,9 @@ var ObjectStore = class {
|
|
|
210
270
|
}
|
|
211
271
|
}
|
|
212
272
|
}
|
|
273
|
+
if (meta.testId) {
|
|
274
|
+
r3fLog("store", `Unregistered "${meta.testId}" (${meta.type})`);
|
|
275
|
+
}
|
|
213
276
|
this._emit({ type: "remove", object: obj, metadata: meta });
|
|
214
277
|
}
|
|
215
278
|
/**
|
|
@@ -227,13 +290,22 @@ var ObjectStore = class {
|
|
|
227
290
|
/**
|
|
228
291
|
* Refresh Tier 1 metadata from the live Three.js object.
|
|
229
292
|
* Returns true if any values changed.
|
|
293
|
+
* Returns false (no change) if extracting metadata throws so that the
|
|
294
|
+
* previous metadata is preserved.
|
|
230
295
|
*/
|
|
231
296
|
update(obj) {
|
|
232
297
|
const prev = this._metaByObject.get(obj);
|
|
233
298
|
if (!prev) return false;
|
|
234
|
-
|
|
299
|
+
let curr;
|
|
300
|
+
try {
|
|
301
|
+
curr = extractMetadata(obj);
|
|
302
|
+
} catch (err) {
|
|
303
|
+
r3fLog("store", `update: extractMetadata failed for "${obj.name || obj.uuid}"`, err);
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
235
306
|
if (hasChanged(prev, curr)) {
|
|
236
307
|
if (prev.testId !== curr.testId) {
|
|
308
|
+
r3fLog("store", `testId changed: "${prev.testId}" \u2192 "${curr.testId}" (${curr.type})`);
|
|
237
309
|
if (prev.testId) this._objectsByTestId.delete(prev.testId);
|
|
238
310
|
if (curr.testId) this._objectsByTestId.set(curr.testId, obj);
|
|
239
311
|
}
|
|
@@ -301,6 +373,64 @@ var ObjectStore = class {
|
|
|
301
373
|
}
|
|
302
374
|
return results;
|
|
303
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Batch lookup: get metadata for multiple objects by testId or uuid.
|
|
378
|
+
* Returns a Map from the requested id to its metadata (or null if not found).
|
|
379
|
+
* Single round-trip — much more efficient than calling getByTestId/getByUuid
|
|
380
|
+
* in a loop for BIM/CAD scenes with many objects.
|
|
381
|
+
* O(k) where k is the number of requested ids.
|
|
382
|
+
*/
|
|
383
|
+
getObjects(ids) {
|
|
384
|
+
const results = /* @__PURE__ */ new Map();
|
|
385
|
+
for (const id of ids) {
|
|
386
|
+
const meta = this.getByTestId(id) ?? this.getByUuid(id);
|
|
387
|
+
results.set(id, meta);
|
|
388
|
+
}
|
|
389
|
+
return results;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get all objects of a given Three.js type (e.g. "Mesh", "Group", "Line").
|
|
393
|
+
* Linear scan — O(n) where n is total tracked objects.
|
|
394
|
+
*/
|
|
395
|
+
getByType(type) {
|
|
396
|
+
const results = [];
|
|
397
|
+
for (const obj of this.getFlatList()) {
|
|
398
|
+
const meta = this._metaByObject.get(obj);
|
|
399
|
+
if (meta && meta.type === type) results.push(meta);
|
|
400
|
+
}
|
|
401
|
+
return results;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Get all objects that have a specific userData key.
|
|
405
|
+
* If `value` is provided, only returns objects where `userData[key]` matches.
|
|
406
|
+
* Uses JSON.stringify for deep equality on complex values.
|
|
407
|
+
* Linear scan — O(n).
|
|
408
|
+
*/
|
|
409
|
+
getByUserData(key, value) {
|
|
410
|
+
const results = [];
|
|
411
|
+
const checkValue = value !== void 0;
|
|
412
|
+
const valueJson = checkValue ? JSON.stringify(value) : "";
|
|
413
|
+
for (const obj of this.getFlatList()) {
|
|
414
|
+
if (!(key in obj.userData)) continue;
|
|
415
|
+
if (checkValue && JSON.stringify(obj.userData[key]) !== valueJson) continue;
|
|
416
|
+
const meta = this._metaByObject.get(obj);
|
|
417
|
+
if (meta) results.push(meta);
|
|
418
|
+
}
|
|
419
|
+
return results;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Count objects of a given Three.js type.
|
|
423
|
+
* More efficient than getByType().length — no array allocation.
|
|
424
|
+
* Linear scan — O(n).
|
|
425
|
+
*/
|
|
426
|
+
getCountByType(type) {
|
|
427
|
+
let count = 0;
|
|
428
|
+
for (const obj of this.getFlatList()) {
|
|
429
|
+
const meta = this._metaByObject.get(obj);
|
|
430
|
+
if (meta && meta.type === type) count++;
|
|
431
|
+
}
|
|
432
|
+
return count;
|
|
433
|
+
}
|
|
304
434
|
/** Get the raw Three.js Object3D by testId or uuid. */
|
|
305
435
|
getObject3D(idOrUuid) {
|
|
306
436
|
return this._objectsByTestId.get(idOrUuid) ?? this._objectByUuid.get(idOrUuid) ?? null;
|
|
@@ -1034,6 +1164,7 @@ function registerSubtree(obj, store, mirror) {
|
|
|
1034
1164
|
function patchObject3D(store, mirror) {
|
|
1035
1165
|
_activePairs.push({ store, mirror });
|
|
1036
1166
|
if (!_patched) {
|
|
1167
|
+
r3fLog("patch", "Patching Object3D.prototype.add and .remove");
|
|
1037
1168
|
_originalAdd = three.Object3D.prototype.add;
|
|
1038
1169
|
_originalRemove = three.Object3D.prototype.remove;
|
|
1039
1170
|
three.Object3D.prototype.add = function patchedAdd(...objects) {
|
|
@@ -1042,8 +1173,15 @@ function patchObject3D(store, mirror) {
|
|
|
1042
1173
|
if (pair) {
|
|
1043
1174
|
for (const obj of objects) {
|
|
1044
1175
|
if (obj === this) continue;
|
|
1045
|
-
|
|
1176
|
+
try {
|
|
1177
|
+
r3fLog("patch", `patchedAdd: "${obj.name || obj.type}" added to "${this.name || this.type}"`);
|
|
1178
|
+
registerSubtree(obj, pair.store, pair.mirror);
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
r3fLog("patch", `patchedAdd: failed to register "${obj.name || obj.type}"`, err);
|
|
1181
|
+
}
|
|
1046
1182
|
}
|
|
1183
|
+
pair.store.update(this);
|
|
1184
|
+
pair.store.markDirty(this);
|
|
1047
1185
|
}
|
|
1048
1186
|
return this;
|
|
1049
1187
|
};
|
|
@@ -1052,11 +1190,18 @@ function patchObject3D(store, mirror) {
|
|
|
1052
1190
|
if (pair) {
|
|
1053
1191
|
for (const obj of objects) {
|
|
1054
1192
|
if (obj === this) continue;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
pair.
|
|
1058
|
-
|
|
1193
|
+
try {
|
|
1194
|
+
r3fLog("patch", `patchedRemove: "${obj.name || obj.type}" removed from "${this.name || this.type}"`);
|
|
1195
|
+
pair.mirror.onObjectRemoved(obj);
|
|
1196
|
+
obj.traverse((child) => {
|
|
1197
|
+
pair.store.unregister(child);
|
|
1198
|
+
});
|
|
1199
|
+
} catch (err) {
|
|
1200
|
+
r3fLog("patch", `patchedRemove: failed to unregister "${obj.name || obj.type}"`, err);
|
|
1201
|
+
}
|
|
1059
1202
|
}
|
|
1203
|
+
pair.store.update(this);
|
|
1204
|
+
pair.store.markDirty(this);
|
|
1060
1205
|
}
|
|
1061
1206
|
_originalRemove.call(this, ...objects);
|
|
1062
1207
|
return this;
|
|
@@ -1077,6 +1222,7 @@ function patchObject3D(store, mirror) {
|
|
|
1077
1222
|
}
|
|
1078
1223
|
function restoreObject3D() {
|
|
1079
1224
|
if (!_patched) return;
|
|
1225
|
+
r3fLog("patch", "Restoring original Object3D.prototype.add and .remove");
|
|
1080
1226
|
if (_originalAdd) {
|
|
1081
1227
|
three.Object3D.prototype.add = _originalAdd;
|
|
1082
1228
|
_originalAdd = null;
|
|
@@ -1100,7 +1246,7 @@ function buildNodeTree(store, meta) {
|
|
|
1100
1246
|
children.push(buildNodeTree(store, childMeta));
|
|
1101
1247
|
}
|
|
1102
1248
|
}
|
|
1103
|
-
|
|
1249
|
+
const node = {
|
|
1104
1250
|
uuid: meta.uuid,
|
|
1105
1251
|
name: meta.name,
|
|
1106
1252
|
type: meta.type,
|
|
@@ -1111,6 +1257,12 @@ function buildNodeTree(store, meta) {
|
|
|
1111
1257
|
scale: [...meta.scale],
|
|
1112
1258
|
children
|
|
1113
1259
|
};
|
|
1260
|
+
if (meta.geometryType) node.geometryType = meta.geometryType;
|
|
1261
|
+
if (meta.materialType) node.materialType = meta.materialType;
|
|
1262
|
+
if (meta.vertexCount != null) node.vertexCount = meta.vertexCount;
|
|
1263
|
+
if (meta.triangleCount != null) node.triangleCount = meta.triangleCount;
|
|
1264
|
+
if (meta.instanceCount != null) node.instanceCount = meta.instanceCount;
|
|
1265
|
+
return node;
|
|
1114
1266
|
}
|
|
1115
1267
|
function findRoot(store) {
|
|
1116
1268
|
const allObjects = store.getFlatList();
|
|
@@ -1699,6 +1851,7 @@ function click3D(idOrUuid, options = {}) {
|
|
|
1699
1851
|
const camera = getCamera();
|
|
1700
1852
|
const gl = getRenderer();
|
|
1701
1853
|
const size = getCanvasSize();
|
|
1854
|
+
r3fLog("click", `click3D("${idOrUuid}") \u2014 resolving projection`);
|
|
1702
1855
|
const projection = projectToScreen(obj, camera, size);
|
|
1703
1856
|
if (!projection) {
|
|
1704
1857
|
throw new Error(
|
|
@@ -1791,6 +1944,7 @@ function hover3D(idOrUuid, options = {}) {
|
|
|
1791
1944
|
const camera = getCamera();
|
|
1792
1945
|
const gl = getRenderer();
|
|
1793
1946
|
const size = getCanvasSize();
|
|
1947
|
+
r3fLog("hover", `hover3D("${idOrUuid}") \u2014 resolving projection`);
|
|
1794
1948
|
const projection = projectToScreen(obj, camera, size);
|
|
1795
1949
|
if (!projection) {
|
|
1796
1950
|
throw new Error(
|
|
@@ -1825,6 +1979,7 @@ async function drag3D(idOrUuid, delta, options = {}) {
|
|
|
1825
1979
|
const camera = getCamera();
|
|
1826
1980
|
const gl = getRenderer();
|
|
1827
1981
|
const size = getCanvasSize();
|
|
1982
|
+
r3fLog("drag", `drag3D("${idOrUuid}") mode=${mode}`, delta);
|
|
1828
1983
|
const projection = projectToScreen(obj, camera, size);
|
|
1829
1984
|
if (!projection) {
|
|
1830
1985
|
throw new Error(
|
|
@@ -1894,6 +2049,162 @@ function pointerMiss3D(options = {}) {
|
|
|
1894
2049
|
dispatchPointerMiss(canvas, options.point);
|
|
1895
2050
|
}
|
|
1896
2051
|
|
|
2052
|
+
// src/interactions/drawPath.ts
|
|
2053
|
+
var _nextDrawPointerId = 5e3;
|
|
2054
|
+
function makeDrawPointerInit(canvas, point, pointerId, pointerType, overrides) {
|
|
2055
|
+
const rect = canvas.getBoundingClientRect();
|
|
2056
|
+
const clientX = rect.left + point.x;
|
|
2057
|
+
const clientY = rect.top + point.y;
|
|
2058
|
+
return {
|
|
2059
|
+
bubbles: true,
|
|
2060
|
+
cancelable: true,
|
|
2061
|
+
composed: true,
|
|
2062
|
+
clientX,
|
|
2063
|
+
clientY,
|
|
2064
|
+
screenX: clientX,
|
|
2065
|
+
screenY: clientY,
|
|
2066
|
+
pointerId,
|
|
2067
|
+
pointerType,
|
|
2068
|
+
isPrimary: true,
|
|
2069
|
+
button: 0,
|
|
2070
|
+
buttons: 1,
|
|
2071
|
+
width: 1,
|
|
2072
|
+
height: 1,
|
|
2073
|
+
pressure: point.pressure ?? 0.5,
|
|
2074
|
+
...overrides
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
async function drawPath(points, options = {}) {
|
|
2078
|
+
if (points.length < 2) {
|
|
2079
|
+
throw new Error(
|
|
2080
|
+
`[react-three-dom] drawPath requires at least 2 points, got ${points.length}.`
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
const {
|
|
2084
|
+
stepDelayMs = 0,
|
|
2085
|
+
pointerType = "mouse",
|
|
2086
|
+
clickAtEnd = false,
|
|
2087
|
+
canvas: explicitCanvas
|
|
2088
|
+
} = options;
|
|
2089
|
+
const canvas = explicitCanvas ?? getRenderer().domElement;
|
|
2090
|
+
const pointerId = _nextDrawPointerId++;
|
|
2091
|
+
let eventCount = 0;
|
|
2092
|
+
r3fLog("draw", `drawPath \u2014 ${points.length} points, delay=${stepDelayMs}ms, pointerType=${pointerType}`);
|
|
2093
|
+
const first = points[0];
|
|
2094
|
+
canvas.dispatchEvent(
|
|
2095
|
+
new PointerEvent("pointerdown", makeDrawPointerInit(canvas, first, pointerId, pointerType))
|
|
2096
|
+
);
|
|
2097
|
+
eventCount++;
|
|
2098
|
+
for (let i = 1; i < points.length; i++) {
|
|
2099
|
+
if (stepDelayMs > 0) {
|
|
2100
|
+
await sleep2(stepDelayMs);
|
|
2101
|
+
}
|
|
2102
|
+
canvas.dispatchEvent(
|
|
2103
|
+
new PointerEvent(
|
|
2104
|
+
"pointermove",
|
|
2105
|
+
makeDrawPointerInit(canvas, points[i], pointerId, pointerType)
|
|
2106
|
+
)
|
|
2107
|
+
);
|
|
2108
|
+
eventCount++;
|
|
2109
|
+
}
|
|
2110
|
+
const last = points[points.length - 1];
|
|
2111
|
+
canvas.dispatchEvent(
|
|
2112
|
+
new PointerEvent(
|
|
2113
|
+
"pointerup",
|
|
2114
|
+
makeDrawPointerInit(canvas, last, pointerId, pointerType, {
|
|
2115
|
+
buttons: 0,
|
|
2116
|
+
pressure: 0
|
|
2117
|
+
})
|
|
2118
|
+
)
|
|
2119
|
+
);
|
|
2120
|
+
eventCount++;
|
|
2121
|
+
if (clickAtEnd) {
|
|
2122
|
+
const rect = canvas.getBoundingClientRect();
|
|
2123
|
+
canvas.dispatchEvent(
|
|
2124
|
+
new MouseEvent("click", {
|
|
2125
|
+
bubbles: true,
|
|
2126
|
+
cancelable: true,
|
|
2127
|
+
clientX: rect.left + last.x,
|
|
2128
|
+
clientY: rect.top + last.y,
|
|
2129
|
+
button: 0
|
|
2130
|
+
})
|
|
2131
|
+
);
|
|
2132
|
+
eventCount++;
|
|
2133
|
+
}
|
|
2134
|
+
r3fLog("draw", `drawPath complete \u2014 ${eventCount} events dispatched`);
|
|
2135
|
+
return {
|
|
2136
|
+
eventCount,
|
|
2137
|
+
pointCount: points.length,
|
|
2138
|
+
startPoint: first,
|
|
2139
|
+
endPoint: last
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
function linePath(start, end, steps = 10, pressure = 0.5) {
|
|
2143
|
+
const points = [];
|
|
2144
|
+
const totalSteps = steps + 1;
|
|
2145
|
+
for (let i = 0; i <= totalSteps; i++) {
|
|
2146
|
+
const t = i / totalSteps;
|
|
2147
|
+
points.push({
|
|
2148
|
+
x: start.x + (end.x - start.x) * t,
|
|
2149
|
+
y: start.y + (end.y - start.y) * t,
|
|
2150
|
+
pressure
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
return points;
|
|
2154
|
+
}
|
|
2155
|
+
function curvePath(start, control, end, steps = 20, pressure = 0.5) {
|
|
2156
|
+
const points = [];
|
|
2157
|
+
for (let i = 0; i <= steps; i++) {
|
|
2158
|
+
const t = i / steps;
|
|
2159
|
+
const invT = 1 - t;
|
|
2160
|
+
points.push({
|
|
2161
|
+
x: invT * invT * start.x + 2 * invT * t * control.x + t * t * end.x,
|
|
2162
|
+
y: invT * invT * start.y + 2 * invT * t * control.y + t * t * end.y,
|
|
2163
|
+
pressure
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2166
|
+
return points;
|
|
2167
|
+
}
|
|
2168
|
+
function rectPath(topLeft, bottomRight, pointsPerSide = 5, pressure = 0.5) {
|
|
2169
|
+
const topRight = { x: bottomRight.x, y: topLeft.y };
|
|
2170
|
+
const bottomLeft = { x: topLeft.x, y: bottomRight.y };
|
|
2171
|
+
const sides = [
|
|
2172
|
+
[topLeft, topRight],
|
|
2173
|
+
[topRight, bottomRight],
|
|
2174
|
+
[bottomRight, bottomLeft],
|
|
2175
|
+
[bottomLeft, topLeft]
|
|
2176
|
+
];
|
|
2177
|
+
const points = [];
|
|
2178
|
+
for (const [from, to] of sides) {
|
|
2179
|
+
for (let i = 0; i < pointsPerSide; i++) {
|
|
2180
|
+
const t = i / pointsPerSide;
|
|
2181
|
+
points.push({
|
|
2182
|
+
x: from.x + (to.x - from.x) * t,
|
|
2183
|
+
y: from.y + (to.y - from.y) * t,
|
|
2184
|
+
pressure
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
points.push({ x: topLeft.x, y: topLeft.y, pressure });
|
|
2189
|
+
return points;
|
|
2190
|
+
}
|
|
2191
|
+
function circlePath(center, radiusX, radiusY, steps = 36, pressure = 0.5) {
|
|
2192
|
+
const ry = radiusY ?? radiusX;
|
|
2193
|
+
const points = [];
|
|
2194
|
+
for (let i = 0; i <= steps; i++) {
|
|
2195
|
+
const angle = i / steps * Math.PI * 2;
|
|
2196
|
+
points.push({
|
|
2197
|
+
x: center.x + Math.cos(angle) * radiusX,
|
|
2198
|
+
y: center.y + Math.sin(angle) * ry,
|
|
2199
|
+
pressure
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
return points;
|
|
2203
|
+
}
|
|
2204
|
+
function sleep2(ms) {
|
|
2205
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2206
|
+
}
|
|
2207
|
+
|
|
1897
2208
|
// src/highlight/SelectionManager.ts
|
|
1898
2209
|
var SelectionManager = class {
|
|
1899
2210
|
constructor() {
|
|
@@ -2048,10 +2359,22 @@ function projectToScreenRect(obj, camera, canvasRect) {
|
|
|
2048
2359
|
}
|
|
2049
2360
|
function exposeGlobalAPI(store) {
|
|
2050
2361
|
const api = {
|
|
2362
|
+
_ready: true,
|
|
2051
2363
|
getByTestId: (id) => store.getByTestId(id),
|
|
2052
2364
|
getByUuid: (uuid) => store.getByUuid(uuid),
|
|
2053
2365
|
getByName: (name) => store.getByName(name),
|
|
2054
2366
|
getCount: () => store.getCount(),
|
|
2367
|
+
getByType: (type) => store.getByType(type),
|
|
2368
|
+
getByUserData: (key, value) => store.getByUserData(key, value),
|
|
2369
|
+
getCountByType: (type) => store.getCountByType(type),
|
|
2370
|
+
getObjects: (ids) => {
|
|
2371
|
+
const map = store.getObjects(ids);
|
|
2372
|
+
const result = {};
|
|
2373
|
+
for (const [id, meta] of map) {
|
|
2374
|
+
result[id] = meta;
|
|
2375
|
+
}
|
|
2376
|
+
return result;
|
|
2377
|
+
},
|
|
2055
2378
|
snapshot: () => createSnapshot(store),
|
|
2056
2379
|
inspect: (idOrUuid) => store.inspect(idOrUuid),
|
|
2057
2380
|
click: (idOrUuid) => {
|
|
@@ -2066,8 +2389,8 @@ function exposeGlobalAPI(store) {
|
|
|
2066
2389
|
hover: (idOrUuid) => {
|
|
2067
2390
|
hover3D(idOrUuid);
|
|
2068
2391
|
},
|
|
2069
|
-
drag: (idOrUuid, delta) => {
|
|
2070
|
-
|
|
2392
|
+
drag: async (idOrUuid, delta) => {
|
|
2393
|
+
await drag3D(idOrUuid, delta);
|
|
2071
2394
|
},
|
|
2072
2395
|
wheel: (idOrUuid, options) => {
|
|
2073
2396
|
wheel3D(idOrUuid, options);
|
|
@@ -2075,6 +2398,10 @@ function exposeGlobalAPI(store) {
|
|
|
2075
2398
|
pointerMiss: () => {
|
|
2076
2399
|
pointerMiss3D();
|
|
2077
2400
|
},
|
|
2401
|
+
drawPath: async (points, options) => {
|
|
2402
|
+
const result = await drawPath(points, options);
|
|
2403
|
+
return { eventCount: result.eventCount, pointCount: result.pointCount };
|
|
2404
|
+
},
|
|
2078
2405
|
select: (idOrUuid) => {
|
|
2079
2406
|
const obj = store.getObject3D(idOrUuid);
|
|
2080
2407
|
if (obj && _selectionManager) _selectionManager.select(obj);
|
|
@@ -2082,13 +2409,28 @@ function exposeGlobalAPI(store) {
|
|
|
2082
2409
|
clearSelection: () => {
|
|
2083
2410
|
_selectionManager?.clearSelection();
|
|
2084
2411
|
},
|
|
2412
|
+
getSelection: () => _selectionManager ? _selectionManager.getSelected().map((o) => o.uuid) : [],
|
|
2085
2413
|
getObject3D: (idOrUuid) => store.getObject3D(idOrUuid),
|
|
2086
2414
|
version
|
|
2087
2415
|
};
|
|
2088
2416
|
window.__R3F_DOM__ = api;
|
|
2089
2417
|
}
|
|
2090
|
-
function removeGlobalAPI() {
|
|
2091
|
-
|
|
2418
|
+
function removeGlobalAPI(onlyIfEquals) {
|
|
2419
|
+
r3fLog("bridge", "removeGlobalAPI called (deferred)");
|
|
2420
|
+
if (onlyIfEquals !== void 0) {
|
|
2421
|
+
const ref = onlyIfEquals;
|
|
2422
|
+
queueMicrotask(() => {
|
|
2423
|
+
if (window.__R3F_DOM__ === ref) {
|
|
2424
|
+
delete window.__R3F_DOM__;
|
|
2425
|
+
r3fLog("bridge", "Global API removed");
|
|
2426
|
+
} else {
|
|
2427
|
+
r3fLog("bridge", "Global API not removed \u2014 replaced by new instance (Strict Mode remount)");
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
} else {
|
|
2431
|
+
delete window.__R3F_DOM__;
|
|
2432
|
+
r3fLog("bridge", "Global API removed (immediate)");
|
|
2433
|
+
}
|
|
2092
2434
|
}
|
|
2093
2435
|
function setElementRect(el, l, t, w, h) {
|
|
2094
2436
|
const d = el.dataset;
|
|
@@ -2109,7 +2451,8 @@ function ThreeDom({
|
|
|
2109
2451
|
timeBudgetMs = 0.5,
|
|
2110
2452
|
maxDomNodes = 2e3,
|
|
2111
2453
|
initialDepth = 3,
|
|
2112
|
-
enabled = true
|
|
2454
|
+
enabled = true,
|
|
2455
|
+
debug = false
|
|
2113
2456
|
} = {}) {
|
|
2114
2457
|
const scene = fiber.useThree((s) => s.scene);
|
|
2115
2458
|
const camera = fiber.useThree((s) => s.camera);
|
|
@@ -2119,6 +2462,8 @@ function ThreeDom({
|
|
|
2119
2462
|
const positionCursorRef = react.useRef(0);
|
|
2120
2463
|
react.useEffect(() => {
|
|
2121
2464
|
if (!enabled) return;
|
|
2465
|
+
if (debug) enableDebug(true);
|
|
2466
|
+
r3fLog("setup", "ThreeDom effect started", { enabled, debug, root, maxDomNodes });
|
|
2122
2467
|
const canvas = gl.domElement;
|
|
2123
2468
|
const canvasParent = canvas.parentElement;
|
|
2124
2469
|
let rootElement = null;
|
|
@@ -2145,57 +2490,115 @@ function ThreeDom({
|
|
|
2145
2490
|
"overflow: hidden",
|
|
2146
2491
|
"z-index: 10"
|
|
2147
2492
|
].join(";");
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
if (obj.
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2493
|
+
let store = null;
|
|
2494
|
+
let mirror = null;
|
|
2495
|
+
let unpatch = null;
|
|
2496
|
+
let selectionManager = null;
|
|
2497
|
+
let currentApi;
|
|
2498
|
+
try {
|
|
2499
|
+
store = new ObjectStore();
|
|
2500
|
+
mirror = new DomMirror(store, maxDomNodes);
|
|
2501
|
+
mirror.setRoot(rootElement);
|
|
2502
|
+
r3fLog("setup", "Store and mirror created");
|
|
2503
|
+
ensureCustomElements(store);
|
|
2504
|
+
store.registerTree(scene);
|
|
2505
|
+
r3fLog("setup", `Registered scene tree: ${store.getCount()} objects`);
|
|
2506
|
+
mirror.materializeSubtree(scene.uuid, initialDepth);
|
|
2507
|
+
unpatch = patchObject3D(store, mirror);
|
|
2508
|
+
setInteractionState(store, camera, gl, size);
|
|
2509
|
+
r3fLog("setup", "Object3D patched, interaction state set");
|
|
2510
|
+
selectionManager = new SelectionManager();
|
|
2511
|
+
_selectionManager = selectionManager;
|
|
2512
|
+
_highlighter = null;
|
|
2513
|
+
exposeGlobalAPI(store);
|
|
2514
|
+
r3fLog("bridge", "exposeGlobalAPI called \u2014 bridge is live, _ready=true");
|
|
2515
|
+
currentApi = window.__R3F_DOM__;
|
|
2516
|
+
_store3 = store;
|
|
2517
|
+
_mirror = mirror;
|
|
2518
|
+
const initialCanvasRect = canvas.getBoundingClientRect();
|
|
2519
|
+
const allObjects = store.getFlatList();
|
|
2520
|
+
for (const obj of allObjects) {
|
|
2521
|
+
if (obj.userData?.__r3fdom_internal) continue;
|
|
2522
|
+
const el = mirror.getElement(obj.uuid);
|
|
2523
|
+
if (!el) continue;
|
|
2524
|
+
if (obj.type === "Scene") {
|
|
2525
|
+
setElementRect(el, 0, 0, Math.round(initialCanvasRect.width), Math.round(initialCanvasRect.height));
|
|
2526
|
+
continue;
|
|
2527
|
+
}
|
|
2528
|
+
const rect = projectToScreenRect(obj, camera, initialCanvasRect);
|
|
2529
|
+
if (rect) {
|
|
2530
|
+
let parentLeft = 0;
|
|
2531
|
+
let parentTop = 0;
|
|
2532
|
+
if (obj.parent && obj.parent.type !== "Scene") {
|
|
2533
|
+
const parentRect = projectToScreenRect(obj.parent, camera, initialCanvasRect);
|
|
2534
|
+
if (parentRect) {
|
|
2535
|
+
parentLeft = Math.round(parentRect.left);
|
|
2536
|
+
parentTop = Math.round(parentRect.top);
|
|
2537
|
+
}
|
|
2181
2538
|
}
|
|
2539
|
+
setElementRect(
|
|
2540
|
+
el,
|
|
2541
|
+
Math.round(rect.left) - parentLeft,
|
|
2542
|
+
Math.round(rect.top) - parentTop,
|
|
2543
|
+
Math.round(rect.width),
|
|
2544
|
+
Math.round(rect.height)
|
|
2545
|
+
);
|
|
2182
2546
|
}
|
|
2183
|
-
setElementRect(
|
|
2184
|
-
el,
|
|
2185
|
-
Math.round(rect.left) - parentLeft,
|
|
2186
|
-
Math.round(rect.top) - parentTop,
|
|
2187
|
-
Math.round(rect.width),
|
|
2188
|
-
Math.round(rect.height)
|
|
2189
|
-
);
|
|
2190
2547
|
}
|
|
2548
|
+
} catch (err) {
|
|
2549
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2550
|
+
r3fLog("setup", "ThreeDom setup failed", err);
|
|
2551
|
+
console.error("[react-three-dom] Setup failed:", err);
|
|
2552
|
+
window.__R3F_DOM__ = {
|
|
2553
|
+
_ready: false,
|
|
2554
|
+
_error: errorMsg,
|
|
2555
|
+
getByTestId: () => null,
|
|
2556
|
+
getByUuid: () => null,
|
|
2557
|
+
getByName: () => [],
|
|
2558
|
+
getCount: () => 0,
|
|
2559
|
+
getByType: () => [],
|
|
2560
|
+
getByUserData: () => [],
|
|
2561
|
+
getCountByType: () => 0,
|
|
2562
|
+
getObjects: (ids) => {
|
|
2563
|
+
const result = {};
|
|
2564
|
+
for (const id of ids) result[id] = null;
|
|
2565
|
+
return result;
|
|
2566
|
+
},
|
|
2567
|
+
snapshot: () => ({ timestamp: 0, objectCount: 0, tree: { uuid: "", name: "", type: "Scene", visible: true, position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1], children: [] } }),
|
|
2568
|
+
inspect: () => null,
|
|
2569
|
+
click: () => {
|
|
2570
|
+
},
|
|
2571
|
+
doubleClick: () => {
|
|
2572
|
+
},
|
|
2573
|
+
contextMenu: () => {
|
|
2574
|
+
},
|
|
2575
|
+
hover: () => {
|
|
2576
|
+
},
|
|
2577
|
+
drag: async () => {
|
|
2578
|
+
},
|
|
2579
|
+
wheel: () => {
|
|
2580
|
+
},
|
|
2581
|
+
pointerMiss: () => {
|
|
2582
|
+
},
|
|
2583
|
+
drawPath: async () => ({ eventCount: 0, pointCount: 0 }),
|
|
2584
|
+
select: () => {
|
|
2585
|
+
},
|
|
2586
|
+
clearSelection: () => {
|
|
2587
|
+
},
|
|
2588
|
+
getSelection: () => [],
|
|
2589
|
+
getObject3D: () => null,
|
|
2590
|
+
version
|
|
2591
|
+
};
|
|
2592
|
+
currentApi = window.__R3F_DOM__;
|
|
2191
2593
|
}
|
|
2192
2594
|
return () => {
|
|
2193
|
-
|
|
2194
|
-
|
|
2595
|
+
r3fLog("setup", "ThreeDom cleanup started");
|
|
2596
|
+
if (unpatch) unpatch();
|
|
2597
|
+
removeGlobalAPI(currentApi);
|
|
2195
2598
|
clearInteractionState();
|
|
2196
|
-
selectionManager.dispose();
|
|
2197
|
-
mirror.dispose();
|
|
2198
|
-
store.dispose();
|
|
2599
|
+
if (selectionManager) selectionManager.dispose();
|
|
2600
|
+
if (mirror) mirror.dispose();
|
|
2601
|
+
if (store) store.dispose();
|
|
2199
2602
|
if (createdRoot && rootElement?.parentNode) {
|
|
2200
2603
|
rootElement.parentNode.removeChild(rootElement);
|
|
2201
2604
|
}
|
|
@@ -2203,69 +2606,75 @@ function ThreeDom({
|
|
|
2203
2606
|
_mirror = null;
|
|
2204
2607
|
_selectionManager = null;
|
|
2205
2608
|
_highlighter = null;
|
|
2609
|
+
if (debug) enableDebug(false);
|
|
2610
|
+
r3fLog("setup", "ThreeDom cleanup complete");
|
|
2206
2611
|
};
|
|
2207
|
-
}, [scene, camera, gl, size, enabled, root, maxDomNodes, initialDepth]);
|
|
2612
|
+
}, [scene, camera, gl, size, enabled, root, maxDomNodes, initialDepth, debug]);
|
|
2208
2613
|
fiber.useFrame(() => {
|
|
2209
2614
|
if (!enabled || !_store3 || !_mirror) return;
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
const budgetRemaining = timeBudgetMs - (performance.now() - start);
|
|
2222
|
-
if (budgetRemaining > 0.1) {
|
|
2223
|
-
const objects2 = store.getFlatList();
|
|
2224
|
-
if (objects2.length > 0) {
|
|
2225
|
-
const end = Math.min(cursorRef.current + batchSize, objects2.length);
|
|
2226
|
-
for (let i = cursorRef.current; i < end; i++) {
|
|
2227
|
-
if (performance.now() - start > timeBudgetMs) break;
|
|
2228
|
-
const obj = objects2[i];
|
|
2229
|
-
const changed = store.update(obj);
|
|
2230
|
-
if (changed) mirror.syncAttributes(obj);
|
|
2231
|
-
}
|
|
2232
|
-
cursorRef.current = end >= objects2.length ? 0 : end;
|
|
2615
|
+
try {
|
|
2616
|
+
setInteractionState(_store3, camera, gl, size);
|
|
2617
|
+
const store = _store3;
|
|
2618
|
+
const mirror = _mirror;
|
|
2619
|
+
const canvas = gl.domElement;
|
|
2620
|
+
const canvasRect = canvas.getBoundingClientRect();
|
|
2621
|
+
const start = performance.now();
|
|
2622
|
+
const dirtyObjects = store.drainDirtyQueue();
|
|
2623
|
+
for (const obj of dirtyObjects) {
|
|
2624
|
+
store.update(obj);
|
|
2625
|
+
mirror.syncAttributes(obj);
|
|
2233
2626
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2627
|
+
const budgetRemaining = timeBudgetMs - (performance.now() - start);
|
|
2628
|
+
if (budgetRemaining > 0.1) {
|
|
2629
|
+
const objects2 = store.getFlatList();
|
|
2630
|
+
if (objects2.length > 0) {
|
|
2631
|
+
const end = Math.min(cursorRef.current + batchSize, objects2.length);
|
|
2632
|
+
for (let i = cursorRef.current; i < end; i++) {
|
|
2633
|
+
if (performance.now() - start > timeBudgetMs) break;
|
|
2634
|
+
const obj = objects2[i];
|
|
2635
|
+
const changed = store.update(obj);
|
|
2636
|
+
if (changed) mirror.syncAttributes(obj);
|
|
2637
|
+
}
|
|
2638
|
+
cursorRef.current = end >= objects2.length ? 0 : end;
|
|
2246
2639
|
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2640
|
+
}
|
|
2641
|
+
const objects = store.getFlatList();
|
|
2642
|
+
if (objects.length > 0) {
|
|
2643
|
+
const posEnd = Math.min(positionCursorRef.current + 50, objects.length);
|
|
2644
|
+
for (let i = positionCursorRef.current; i < posEnd; i++) {
|
|
2645
|
+
const obj = objects[i];
|
|
2646
|
+
if (obj.userData?.__r3fdom_internal) continue;
|
|
2647
|
+
const el = mirror.getElement(obj.uuid);
|
|
2648
|
+
if (!el) continue;
|
|
2649
|
+
if (obj.type === "Scene") {
|
|
2650
|
+
setElementRect(el, 0, 0, Math.round(canvasRect.width), Math.round(canvasRect.height));
|
|
2651
|
+
continue;
|
|
2652
|
+
}
|
|
2653
|
+
const rect = projectToScreenRect(obj, camera, canvasRect);
|
|
2654
|
+
if (rect) {
|
|
2655
|
+
let parentLeft = 0;
|
|
2656
|
+
let parentTop = 0;
|
|
2657
|
+
if (obj.parent && obj.parent.type !== "Scene") {
|
|
2658
|
+
const parentRect = projectToScreenRect(obj.parent, camera, canvasRect);
|
|
2659
|
+
if (parentRect) {
|
|
2660
|
+
parentLeft = Math.round(parentRect.left);
|
|
2661
|
+
parentTop = Math.round(parentRect.top);
|
|
2662
|
+
}
|
|
2256
2663
|
}
|
|
2664
|
+
const l = Math.round(rect.left) - parentLeft;
|
|
2665
|
+
const t = Math.round(rect.top) - parentTop;
|
|
2666
|
+
const w = Math.round(rect.width);
|
|
2667
|
+
const h = Math.round(rect.height);
|
|
2668
|
+
setElementRect(el, l, t, w, h);
|
|
2669
|
+
if (el.style.display === "none") el.style.display = "block";
|
|
2670
|
+
} else {
|
|
2671
|
+
if (el.style.display !== "none") el.style.display = "none";
|
|
2257
2672
|
}
|
|
2258
|
-
const l = Math.round(rect.left) - parentLeft;
|
|
2259
|
-
const t = Math.round(rect.top) - parentTop;
|
|
2260
|
-
const w = Math.round(rect.width);
|
|
2261
|
-
const h = Math.round(rect.height);
|
|
2262
|
-
setElementRect(el, l, t, w, h);
|
|
2263
|
-
if (el.style.display === "none") el.style.display = "block";
|
|
2264
|
-
} else {
|
|
2265
|
-
if (el.style.display !== "none") el.style.display = "none";
|
|
2266
2673
|
}
|
|
2674
|
+
positionCursorRef.current = posEnd >= objects.length ? 0 : posEnd;
|
|
2267
2675
|
}
|
|
2268
|
-
|
|
2676
|
+
} catch (err) {
|
|
2677
|
+
r3fLog("sync", "Per-frame sync error", err);
|
|
2269
2678
|
}
|
|
2270
2679
|
});
|
|
2271
2680
|
return null;
|
|
@@ -2625,11 +3034,13 @@ exports.TAG_MAP = TAG_MAP;
|
|
|
2625
3034
|
exports.ThreeDom = ThreeDom;
|
|
2626
3035
|
exports.ThreeElement = ThreeElement;
|
|
2627
3036
|
exports.applyAttributes = applyAttributes;
|
|
3037
|
+
exports.circlePath = circlePath;
|
|
2628
3038
|
exports.click3D = click3D;
|
|
2629
3039
|
exports.computeAttributes = computeAttributes;
|
|
2630
3040
|
exports.contextMenu3D = contextMenu3D;
|
|
2631
3041
|
exports.createFlatSnapshot = createFlatSnapshot;
|
|
2632
3042
|
exports.createSnapshot = createSnapshot;
|
|
3043
|
+
exports.curvePath = curvePath;
|
|
2633
3044
|
exports.dispatchClick = dispatchClick;
|
|
2634
3045
|
exports.dispatchContextMenu = dispatchContextMenu;
|
|
2635
3046
|
exports.dispatchDoubleClick = dispatchDoubleClick;
|
|
@@ -2640,6 +3051,8 @@ exports.dispatchUnhover = dispatchUnhover;
|
|
|
2640
3051
|
exports.dispatchWheel = dispatchWheel;
|
|
2641
3052
|
exports.doubleClick3D = doubleClick3D;
|
|
2642
3053
|
exports.drag3D = drag3D;
|
|
3054
|
+
exports.drawPath = drawPath;
|
|
3055
|
+
exports.enableDebug = enableDebug;
|
|
2643
3056
|
exports.ensureCustomElements = ensureCustomElements;
|
|
2644
3057
|
exports.getHighlighter = getHighlighter;
|
|
2645
3058
|
exports.getMirror = getMirror;
|
|
@@ -2647,13 +3060,17 @@ exports.getSelectionManager = getSelectionManager;
|
|
|
2647
3060
|
exports.getStore = getStore2;
|
|
2648
3061
|
exports.getTagForType = getTagForType;
|
|
2649
3062
|
exports.hover3D = hover3D;
|
|
3063
|
+
exports.isDebugEnabled = isDebugEnabled;
|
|
2650
3064
|
exports.isInFrustum = isInFrustum;
|
|
2651
3065
|
exports.isPatched = isPatched;
|
|
3066
|
+
exports.linePath = linePath;
|
|
2652
3067
|
exports.patchObject3D = patchObject3D;
|
|
2653
3068
|
exports.pointerMiss3D = pointerMiss3D;
|
|
2654
3069
|
exports.previewDragWorldDelta = previewDragWorldDelta;
|
|
2655
3070
|
exports.projectAllSamplePoints = projectAllSamplePoints;
|
|
2656
3071
|
exports.projectToScreen = projectToScreen;
|
|
3072
|
+
exports.r3fLog = r3fLog;
|
|
3073
|
+
exports.rectPath = rectPath;
|
|
2657
3074
|
exports.resolveObject = resolveObject;
|
|
2658
3075
|
exports.restoreObject3D = restoreObject3D;
|
|
2659
3076
|
exports.screenDeltaToWorld = screenDeltaToWorld;
|