@nebula-spatial/viewer 0.2.4 → 0.4.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.js CHANGED
@@ -1,3973 +1,6 @@
1
- var ze = Object.defineProperty;
2
- var He = (n, e, t) => e in n ? ze(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
3
- var a = (n, e, t) => He(n, typeof e != "symbol" ? e + "" : e, t);
4
- import { Texture as Ye, Vector3 as w, MathUtils as R, Scene as ee, Group as Ie, Raycaster as Xe, Vector2 as q, WebGLRenderer as _e, Color as qe, Euler as We, OrthographicCamera as te, PerspectiveCamera as $e, MOUSE as W, TOUCH as ie, Vector4 as je, Plane as Ue, WebGLRenderTarget as Ke, Box3 as S, Object3D as Ge, PMREMGenerator as Ze, EquirectangularReflectionMapping as Je, BoxHelper as Qe, Box3Helper as et, BoxGeometry as tt, Mesh as it, MeshBasicMaterial as rt, LineSegments as nt, EdgesGeometry as st, LineBasicMaterial as ot } from "three";
5
- import { RoomEnvironment as at } from "three/addons/environments/RoomEnvironment.js";
6
- import { MapControls as lt } from "three/addons/controls/MapControls.js";
7
- import { OrbitControls as ct } from "three/addons/controls/OrbitControls.js";
8
- import { HDRLoader as dt } from "three/addons/loaders/HDRLoader.js";
9
- import { createCadDocumentSession as ht, getCadDocumentRenderResource as ut, formatCadInsertId as pt } from "@nebula-spatial/cad-loader";
10
- import { configureCadTextBuilder as _i } from "@nebula-spatial/cad-loader";
11
- const mt = [
12
- "alphaMap",
13
- "aoMap",
14
- "bumpMap",
15
- "clearcoatMap",
16
- "clearcoatNormalMap",
17
- "clearcoatRoughnessMap",
18
- "displacementMap",
19
- "emissiveMap",
20
- "envMap",
21
- "gradientMap",
22
- "iridescenceMap",
23
- "iridescenceThicknessMap",
24
- "lightMap",
25
- "map",
26
- "metalnessMap",
27
- "normalMap",
28
- "roughnessMap",
29
- "sheenColorMap",
30
- "sheenRoughnessMap",
31
- "specularColorMap",
32
- "specularIntensityMap",
33
- "specularMap",
34
- "thicknessMap",
35
- "transmissionMap"
36
- ];
37
- function X(n, e) {
38
- e.has(n) || (e.add(n), n.dispose());
39
- }
40
- function vt(n, e, t) {
41
- if (e) {
42
- const i = n;
43
- for (const r of mt) {
44
- const s = i[r];
45
- s instanceof Ye && X(s, t);
46
- }
47
- }
48
- X(n, t);
49
- }
50
- function re(n, e = {}) {
51
- const t = e.disposedResources ?? /* @__PURE__ */ new WeakSet(), i = e.disposeTextures !== !1;
52
- n.traverse((r) => {
53
- const s = r;
54
- s.geometry && X(s.geometry, t);
55
- const o = Array.isArray(s.material) ? s.material : [s.material];
56
- for (const c of o)
57
- c && vt(c, i, t);
58
- s.renderTarget && X(s.renderTarget, t);
59
- });
60
- }
61
- function ft(n) {
62
- const {
63
- dxPx: e,
64
- dyPx: t,
65
- viewportWidthPx: i,
66
- viewportHeightPx: r,
67
- orbitDistance: s,
68
- fovDeg: o,
69
- aspect: c,
70
- orbitYaw: l,
71
- panSpeed: d
72
- } = n;
73
- if (!Number.isFinite(e) || !Number.isFinite(t) || !Number.isFinite(i) || !Number.isFinite(r) || !Number.isFinite(s) || !Number.isFinite(o) || !Number.isFinite(c) || !Number.isFinite(l) || !Number.isFinite(d) || i <= 0 || r <= 0 || s <= 0 || o <= 0 || o >= 180 || c <= 0)
74
- return { x: 0, y: 0, z: 0 };
75
- const h = 2 * s * Math.tan(o * Math.PI / 360), u = h / r, f = h * c / i, p = Math.sin(l), m = Math.cos(l), b = -p, g = m, y = -m, P = -p, x = -e * f * d, E = t * u * d;
76
- return {
77
- x: b * x + y * E,
78
- y: g * x + P * E,
79
- z: 0
80
- };
81
- }
82
- function bt(n, e) {
83
- return !Number.isFinite(n.x) || !Number.isFinite(n.y) || !Number.isFinite(n.z) || !Number.isFinite(e.x) || !Number.isFinite(e.y) || !Number.isFinite(e.z) ? { x: 0, y: 0, z: 0 } : {
84
- x: n.x - e.x,
85
- y: n.y - e.y,
86
- z: 0
87
- };
88
- }
89
- function gt(n, e, t) {
90
- if (!Number.isFinite(n.x) || !Number.isFinite(n.y) || !Number.isFinite(n.z))
91
- return { x: 0, y: 0, z: 0 };
92
- const i = bt(e, t);
93
- return {
94
- x: n.x + i.x,
95
- y: n.y + i.y,
96
- z: n.z
97
- };
98
- }
99
- const yt = 0.35, L = 1, xt = 1, wt = 170;
100
- function N(n, e, t) {
101
- return Math.max(e, Math.min(t, n));
102
- }
103
- function M(n, e, t, i) {
104
- const r = Number.isFinite(n) ? n : e;
105
- return N(r, t, i);
106
- }
107
- function Me(n) {
108
- return Number.isFinite(n) ? N(n, 0.1, 0.95) : yt;
109
- }
110
- function Ct(n, e) {
111
- return !Number.isFinite(n) || !Number.isFinite(e) || n <= 0 || e <= 0 ? 1 : n / e;
112
- }
113
- function ke(n) {
114
- const e = M(n, 50, xt, wt), t = Math.tan(e * Math.PI / 360);
115
- return Math.max(t, 1e-4);
116
- }
117
- function Pt(n) {
118
- return Number.isFinite(n) ? Math.max(0.2, Math.abs(Math.sin(n))) : 0.2;
119
- }
120
- function St(n) {
121
- return Number.isFinite(n) ? {
122
- sinAbs: Math.max(1e-4, Math.abs(Math.sin(n))),
123
- cosAbs: Math.abs(Math.cos(n))
124
- } : {
125
- sinAbs: 0.2,
126
- cosAbs: Math.sqrt(1 - 0.2 * 0.2)
127
- };
128
- }
129
- function Te(n, e, t) {
130
- const i = Math.max(1e-4, 2 * n * e / Pt(t)), { sinAbs: r, cosAbs: s } = St(t), o = n * e, c = r * r - o * o * s * s;
131
- if (!Number.isFinite(c) || c <= 1e-4)
132
- return i;
133
- const l = 2 * o * r / c;
134
- return !Number.isFinite(l) || l <= 1e-4 ? i : l;
135
- }
136
- function Rt(n) {
137
- const e = Me(n.ndcBand), t = n.useFixedOrientation ? n.fixedOrbitYaw : n.orbitYaw, i = n.useFixedOrientation ? n.fixedOrbitPitch : n.orbitPitch, r = Math.max(M(n.orthoFrustumSize, L, L, Number.MAX_VALUE), L), s = ke(n.perspectiveFovDeg), o = Te(e, s, i), c = M(n.orbitDistanceMin, 500, 1, Number.MAX_VALUE), l = Math.max(c, M(n.orbitDistanceMax, 2e5, c, Number.MAX_VALUE)), d = N(r / o, c, l);
138
- return {
139
- orbitTarget: { x: n.orthoCenterX, y: n.orthoCenterY, z: 0 },
140
- orbitDistance: d,
141
- orbitYaw: t,
142
- orbitPitch: i
143
- };
144
- }
145
- function Et(n) {
146
- const e = Ct(n.viewportWidthPx, n.viewportHeightPx), t = Me(n.ndcBand), i = ke(n.perspectiveFovDeg), r = Te(t, i, n.orbitPitch), s = M(n.orthoFrustumMin, 1e3, L, Number.MAX_VALUE), o = Math.max(s, M(n.orthoFrustumMax, 1e5, s, Number.MAX_VALUE)), c = Math.max(M(n.orbitDistance, s, s, Number.MAX_VALUE), L), l = c * r, d = Number.isFinite(l) && l >= L ? l : N(c / Math.max(0.5, Math.min(2, e)), s, o);
147
- return {
148
- orthoCenterX: n.orbitTarget.x,
149
- orthoCenterY: n.orbitTarget.y,
150
- orthoFrustumSize: N(d, s, o)
151
- };
152
- }
153
- function It(n) {
154
- const e = Math.max(n.xmax - n.xmin, n.ymax - n.ymin, 1), t = Math.max(1e-4, Math.tan(n.fovDeg * Math.PI / 360)), i = Math.max(1e-4, Math.abs(Math.sin(n.orbitPitchRad))), r = Math.abs(Math.cos(n.orbitPitchRad)), s = n.ndcBand * t, o = i * i - s * s * r * r, c = Math.max(
155
- 1e-4,
156
- 2 * n.ndcBand * t / Math.max(0.2, i)
157
- ), l = Number.isFinite(o) && o > 1e-4 ? Math.max(1e-4, 2 * s * i / o) : c;
158
- return e / l;
159
- }
160
- function Mt(n, e, t, i = 40) {
161
- return e === 1 ? n * i : e === 2 ? n * t : n;
162
- }
163
- function kt(n) {
164
- const { valuePx: e, ctrlKey: t, eventTimeMs: i, state: r, resetMs: s } = n, o = Math.abs(e), c = r.lastEventTimeMs === 0 ? 0 : i - r.lastEventTimeMs;
165
- let l = r.type;
166
- if (t)
167
- l = "trackpad";
168
- else if (o > 0 && o < 4)
169
- l = "trackpad";
170
- else if (r.lastEventTimeMs === 0 || c > s)
171
- l = "wheel";
172
- else if (!l) {
173
- const d = Math.abs(e - r.lastValue);
174
- l = o >= 4 && d <= 4 ? "wheel" : "trackpad";
175
- }
176
- return {
177
- lastEventTimeMs: i,
178
- lastValue: e,
179
- type: l
180
- };
181
- }
182
- function Tt(n, e, t) {
183
- if (!Number.isFinite(n) || n === 0) return 1;
184
- const i = e === "wheel" ? t.wheelZoomRate : t.trackpadZoomRate;
185
- if (e === "wheel") {
186
- const o = Math.log(t.maxScalePerFrame), c = Math.max(-o, Math.min(o, n * i));
187
- return Math.exp(c);
188
- }
189
- let r = t.maxScalePerFrame / (1 + Math.exp(-Math.abs(n * i)));
190
- n < 0 && r !== 0 && (r = 1 / r);
191
- const s = 1 / t.maxScalePerFrame;
192
- return Math.max(s, Math.min(t.maxScalePerFrame, r));
193
- }
194
- const $ = 500, ne = 2e5, j = 0.95, se = -Math.PI / 2, oe = R.degToRad(55);
195
- function k(n, e, t = 1e-4) {
196
- return Math.abs(n - e) <= t;
197
- }
198
- class Dt {
199
- constructor() {
200
- a(this, "orbitTarget", new w(0, 0, 0));
201
- a(this, "orbitDistance", 14e3);
202
- a(this, "orbitYaw", se);
203
- a(this, "orbitPitch", oe);
204
- a(this, "viewportSnapshotBefore3D", null);
205
- a(this, "orbitSnapshotOn3DEntry", null);
206
- a(this, "dragMode", null);
207
- a(this, "dragPointerId", null);
208
- a(this, "dragPrevX", 0);
209
- a(this, "dragPrevY", 0);
210
- a(this, "groundPanAnchor", null);
211
- a(this, "wheelClassifier", {
212
- lastEventTimeMs: 0,
213
- lastValue: 0,
214
- type: null
215
- });
216
- a(this, "runtime", null);
217
- }
218
- attach(e) {
219
- this.runtime = e, e.controls3d.enabled = !1, this.bindInput(e), this.syncPerspectiveCamera();
220
- }
221
- dispose() {
222
- this.clearDrag(), this.runtime = null;
223
- }
224
- setViewMode(e, t = {}) {
225
- const i = this.runtime;
226
- if (!i) return;
227
- const r = t.preserveOrbit === !0;
228
- if (this.clearDrag(), i.controls3d.enabled = !1, e === "3d")
229
- this.viewportSnapshotBefore3D = this.capture2DSnapshot(), i.controls2d.enabled = !1, r || this.syncOrbitFrom2D(!0), this.syncPerspectiveCamera(), this.orbitSnapshotOn3DEntry = this.captureOrbitSnapshot();
230
- else {
231
- if (i.controls2d.enabled = !0, !r) {
232
- const s = this.viewportSnapshotBefore3D;
233
- s && this.isOrbitUnchangedFrom3DEntry() ? i.setOrthographicViewport(s) : this.sync2DFromOrbit();
234
- }
235
- this.viewportSnapshotBefore3D = null, this.orbitSnapshotOn3DEntry = null;
236
- }
237
- }
238
- fit3D(e) {
239
- const t = this.runtime;
240
- if (!t) return;
241
- const i = e.getSize(new w()), r = e.getCenter(new w());
242
- this.orbitTarget.set(r.x, r.y, 0);
243
- const s = It({
244
- xmin: e.min.x,
245
- ymin: e.min.y,
246
- xmax: e.max.x,
247
- ymax: e.max.y,
248
- fovDeg: t.camera3d.fov,
249
- orbitPitchRad: this.orbitPitch,
250
- ndcBand: j
251
- }), o = t.camera3d.fov * Math.PI / 180, c = Math.max(i.length() / 2, 1e-6) / Math.max(Math.sin(o / 2), 1e-6) * 1.05;
252
- this.orbitDistance = this.clampDistance(Math.max(s, c)), this.syncPerspectiveCamera();
253
- }
254
- getVisibleDocumentRect() {
255
- const e = this.runtime;
256
- if (!e) return null;
257
- if (e.viewMode === "2d") {
258
- const t = e.camera2d, i = t.zoom > 0 ? t.zoom : 1, r = (t.right - t.left) / (2 * i), s = (t.top - t.bottom) / (2 * i);
259
- return {
260
- minX: t.position.x - r,
261
- minY: t.position.y - s,
262
- maxX: t.position.x + r,
263
- maxY: t.position.y + s
264
- };
265
- }
266
- return this.getPerspectiveDocumentRect();
267
- }
268
- captureOrbitSnapshot() {
269
- return {
270
- targetX: this.orbitTarget.x,
271
- targetY: this.orbitTarget.y,
272
- targetZ: this.orbitTarget.z,
273
- distance: this.orbitDistance,
274
- yaw: this.orbitYaw,
275
- pitch: this.orbitPitch
276
- };
277
- }
278
- applyOrbitSnapshot(e) {
279
- this.runtime && (this.orbitTarget.set(e.targetX, e.targetY, e.targetZ), this.orbitDistance = this.clampDistance(e.distance), this.orbitYaw = e.yaw, this.orbitPitch = R.clamp(e.pitch, R.degToRad(5), R.degToRad(85)), this.syncPerspectiveCamera({ sync2D: !1 }));
280
- }
281
- bindInput(e) {
282
- const t = e.renderer.domElement;
283
- e.bindRuntimeEvent(t, "contextmenu", (r) => {
284
- e.viewMode === "3d" && r.preventDefault();
285
- }), e.bindRuntimeEvent(t, "pointerdown", (r) => {
286
- var c;
287
- const s = r;
288
- if (e.viewMode !== "3d") return;
289
- const o = s.button === 0 ? "pan" : s.button === 2 ? "rotate" : null;
290
- !o || this.dragPointerId !== null || ((c = s.preventDefault) == null || c.call(s), this.beginDrag(o, s.pointerId, s.clientX, s.clientY));
291
- });
292
- const i = typeof globalThis.window > "u" ? t : globalThis.window;
293
- e.bindRuntimeEvent(i, "pointermove", (r) => {
294
- this.handlePointerMove(r);
295
- }), e.bindRuntimeEvent(i, "pointerup", (r) => {
296
- this.endDrag(r);
297
- }), e.bindRuntimeEvent(i, "pointercancel", (r) => {
298
- this.endDrag(r);
299
- }), e.bindRuntimeEvent(t, "lostpointercapture", (r) => {
300
- const s = r;
301
- this.dragPointerId === s.pointerId && this.clearDrag({ releaseCapture: !1 });
302
- }), i !== t && e.bindRuntimeEvent(i, "blur", () => this.clearDrag()), e.bindRuntimeEvent(t, "wheel", (r) => {
303
- this.handleWheel(r);
304
- }, { passive: !1 });
305
- }
306
- beginDrag(e, t, i, r) {
307
- var o;
308
- this.dragMode = e, this.dragPointerId = t, this.dragPrevX = i, this.dragPrevY = r, this.groundPanAnchor = e === "pan" ? this.screenToGround(i, r) : null;
309
- const s = (o = this.runtime) == null ? void 0 : o.renderer.domElement;
310
- if (s && typeof s.setPointerCapture == "function")
311
- try {
312
- s.setPointerCapture(t);
313
- } catch {
314
- }
315
- }
316
- handlePointerMove(e) {
317
- const t = this.runtime;
318
- if (!(!t || t.viewMode !== "3d" || !this.dragMode || e.pointerId !== this.dragPointerId)) {
319
- if (this.dragMode === "pan") {
320
- const i = this.screenToGround(e.clientX, e.clientY);
321
- if (this.groundPanAnchor && i) {
322
- const r = gt(this.orbitTarget, this.groundPanAnchor, i);
323
- this.translateOrbit(r.x - this.orbitTarget.x, r.y - this.orbitTarget.y);
324
- } else
325
- this.panByScreenDelta(
326
- e.clientX - this.dragPrevX,
327
- e.clientY - this.dragPrevY
328
- );
329
- } else
330
- this.rotateByScreenDelta(e.clientX - this.dragPrevX, e.clientY - this.dragPrevY);
331
- this.dragPrevX = e.clientX, this.dragPrevY = e.clientY;
332
- }
333
- }
334
- endDrag(e) {
335
- e.pointerId === this.dragPointerId && this.clearDrag();
336
- }
337
- handleWheel(e) {
338
- var d;
339
- const t = this.runtime;
340
- if (!t || t.viewMode !== "3d") return;
341
- (d = e.preventDefault) == null || d.call(e);
342
- const i = Math.max(1, t.canvasArea.clientHeight), r = Mt(e.deltaY, e.deltaMode, i);
343
- this.wheelClassifier = kt({
344
- valuePx: r,
345
- ctrlKey: e.ctrlKey,
346
- eventTimeMs: typeof performance > "u" ? Date.now() : performance.now(),
347
- state: this.wheelClassifier,
348
- resetMs: 400
349
- });
350
- const s = this.wheelClassifier.type ?? "trackpad", o = s === "wheel" ? Math.sign(r) * 100 : r, c = Tt(-o, s, {
351
- trackpadZoomRate: 1 / 700,
352
- wheelZoomRate: 1 / 3200,
353
- maxScalePerFrame: 2
354
- });
355
- if (!Number.isFinite(c) || c <= 0) return;
356
- const l = this.clampDistance(this.orbitDistance / c);
357
- l !== this.orbitDistance && (this.orbitDistance = l, this.syncPerspectiveCamera());
358
- }
359
- panByScreenDelta(e, t) {
360
- const i = this.runtime;
361
- if (!i) return;
362
- const r = ft({
363
- dxPx: e,
364
- dyPx: t,
365
- viewportWidthPx: Math.max(1, i.canvasArea.clientWidth),
366
- viewportHeightPx: Math.max(1, i.canvasArea.clientHeight),
367
- orbitDistance: this.orbitDistance,
368
- fovDeg: i.camera3d.fov,
369
- aspect: i.camera3d.aspect > 0 ? i.camera3d.aspect : Math.max(1, i.canvasArea.clientWidth) / Math.max(1, i.canvasArea.clientHeight),
370
- orbitYaw: this.orbitYaw,
371
- panSpeed: 1
372
- });
373
- this.translateOrbit(r.x, r.y);
374
- }
375
- rotateByScreenDelta(e, t) {
376
- const i = this.orbitYaw - e * 5e-3, r = R.clamp(
377
- this.orbitPitch + t * 5e-3,
378
- R.degToRad(5),
379
- R.degToRad(85)
380
- );
381
- i === this.orbitYaw && r === this.orbitPitch || (this.orbitYaw = i, this.orbitPitch = r, this.syncPerspectiveCamera());
382
- }
383
- translateOrbit(e, t) {
384
- !Number.isFinite(e) || !Number.isFinite(t) || e === 0 && t === 0 || (this.orbitTarget.x += e, this.orbitTarget.y += t, this.orbitTarget.z = 0, this.syncPerspectiveCamera());
385
- }
386
- screenToGround(e, t) {
387
- const i = this.runtime;
388
- return i ? i.screenToWorldOnPlane(e, t, 0) : null;
389
- }
390
- syncOrbitFrom2D(e) {
391
- var c, l;
392
- const t = this.runtime;
393
- if (!t) return;
394
- const i = t.camera2d, r = i.zoom > 0 ? i.zoom : 1, s = Math.max((i.top - i.bottom) / r, 1), o = Rt({
395
- orthoCenterX: ((c = t.controls2d.target) == null ? void 0 : c.x) ?? i.position.x,
396
- orthoCenterY: ((l = t.controls2d.target) == null ? void 0 : l.y) ?? i.position.y,
397
- orthoFrustumSize: s,
398
- viewportWidthPx: Math.max(1, t.canvasArea.clientWidth),
399
- viewportHeightPx: Math.max(1, t.canvasArea.clientHeight),
400
- perspectiveFovDeg: t.camera3d.fov,
401
- orbitYaw: this.orbitYaw,
402
- orbitPitch: this.orbitPitch,
403
- orbitDistanceMin: $,
404
- orbitDistanceMax: ne,
405
- useFixedOrientation: e,
406
- fixedOrbitYaw: se,
407
- fixedOrbitPitch: oe,
408
- ndcBand: j
409
- });
410
- this.orbitTarget.set(o.orbitTarget.x, o.orbitTarget.y, o.orbitTarget.z), this.orbitDistance = this.clampDistance(o.orbitDistance), this.orbitYaw = o.orbitYaw, this.orbitPitch = o.orbitPitch;
411
- }
412
- sync2DFromOrbit() {
413
- const e = this.runtime;
414
- if (!e) return;
415
- const t = e.camera2d, i = Math.max(1, e.canvasArea.clientWidth), r = Math.max(1, e.canvasArea.clientHeight), s = i / r, o = t.top - t.bottom, c = o > 0 ? o : 2, l = e.controls2d.minZoom > 0 ? e.controls2d.minZoom : 2e-3, d = Math.max(l, e.controls2d.maxZoom > 0 ? e.controls2d.maxZoom : 5e4), h = Math.max(1, c / d), u = Math.max(h, c / l), v = Et({
416
- orbitTarget: { x: this.orbitTarget.x, y: this.orbitTarget.y, z: this.orbitTarget.z },
417
- orbitDistance: this.orbitDistance,
418
- orbitYaw: this.orbitYaw,
419
- orbitPitch: this.orbitPitch,
420
- perspectiveFovDeg: e.camera3d.fov,
421
- viewportWidthPx: i,
422
- viewportHeightPx: r,
423
- orthoFrustumMin: h,
424
- orthoFrustumMax: u,
425
- ndcBand: j
426
- }), f = v.orthoFrustumSize > 0 ? v.orthoFrustumSize : c, p = c / 2;
427
- e.setOrthographicViewport({
428
- left: -p * s,
429
- right: p * s,
430
- top: p,
431
- bottom: -p,
432
- zoom: R.clamp(c / f, l, d),
433
- positionX: v.orthoCenterX,
434
- positionY: v.orthoCenterY,
435
- targetX: v.orthoCenterX,
436
- targetY: v.orthoCenterY
437
- });
438
- }
439
- syncPerspectiveCamera(e = {}) {
440
- const t = this.runtime;
441
- if (!t) return;
442
- const i = Math.cos(this.orbitPitch), r = new w(
443
- this.orbitTarget.x + this.orbitDistance * Math.cos(this.orbitYaw) * i,
444
- this.orbitTarget.y + this.orbitDistance * Math.sin(this.orbitYaw) * i,
445
- this.orbitTarget.z + this.orbitDistance * Math.sin(this.orbitPitch)
446
- );
447
- t.setPerspectivePose(r, this.orbitTarget), e.sync2D !== !1 && this.sync2DFromOrbit();
448
- }
449
- capture2DSnapshot() {
450
- const e = this.runtime;
451
- if (!e) return null;
452
- const t = e.camera2d;
453
- return {
454
- left: t.left,
455
- right: t.right,
456
- top: t.top,
457
- bottom: t.bottom,
458
- zoom: t.zoom,
459
- positionX: t.position.x,
460
- positionY: t.position.y,
461
- targetX: e.controls2d.target.x,
462
- targetY: e.controls2d.target.y
463
- };
464
- }
465
- isOrbitUnchangedFrom3DEntry() {
466
- const e = this.orbitSnapshotOn3DEntry;
467
- return e ? k(this.orbitTarget.x, e.targetX) && k(this.orbitTarget.y, e.targetY) && k(this.orbitTarget.z, e.targetZ) && k(this.orbitDistance, e.distance) && k(this.orbitYaw, e.yaw) && k(this.orbitPitch, e.pitch) : !1;
468
- }
469
- getPerspectiveDocumentRect() {
470
- const e = this.runtime;
471
- if (!e) return null;
472
- const t = [];
473
- for (const i of [-1, 1])
474
- for (const r of [-1, 1]) {
475
- const s = new w(i, r, -1).unproject(e.camera3d), c = new w(i, r, 1).unproject(e.camera3d).sub(s).normalize();
476
- if (Math.abs(c.z) < 1e-8) continue;
477
- const l = (0 - s.z) / c.z;
478
- !Number.isFinite(l) || l < 0 || t.push({
479
- x: s.x + c.x * l,
480
- y: s.y + c.y * l
481
- });
482
- }
483
- if (t.length === 0) {
484
- const i = this.orbitDistance * Math.tan(e.camera3d.fov * Math.PI / 360);
485
- return {
486
- minX: this.orbitTarget.x - i,
487
- minY: this.orbitTarget.y - i,
488
- maxX: this.orbitTarget.x + i,
489
- maxY: this.orbitTarget.y + i
490
- };
491
- }
492
- return {
493
- minX: Math.min(...t.map((i) => i.x)),
494
- minY: Math.min(...t.map((i) => i.y)),
495
- maxX: Math.max(...t.map((i) => i.x)),
496
- maxY: Math.max(...t.map((i) => i.y))
497
- };
498
- }
499
- clampDistance(e) {
500
- return R.clamp(
501
- Number.isFinite(e) ? e : $,
502
- $,
503
- ne
504
- );
505
- }
506
- clearDrag(e = {}) {
507
- var r;
508
- const t = this.dragPointerId;
509
- if (this.dragMode = null, this.dragPointerId = null, this.groundPanAnchor = null, t === null || e.releaseCapture === !1) return;
510
- const i = (r = this.runtime) == null ? void 0 : r.renderer.domElement;
511
- if (!(!i || typeof i.releasePointerCapture != "function"))
512
- try {
513
- (typeof i.hasPointerCapture != "function" || i.hasPointerCapture(t)) && i.releasePointerCapture(t);
514
- } catch {
515
- }
516
- }
517
- }
518
- const Lt = 1e3, At = -1e4, Ft = 1e4, ae = 1e3, Nt = 2e6, Ot = 1.15, Vt = 1.1, le = new We(Math.PI / 2, 0, Math.PI * 0.98 * 0.8);
519
- class Bt {
520
- constructor(e) {
521
- a(this, "canvasArea");
522
- a(this, "canvas");
523
- a(this, "scene", new ee());
524
- a(this, "overlayRoot", new Ie());
525
- a(this, "renderer");
526
- a(this, "camera2d");
527
- a(this, "camera3d");
528
- a(this, "activeCamera");
529
- a(this, "controls2d");
530
- a(this, "controls3d");
531
- a(this, "options");
532
- a(this, "mountedObjects", /* @__PURE__ */ new Set());
533
- a(this, "ownedObjects", /* @__PURE__ */ new Set());
534
- a(this, "overlays", /* @__PURE__ */ new Map());
535
- a(this, "eventCleanups", []);
536
- a(this, "raycaster", new Xe());
537
- a(this, "pointerNdc", new q());
538
- a(this, "viewModeValue");
539
- a(this, "initialized", !1);
540
- a(this, "disposed", !1);
541
- a(this, "renderRequested", !0);
542
- a(this, "animationFrameId", null);
543
- a(this, "animationLoopActive", !1);
544
- a(this, "lastFrameTime", null);
545
- a(this, "infoFrames", 0);
546
- a(this, "infoLastTime", 0);
547
- a(this, "fpsCurrent", 0);
548
- a(this, "viewerOrbit", null);
549
- a(this, "previewScene", null);
550
- a(this, "previewCamera", null);
551
- a(this, "previewRenderTarget", null);
552
- a(this, "previewRenderTargetSize", { width: 0, height: 0 });
553
- a(this, "previewPixelBuffer", null);
554
- a(this, "previewImageDataBuffer", null);
555
- a(this, "environmentResource", null);
556
- a(this, "environmentLoadGeneration", 0);
557
- this.options = e, this.canvasArea = e.canvasArea, this.canvas = e.canvas, this.viewModeValue = e.initialViewMode ?? "2d", this.overlayRoot.name = "scene-runtime:overlays", this.scene.add(this.overlayRoot);
558
- }
559
- initialize() {
560
- if (this.disposed) throw new Error("SceneRuntime has been disposed.");
561
- if (this.initialized) return;
562
- const e = this.options.factories, t = (e == null ? void 0 : e.createRenderer) ?? ((l) => new _e(l)), i = (e == null ? void 0 : e.createMapControls) ?? ((l, d) => new lt(l, d)), r = (e == null ? void 0 : e.createOrbitControls) ?? ((l, d) => new ct(l, d));
563
- this.renderer = t({
564
- canvas: this.canvas,
565
- antialias: this.options.antialias ?? !0,
566
- alpha: this.options.alpha ?? !1,
567
- powerPreference: this.options.powerPreference ?? "high-performance"
568
- }), this.options.clearColor !== void 0 && this.renderer.setClearColor(new qe(this.options.clearColor));
569
- const s = Math.max(1, this.canvasArea.clientWidth || 1), o = Math.max(1, this.canvasArea.clientHeight || 1), c = s / o;
570
- if (this.renderer.setPixelRatio(this.readDevicePixelRatio()), this.renderer.setSize(s, o, !1), this.options.environment) {
571
- const l = (e == null ? void 0 : e.createEnvironment) ?? zt;
572
- this.scene.environmentIntensity = Vt, this.scene.environmentRotation.copy(le), this.scene.backgroundRotation.copy(le);
573
- const d = ++this.environmentLoadGeneration, h = l(this.renderer, this.options.environment);
574
- Ht(h) ? h.then(
575
- (u) => this.applyEnvironmentResource(u, d),
576
- (u) => {
577
- !this.disposed && d === this.environmentLoadGeneration && console.warn("Failed to load scene environment HDR.", u);
578
- }
579
- ) : this.applyEnvironmentResource(h, d);
580
- }
581
- this.camera2d = new te(-c, c, 1, -1, At, Ft), this.camera2d.position.set(0, 0, 1), this.camera2d.lookAt(0, 0, 0), this.camera3d = new $e(40, c, ae, Nt), this.camera3d.up.set(0, 0, 1), this.camera3d.position.set(1e4, 0, 1e4), this.camera3d.lookAt(0, 0, 0), this.controls2d = i(this.camera2d, this.renderer.domElement), this.controls2d.enableRotate = !1, this.controls2d.screenSpacePanning = !0, this.controls2d.zoomToCursor = !0, this.controls2d.minZoom = 2e-3, this.controls2d.maxZoom = 5e4, this.controls2d.mouseButtons = {
582
- LEFT: W.PAN,
583
- MIDDLE: W.DOLLY,
584
- RIGHT: W.PAN
585
- }, this.controls2d.touches = {
586
- ONE: ie.PAN,
587
- TWO: ie.DOLLY_PAN
588
- }, this.controls3d = r(this.camera3d, this.renderer.domElement), this.controls3d.enablePan = !1, this.listen(this.controls2d, "change", () => this.requestRender()), this.listen(this.controls3d, "change", () => this.requestRender()), this.options.navigationPolicy === "viewer" && (this.viewerOrbit = new Dt(), this.viewerOrbit.attach(this)), this.initialized = !0, this.setViewMode(this.viewModeValue);
589
- }
590
- get isInitialized() {
591
- return this.initialized;
592
- }
593
- get isDisposed() {
594
- return this.disposed;
595
- }
596
- get viewMode() {
597
- return this.viewModeValue;
598
- }
599
- get needsRender() {
600
- return this.renderRequested;
601
- }
602
- set needsRender(e) {
603
- this.renderRequested = e;
604
- }
605
- requestRender() {
606
- this.disposed || (this.renderRequested = !0);
607
- }
608
- setClearColor(e) {
609
- var t;
610
- (t = this.renderer) == null || t.setClearColor(e), this.requestRender();
611
- }
612
- setViewMode(e, t = {}) {
613
- const i = e !== this.viewModeValue;
614
- this.viewModeValue = e, !(!this.camera2d || !this.camera3d) && (this.activeCamera = e === "3d" ? this.camera3d : this.camera2d, this.viewerOrbit ? (this.controls3d.enabled = !1, i ? this.viewerOrbit.setViewMode(e, t) : this.controls2d.enabled = e === "2d") : (this.controls2d && (this.controls2d.enabled = e === "2d"), this.controls3d && (this.controls3d.enabled = e === "3d")), this.requestRender());
615
- }
616
- setPerspectivePose(e, t) {
617
- var i;
618
- return !this.camera3d || ![e.x, e.y, e.z, t.x, t.y, t.z].every(Number.isFinite) ? !1 : (this.camera3d.position.copy(e), this.camera3d.lookAt(t), this.camera3d.updateProjectionMatrix(), (i = this.controls3d) == null || i.target.copy(t), this.requestRender(), !0);
619
- }
620
- setPerspectiveAspect(e) {
621
- return !this.camera3d || !Number.isFinite(e) || e <= 0 ? !1 : (this.camera3d.aspect = e, this.camera3d.updateProjectionMatrix(), this.requestRender(), !0);
622
- }
623
- setOrthographicViewport(e) {
624
- var t;
625
- return !this.camera2d || ![
626
- e.left,
627
- e.right,
628
- e.top,
629
- e.bottom,
630
- e.zoom,
631
- e.positionX,
632
- e.positionY,
633
- e.targetX,
634
- e.targetY
635
- ].every(Number.isFinite) || e.left >= e.right || e.bottom >= e.top || e.zoom <= 0 ? !1 : (this.camera2d.left = e.left, this.camera2d.right = e.right, this.camera2d.top = e.top, this.camera2d.bottom = e.bottom, this.camera2d.zoom = e.zoom, this.camera2d.position.set(e.positionX, e.positionY, 1), this.camera2d.updateProjectionMatrix(), (t = this.controls2d) != null && t.target && (this.controls2d.target.set(e.targetX, e.targetY, 0), this.controls2d.update()), this.requestRender(), !0);
636
- }
637
- addObject(e, t = {}) {
638
- if (this.disposed) throw new Error("SceneRuntime has been disposed.");
639
- this.mountedObjects.has(e) && e.parent === this.scene || (this.scene.add(e), this.mountedObjects.add(e), t.owned && this.ownedObjects.add(e), this.requestRender());
640
- }
641
- removeObject(e, t = {}) {
642
- if (!this.mountedObjects.has(e) && e.parent !== this.scene) return !1;
643
- e.removeFromParent(), this.mountedObjects.delete(e);
644
- const i = this.ownedObjects.delete(e);
645
- return (t.dispose === !0 || t.dispose === void 0 && i) && re(e), this.requestRender(), !0;
646
- }
647
- setObjectVisible(e, t) {
648
- e.visible !== t && (e.visible = t, this.requestRender());
649
- }
650
- mountOverlay(e, t, i = {}) {
651
- this.removeOverlay(e), this.overlayRoot.parent !== this.scene && this.scene.add(this.overlayRoot), this.overlayRoot.add(t), this.overlays.set(e, { object: t, owned: i.owned === !0 }), this.requestRender();
652
- }
653
- removeOverlay(e, t = {}) {
654
- const i = this.overlays.get(e);
655
- return i ? (this.overlays.delete(e), i.object.removeFromParent(), (t.dispose === !0 || t.dispose === void 0 && i.owned) && re(i.object), this.requestRender(), i.object) : null;
656
- }
657
- setHighlight(e, t = {}) {
658
- if (!e) {
659
- this.removeOverlay("highlight");
660
- return;
661
- }
662
- this.mountOverlay("highlight", e, t);
663
- }
664
- resize() {
665
- if (!this.renderer) return !1;
666
- const e = this.canvasArea.clientWidth, t = this.canvasArea.clientHeight;
667
- if (!Number.isFinite(e) || !Number.isFinite(t) || e <= 0 || t <= 0) return !1;
668
- if (this.renderer.setPixelRatio(this.readDevicePixelRatio()), this.renderer.setSize(e, t, !1), this.camera2d) {
669
- const i = (this.camera2d.top - this.camera2d.bottom) / 2, r = i * (e / t), s = (this.camera2d.left + this.camera2d.right) / 2, o = (this.camera2d.top + this.camera2d.bottom) / 2;
670
- this.camera2d.left = s - r, this.camera2d.right = s + r, this.camera2d.top = o + i, this.camera2d.bottom = o - i, this.camera2d.updateProjectionMatrix();
671
- }
672
- return this.camera3d && (this.camera3d.aspect = e / t, this.camera3d.updateProjectionMatrix()), this.requestRender(), !0;
673
- }
674
- fitView(e, t = {}) {
675
- if (!this.camera2d || !this.camera3d) return !1;
676
- const i = this.resolveBox3(e);
677
- if (!i || i.isEmpty()) return !1;
678
- const r = i.getSize(new w()), s = i.getCenter(new w());
679
- if (![r.x, r.y, r.z, s.x, s.y, s.z].every(Number.isFinite)) return !1;
680
- const o = Math.max(1, t.padding ?? 1.05);
681
- if ((t.viewMode ?? this.viewModeValue) === "2d") {
682
- const l = Math.max(r.x, 1e-6) * o, d = Math.max(r.y, 1e-6) * o, h = Math.max(1, this.canvasArea.clientWidth), u = Math.max(1, this.canvasArea.clientHeight), v = h / u;
683
- let f = l / 2, p = d / 2;
684
- f / p < v ? f = p * v : p = f / v, this.camera2d.left = -f, this.camera2d.right = f, this.camera2d.top = p, this.camera2d.bottom = -p, this.camera2d.zoom = 1, this.camera2d.position.set(s.x, s.y, Math.max(s.z + 1, 1)), this.camera2d.updateProjectionMatrix(), this.controls2d.target.copy(s), this.controls2d.update();
685
- } else if (this.viewerOrbit)
686
- this.viewerOrbit.fit3D(i);
687
- else {
688
- const l = s.clone(), d = this.camera3d.fov * Math.PI / 180, h = 2 * Math.atan(Math.tan(d / 2) * Math.max(this.camera3d.aspect, 1e-6)), u = Math.max(r.length() / 2, 1e-6), v = Math.max(Math.min(d, h) / 2, 1e-6), f = u / Math.sin(v) * o, p = this.camera3d.position.clone().sub(this.controls3d.target);
689
- p.lengthSq() <= 1e-12 && p.set(1, -1, 1), p.normalize(), this.controls3d.target.copy(l), this.camera3d.position.copy(l).addScaledVector(p, f), this.camera3d.lookAt(l), this.camera3d.near = Math.min(
690
- ae,
691
- Math.max(f * 0.01, 1e-6)
692
- ), this.camera3d.updateProjectionMatrix(), this.controls3d.update();
693
- }
694
- return this.requestRender(), !0;
695
- }
696
- getVisibleDocumentRect() {
697
- if (this.viewerOrbit) return this.viewerOrbit.getVisibleDocumentRect();
698
- if (!this.camera2d) return null;
699
- const e = this.camera2d.zoom > 0 ? this.camera2d.zoom : 1, t = (this.camera2d.right - this.camera2d.left) / (2 * e), i = (this.camera2d.top - this.camera2d.bottom) / (2 * e);
700
- return {
701
- minX: this.camera2d.position.x - t,
702
- minY: this.camera2d.position.y - i,
703
- maxX: this.camera2d.position.x + t,
704
- maxY: this.camera2d.position.y + i
705
- };
706
- }
707
- getWorldUnitsPerPixel() {
708
- const e = this.getVisibleDocumentRect(), t = Math.max(1, this.canvasArea.clientWidth || 1);
709
- if (!e) return Number.POSITIVE_INFINITY;
710
- const i = e.maxX - e.minX, r = e.maxY - e.minY;
711
- return Math.max(i / t, r / Math.max(1, this.canvasArea.clientHeight || 1));
712
- }
713
- captureOrthographicViewportState() {
714
- return this.camera2d ? {
715
- left: this.camera2d.left,
716
- right: this.camera2d.right,
717
- top: this.camera2d.top,
718
- bottom: this.camera2d.bottom,
719
- zoom: this.camera2d.zoom,
720
- positionX: this.camera2d.position.x,
721
- positionY: this.camera2d.position.y,
722
- targetX: this.controls2d.target.x,
723
- targetY: this.controls2d.target.y
724
- } : null;
725
- }
726
- captureCameraSnapshot() {
727
- var t;
728
- if (this.viewModeValue === "2d") {
729
- const i = this.captureOrthographicViewportState();
730
- return i ? { schemaVersion: 1, viewMode: "2d", orthographic: i } : null;
731
- }
732
- const e = (t = this.viewerOrbit) == null ? void 0 : t.captureOrbitSnapshot();
733
- return e ? { schemaVersion: 1, viewMode: "3d", orbit: e } : null;
734
- }
735
- applyCameraSnapshot(e) {
736
- return e.viewMode === "2d" ? (this.viewModeValue !== "2d" && this.setViewMode("2d", { preserveOrbit: !0 }), e.orthographic ? this.setOrthographicViewport(e.orthographic) : !1) : (this.viewModeValue !== "3d" && this.setViewMode("3d", { preserveOrbit: !0 }), !e.orbit || !this.viewerOrbit ? !1 : (this.viewerOrbit.applyOrbitSnapshot(e.orbit), this.requestRender(), !0));
737
- }
738
- getViewerOrbit() {
739
- return this.viewerOrbit;
740
- }
741
- getDevicePixelRatio() {
742
- return this.readDevicePixelRatio();
743
- }
744
- getRenderStats() {
745
- var t, i;
746
- const e = (i = (t = this.renderer) == null ? void 0 : t.info) == null ? void 0 : i.render;
747
- return {
748
- drawCalls: (e == null ? void 0 : e.calls) ?? 0,
749
- submittedVertexRefs: e ? e.triangles * 3 + e.lines * 2 + e.points : 0,
750
- fps: this.fpsCurrent
751
- };
752
- }
753
- renderOrthographicPreview(e, t, i) {
754
- var J;
755
- if (!this.renderer || this.disposed || t.parent || !Yt(i)) return !1;
756
- const r = Math.max(1, Math.floor(e.clientWidth || e.width || 0)), s = Math.max(1, Math.floor(e.clientHeight || e.height || 0)), o = this.resolveRendererPixelRatio(), c = Math.min(2, o), l = Math.max(1, Math.floor(r * c)), d = Math.max(1, Math.floor(s * c)), h = l / o, u = d / o, v = Math.min(h, u), f = (i.minX + i.maxX) / 2, p = (i.minY + i.maxY) / 2, b = Math.max(i.maxX - i.minX, i.maxY - i.minY, 1) * Ot / 2, g = this.getOrCreatePreviewRenderTarget(l, d), y = this.getOrCreatePreviewCamera();
757
- y.left = -b, y.right = b, y.top = b, y.bottom = -b, y.position.set(f, p, 1), y.up.copy(((J = this.camera2d) == null ? void 0 : J.up) ?? new w(0, 1, 0)), y.lookAt(f, p, 0), y.updateProjectionMatrix(), y.updateMatrixWorld(!0);
758
- const P = this.getOrCreatePreviewScene(), x = this.renderer, E = x.getRenderTarget(), Oe = x.getClearAlpha(), Ve = x.autoClear, A = new je();
759
- x.getViewport(A);
760
- const Be = x.getScissorTest();
761
- P.add(t);
762
- try {
763
- x.setRenderTarget(g), x.setViewport(0, 0, h, u), x.setScissorTest(!1), x.setClearAlpha(0), x.autoClear = !1, x.clear(), x.setViewport(
764
- Math.floor((h - v) / 2),
765
- Math.floor((u - v) / 2),
766
- v,
767
- v
768
- ), x.render(P, y);
769
- const Q = this.getOrCreatePreviewPixelBuffer(l, d);
770
- return x.readRenderTargetPixels(g, 0, 0, l, d, Q), this.blitPreviewPixelsToCanvas(e, Q, l, d);
771
- } finally {
772
- t.removeFromParent(), x.setRenderTarget(E), x.setClearAlpha(Oe), x.autoClear = Ve, x.setViewport(
773
- A.x,
774
- A.y,
775
- A.z,
776
- A.w
777
- ), x.setScissorTest(Be), this.requestRender();
778
- }
779
- }
780
- bindRuntimeEvent(e, t, i, r) {
781
- this.listen(e, t, i, r);
782
- }
783
- clientToNdc(e, t, i = new q()) {
784
- var o;
785
- const r = (o = this.renderer) == null ? void 0 : o.domElement;
786
- if (!r) return null;
787
- const s = r.getBoundingClientRect();
788
- return !Number.isFinite(s.width) || !Number.isFinite(s.height) || s.width <= 0 || s.height <= 0 ? null : (i.set(
789
- (e - s.left) / s.width * 2 - 1,
790
- -((t - s.top) / s.height) * 2 + 1
791
- ), i);
792
- }
793
- screenToWorldOnPlane(e, t, i = 0) {
794
- const r = this.clientToNdc(e, t, this.pointerNdc);
795
- return !r || !this.activeCamera ? null : (this.raycaster.setFromCamera(r, this.activeCamera), this.raycaster.ray.intersectPlane(new Ue(new w(0, 0, 1), -i), new w()));
796
- }
797
- worldToClient(e, t = new q()) {
798
- var o;
799
- const i = (o = this.renderer) == null ? void 0 : o.domElement;
800
- if (!i || !this.activeCamera) return null;
801
- const r = i.getBoundingClientRect();
802
- if (!Number.isFinite(r.width) || !Number.isFinite(r.height) || r.width <= 0 || r.height <= 0) return null;
803
- const s = e.clone().project(this.activeCamera);
804
- return t.set(
805
- r.left + (s.x + 1) / 2 * r.width,
806
- r.top + (1 - s.y) / 2 * r.height
807
- ), t;
808
- }
809
- pick(e, t, i = {}) {
810
- const r = this.clientToNdc(e, t, this.pointerNdc);
811
- if (!r || !this.activeCamera) return null;
812
- const s = i.roots ?? [...this.mountedObjects], o = i.includeInvisible ? s : s.filter((h) => h.visible);
813
- this.raycaster.setFromCamera(r, this.activeCamera);
814
- const [c] = this.raycaster.intersectObjects([...o], i.recursive !== !1);
815
- if (!c) return null;
816
- const d = o.find((h) => h === c.object || this.isDescendantOf(c.object, h)) ?? c.object;
817
- return {
818
- object: c.object,
819
- root: d,
820
- point: c.point.clone(),
821
- distance: c.distance,
822
- faceIndex: c.faceIndex ?? null,
823
- instanceId: c.instanceId,
824
- intersection: c
825
- };
826
- }
827
- startAnimation(e = {}) {
828
- if (this.disposed || this.animationLoopActive) return;
829
- this.animationLoopActive = !0, this.infoFrames = 0, this.infoLastTime = performance.now();
830
- const t = e.infoUpdateIntervalMs ?? Lt, i = (r = performance.now()) => {
831
- var c, l, d, h, u, v, f;
832
- if (!this.animationLoopActive || this.disposed) {
833
- this.animationFrameId = null;
834
- return;
835
- }
836
- this.animationFrameId = requestAnimationFrame(i);
837
- const s = this.lastFrameTime === null ? 0 : Math.max(0, (r - this.lastFrameTime) / 1e3);
838
- this.lastFrameTime = r, this.viewModeValue === "2d" && ((c = this.controls2d) == null || c.update()), (l = e.onFrame) == null || l.call(e, r, s);
839
- let o = !1;
840
- if (this.renderRequested && (((d = e.shouldRender) == null ? void 0 : d.call(e, r)) ?? !0) && ((h = e.beforeRender) == null || h.call(e, r), this.renderer.render(this.scene, this.activeCamera), this.renderRequested = !1, o = !0, (u = e.afterRender) == null || u.call(e, r)), this.infoFrames += 1, r - this.infoLastTime >= t) {
841
- const p = Math.max(1, r - this.infoLastTime);
842
- this.fpsCurrent = this.infoFrames * 1e3 / p, this.infoFrames = 0, this.infoLastTime = r, (v = e.onInfoUpdate) == null || v.call(e);
843
- } else o && ((f = e.onInfoUpdate) == null || f.call(e));
844
- };
845
- i();
846
- }
847
- stopAnimation() {
848
- this.animationLoopActive = !1, this.animationFrameId !== null && typeof cancelAnimationFrame == "function" && cancelAnimationFrame(this.animationFrameId), this.animationFrameId = null, this.lastFrameTime = null;
849
- }
850
- dispose() {
851
- var e, t, i, r, s, o, c, l, d;
852
- if (!this.disposed) {
853
- for (this.disposed = !0, this.stopAnimation(), (e = this.viewerOrbit) == null || e.dispose(), this.viewerOrbit = null; this.eventCleanups.length > 0; ) (t = this.eventCleanups.pop()) == null || t();
854
- for (const h of [...this.overlays.keys()]) this.removeOverlay(h);
855
- for (const h of [...this.mountedObjects]) this.removeObject(h);
856
- (r = (i = this.controls2d) == null ? void 0 : i.dispose) == null || r.call(i), (o = (s = this.controls3d) == null ? void 0 : s.dispose) == null || o.call(s), this.disposePreviewResources(), this.environmentLoadGeneration += 1, this.scene.environment = null, (c = this.environmentResource) == null || c.dispose(), this.environmentResource = null, (d = (l = this.renderer) == null ? void 0 : l.dispose) == null || d.call(l);
857
- }
858
- }
859
- applyEnvironmentResource(e, t) {
860
- var i;
861
- if (this.disposed || t !== this.environmentLoadGeneration) {
862
- e.dispose();
863
- return;
864
- }
865
- (i = this.environmentResource) == null || i.dispose(), this.environmentResource = e, this.scene.environment = e.texture, this.requestRender();
866
- }
867
- getOrCreatePreviewScene() {
868
- return this.previewScene || (this.previewScene = new ee()), this.previewScene;
869
- }
870
- getOrCreatePreviewCamera() {
871
- return this.previewCamera || (this.previewCamera = new te(-1, 1, 1, -1, -1e6, 1e6)), this.previewCamera;
872
- }
873
- getOrCreatePreviewRenderTarget(e, t) {
874
- var i;
875
- return this.previewRenderTarget && this.previewRenderTargetSize.width === e && this.previewRenderTargetSize.height === t ? this.previewRenderTarget : ((i = this.previewRenderTarget) == null || i.dispose(), this.previewRenderTarget = new Ke(e, t), this.previewRenderTargetSize = { width: e, height: t }, this.previewRenderTarget);
876
- }
877
- getOrCreatePreviewPixelBuffer(e, t) {
878
- const i = e * t * 4;
879
- return (!this.previewPixelBuffer || this.previewPixelBuffer.length !== i) && (this.previewPixelBuffer = new Uint8Array(i)), this.previewPixelBuffer;
880
- }
881
- blitPreviewPixelsToCanvas(e, t, i, r) {
882
- e.width !== i && (e.width = i), e.height !== r && (e.height = r);
883
- const s = e.getContext("2d");
884
- if (!s) return !1;
885
- const o = i * r * 4;
886
- (!this.previewImageDataBuffer || this.previewImageDataBuffer.length !== o) && (this.previewImageDataBuffer = new Uint8ClampedArray(o));
887
- const c = this.previewImageDataBuffer, l = i * 4;
888
- for (let h = 0; h < r; h += 1) {
889
- const u = (r - 1 - h) * l;
890
- c.set(t.subarray(u, u + l), h * l);
891
- }
892
- const d = s.createImageData(i, r);
893
- return d.data.set(c), s.putImageData(d, 0, 0), !0;
894
- }
895
- resolveRendererPixelRatio() {
896
- const e = this.renderer.getPixelRatio();
897
- return Number.isFinite(e) && e > 0 ? e : 1;
898
- }
899
- disposePreviewResources() {
900
- var e, t;
901
- (e = this.previewRenderTarget) == null || e.dispose(), this.previewRenderTarget = null, this.previewRenderTargetSize = { width: 0, height: 0 }, this.previewCamera = null, this.previewPixelBuffer = null, this.previewImageDataBuffer = null, (t = this.previewScene) == null || t.clear(), this.previewScene = null;
902
- }
903
- readDevicePixelRatio() {
904
- const e = globalThis.devicePixelRatio;
905
- return typeof e == "number" && Number.isFinite(e) && e > 0 ? e : 1;
906
- }
907
- resolveBox3(e) {
908
- if (e instanceof S) return e.clone();
909
- if (e instanceof Ge) return new S().setFromObject(e);
910
- const { minX: t, minY: i, maxX: r, maxY: s } = e;
911
- return ![t, i, r, s].every(Number.isFinite) || r < t || s < i ? null : new S(new w(t, i, 0), new w(r, s, 0));
912
- }
913
- isDescendantOf(e, t) {
914
- let i = e.parent;
915
- for (; i; ) {
916
- if (i === t) return !0;
917
- i = i.parent;
918
- }
919
- return !1;
920
- }
921
- listen(e, t, i, r) {
922
- e.addEventListener(t, i, r), this.eventCleanups.push(() => {
923
- var s;
924
- return (s = e.removeEventListener) == null ? void 0 : s.call(e, t, i, r);
925
- });
926
- }
927
- }
928
- function zt(n, e) {
929
- if (e === "room") {
930
- const t = new Ze(n), i = new at(), r = t.fromScene(i);
931
- return i.dispose(), t.dispose(), {
932
- texture: r.texture,
933
- dispose: () => r.dispose()
934
- };
935
- }
936
- if (e.type !== "hdr" || e.url.trim().length === 0)
937
- throw new Error("HDR scene environment requires a non-empty URL.");
938
- return new dt().loadAsync(e.url).then((t) => (t.mapping = Je, {
939
- texture: t,
940
- dispose: () => t.dispose()
941
- }));
942
- }
943
- function Ht(n) {
944
- return typeof (n == null ? void 0 : n.then) == "function";
945
- }
946
- function Yt(n) {
947
- return Number.isFinite(n.minX) && Number.isFinite(n.minY) && Number.isFinite(n.maxX) && Number.isFinite(n.maxY) && n.maxX >= n.minX && n.maxY >= n.minY;
948
- }
949
- const Z = 1;
950
- function Xt() {
951
- return typeof globalThis.localStorage > "u" ? _t() : {
952
- read(n) {
953
- try {
954
- const e = globalThis.localStorage.getItem(ce(n));
955
- return e ? JSON.parse(e) : null;
956
- } catch {
957
- return null;
958
- }
959
- },
960
- write(n, e) {
961
- try {
962
- globalThis.localStorage.setItem(ce(n), JSON.stringify(e));
963
- } catch {
964
- }
965
- }
966
- };
967
- }
968
- function _t() {
969
- const n = /* @__PURE__ */ new Map();
970
- return {
971
- read(e) {
972
- return n.get(e) ?? null;
973
- },
974
- write(e, t) {
975
- n.set(e, t);
976
- }
977
- };
978
- }
979
- function qt(n, e) {
980
- if (!n || !e) return null;
981
- const t = n.read(e);
982
- if (!t || typeof t != "object") return null;
983
- const i = t;
984
- return i.schemaVersion !== Z || i.viewMode !== "2d" && i.viewMode !== "3d" || i.viewMode === "2d" && !i.orthographic || i.viewMode === "3d" && !i.orbit ? null : i;
985
- }
986
- function Wt(n, e, t) {
987
- !n || !e || n.write(e, t);
988
- }
989
- function $t(n) {
990
- return {
991
- schemaVersion: Z,
992
- viewMode: n.viewMode,
993
- ...n.orthographic ? { orthographic: n.orthographic } : {},
994
- ...n.orbit ? { orbit: n.orbit } : {}
995
- };
996
- }
997
- function ce(n) {
998
- return `nebula-viewer:camera:v${Z}:${n}`;
999
- }
1000
- function V(n) {
1001
- return new S(
1002
- new w(n.minX, n.minY, n.minZ),
1003
- new w(n.maxX, n.maxY, n.maxZ)
1004
- );
1005
- }
1006
- function De(n) {
1007
- const [e, t, i] = n;
1008
- return (e & 255) << 16 | (t & 255) << 8 | i & 255;
1009
- }
1010
- class jt {
1011
- constructor() {
1012
- a(this, "rows", []);
1013
- a(this, "allColor", null);
1014
- a(this, "renderNode", null);
1015
- a(this, "onChange", () => {
1016
- });
1017
- a(this, "visibilityRevision", 0);
1018
- }
1019
- bind(e, t) {
1020
- this.renderNode = e, this.onChange = t;
1021
- }
1022
- sync(e) {
1023
- const t = new Map(this.rows.map((i) => [i.index, i.color]));
1024
- this.rows = e.getLayers().map((i) => ({
1025
- index: i.index,
1026
- name: i.name,
1027
- visible: i.visible,
1028
- color: t.get(i.index) ?? this.allColor,
1029
- sourceColor: i.color
1030
- })), this.onChange();
1031
- }
1032
- clear() {
1033
- this.rows = [], this.allColor = null, this.renderNode = null, this.onChange();
1034
- }
1035
- getVisibilityRevision() {
1036
- return this.visibilityRevision;
1037
- }
1038
- toggle(e) {
1039
- const t = this.rows.find((r) => r.index === e);
1040
- if (!t || !this.renderNode) return;
1041
- const i = !t.visible;
1042
- this.renderNode.setLayerVisible(e, i), t.visible = i, this.visibilityRevision += 1, this.onChange();
1043
- }
1044
- showAll() {
1045
- if (this.renderNode) {
1046
- for (const e of this.rows)
1047
- e.visible || (this.renderNode.setLayerVisible(e.index, !0), e.visible = !0);
1048
- this.visibilityRevision += 1, this.onChange();
1049
- }
1050
- }
1051
- hideAll() {
1052
- if (this.renderNode) {
1053
- for (const e of this.rows)
1054
- e.visible && (this.renderNode.setLayerVisible(e.index, !1), e.visible = !1);
1055
- this.visibilityRevision += 1, this.onChange();
1056
- }
1057
- }
1058
- setColor(e, t) {
1059
- if (!this.renderNode) return;
1060
- this.renderNode.setLayerColorOverride(e, t);
1061
- const i = this.rows.find((r) => r.index === e);
1062
- i && (i.color = t);
1063
- }
1064
- setAllColor(e) {
1065
- if (this.renderNode) {
1066
- for (const t of this.rows)
1067
- this.renderNode.setLayerColorOverride(t.index, e), t.color = e;
1068
- this.allColor = e;
1069
- }
1070
- }
1071
- }
1072
- class Ut {
1073
- constructor() {
1074
- a(this, "usages", []);
1075
- a(this, "renderNode", null);
1076
- a(this, "onChange", () => {
1077
- });
1078
- a(this, "visibilityRevision", 0);
1079
- }
1080
- bind(e, t) {
1081
- this.renderNode = e, this.onChange = t;
1082
- }
1083
- sync(e) {
1084
- this.usages = e.listInsertUsages().map((t) => ({
1085
- insertId: t.insertId,
1086
- blockName: t.blockName,
1087
- layerName: t.layerName,
1088
- layerIndex: t.layerIndex,
1089
- variantId: t.variantId,
1090
- instanceId: t.instanceId,
1091
- label: t.label,
1092
- canHighlight: t.canHighlight,
1093
- visible: t.visible
1094
- })), this.onChange();
1095
- }
1096
- clear() {
1097
- this.usages = [], this.renderNode = null, this.onChange();
1098
- }
1099
- getVisibilityRevision() {
1100
- return this.visibilityRevision;
1101
- }
1102
- toggleInsert(e, t) {
1103
- if (!this.renderNode) return;
1104
- const i = this.usages.find((s) => s.insertId === e);
1105
- if (!i) return;
1106
- const r = t ?? !i.visible;
1107
- this.renderNode.setInsertVisible(e, r), i.visible = r, this.visibilityRevision += 1, this.onChange();
1108
- }
1109
- batchToggleInserts(e) {
1110
- if (!this.renderNode || e.length === 0) return;
1111
- const t = [];
1112
- for (const i of e) {
1113
- const r = this.usages.find((s) => s.insertId === i.insertId);
1114
- r && (t.push(i), r.visible = i.visible);
1115
- }
1116
- t.length !== 0 && (this.renderNode.setInsertVisibleBatch(t), this.visibilityRevision += 1, this.onChange());
1117
- }
1118
- }
1119
- class Kt {
1120
- constructor() {
1121
- a(this, "selection", null);
1122
- a(this, "details", null);
1123
- a(this, "textSelection", null);
1124
- a(this, "textDetails", null);
1125
- }
1126
- setSelection(e, t) {
1127
- this.selection = e, this.details = t, this.textSelection = null, this.textDetails = null;
1128
- }
1129
- setTextSelection(e, t) {
1130
- this.selection = null, this.details = null, this.textSelection = e, this.textDetails = t;
1131
- }
1132
- clear() {
1133
- this.selection = null, this.details = null, this.textSelection = null, this.textDetails = null;
1134
- }
1135
- }
1136
- class Gt {
1137
- constructor() {
1138
- a(this, "filename", null);
1139
- a(this, "fileCount", 0);
1140
- a(this, "visibleLayerCount", 0);
1141
- a(this, "totalLayerCount", 0);
1142
- a(this, "statusMessage", "");
1143
- a(this, "zoomLabel", "");
1144
- a(this, "orbitDistanceLabel", "");
1145
- a(this, "scaleRuler", null);
1146
- a(this, "viewportVertexCount", 0);
1147
- a(this, "drawCalls", 0);
1148
- a(this, "fps", 0);
1149
- a(this, "lineRenderMode", "");
1150
- a(this, "entityCount", 0);
1151
- a(this, "lineCount", 0);
1152
- a(this, "fillCount", 0);
1153
- a(this, "dotCount", 0);
1154
- a(this, "textCount", 0);
1155
- a(this, "renderableCount", 0);
1156
- a(this, "submittedVertexRefs", 0);
1157
- a(this, "blockVariantCount", 0);
1158
- a(this, "blockInstanceCount", 0);
1159
- a(this, "splitBlockLayerGroupCount", 0);
1160
- a(this, "renderDebugBreakdown", []);
1161
- a(this, "splitBlockLayerDetails", []);
1162
- a(this, "phase", null);
1163
- }
1164
- sync(e) {
1165
- var s, o, c, l;
1166
- this.filename = e.filename, this.fileCount = e.hasDocument ? 1 : 0, this.totalLayerCount = e.layers.length, this.visibleLayerCount = e.layers.filter((d) => d.visible).length, this.statusMessage = ((s = e.statusMessage) == null ? void 0 : s.trim()) ?? "";
1167
- const t = e.host.runtime.getRenderStats();
1168
- this.viewportVertexCount = t.submittedVertexRefs, this.drawCalls = t.drawCalls, this.fps = t.fps;
1169
- const i = e.diagnostics;
1170
- this.lineRenderMode = (i == null ? void 0 : i.lineRenderMode) ?? "", this.entityCount = (i == null ? void 0 : i.entityCount) ?? 0, this.lineCount = (i == null ? void 0 : i.lineCount) ?? 0, this.fillCount = (i == null ? void 0 : i.fillCount) ?? 0, this.dotCount = (i == null ? void 0 : i.dotCount) ?? 0, this.textCount = (i == null ? void 0 : i.textCount) ?? 0, this.renderableCount = (i == null ? void 0 : i.renderableCount) ?? 0, this.submittedVertexRefs = (i == null ? void 0 : i.submittedVertexRefs) ?? 0, this.blockVariantCount = (i == null ? void 0 : i.blockVariantCount) ?? 0, this.blockInstanceCount = (i == null ? void 0 : i.blockInstanceCount) ?? 0, this.splitBlockLayerGroupCount = (i == null ? void 0 : i.splitBlockLayerGroupCount) ?? 0, this.renderDebugBreakdown = (i == null ? void 0 : i.renderDebugBreakdown) ?? [], this.splitBlockLayerDetails = (i == null ? void 0 : i.splitBlockLayerDetails) ?? [], this.phase = e.phase ?? null;
1171
- const r = e.host.runtime.getWorldUnitsPerPixel();
1172
- if (e.host.viewMode === "2d" && Number.isFinite(r) && r > 0) {
1173
- const d = r * 100, h = (c = (o = e.host.runtime.captureCameraSnapshot()) == null ? void 0 : o.orthographic) == null ? void 0 : c.zoom;
1174
- this.zoomLabel = Number.isFinite(h) && (h ?? 0) > 0 ? `${Math.round(h * 100)}%` : "", this.scaleRuler = {
1175
- visible: !0,
1176
- worldLength: d,
1177
- lengthLabel: Zt(d)
1178
- }, this.orbitDistanceLabel = "";
1179
- } else {
1180
- const d = (l = e.host.runtime.captureCameraSnapshot()) == null ? void 0 : l.orbit;
1181
- this.zoomLabel = "", this.orbitDistanceLabel = d ? `${Math.round(d.distance)}` : "", this.scaleRuler = null;
1182
- }
1183
- }
1184
- clear() {
1185
- this.filename = null, this.fileCount = 0, this.visibleLayerCount = 0, this.totalLayerCount = 0, this.statusMessage = "", this.zoomLabel = "", this.orbitDistanceLabel = "", this.scaleRuler = null, this.viewportVertexCount = 0, this.drawCalls = 0, this.fps = 0, this.lineRenderMode = "", this.entityCount = 0, this.lineCount = 0, this.fillCount = 0, this.dotCount = 0, this.textCount = 0, this.renderableCount = 0, this.submittedVertexRefs = 0, this.blockVariantCount = 0, this.blockInstanceCount = 0, this.splitBlockLayerGroupCount = 0, this.renderDebugBreakdown = [], this.splitBlockLayerDetails = [], this.phase = null;
1186
- }
1187
- }
1188
- function Zt(n) {
1189
- return n >= 1e6 ? `${(n / 1e6).toFixed(2)} km` : n >= 1e3 ? `${(n / 1e3).toFixed(2)} m` : `${n.toFixed(2)} mm`;
1190
- }
1191
- function de(n) {
1192
- return {
1193
- documentKey: n.documentKey,
1194
- filename: n.filename,
1195
- phase: n.phase,
1196
- headerBounds: n.headerBounds,
1197
- layerCount: n.layerCount
1198
- };
1199
- }
1200
- const Jt = 160;
1201
- class Qt {
1202
- constructor(e) {
1203
- a(this, "layers", new jt());
1204
- a(this, "blocks", new Ut());
1205
- a(this, "inspector", new Kt());
1206
- a(this, "hud", new Gt());
1207
- a(this, "document", null);
1208
- a(this, "loadTiming", null);
1209
- a(this, "listeners", /* @__PURE__ */ new Map());
1210
- a(this, "host");
1211
- a(this, "disposed", !1);
1212
- a(this, "openEpoch", 0);
1213
- a(this, "cameraSettled", !1);
1214
- a(this, "initialDemandSettled", !1);
1215
- a(this, "initialCameraRestored", !1);
1216
- a(this, "cameraChangedDuringInitialLoad", !1);
1217
- a(this, "statusMessage", "");
1218
- a(this, "session", null);
1219
- a(this, "renderNode", null);
1220
- a(this, "unsubscribers", []);
1221
- a(this, "viewportDemandTimer", null);
1222
- this.host = e, this.layers.bind(null, () => this.emit("layer-change", { layers: this.layers })), this.blocks.bind(null, () => this.emit("block-change", { blocks: this.blocks }));
1223
- }
1224
- async open(e, t = {}) {
1225
- this.assertActive(), this.closeInternal();
1226
- const i = ++this.openEpoch;
1227
- this.cameraSettled = !1, this.initialDemandSettled = !1, this.initialCameraRestored = !1, this.cameraChangedDuringInitialLoad = !1, this.loadTiming = null;
1228
- const r = t.documentKey ?? ei(e), s = ht(e, {
1229
- documentKey: r ?? void 0,
1230
- filename: t.filename,
1231
- loadAnnotations: t.loadAnnotations,
1232
- workerUrl: t.workerUrl,
1233
- workerFactory: t.workerFactory,
1234
- fontResolver: t.fontResolver,
1235
- resolveTextFont: t.resolveTextFont,
1236
- textWarmup: t.textWarmup
1237
- });
1238
- this.session = s, this.loadTiming = s.loadTiming, this.statusMessage = "加载中…";
1239
- const o = ut(s);
1240
- this.renderNode = o, this.layers.bind(o, () => this.emit("layer-change", { layers: this.layers })), this.blocks.bind(o, () => this.emit("block-change", { blocks: this.blocks })), this.unsubscribers.push(
1241
- o.onChange(() => {
1242
- this.host.requestRender(), this.host.emitContentChange();
1243
- }),
1244
- s.on("content-appended", ({ kind: l }) => {
1245
- var d;
1246
- this.openEpoch === i && (!this.initialDemandSettled && l === "geometry" && (this.statusMessage = "增量渲染中…"), this.layers.sync(s), this.blocks.sync(s), this.refreshInspectorDetails(s), this.syncHud(((d = this.document) == null ? void 0 : d.filename) ?? null), this.host.requestRender(), this.host.emitContentChange(), this.emit("layer-change", { layers: this.layers }), this.emit("block-change", { blocks: this.blocks }), this.emit("hud-change", { hud: this.hud }));
1247
- }),
1248
- s.on("phase-change", (l) => {
1249
- var d;
1250
- this.openEpoch === i && (l.phase === "header-ready" || l.phase === "initial-loading" ? this.statusMessage = "解析中…" : l.phase === "interactive" && !this.initialDemandSettled ? this.statusMessage = "增量渲染中…" : (l.phase === "failed" || l.phase === "disposed") && (this.statusMessage = ""), this.syncHud(((d = this.document) == null ? void 0 : d.filename) ?? t.filename ?? null), this.document && (this.publishDocumentPhase(
1251
- l.phase === "interactive" && !this.initialDemandSettled ? "initial-loading" : l.phase
1252
- ), this.emit("hud-change", { hud: this.hud })));
1253
- }),
1254
- s.on("fatal-error", ({ error: l }) => {
1255
- this.openEpoch === i && console.error("[cad-viewer] CAD session failed", l);
1256
- }),
1257
- s.on("load-timing", ({ timing: l }) => {
1258
- this.openEpoch === i && (this.loadTiming = l, this.emit("load-timing", { timing: l }));
1259
- }),
1260
- s.on("demand-progress", (l) => {
1261
- var d, h;
1262
- if (this.openEpoch === i) {
1263
- if (this.statusMessage = l.settled ? "" : "增量渲染中…", this.syncHud(((d = this.document) == null ? void 0 : d.filename) ?? t.filename ?? null), !l.settled || this.initialDemandSettled) {
1264
- this.emit("hud-change", { hud: this.hud });
1265
- return;
1266
- }
1267
- if (this.initialDemandSettled = !0, this.layers.sync(s), this.blocks.sync(s), !this.initialCameraRestored && !this.cameraChangedDuringInitialLoad) {
1268
- const u = o.getAuthoritativeBounds({
1269
- includeUnmountedExternal: !0
1270
- });
1271
- u && this.host.fitBox3(V(u));
1272
- }
1273
- this.syncDisplayContext(), this.syncHud(((h = this.document) == null ? void 0 : h.filename) ?? null), this.publishDocumentPhase(s.phase), this.emit("layer-change", { layers: this.layers }), this.emit("block-change", { blocks: this.blocks }), this.emit("hud-change", { hud: this.hud }), this.flushViewportDemand();
1274
- }
1275
- })
1276
- ), this.host.mountCadObject(o.object), this.host.runtime.resize(), this.syncDisplayContext(), this.syncHud(t.filename ?? null), this.emit("hud-change", { hud: this.hud });
1277
- const c = await s.headerReady;
1278
- if (this.openEpoch !== i)
1279
- throw new Error("CAD open superseded");
1280
- return this.layers.sync(s), this.blocks.sync(s), this.syncHud(t.filename ?? c.filename ?? null), this.initialCameraRestored = this.tryRestoreCamera(r), !this.initialCameraRestored && c.bounds && this.host.fitBox3(V(c.bounds)), this.cameraSettled = !0, this.document = de({
1281
- documentKey: r,
1282
- filename: t.filename ?? c.filename ?? null,
1283
- phase: s.phase === "interactive" && !this.initialDemandSettled ? "initial-loading" : s.phase,
1284
- headerBounds: c.bounds,
1285
- layerCount: c.layers.length
1286
- }), this.emit("document-change", { document: this.document }), this.emit("layer-change", { layers: this.layers }), this.emit("block-change", { blocks: this.blocks }), this.emit("hud-change", { hud: this.hud }), this.flushViewportDemand(), s.initialContentReady.then(() => {
1287
- var l;
1288
- this.openEpoch !== i || !this.session || (this.layers.sync(s), this.blocks.sync(s), this.syncHud(((l = this.document) == null ? void 0 : l.filename) ?? null), this.flushViewportDemand(), this.emit("layer-change", { layers: this.layers }), this.emit("block-change", { blocks: this.blocks }), this.emit("hud-change", { hud: this.hud }));
1289
- }).catch(() => {
1290
- }), this.document;
1291
- }
1292
- close() {
1293
- this.assertActive(), this.closeInternal();
1294
- }
1295
- selectInsert(e) {
1296
- var i;
1297
- if (!e || !this.session) {
1298
- this.setInsertSelection(null, null);
1299
- return;
1300
- }
1301
- const t = this.session.getBlockDetails(e);
1302
- if (!t) {
1303
- this.setInsertSelection(null, null);
1304
- return;
1305
- }
1306
- this.setInsertSelection({
1307
- documentKey: ((i = this.document) == null ? void 0 : i.documentKey) ?? null,
1308
- id: t.id
1309
- }, t);
1310
- }
1311
- renderInsertPreview(e) {
1312
- var r;
1313
- const t = this.inspector.selection, i = t ? ((r = this.renderNode) == null ? void 0 : r.createInsertPreview(t.id.instanceId)) ?? null : null;
1314
- if (!i) return !1;
1315
- try {
1316
- return this.host.runtime.renderOrthographicPreview(e, i.object, {
1317
- minX: i.bounds.minX,
1318
- minY: i.bounds.minY,
1319
- maxX: i.bounds.maxX,
1320
- maxY: i.bounds.maxY
1321
- });
1322
- } finally {
1323
- i.dispose();
1324
- }
1325
- }
1326
- on(e, t) {
1327
- this.assertActive();
1328
- const i = t, r = this.listeners.get(e) ?? /* @__PURE__ */ new Set();
1329
- r.add(i), this.listeners.set(e, r);
1330
- let s = !0;
1331
- return () => {
1332
- s && (s = !1, r.delete(i), r.size === 0 && this.listeners.delete(e));
1333
- };
1334
- }
1335
- dispose() {
1336
- this.closeInternal(), this.disposed = !0, this.listeners.clear();
1337
- }
1338
- pickInsertAtScreen(e, t) {
1339
- if (!this.session) return null;
1340
- const i = this.host.screenToWorldOnPlane(e, t, 0), r = this.host.getVisibleDocumentRect();
1341
- if (!i || !r) return null;
1342
- const s = this.session.pickInsertAtPoint(
1343
- { x: i.x, y: i.y, z: i.z },
1344
- r
1345
- );
1346
- return s ? {
1347
- selection: { documentKey: s.documentKey, id: s.id },
1348
- details: s.details
1349
- } : null;
1350
- }
1351
- pickTextAtScreen(e, t) {
1352
- var s, o;
1353
- if (!this.session) return null;
1354
- const i = this.host.screenToWorldOnPlane(e, t, 0);
1355
- if (!i) return null;
1356
- const r = (o = (s = this.session).pickTextAtPoint) == null ? void 0 : o.call(s, { x: i.x, y: i.y, z: i.z });
1357
- return r ? {
1358
- selection: { documentKey: r.documentKey, id: r.id },
1359
- details: r.details
1360
- } : null;
1361
- }
1362
- getVisibilityRevision() {
1363
- return this.layers.getVisibilityRevision() + this.blocks.getVisibilityRevision();
1364
- }
1365
- setInsertSelection(e, t) {
1366
- this.inspector.setSelection(e, t), this.emit("inspector-change", { inspector: this.inspector });
1367
- }
1368
- setTextSelection(e, t) {
1369
- this.inspector.setTextSelection(e, t), this.emit("inspector-change", { inspector: this.inspector });
1370
- }
1371
- getAuthoritativeBoundsBox() {
1372
- if (!this.session) return null;
1373
- const e = this.session.getBounds({});
1374
- return e ? V(e) : null;
1375
- }
1376
- getMountedBoundsBox() {
1377
- var t;
1378
- const e = ((t = this.renderNode) == null ? void 0 : t.getMountedBounds({})) ?? null;
1379
- return e ? V(e) : null;
1380
- }
1381
- onViewportChanged() {
1382
- var e;
1383
- this.cameraSettled && (this.syncDisplayContext(), this.scheduleViewportDemand(), this.syncHud(((e = this.document) == null ? void 0 : e.filename) ?? null), this.emit("hud-change", { hud: this.hud }));
1384
- }
1385
- onBeforeRender() {
1386
- this.cameraSettled && this.syncDisplayContext();
1387
- }
1388
- onInteractionChanged() {
1389
- this.cameraSettled && (this.clearViewportDemandTimer(), this.syncDisplayContext(), this.flushViewportDemand());
1390
- }
1391
- onCameraChanged() {
1392
- var e;
1393
- this.cameraSettled && (this.initialDemandSettled || (this.cameraChangedDuringInitialLoad = !0), this.persistCurrentCamera(), this.host.emitCameraChange(), this.scheduleViewportDemand(), this.syncHud(((e = this.document) == null ? void 0 : e.filename) ?? null), this.emit("hud-change", { hud: this.hud }));
1394
- }
1395
- onRuntimeStatsChanged() {
1396
- this.document && (this.syncHud(this.document.filename), this.emit("hud-change", { hud: this.hud }));
1397
- }
1398
- closeInternal() {
1399
- this.openEpoch += 1, this.clearViewportDemandTimer(), this.cameraSettled && this.persistCurrentCamera(), this.cameraSettled = !1, this.initialDemandSettled = !1, this.initialCameraRestored = !1, this.cameraChangedDuringInitialLoad = !1, this.statusMessage = "", this.loadTiming = null;
1400
- for (const e of this.unsubscribers) e();
1401
- this.unsubscribers = [], this.session && (this.session.dispose(), this.session = null), this.renderNode = null, this.host.unmountCadObject(), this.document = null, this.layers.clear(), this.blocks.clear(), this.inspector.clear(), this.hud.clear(), this.layers.bind(null, () => this.emit("layer-change", { layers: this.layers })), this.blocks.bind(null, () => this.emit("block-change", { blocks: this.blocks })), this.emit("document-change", { document: null }), this.emit("layer-change", { layers: this.layers }), this.emit("block-change", { blocks: this.blocks }), this.emit("inspector-change", { inspector: this.inspector }), this.emit("hud-change", { hud: this.hud });
1402
- }
1403
- refreshInspectorDetails(e) {
1404
- const t = this.inspector.selection;
1405
- if (!t) return;
1406
- const i = e.getBlockDetails(pt(t.id));
1407
- if (!i) return;
1408
- const r = this.inspector.details, s = r == null ? void 0 : r.bbox;
1409
- (s == null ? void 0 : s.minX) === i.bbox.minX && (s == null ? void 0 : s.minY) === i.bbox.minY && (s == null ? void 0 : s.maxX) === i.bbox.maxX && (s == null ? void 0 : s.maxY) === i.bbox.maxY || (this.inspector.setSelection(t, i), this.emit("inspector-change", { inspector: this.inspector }));
1410
- }
1411
- tryRestoreCamera(e) {
1412
- const t = this.host.readCameraSnapshot(e);
1413
- return t ? this.host.runtime.applyCameraSnapshot(t) : !1;
1414
- }
1415
- persistCurrentCamera() {
1416
- var t;
1417
- if (!((t = this.document) != null && t.documentKey)) return;
1418
- const e = this.host.runtime.captureCameraSnapshot();
1419
- e && this.host.persistCameraSnapshot(
1420
- this.document.documentKey,
1421
- $t(e)
1422
- );
1423
- }
1424
- syncDisplayContext() {
1425
- if (!this.session) return;
1426
- const { width: e, height: t } = this.host.getViewportSize(), i = this.host.getVisibleDocumentRect();
1427
- this.session.setDisplayContext({
1428
- documentRect: i,
1429
- viewportWidthPx: e,
1430
- viewportHeightPx: t,
1431
- devicePixelRatio: this.host.runtime.getDevicePixelRatio(),
1432
- worldUnitsPerPixel: this.host.runtime.getWorldUnitsPerPixel(),
1433
- viewMode: this.host.viewMode,
1434
- quality: this.host.interaction === "active" ? "stress" : "auto"
1435
- });
1436
- }
1437
- flushViewportDemand() {
1438
- if (!this.session || !this.cameraSettled) return;
1439
- const e = this.host.getVisibleDocumentRect();
1440
- if (!e) return;
1441
- const { width: t, height: i } = this.host.getViewportSize(), r = this.host.runtime.getWorldUnitsPerPixel();
1442
- this.session.setViewportDemand({
1443
- documentRect: e,
1444
- worldUnitsPerPixel: r,
1445
- viewportWidthPx: t,
1446
- viewportHeightPx: i,
1447
- viewMode: this.host.viewMode,
1448
- interaction: this.host.interaction,
1449
- visibilityRevision: this.host.visibilityRevision
1450
- }), this.host.emitViewportChange({
1451
- viewportWidthPx: t,
1452
- viewportHeightPx: i,
1453
- worldUnitsPerPixel: r,
1454
- interaction: this.host.interaction
1455
- });
1456
- }
1457
- /**
1458
- * Demand scheduling state machine:
1459
- * - idle viewport changes flush immediately (small cost, sharp UX).
1460
- * - active viewport changes coalesce behind a trailing timer (default 160ms)
1461
- * so continuous camera motion does not plan a full demand every frame.
1462
- * Interaction edges (active/idle) bypass this and always flush immediately
1463
- * (leading-active + immediate-idle).
1464
- */
1465
- scheduleViewportDemand() {
1466
- if (this.host.interaction !== "active") {
1467
- this.flushViewportDemand();
1468
- return;
1469
- }
1470
- this.viewportDemandTimer === null && (this.viewportDemandTimer = globalThis.setTimeout(() => {
1471
- this.viewportDemandTimer = null, !(this.disposed || !this.session) && this.host.interaction === "active" && this.flushViewportDemand();
1472
- }, Math.max(0, this.host.viewportDemandCoalesceMs ?? Jt)));
1473
- }
1474
- clearViewportDemandTimer() {
1475
- this.viewportDemandTimer !== null && (globalThis.clearTimeout(this.viewportDemandTimer), this.viewportDemandTimer = null);
1476
- }
1477
- syncHud(e) {
1478
- var t;
1479
- this.hud.sync({
1480
- filename: e,
1481
- hasDocument: this.session !== null,
1482
- layers: this.layers.rows,
1483
- host: this.host,
1484
- statusMessage: this.statusMessage,
1485
- diagnostics: this.renderNode && typeof this.renderNode.getDiagnostics == "function" ? this.renderNode.getDiagnostics() : null,
1486
- phase: ((t = this.document) == null ? void 0 : t.phase) ?? null
1487
- });
1488
- }
1489
- publishDocumentPhase(e) {
1490
- this.document && (this.document = de({
1491
- documentKey: this.document.documentKey,
1492
- filename: this.document.filename,
1493
- phase: e,
1494
- headerBounds: this.document.headerBounds,
1495
- layerCount: this.document.layerCount
1496
- }), this.emit("document-change", { document: this.document }));
1497
- }
1498
- emit(e, t) {
1499
- for (const i of this.listeners.get(e) ?? [])
1500
- i(t);
1501
- }
1502
- assertActive() {
1503
- if (this.disposed) throw new Error("Viewer has been disposed.");
1504
- }
1505
- }
1506
- function ei(n) {
1507
- let e = null;
1508
- if (typeof n == "string" ? e = n : typeof URL < "u" && n instanceof URL ? e = n.href : typeof n == "object" && n !== null && "kind" in n && n.kind === "url" && (e = n.url), !e) return null;
1509
- let t = 2166136261;
1510
- for (let i = 0; i < e.length; i += 1)
1511
- t ^= e.charCodeAt(i), t = Math.imul(t, 16777619);
1512
- return `reference:src-${(t >>> 0).toString(16)}`;
1513
- }
1514
- const he = 1;
1515
- class ti {
1516
- constructor(e) {
1517
- a(this, "startX", 0);
1518
- a(this, "startY", 0);
1519
- a(this, "lastX", 0);
1520
- a(this, "lastY", 0);
1521
- a(this, "primaryPointerDown", !1);
1522
- a(this, "exceededDragThreshold", !1);
1523
- a(this, "travelPx", 0);
1524
- this.thresholds = e;
1525
- }
1526
- get isPrimaryPointerDown() {
1527
- return this.primaryPointerDown;
1528
- }
1529
- get hasExceededDragThreshold() {
1530
- return this.exceededDragThreshold;
1531
- }
1532
- get pointerTravelPx() {
1533
- return this.travelPx;
1534
- }
1535
- begin(e, t) {
1536
- this.primaryPointerDown = !0, this.startX = e, this.startY = t, this.lastX = e, this.lastY = t, this.resetMotion();
1537
- }
1538
- release() {
1539
- const e = this.primaryPointerDown;
1540
- return this.primaryPointerDown = !1, e;
1541
- }
1542
- update(e, t, i) {
1543
- const r = e - this.lastX, s = t - this.lastY;
1544
- this.isTrackingPrimaryPointer(i) && (this.travelPx += Math.sqrt(r * r + s * s)), this.lastX = e, this.lastY = t, this.isTrackingPrimaryPointer(i) && !this.exceededDragThreshold && this.isPastDragIntentThreshold(e, t) && (this.exceededDragThreshold = !0);
1545
- }
1546
- isPastDistanceThreshold(e, t) {
1547
- const i = e - this.startX, r = t - this.startY;
1548
- return i * i + r * r > this.thresholds.distancePx * this.thresholds.distancePx;
1549
- }
1550
- isPastDragIntentThreshold(e, t) {
1551
- return this.isPastDistanceThreshold(e, t) || this.travelPx > this.thresholds.pathPx;
1552
- }
1553
- resetMotion() {
1554
- this.exceededDragThreshold = !1, this.travelPx = 0;
1555
- }
1556
- reset() {
1557
- this.release(), this.resetMotion(), this.startX = 0, this.startY = 0, this.lastX = 0, this.lastY = 0;
1558
- }
1559
- isTrackingPrimaryPointer(e) {
1560
- return this.primaryPointerDown || (e & he) === he;
1561
- }
1562
- }
1563
- function U(n, e) {
1564
- return `${n}:${e}`;
1565
- }
1566
- class ii {
1567
- constructor() {
1568
- a(this, "expandedLayers", /* @__PURE__ */ new Set());
1569
- a(this, "expandedBlocks", /* @__PURE__ */ new Set());
1570
- a(this, "blockVisibilitySnapshots", /* @__PURE__ */ new Map());
1571
- a(this, "filterText", "");
1572
- a(this, "scopeKey", null);
1573
- a(this, "revealedSelectionKey", null);
1574
- a(this, "suppressedSelectionKey", null);
1575
- }
1576
- collapseAll(e = null) {
1577
- this.expandedLayers.clear(), this.expandedBlocks.clear(), this.suppressedSelectionKey = e;
1578
- }
1579
- resetForScope(e) {
1580
- this.scopeKey !== e && (this.scopeKey = e, this.expandedLayers.clear(), this.expandedBlocks.clear(), this.blockVisibilitySnapshots.clear(), this.filterText = "", this.revealedSelectionKey = null, this.suppressedSelectionKey = null);
1581
- }
1582
- toggleLayerExpanded(e) {
1583
- this.expandedLayers.has(e) ? this.expandedLayers.delete(e) : this.expandedLayers.add(e), this.suppressedSelectionKey = null;
1584
- }
1585
- toggleBlockExpanded(e, t) {
1586
- const i = U(e, t);
1587
- this.expandedBlocks.has(i) ? this.expandedBlocks.delete(i) : this.expandedBlocks.add(i), this.suppressedSelectionKey = null;
1588
- }
1589
- prune(e, t) {
1590
- for (const i of this.expandedLayers)
1591
- e.has(i) || this.expandedLayers.delete(i);
1592
- for (const i of this.expandedBlocks)
1593
- t.has(i) || this.expandedBlocks.delete(i);
1594
- for (const i of this.blockVisibilitySnapshots.keys())
1595
- t.has(i) || this.blockVisibilitySnapshots.delete(i);
1596
- }
1597
- ensureSelectionVisible(e, t, i) {
1598
- this.suppressedSelectionKey !== e && (this.suppressedSelectionKey !== null && (this.suppressedSelectionKey = null), this.expandedLayers.add(t), this.expandedBlocks.add(U(t, i)));
1599
- }
1600
- needsSelectionReveal(e) {
1601
- return this.revealedSelectionKey !== e;
1602
- }
1603
- markSelectionRevealed(e) {
1604
- this.revealedSelectionKey = e;
1605
- }
1606
- clearSelectionReveal() {
1607
- this.revealedSelectionKey = null, this.suppressedSelectionKey = null;
1608
- }
1609
- }
1610
- function ri(n) {
1611
- const e = /* @__PURE__ */ new Map();
1612
- for (const i of n.usages) {
1613
- const r = e.get(i.layerIndex) ?? /* @__PURE__ */ new Map(), s = U(i.layerIndex, i.variantId);
1614
- let o = r.get(s);
1615
- o || (o = {
1616
- key: s,
1617
- layerIndex: i.layerIndex,
1618
- variantId: i.variantId,
1619
- blockName: i.blockName,
1620
- canHighlight: !1,
1621
- inserts: []
1622
- }, r.set(s, o)), o.inserts.push({
1623
- insertId: i.insertId,
1624
- instanceId: i.instanceId,
1625
- label: i.label,
1626
- canHighlight: i.canHighlight !== !1,
1627
- visible: i.visible
1628
- }), o.canHighlight || (o.canHighlight = i.canHighlight !== !1), e.set(i.layerIndex, r);
1629
- }
1630
- const t = /* @__PURE__ */ new Map();
1631
- for (const [i, r] of e)
1632
- t.set(i, [...r.values()]);
1633
- return t;
1634
- }
1635
- function ni(n, e) {
1636
- return n ? e.toLowerCase().includes(n) : !0;
1637
- }
1638
- function _(n, e) {
1639
- n.dataset.visible = e ? "1" : "0", n.classList.toggle("off", !e), n.textContent = e ? "●" : "○", n.title = e ? "隐藏该 insert" : "显示该 insert";
1640
- }
1641
- function Y(n, e) {
1642
- n.classList.toggle("off", e === "none"), n.classList.toggle("mixed", e === "mixed"), n.textContent = e === "all" ? "●" : e === "none" ? "○" : "◐", n.title = e === "all" ? "隐藏整个 block" : e === "none" ? "显示整个 block(恢复子 insert 状态)" : "隐藏整个 block(保留子 insert 状态)";
1643
- }
1644
- function si(n) {
1645
- let e = 0;
1646
- for (const t of n) t.visible && (e += 1);
1647
- return e <= 0 ? "none" : e >= n.length ? "all" : "mixed";
1648
- }
1649
- function Le(n, e, t, i) {
1650
- if (!n) return !1;
1651
- const r = n.id;
1652
- return r.layerIndex !== e || r.variantId !== t ? !1 : i === void 0 ? !0 : r.instanceId === i;
1653
- }
1654
- function ue(n) {
1655
- const e = n.querySelector(".vui-lrow-insert.active") ?? n.querySelector(".vui-lrow-block.active");
1656
- if (!e) return !1;
1657
- const t = n.getBoundingClientRect(), i = e.getBoundingClientRect();
1658
- if (t.height <= 0) return !1;
1659
- const r = i.top - t.top, s = n.scrollTop + r - (t.height - i.height) / 2;
1660
- return n.scrollTop = Math.max(0, s), !0;
1661
- }
1662
- function Ae(n, e, t = 0) {
1663
- const i = () => {
1664
- const r = n.getBoundingClientRect(), s = e.getBoundingClientRect();
1665
- if (r.height <= 0) return;
1666
- const o = r.top + t, c = r.bottom;
1667
- if (!(s.top >= o && s.bottom <= c)) {
1668
- if (s.top < o) {
1669
- const l = s.top - r.top + n.scrollTop, d = Math.max(0, l - t);
1670
- n.scrollTop = d;
1671
- return;
1672
- }
1673
- if (s.bottom > c) {
1674
- const l = s.bottom - r.top + n.scrollTop, d = Math.max(0, l - r.height);
1675
- n.scrollTop = d;
1676
- }
1677
- }
1678
- };
1679
- i(), typeof requestAnimationFrame == "function" && requestAnimationFrame(i);
1680
- }
1681
- function oi(n, e) {
1682
- const t = e.trim().toLowerCase();
1683
- n.querySelectorAll(".vui-lrow-wrap").forEach((i) => {
1684
- var c, l;
1685
- const r = i.querySelector(".vui-lrow:not(.vui-lrow-all)");
1686
- if (!r) return;
1687
- const s = ((l = (c = r.querySelector(".vui-lrow-name")) == null ? void 0 : c.textContent) == null ? void 0 : l.toLowerCase()) ?? "", o = !t || s.includes(t);
1688
- i.style.display = o ? "" : "none";
1689
- });
1690
- }
1691
- function ai(n, e) {
1692
- if (n.querySelectorAll(".vui-lrow-block.active, .vui-lrow-insert.active").forEach((c) => {
1693
- c.classList.remove("active");
1694
- }), !e) return;
1695
- const { layerIndex: t, variantId: i, instanceId: r } = e.id, s = n.querySelector(
1696
- `.vui-lrow[data-layer-index="${t}"] ~ .vui-lrow-blocks .vui-lrow-block[data-variant-id="${i}"]`
1697
- );
1698
- s && s.classList.add("active");
1699
- const o = n.querySelector(
1700
- `.vui-lrow-insert[data-layer-index="${t}"][data-variant-id="${i}"][data-instance-id="${r}"]`
1701
- );
1702
- o && o.classList.add("active");
1703
- }
1704
- function pe(n, e, t) {
1705
- var f;
1706
- e.resetForScope(t.scopeKey);
1707
- const i = e.filterText.trim().toLowerCase(), r = t.layers.rows, s = ri(t.blocks);
1708
- if (n.list.replaceChildren(), n.count.textContent = String(r.length), r.length === 0) {
1709
- n.list.appendChild(di("加载 CAD 文档后显示图层。"));
1710
- return;
1711
- }
1712
- const o = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set();
1713
- for (const p of r) {
1714
- o.add(p.index);
1715
- for (const m of s.get(p.index) ?? []) c.add(m.key);
1716
- }
1717
- e.prune(o, c);
1718
- const l = (f = t.selection) == null ? void 0 : f.id, d = l ? `${l.layerIndex}:${l.variantId}:${l.instanceId}` : null;
1719
- l && d && e.needsSelectionReveal(d) ? e.ensureSelectionVisible(d, l.layerIndex, l.variantId) : l || e.clearSelectionReveal();
1720
- const h = document.createElement("div");
1721
- h.className = "vui-lrow vui-lrow-all", h.innerHTML = `
1722
- <span class="vui-lrow-eye">◎</span>
1723
- <span class="vui-lrow-dot"></span>
1724
- <span class="vui-lrow-toggle placeholder">▶</span>
1725
- <span class="vui-lrow-name">全部图层</span>
1726
- <span class="vui-lrow-clr" style="background: ${t.layers.allColor == null ? "#c8c8c8" : `#${t.layers.allColor.toString(16).padStart(6, "0")}`}" title="全部图层颜色覆盖(右键重置)">
1727
- <input type="color" value="#${(t.layers.allColor ?? 13158600).toString(16).padStart(6, "0")}">
1728
- </span>`;
1729
- const u = h.querySelector('input[type="color"]');
1730
- u && u.addEventListener("input", () => {
1731
- var g;
1732
- const p = Number.parseInt(u.value.slice(1), 16), m = Number.isFinite(p) ? p : null;
1733
- if (t.setAllLayerColor) t.setAllLayerColor(m);
1734
- else for (const y of r) t.setLayerColor(y.index, m);
1735
- const b = m === null ? "#c8c8c8" : O(m);
1736
- (g = u.closest(".vui-lrow-clr")) == null || g.style.setProperty("background", b), me(n.list, r, m);
1737
- }), u == null || u.addEventListener("click", (p) => p.stopPropagation());
1738
- const v = h.querySelector(".vui-lrow-clr");
1739
- v && v.addEventListener("contextmenu", (p) => {
1740
- var m;
1741
- if (p.preventDefault(), p.stopPropagation(), t.setAllLayerColor) t.setAllLayerColor(null);
1742
- else for (const b of r) t.setLayerColor(b.index, null);
1743
- (m = u == null ? void 0 : u.closest(".vui-lrow-clr")) == null || m.style.setProperty("background", "#c8c8c8"), u && (u.value = "#c8c8c8"), me(n.list, r, null);
1744
- }), n.list.appendChild(h);
1745
- for (const p of r)
1746
- ni(i, p.name) && n.list.appendChild(ci(p, e, t, s.get(p.index) ?? []));
1747
- d && e.needsSelectionReveal(d) && (ue(n.list) && e.markSelectionRevealed(d), typeof requestAnimationFrame == "function" && requestAnimationFrame(() => {
1748
- e.needsSelectionReveal(d) && ue(n.list) && e.markSelectionRevealed(d);
1749
- }));
1750
- }
1751
- function O(n) {
1752
- return `#${n.toString(16).padStart(6, "0")}`;
1753
- }
1754
- function Fe(n) {
1755
- return n.color !== null ? O(n.color) : O(De(n.sourceColor));
1756
- }
1757
- function li(n, e) {
1758
- var r;
1759
- (r = n.querySelector(".vui-lrow-dot")) == null || r.style.setProperty("background", e);
1760
- const t = n.querySelector(".vui-lrow-clr");
1761
- t == null || t.style.setProperty("background", e);
1762
- const i = n.querySelector('input[type="color"]');
1763
- i && i.value !== e && (i.value = e);
1764
- }
1765
- function me(n, e, t) {
1766
- const i = new Map(e.map((r) => [r.index, r]));
1767
- n.querySelectorAll(".vui-lrow[data-layer-index]").forEach((r) => {
1768
- const s = i.get(Number(r.dataset.layerIndex));
1769
- s && li(r, t === null ? Fe(s) : O(t));
1770
- });
1771
- }
1772
- function ci(n, e, t, i) {
1773
- const r = i.length > 0, s = e.expandedLayers.has(n.index), o = Fe(n), c = document.createElement("div");
1774
- c.className = "vui-lrow-wrap";
1775
- const l = document.createElement("div");
1776
- l.className = `vui-lrow${n.visible ? "" : " off"}`, l.dataset.layerIndex = String(n.index);
1777
- const d = document.createElement("button");
1778
- d.type = "button", d.className = "vui-lrow-eye", d.textContent = n.visible ? "●" : "○", d.title = n.visible ? "隐藏图层" : "显示图层", d.addEventListener("click", (b) => {
1779
- b.stopPropagation();
1780
- const g = !n.visible;
1781
- d.textContent = g ? "●" : "○", d.title = g ? "隐藏图层" : "显示图层", l.classList.toggle("off", !g), t.toggleLayer(n.index);
1782
- });
1783
- const h = document.createElement("span");
1784
- h.className = "vui-lrow-dot", h.style.background = o;
1785
- const u = document.createElement("button");
1786
- u.type = "button", u.className = `vui-lrow-toggle${r ? "" : " placeholder"}`, u.textContent = s ? "▼" : "▶", u.disabled = !r;
1787
- const v = (b) => {
1788
- if (b.stopPropagation(), !r) return;
1789
- e.toggleLayerExpanded(n.index);
1790
- const g = e.expandedLayers.has(n.index);
1791
- u.textContent = g ? "▼" : "▶";
1792
- let y = c.querySelector(":scope > .vui-lrow-blocks");
1793
- if (g)
1794
- if (y)
1795
- y.classList.add("open");
1796
- else {
1797
- y = document.createElement("div"), y.className = "vui-lrow-blocks open";
1798
- for (const P of i)
1799
- y.appendChild(ve(P, e, t));
1800
- c.appendChild(y);
1801
- }
1802
- else {
1803
- y == null || y.classList.remove("open");
1804
- const P = c.closest(".vui-section-list") ?? c.parentElement;
1805
- P && Ae(P, l, 0);
1806
- }
1807
- };
1808
- u.addEventListener("click", v);
1809
- const f = document.createElement("span");
1810
- f.className = "vui-lrow-name", f.textContent = n.name || "(unnamed)", f.title = n.name;
1811
- const p = document.createElement("span");
1812
- p.className = "vui-lrow-clr", p.style.background = o, p.title = "覆盖颜色(右键重置)";
1813
- const m = document.createElement("input");
1814
- if (m.type = "color", m.value = o, m.addEventListener("input", () => {
1815
- const b = Number.parseInt(m.value.slice(1), 16);
1816
- t.setLayerColor(n.index, Number.isFinite(b) ? b : null), h.style.background = m.value, p.style.background = m.value;
1817
- }), m.addEventListener("click", (b) => b.stopPropagation()), p.addEventListener("contextmenu", (b) => {
1818
- b.preventDefault(), b.stopPropagation(), t.setLayerColor(n.index, null);
1819
- const g = O(De(n.sourceColor));
1820
- m.value = g, h.style.background = g, p.style.background = g;
1821
- }), p.appendChild(m), l.append(d, h, u, f, p), l.addEventListener("click", (b) => {
1822
- const g = b.target;
1823
- if (g instanceof HTMLInputElement && g.type === "color" || g.closest(".vui-lrow-clr") || g.closest(".vui-lrow-toggle") || g.closest(".vui-lrow-eye")) return;
1824
- const y = !n.visible;
1825
- d.textContent = y ? "●" : "○", d.title = y ? "隐藏图层" : "显示图层", l.classList.toggle("off", !y), t.toggleLayer(n.index);
1826
- }), c.appendChild(l), r && s) {
1827
- const b = document.createElement("div");
1828
- b.className = "vui-lrow-blocks open";
1829
- for (const g of i)
1830
- b.appendChild(ve(g, e, t));
1831
- c.appendChild(b);
1832
- }
1833
- return c;
1834
- }
1835
- function ve(n, e, t) {
1836
- const i = document.createElement("div");
1837
- i.className = "vui-lrow-block-wrap";
1838
- const r = e.expandedBlocks.has(n.key), s = si(n.inserts), o = Le(t.selection, n.layerIndex, n.variantId), c = document.createElement("div");
1839
- c.className = `vui-lrow-block${o ? " active" : ""}${n.canHighlight ? "" : " disabled"}`, c.dataset.variantId = String(n.variantId), c.dataset.layerIndex = String(n.layerIndex), n.canHighlight || (c.title = "该 block 无可高亮几何");
1840
- const l = document.createElement("button");
1841
- l.type = "button", l.className = "vui-lrow-block-visibility", Y(l, s), l.addEventListener("click", (f) => {
1842
- if (f.stopPropagation(), s === "none") {
1843
- const m = e.blockVisibilitySnapshots.get(n.key), b = [];
1844
- for (const g of n.inserts) {
1845
- const y = (m == null ? void 0 : m.get(g.instanceId)) ?? !0;
1846
- g.visible !== y && b.push({ insertId: g.insertId, visible: y });
1847
- }
1848
- if (Y(l, "all"), i.querySelectorAll(".vui-lrow-insert-visibility").forEach((g) => {
1849
- _(g, !0);
1850
- }), t.batchToggleInserts && b.length > 0)
1851
- t.batchToggleInserts(b);
1852
- else
1853
- for (const g of b) t.toggleInsert(g.insertId, g.visible);
1854
- return;
1855
- }
1856
- e.blockVisibilitySnapshots.set(
1857
- n.key,
1858
- new Map(n.inserts.map((m) => [m.instanceId, m.visible]))
1859
- );
1860
- const p = n.inserts.filter((m) => m.visible).map((m) => ({ insertId: m.insertId, visible: !1 }));
1861
- if (Y(l, "none"), i.querySelectorAll(".vui-lrow-insert-visibility").forEach((m) => {
1862
- _(m, !1);
1863
- }), t.batchToggleInserts && p.length > 0)
1864
- t.batchToggleInserts(p);
1865
- else
1866
- for (const m of p) t.toggleInsert(m.insertId, !1);
1867
- });
1868
- const d = document.createElement("button");
1869
- d.type = "button", d.className = "vui-lrow-block-toggle", d.textContent = r ? "▼" : "▶";
1870
- const h = (f) => {
1871
- f.stopPropagation(), e.toggleBlockExpanded(n.layerIndex, n.variantId);
1872
- const p = e.expandedBlocks.has(n.key);
1873
- d.textContent = p ? "▼" : "▶";
1874
- let m = i.querySelector(":scope > .vui-lrow-inserts");
1875
- if (p)
1876
- m ? m.classList.add("open") : (m = fe(n, e, t, l), i.appendChild(m));
1877
- else {
1878
- m == null || m.classList.remove("open");
1879
- const b = i.closest(".vui-section-list") ?? i.parentElement;
1880
- b && Ae(b, c, 24);
1881
- }
1882
- };
1883
- d.addEventListener("click", h);
1884
- const u = document.createElement("span");
1885
- u.className = "vui-lrow-block-name", u.textContent = n.blockName, u.title = n.blockName;
1886
- const v = document.createElement("span");
1887
- return v.className = "vui-lrow-block-count", v.textContent = `×${n.inserts.length}`, c.append(l, d, u, v), n.inserts.length === 1 && c.addEventListener("click", () => {
1888
- if (!n.canHighlight) return;
1889
- const f = n.inserts[0], p = `${n.layerIndex}:${n.variantId}:${f.instanceId}`;
1890
- e.markSelectionRevealed(p), t.selectInsert(f.insertId);
1891
- }), i.appendChild(c), r && i.appendChild(fe(n, e, t, l)), i;
1892
- }
1893
- function fe(n, e, t, i) {
1894
- const r = document.createElement("div");
1895
- r.className = "vui-lrow-inserts open";
1896
- for (const s of n.inserts) {
1897
- const o = document.createElement("div");
1898
- o.className = `vui-lrow-insert${Le(t.selection, n.layerIndex, n.variantId, s.instanceId) ? " active" : ""}${s.canHighlight === !1 ? " disabled" : ""}`, o.dataset.layerIndex = String(n.layerIndex), o.dataset.variantId = String(n.variantId), o.dataset.instanceId = String(s.instanceId), o.dataset.insertId = s.insertId, s.canHighlight === !1 && (o.title = "该 insert 无可高亮几何");
1899
- const c = document.createElement("button");
1900
- c.type = "button", c.className = "vui-lrow-insert-visibility", _(c, s.visible), c.addEventListener("click", (d) => {
1901
- d.stopPropagation();
1902
- const h = !s.visible;
1903
- _(c, h);
1904
- const u = r.querySelectorAll(".vui-lrow-insert-visibility");
1905
- let v = 0;
1906
- u.forEach((p) => {
1907
- p.dataset.visible === "1" && v++;
1908
- });
1909
- const f = v === 0 ? "none" : v === u.length ? "all" : "mixed";
1910
- Y(i, f), t.toggleInsert(s.insertId, h);
1911
- });
1912
- const l = document.createElement("span");
1913
- l.className = "vui-lrow-insert-label", l.textContent = s.label, l.title = s.label, o.append(c, l), o.addEventListener("click", () => {
1914
- if (s.canHighlight === !1) return;
1915
- const d = `${n.layerIndex}:${n.variantId}:${s.instanceId}`;
1916
- e.markSelectionRevealed(d), t.selectInsert(s.insertId);
1917
- }), r.appendChild(o);
1918
- }
1919
- return r;
1920
- }
1921
- function di(n) {
1922
- const e = document.createElement("div");
1923
- return e.className = "vui-empty-note", e.textContent = n, e;
1924
- }
1925
- function hi(n, e) {
1926
- const t = () => {
1927
- e(n.value);
1928
- };
1929
- return n.addEventListener("input", t), () => n.removeEventListener("input", t);
1930
- }
1931
- function F(n) {
1932
- return n.replace(/[&<>"']/g, (e) => ({
1933
- "&": "&amp;",
1934
- "<": "&lt;",
1935
- ">": "&gt;",
1936
- '"': "&quot;",
1937
- "'": "&#39;"
1938
- })[e] ?? e);
1939
- }
1940
- function K(n) {
1941
- return n == null || !Number.isFinite(n) ? "—" : n.toFixed(4).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
1942
- }
1943
- function I(n, e) {
1944
- const t = K(n);
1945
- return t === "—" ? t : `${t} ${e}`;
1946
- }
1947
- function be(n, e) {
1948
- if (n.body.replaceChildren(), e.textSelection && e.textDetails) {
1949
- D(n, !1), pi(n.body, e.textDetails), ge(n.body);
1950
- return;
1951
- }
1952
- if (!e.selection || !e.details) {
1953
- D(n, !1), n.body.appendChild(mi("点击画布中的对象查看属性"));
1954
- return;
1955
- }
1956
- ui(n.body, e.details), ge(n.body);
1957
- }
1958
- function D(n, e) {
1959
- if (!n.previewWrap || !n.previewCanvas || (n.previewWrap.hidden = !e, e)) return;
1960
- const t = n.previewCanvas.getContext("2d");
1961
- t == null || t.clearRect(0, 0, n.previewCanvas.width, n.previewCanvas.height);
1962
- }
1963
- function ui(n, e) {
1964
- Ne(n, "INSERT", e.blockName), G(n, "基本", (t) => {
1965
- C(t, "Block", e.blockName), C(t, "图层", e.layerName), C(t, "Instance", `#${e.id.instanceId}`), C(t, "当前形态", "普通图形");
1966
- }), G(n, "变换", (t) => {
1967
- C(t, "X", I(e.tx, "mm")), C(t, "Y", I(e.ty, "mm")), C(t, "旋转", I(e.rotation, "°")), C(t, "Sx", K(e.scaleX)), C(t, "Sy", K(e.scaleY));
1968
- });
1969
- }
1970
- function pi(n, e) {
1971
- Ne(n, "文本", e.content), G(n, "基本", (t) => {
1972
- C(t, "图层", e.layerName), C(t, "内容", e.content), C(t, "高度", I(e.height, "mm")), C(t, "X", I(e.x, "mm")), C(t, "Y", I(e.y, "mm")), C(t, "旋转", I(e.rotation, "°"));
1973
- });
1974
- }
1975
- function Ne(n, e, t) {
1976
- const i = document.createElement("div");
1977
- i.className = "vui-pp-section vui-pp-title-row", i.innerHTML = `<strong>${F(e)}</strong><span>·</span><span>${F(t)}</span>`, n.appendChild(i);
1978
- }
1979
- function G(n, e, t) {
1980
- const i = document.createElement("section");
1981
- i.className = "vui-pp-section";
1982
- const r = document.createElement("div");
1983
- r.className = "vui-pp-section-label", r.textContent = e, i.appendChild(r), t(i), n.appendChild(i);
1984
- }
1985
- function C(n, e, t) {
1986
- const i = document.createElement("div");
1987
- i.className = "vui-pp-row", i.innerHTML = `<span class="label">${F(e)}</span><span class="value" title="${F(t)}">${F(t)}</span>`, n.appendChild(i);
1988
- }
1989
- function mi(n) {
1990
- const e = document.createElement("div");
1991
- return e.className = "vui-empty-note", e.textContent = n, e;
1992
- }
1993
- function ge(n) {
1994
- if (n.querySelector('input:not([type="color"])'))
1995
- throw new Error("Property panel must not contain editable inputs");
1996
- if (n.querySelector('[contenteditable=""], [contenteditable="true"]'))
1997
- throw new Error("Property panel must not contain contenteditable elements");
1998
- if (n.querySelector("textarea"))
1999
- throw new Error("Property panel must not contain textarea elements");
2000
- }
2001
- const vi = typeof requestAnimationFrame == "function" ? requestAnimationFrame.bind(globalThis) : (n) => globalThis.setTimeout(() => n(performance.now()), 16), fi = typeof cancelAnimationFrame == "function" ? cancelAnimationFrame.bind(globalThis) : (n) => {
2002
- globalThis.clearTimeout(n);
2003
- };
2004
- class bi {
2005
- constructor(e) {
2006
- a(this, "onFlush");
2007
- a(this, "requestFrame");
2008
- a(this, "cancelFrame");
2009
- a(this, "pending", /* @__PURE__ */ new Map());
2010
- a(this, "scheduledHandle", null);
2011
- this.onFlush = e.onFlush, this.requestFrame = e.requestFrame ?? vi, this.cancelFrame = e.cancelFrame ?? fi;
2012
- }
2013
- enqueue(e, t) {
2014
- this.pending.set(e, t), this.scheduledHandle === null && (this.scheduledHandle = this.requestFrame(() => {
2015
- if (this.scheduledHandle = null, this.pending.size === 0)
2016
- return;
2017
- const i = Array.from(this.pending, ([r, s]) => ({ insertId: r, visible: s }));
2018
- this.pending.clear(), this.onFlush(i);
2019
- }));
2020
- }
2021
- enqueueMany(e) {
2022
- for (const t of e)
2023
- this.pending.set(t.insertId, t.visible);
2024
- e.length === 0 || this.scheduledHandle !== null || (this.scheduledHandle = this.requestFrame(() => {
2025
- if (this.scheduledHandle = null, this.pending.size === 0)
2026
- return;
2027
- const t = Array.from(this.pending, ([i, r]) => ({ insertId: i, visible: r }));
2028
- this.pending.clear(), this.onFlush(t);
2029
- }));
2030
- }
2031
- clear() {
2032
- this.pending.clear(), this.scheduledHandle !== null && (this.cancelFrame(this.scheduledHandle), this.scheduledHandle = null);
2033
- }
2034
- }
2035
- const gi = `
2036
- :host {
2037
- display: block;
2038
- width: 100%;
2039
- height: 100%;
2040
- color: var(--vui-text);
2041
- font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
2042
- font-size: 12px;
2043
- line-height: 1.4;
2044
- overflow: hidden;
2045
- }
2046
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
2047
- button, input { font: inherit; }
2048
-
2049
- /* Custom slim scrollbar */
2050
- * {
2051
- scrollbar-width: thin;
2052
- scrollbar-color: var(--vui-scrollbar-thumb) transparent;
2053
- }
2054
- *::-webkit-scrollbar { width: 4px; height: 4px; }
2055
- *::-webkit-scrollbar-track { background: transparent; }
2056
- *::-webkit-scrollbar-thumb { background: var(--vui-scrollbar-thumb); border-radius: 2px; }
2057
- *::-webkit-scrollbar-thumb:hover { background: var(--vui-text-dim); }
2058
-
2059
- /* Root container overlay */
2060
- .vui-root {
2061
- position: absolute;
2062
- inset: 0;
2063
- display: flex;
2064
- flex-direction: column;
2065
- height: 100%;
2066
- width: 100%;
2067
- background: transparent;
2068
- pointer-events: none;
2069
- overflow: hidden;
2070
- }
2071
-
2072
- .vui-sep { width: 1px; height: 22px; background: var(--vui-border); margin: 0 2px; }
2073
- .vui-btn {
2074
- padding: 5px 11px;
2075
- border: 1px solid var(--vui-border);
2076
- background: var(--vui-bg3);
2077
- color: var(--vui-text);
2078
- border-radius: 5px;
2079
- cursor: pointer;
2080
- font-size: 12px;
2081
- white-space: nowrap;
2082
- transition: background 0.12s, border-color 0.12s;
2083
- outline: none;
2084
- display: inline-flex;
2085
- align-items: center;
2086
- gap: 4px;
2087
- }
2088
- .vui-btn:hover { background: var(--vui-btn-hover); border-color: var(--vui-accent); }
2089
- .vui-btn:active { background: var(--vui-btn-active); }
2090
- .vui-btn.active {
2091
- background: var(--vui-btn-primary-bg);
2092
- border-color: var(--vui-accent);
2093
- color: var(--vui-btn-primary-text);
2094
- }
2095
- .vui-kbd {
2096
- display: inline-block;
2097
- font-size: 10px;
2098
- padding: 1px 4px;
2099
- border: 1px solid var(--vui-border);
2100
- border-radius: 3px;
2101
- color: var(--vui-text-dim);
2102
- margin-left: 2px;
2103
- }
2104
- .vui-view-toggle { display: inline-flex; gap: 4px; }
2105
- .vui-info-text { font-size: 12px; color: var(--vui-text-dim); }
2106
-
2107
- /* ── Main Area (Flex row with middle transparent space) ── */
2108
- .vui-main {
2109
- flex: 1;
2110
- display: flex;
2111
- min-height: 0;
2112
- overflow: hidden;
2113
- pointer-events: none;
2114
- position: relative;
2115
- }
2116
-
2117
- /* ── Sidebar shell and floating collapse handle ── */
2118
- .vui-layer-panel-shell {
2119
- flex: 0 0 auto;
2120
- display: flex;
2121
- align-items: stretch;
2122
- position: relative;
2123
- overflow: visible;
2124
- background: var(--vui-bg2);
2125
- pointer-events: auto;
2126
- z-index: 5;
2127
- transition: width 0.15s ease, min-width 0.15s ease;
2128
- }
2129
- .vui-layer-panel-shell.closed {
2130
- width: 0;
2131
- min-width: 0;
2132
- background: transparent;
2133
- }
2134
- .vui-layer-panel-shell.closed .vui-sidebar {
2135
- width: 0;
2136
- min-width: 0;
2137
- border: 0;
2138
- }
2139
- .vui-sidebar {
2140
- width: 236px;
2141
- min-width: 196px;
2142
- background: var(--vui-bg2);
2143
- border-right: 1px solid var(--vui-border);
2144
- display: flex;
2145
- flex-direction: column;
2146
- min-height: 0;
2147
- flex-shrink: 0;
2148
- pointer-events: auto;
2149
- z-index: 5;
2150
- overflow: hidden;
2151
- transition: width 0.15s ease, min-width 0.15s ease;
2152
- }
2153
- .vui-layer-panel-toggle-zone {
2154
- position: absolute;
2155
- top: 0;
2156
- bottom: 0;
2157
- right: -16px;
2158
- z-index: 12;
2159
- width: 16px;
2160
- display: inline-flex;
2161
- align-items: center;
2162
- justify-content: center;
2163
- }
2164
- .vui-layer-panel-toggle {
2165
- width: 16px;
2166
- height: 48px;
2167
- position: relative;
2168
- z-index: 1;
2169
- display: inline-flex;
2170
- align-items: center;
2171
- justify-content: center;
2172
- background: transparent;
2173
- color: var(--vui-text-dim);
2174
- border: 0;
2175
- border-radius: 5px;
2176
- cursor: pointer;
2177
- font-size: 0;
2178
- line-height: 1;
2179
- padding: 0;
2180
- opacity: 0;
2181
- pointer-events: none;
2182
- box-shadow: none;
2183
- transition: color 0.12s, background 0.12s, border-color 0.12s, opacity 0.15s ease, transform 0.15s ease;
2184
- }
2185
- .vui-layer-panel-toggle::before,
2186
- .vui-property-panel-toggle::before {
2187
- content: '';
2188
- position: absolute;
2189
- inset: 0;
2190
- z-index: -1;
2191
- pointer-events: none;
2192
- background: var(--vui-bg2);
2193
- border: 1px solid var(--vui-border);
2194
- border-radius: 5px;
2195
- box-shadow: 0 3px 10px rgba(0, 0, 0, 0.16);
2196
- transition: background 0.12s ease, border-color 0.12s ease;
2197
- }
2198
- .vui-layer-panel-toggle::before { transform: translateX(-4px); }
2199
- .vui-property-panel-toggle::before { transform: translateX(4px); }
2200
- .vui-layer-panel-toggle::after,
2201
- .vui-property-panel-toggle::after {
2202
- position: relative;
2203
- z-index: 2;
2204
- display: block;
2205
- width: 100%;
2206
- color: var(--vui-text-dim);
2207
- font-size: 13px;
2208
- line-height: 1;
2209
- pointer-events: none;
2210
- transition: color 0.12s ease, transform 0.15s ease;
2211
- }
2212
- .vui-layer-panel-toggle::after { content: '‹'; transform: translateX(-4px); }
2213
- .vui-layer-panel-toggle[aria-expanded='false']::after { content: '›'; transform: none; }
2214
- .vui-property-panel-toggle::after { content: '›'; transform: translateX(4px); }
2215
- .vui-property-panel-toggle[aria-expanded='false']::after { content: '‹'; transform: none; }
2216
- .vui-layer-panel-toggle-zone:hover .vui-layer-panel-toggle,
2217
- .vui-layer-panel-toggle:focus-visible {
2218
- opacity: 1;
2219
- pointer-events: auto;
2220
- }
2221
- .vui-layer-panel-toggle:hover {
2222
- background: transparent;
2223
- transform: translateY(-1px);
2224
- }
2225
- .vui-layer-panel-toggle:hover::after,
2226
- .vui-layer-panel-toggle:focus-visible::after,
2227
- .vui-property-panel-toggle:hover::after,
2228
- .vui-property-panel-toggle:focus-visible::after { color: var(--vui-text); }
2229
- .vui-layer-panel-toggle:hover::before,
2230
- .vui-layer-panel-toggle:focus-visible::before,
2231
- .vui-property-panel-toggle:hover::before,
2232
- .vui-property-panel-toggle:focus-visible::before {
2233
- background: var(--vui-btn-hover);
2234
- border-color: var(--vui-accent);
2235
- }
2236
- .vui-layer-panel-shell.closed .vui-layer-panel-toggle-zone {
2237
- right: auto;
2238
- left: 0;
2239
- }
2240
- .vui-layer-panel-shell.closed .vui-layer-panel-toggle {
2241
- opacity: 0.62;
2242
- pointer-events: auto;
2243
- }
2244
- .vui-layer-panel-shell.closed .vui-layer-panel-toggle::before,
2245
- .vui-property-panel-shell.closed .vui-property-panel-toggle::before { transform: none; }
2246
- .vui-layer-panel-shell.closed .vui-layer-panel-toggle:hover,
2247
- .vui-layer-panel-shell.closed .vui-layer-panel-toggle:focus-visible {
2248
- opacity: 1;
2249
- border-color: var(--vui-accent);
2250
- }
2251
- .vui-section { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
2252
- .vui-section-head {
2253
- padding: 9px 12px 7px;
2254
- border-bottom: 1px solid var(--vui-border);
2255
- display: flex;
2256
- align-items: center;
2257
- }
2258
- .vui-section-title {
2259
- font-size: 11px;
2260
- font-weight: 600;
2261
- color: var(--vui-text-dim);
2262
- text-transform: uppercase;
2263
- letter-spacing: 0.6px;
2264
- }
2265
- .vui-section-count { margin-left: auto; font-size: 11px; color: var(--vui-count-text); }
2266
- .vui-section-filter-wrap { padding: 7px 8px 4px; }
2267
- .vui-section-filter {
2268
- width: 100%;
2269
- padding: 5px 8px;
2270
- background: var(--vui-input-bg);
2271
- border: 1px solid var(--vui-border);
2272
- border-radius: 4px;
2273
- color: var(--vui-text);
2274
- font-size: 12px;
2275
- outline: none;
2276
- }
2277
- .vui-section-filter::placeholder { color: var(--vui-placeholder-text); }
2278
- .vui-section-filter:focus { border-color: var(--vui-accent); }
2279
- .vui-layer-quick {
2280
- padding: 3px 8px 6px;
2281
- display: flex;
2282
- gap: 5px;
2283
- }
2284
- .vui-layer-quick .vui-btn { flex: 1; font-size: 11px; padding: 3px 5px; text-align: center; justify-content: center; }
2285
- .vui-section-list {
2286
- flex: 1;
2287
- overflow-y: auto;
2288
- overflow-x: hidden;
2289
- padding: 0 0 6px 0;
2290
- position: relative;
2291
- }
2292
- .vui-empty-note {
2293
- padding: 10px 12px;
2294
- color: var(--vui-empty-text);
2295
- font-size: 12px;
2296
- line-height: 1.45;
2297
- }
2298
-
2299
- /* ── Layer Rows ── */
2300
- .vui-lrow-wrap { display: flex; flex-direction: column; gap: 0; position: relative; }
2301
- .vui-lrow {
2302
- display: flex;
2303
- align-items: center;
2304
- gap: 6px;
2305
- padding: 3px 10px 3px 8px;
2306
- min-height: 24px;
2307
- box-sizing: border-box;
2308
- cursor: pointer;
2309
- user-select: none;
2310
- transition: background 0.1s;
2311
- position: sticky;
2312
- top: 0;
2313
- z-index: 4;
2314
- background-color: var(--vui-bg2);
2315
- }
2316
- .vui-lrow:hover { background-color: var(--vui-row-hover); }
2317
- .vui-lrow.off { background-color: var(--vui-bg2); }
2318
- .vui-lrow.off > :not(.vui-lrow-clr) { opacity: 0.38; }
2319
- .vui-lrow.off:hover { background-color: var(--vui-row-hover); }
2320
- .vui-lrow-all {
2321
- border-bottom: 1px solid var(--vui-layer-divider);
2322
- margin-bottom: 0;
2323
- padding-top: 5px;
2324
- padding-bottom: 5px;
2325
- position: static;
2326
- background: transparent;
2327
- }
2328
- .vui-lrow-all:hover { background-color: var(--vui-row-hover); }
2329
- .vui-lrow-all .vui-lrow-name { color: var(--vui-layer-all-name); font-weight: 600; }
2330
- .vui-lrow-eye {
2331
- width: 13px;
2332
- height: 13px;
2333
- flex-shrink: 0;
2334
- display: flex;
2335
- align-items: center;
2336
- justify-content: center;
2337
- font-size: 11px;
2338
- color: var(--vui-layer-eye);
2339
- border: 0;
2340
- background: transparent;
2341
- padding: 0;
2342
- cursor: pointer;
2343
- }
2344
- .vui-lrow.off .vui-lrow-eye { color: var(--vui-layer-eye-off); }
2345
- .vui-lrow-dot {
2346
- width: 9px;
2347
- height: 9px;
2348
- border-radius: 50%;
2349
- flex-shrink: 0;
2350
- }
2351
- .vui-lrow-name {
2352
- flex: 1;
2353
- font-size: 12px;
2354
- color: var(--vui-layer-name);
2355
- white-space: nowrap;
2356
- overflow: hidden;
2357
- text-overflow: ellipsis;
2358
- }
2359
- .vui-lrow-clr {
2360
- width: 13px;
2361
- height: 13px;
2362
- border-radius: 3px;
2363
- border: 1px solid var(--vui-layer-swatch-border);
2364
- flex-shrink: 0;
2365
- position: relative;
2366
- overflow: hidden;
2367
- cursor: pointer;
2368
- }
2369
- .vui-lrow-clr:hover { border-color: var(--vui-accent); }
2370
- .vui-lrow-clr input[type=color] {
2371
- position: absolute;
2372
- top: -3px;
2373
- left: -3px;
2374
- width: 20px;
2375
- height: 20px;
2376
- opacity: 0;
2377
- cursor: pointer;
2378
- }
2379
- .vui-lrow-toggle {
2380
- width: 12px;
2381
- height: 12px;
2382
- flex-shrink: 0;
2383
- display: flex;
2384
- align-items: center;
2385
- justify-content: center;
2386
- font-size: 10px;
2387
- color: var(--vui-layer-control);
2388
- border: 0;
2389
- background: transparent;
2390
- padding: 0;
2391
- cursor: pointer;
2392
- }
2393
- .vui-lrow-toggle.placeholder { visibility: hidden; cursor: default; }
2394
-
2395
- /* ── Block & Insert Rows ── */
2396
- .vui-lrow-blocks {
2397
- display: none;
2398
- padding: 0;
2399
- margin: 0;
2400
- }
2401
- .vui-lrow-blocks.open { display: block; }
2402
- .vui-lrow-block-wrap { display: flex; flex-direction: column; gap: 0; position: relative; }
2403
- .vui-lrow-block {
2404
- display: flex;
2405
- align-items: center;
2406
- gap: 6px;
2407
- padding: 3px 10px 3px 26px;
2408
- min-height: 24px;
2409
- box-sizing: border-box;
2410
- font-size: 11px;
2411
- cursor: pointer;
2412
- border-radius: 0;
2413
- position: sticky;
2414
- top: 24px;
2415
- z-index: 3;
2416
- background-color: var(--vui-bg2);
2417
- }
2418
- .vui-lrow-block:hover { background-color: var(--vui-row-hover); }
2419
- .vui-lrow-block.active {
2420
- background-color: var(--vui-bg2);
2421
- background-image: linear-gradient(var(--vui-row-active), var(--vui-row-active));
2422
- box-shadow: inset 0 0 0 1px var(--vui-layer-active-shadow);
2423
- }
2424
- .vui-lrow-block.active:hover {
2425
- background-color: var(--vui-row-hover);
2426
- background-image: linear-gradient(var(--vui-row-active), var(--vui-row-active));
2427
- }
2428
- .vui-lrow-block.active .vui-lrow-block-name,
2429
- .vui-lrow-block.active .vui-lrow-block-count { color: var(--vui-layer-active-text); }
2430
- .vui-lrow-block.disabled { cursor: default; background-color: var(--vui-bg2); }
2431
- .vui-lrow-block.disabled > * { opacity: 0.55; }
2432
- .vui-lrow-block.disabled:hover { background-color: var(--vui-bg2); }
2433
- .vui-lrow-block-visibility,
2434
- .vui-lrow-insert-visibility {
2435
- width: 14px;
2436
- height: 14px;
2437
- border: 0;
2438
- background: transparent;
2439
- color: var(--vui-layer-control);
2440
- font-size: 10px;
2441
- line-height: 1;
2442
- cursor: pointer;
2443
- padding: 0;
2444
- flex-shrink: 0;
2445
- }
2446
- .vui-lrow-block-visibility.off, .vui-lrow-insert-visibility.off { color: var(--vui-layer-control-off); }
2447
- .vui-lrow-block-visibility.mixed { color: var(--vui-layer-control-mixed); }
2448
- .vui-lrow-block-visibility.placeholder, .vui-lrow-insert-visibility.placeholder { visibility: hidden; cursor: default; pointer-events: none; }
2449
- .vui-lrow-block-visibility:not(.placeholder):hover, .vui-lrow-insert-visibility:not(.placeholder):hover { color: var(--vui-layer-control-hover); }
2450
- .vui-lrow-block-toggle {
2451
- width: 12px;
2452
- height: 12px;
2453
- flex-shrink: 0;
2454
- display: flex;
2455
- align-items: center;
2456
- justify-content: center;
2457
- font-size: 10px;
2458
- color: var(--vui-layer-control);
2459
- border: 0;
2460
- background: transparent;
2461
- padding: 0;
2462
- cursor: pointer;
2463
- }
2464
- .vui-lrow-block-toggle.placeholder { visibility: hidden; cursor: default; pointer-events: none; }
2465
- .vui-lrow-block-name {
2466
- flex: 1;
2467
- color: var(--vui-layer-block-name);
2468
- white-space: nowrap;
2469
- overflow: hidden;
2470
- text-overflow: ellipsis;
2471
- }
2472
- .vui-lrow-block-count { color: var(--vui-layer-muted); font-size: 11px; flex-shrink: 0; }
2473
- .vui-lrow-inserts {
2474
- display: none;
2475
- margin-left: 32px;
2476
- padding: 2px 8px 4px 10px;
2477
- border-left: 1px solid var(--vui-layer-insert-border);
2478
- }
2479
- .vui-lrow-inserts.open { display: block; }
2480
- .vui-lrow-insert {
2481
- display: flex;
2482
- align-items: center;
2483
- gap: 6px;
2484
- padding: 3px 6px;
2485
- margin-top: 1px;
2486
- border-radius: 4px;
2487
- cursor: pointer;
2488
- }
2489
- .vui-lrow-insert:hover { background-color: var(--vui-row-hover); }
2490
- .vui-lrow-insert.active {
2491
- background: var(--vui-row-active);
2492
- box-shadow: inset 0 0 0 1px var(--vui-layer-active-shadow);
2493
- }
2494
- .vui-lrow-insert.disabled { cursor: default; opacity: 0.55; }
2495
- .vui-lrow-insert.disabled:hover { background: transparent; }
2496
- .vui-lrow-insert-label {
2497
- font-size: 11px;
2498
- color: var(--vui-layer-muted);
2499
- white-space: nowrap;
2500
- overflow: hidden;
2501
- text-overflow: ellipsis;
2502
- }
2503
- .vui-lrow-insert.active .vui-lrow-insert-label { color: var(--vui-layer-active-text); }
2504
-
2505
- /* ── Middle Transparent Canvas Spacer ── */
2506
- .vui-canvas-space {
2507
- flex: 1;
2508
- position: relative;
2509
- min-width: 0;
2510
- pointer-events: none;
2511
- }
2512
-
2513
- /* ── Scalebar Floating (Bottom Left 12px) ── */
2514
- .vui-scalebar {
2515
- position: absolute;
2516
- left: 12px;
2517
- bottom: 8px;
2518
- display: flex;
2519
- flex-direction: column;
2520
- align-items: center;
2521
- pointer-events: none;
2522
- z-index: 5;
2523
- }
2524
- .vui-scalebar-label {
2525
- font-size: 11px;
2526
- font-weight: 400;
2527
- color: var(--vui-scale-color);
2528
- line-height: 1;
2529
- margin-bottom: -4px;
2530
- white-space: nowrap;
2531
- text-align: center;
2532
- }
2533
- .vui-scalebar-line {
2534
- position: relative;
2535
- height: 8px;
2536
- border-bottom: 1px solid var(--vui-scale-color);
2537
- min-width: 24px;
2538
- width: 100px;
2539
- }
2540
- .vui-scalebar-line::before, .vui-scalebar-line::after {
2541
- content: '';
2542
- position: absolute;
2543
- bottom: -1px;
2544
- width: 1px;
2545
- height: 8px;
2546
- background: var(--vui-scale-color);
2547
- }
2548
- .vui-scalebar-line::before { left: 0; }
2549
- .vui-scalebar-line::after { right: 0; }
2550
-
2551
- /* ── Infobar Floating Pill (Bottom Right 12px) ── */
2552
- .vui-infobar {
2553
- position: absolute;
2554
- bottom: 8px;
2555
- right: 12px;
2556
- font-size: 11px;
2557
- color: var(--vui-hud-text);
2558
- pointer-events: auto;
2559
- display: flex;
2560
- align-items: center;
2561
- gap: 0;
2562
- min-height: 30px;
2563
- max-width: calc(100% - 24px);
2564
- background: var(--vui-hud-bg);
2565
- border: 1px solid var(--vui-hud-border);
2566
- border-radius: 6px;
2567
- padding: 4px 8px;
2568
- backdrop-filter: blur(2px);
2569
- z-index: 5;
2570
- transition: background 0.18s ease, border-color 0.18s ease;
2571
- }
2572
- .vui-infobar-toggle {
2573
- width: 20px;
2574
- height: 20px;
2575
- flex: 0 0 20px;
2576
- align-self: center;
2577
- display: inline-flex;
2578
- align-items: center;
2579
- justify-content: center;
2580
- border: 0;
2581
- border-radius: 0;
2582
- background: transparent;
2583
- color: var(--vui-hud-text);
2584
- cursor: pointer;
2585
- font-size: 16px;
2586
- line-height: 1;
2587
- padding: 0;
2588
- transition: color 0.12s ease, transform 0.12s ease;
2589
- }
2590
- .vui-infobar-toggle:hover {
2591
- color: var(--vui-text);
2592
- background: transparent;
2593
- transform: translateX(-1px);
2594
- }
2595
- .vui-infobar-content {
2596
- display: flex;
2597
- align-items: center;
2598
- justify-content: flex-end;
2599
- flex-wrap: wrap;
2600
- gap: 5px 8px;
2601
- min-width: 0;
2602
- max-width: calc(100vw - 64px);
2603
- max-height: 120px;
2604
- margin-left: 8px;
2605
- /* Visible in the expanded state so the debug popup can escape upward
2606
- (mirrors the main editor's unclipped #infobar); the collapsed state
2607
- re-enables clipping for the shrink animation. */
2608
- overflow: visible;
2609
- opacity: 1;
2610
- transform: translateX(0);
2611
- transition: max-width 0.2s ease, max-height 0.2s ease, margin-left 0.2s ease, opacity 0.14s ease, transform 0.2s ease;
2612
- }
2613
- .vui-infobar.collapsed .vui-infobar-content {
2614
- max-width: 0;
2615
- max-height: 0;
2616
- margin-left: 0;
2617
- opacity: 0;
2618
- pointer-events: none;
2619
- transform: translateX(6px);
2620
- overflow: hidden;
2621
- }
2622
- .vui-infobar-tools {
2623
- display: inline-flex;
2624
- align-items: center;
2625
- justify-content: flex-end;
2626
- flex-wrap: wrap;
2627
- gap: 6px;
2628
- min-width: 0;
2629
- max-width: 100%;
2630
- }
2631
- .vui-infobar-tools .vui-info-text {
2632
- flex: 1 1 auto;
2633
- min-width: 0;
2634
- max-width: min(260px, 32vw);
2635
- color: var(--vui-hud-text);
2636
- font-size: 11px;
2637
- overflow: hidden;
2638
- text-overflow: ellipsis;
2639
- white-space: nowrap;
2640
- }
2641
- .vui-infobar-tools .vui-btn {
2642
- padding: 3px 7px;
2643
- font-size: 11px;
2644
- }
2645
- .vui-infobar .vui-view-toggle { gap: 3px; }
2646
- .vui-infobar .vui-seg { color: var(--vui-hud-text); white-space: nowrap; }
2647
- .vui-debug-card {
2648
- position: relative;
2649
- color: var(--vui-hud-text);
2650
- flex: 0 0 auto;
2651
- }
2652
- .vui-debug-card[hidden] { display: none; }
2653
- .vui-debug-summary {
2654
- cursor: pointer;
2655
- white-space: nowrap;
2656
- list-style: none;
2657
- color: var(--vui-hud-file);
2658
- user-select: none;
2659
- }
2660
- .vui-debug-summary::-webkit-details-marker { display: none; }
2661
- .vui-debug-summary::before {
2662
- content: "▸";
2663
- display: inline-block;
2664
- margin-right: 4px;
2665
- color: var(--vui-text-dim);
2666
- }
2667
- .vui-debug-card[open] .vui-debug-summary::before { content: "▾"; }
2668
- .vui-debug-body {
2669
- position: absolute;
2670
- right: 0;
2671
- bottom: calc(100% + 8px);
2672
- width: min(520px, calc(100vw - 32px));
2673
- max-height: min(420px, calc(100vh - 120px));
2674
- overflow: auto;
2675
- display: grid;
2676
- gap: 6px;
2677
- padding: 10px;
2678
- background: var(--vui-bg2);
2679
- border: 1px solid var(--vui-border-strong);
2680
- border-radius: 6px;
2681
- color: var(--vui-text);
2682
- box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
2683
- backdrop-filter: blur(4px);
2684
- }
2685
- .vui-debug-card:not([open]) .vui-debug-body { display: none; }
2686
- .vui-debug-row {
2687
- display: grid;
2688
- grid-template-columns: 88px minmax(0, 1fr);
2689
- gap: 10px;
2690
- align-items: start;
2691
- }
2692
- .vui-debug-row > span {
2693
- color: var(--vui-text-dim);
2694
- white-space: nowrap;
2695
- }
2696
- .vui-debug-value {
2697
- min-width: 0;
2698
- color: var(--vui-hud-text);
2699
- font-weight: 600;
2700
- word-break: break-word;
2701
- }
2702
- .vui-debug-spread-list {
2703
- display: grid;
2704
- gap: 5px;
2705
- margin-top: 6px;
2706
- }
2707
- .vui-debug-spread {
2708
- display: grid;
2709
- gap: 2px;
2710
- font-style: normal;
2711
- }
2712
- .vui-debug-spread b,
2713
- .vui-debug-spread span,
2714
- .vui-debug-spread em {
2715
- min-width: 0;
2716
- font-style: normal;
2717
- }
2718
- .vui-debug-spread span,
2719
- .vui-debug-spread em,
2720
- .vui-debug-spread-muted {
2721
- color: var(--vui-text-dim);
2722
- font-weight: 400;
2723
- }
2724
- .vui-info-file {
2725
- max-width: 300px;
2726
- overflow: hidden;
2727
- text-overflow: ellipsis;
2728
- color: var(--vui-hud-file);
2729
- }
2730
- .vui-fps {
2731
- min-width: 68px;
2732
- padding: 1px 4px;
2733
- border: 1px solid transparent;
2734
- border-radius: 999px;
2735
- font-size: 12px;
2736
- font-weight: 700;
2737
- text-align: center;
2738
- white-space: nowrap;
2739
- }
2740
- .vui-fps.hi { color: var(--vui-fps-hi-text); background: var(--vui-ok); border-color: var(--vui-fps-hi-border); }
2741
- .vui-fps.mid { color: var(--vui-fps-mid-text); background: var(--vui-warn); border-color: var(--vui-fps-mid-border); }
2742
- .vui-fps.low { color: var(--vui-fps-low-text); background: var(--vui-fail); border-color: var(--vui-fps-low-border); }
2743
-
2744
- /* ── Property Panel Shell (Right Drawer with Toggle Button) ── */
2745
- .vui-property-panel-shell {
2746
- flex: 0 0 auto;
2747
- display: flex;
2748
- align-items: stretch;
2749
- background: var(--vui-bg2);
2750
- position: relative;
2751
- border-left: 0;
2752
- overflow: visible;
2753
- pointer-events: auto;
2754
- z-index: 5;
2755
- transition: width 0.15s ease;
2756
- }
2757
- .vui-property-panel-shell.closed {
2758
- width: 0;
2759
- min-width: 0;
2760
- border: 0;
2761
- }
2762
- .vui-property-panel-shell.closed .vui-property-panel { width: 0; border: 0; overflow: hidden; }
2763
- .vui-property-panel {
2764
- width: 280px;
2765
- display: flex;
2766
- flex-direction: column;
2767
- min-height: 0;
2768
- overflow: hidden;
2769
- transition: none;
2770
- }
2771
- .vui-property-panel-toggle-zone {
2772
- position: absolute;
2773
- top: 0;
2774
- bottom: 0;
2775
- left: -16px;
2776
- z-index: 12;
2777
- width: 16px;
2778
- display: inline-flex;
2779
- align-items: center;
2780
- justify-content: center;
2781
- }
2782
- .vui-property-panel-toggle {
2783
- width: 16px;
2784
- height: 48px;
2785
- align-self: center;
2786
- position: relative;
2787
- z-index: 1;
2788
- background: transparent;
2789
- color: var(--vui-text-dim);
2790
- border: 0;
2791
- border-radius: 5px;
2792
- cursor: pointer;
2793
- font-size: 0;
2794
- padding: 0;
2795
- display: inline-flex;
2796
- align-items: center;
2797
- justify-content: center;
2798
- opacity: 0;
2799
- pointer-events: none;
2800
- box-shadow: none;
2801
- transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease, opacity 0.15s ease, transform 0.15s ease;
2802
- }
2803
- .vui-property-panel-toggle-zone:hover .vui-property-panel-toggle,
2804
- .vui-property-panel-toggle:focus-visible {
2805
- opacity: 1;
2806
- pointer-events: auto;
2807
- }
2808
- .vui-property-panel-toggle:hover {
2809
- transform: translateY(-1px);
2810
- }
2811
- .vui-property-panel-shell.closed .vui-property-panel-toggle-zone {
2812
- left: auto;
2813
- right: 0;
2814
- }
2815
- .vui-property-panel-shell.closed .vui-property-panel-toggle {
2816
- opacity: 0.62;
2817
- pointer-events: auto;
2818
- }
2819
- .vui-property-panel-shell.closed .vui-property-panel-toggle:hover,
2820
- .vui-property-panel-shell.closed .vui-property-panel-toggle:focus-visible {
2821
- opacity: 1;
2822
- border-color: var(--vui-accent);
2823
- }
2824
- .vui-pp-head { padding: 9px 12px 7px; border-bottom: 1px solid var(--vui-border); }
2825
- .vui-pp-preview-wrap {
2826
- flex: 0 0 auto;
2827
- min-width: 0;
2828
- padding: 8px 12px 4px;
2829
- border-bottom: 1px solid var(--vui-border);
2830
- }
2831
- .vui-pp-preview-wrap[hidden] { display: none; }
2832
- .vui-pp-preview {
2833
- display: block;
2834
- width: 100%;
2835
- max-width: 100%;
2836
- height: auto;
2837
- aspect-ratio: 1 / 1;
2838
- object-fit: contain;
2839
- flex: 0 0 auto;
2840
- background: var(--vui-bg);
2841
- border: 1px solid var(--vui-border);
2842
- border-radius: 4px;
2843
- }
2844
- .vui-pp-title-text {
2845
- font-size: 11px;
2846
- font-weight: 600;
2847
- color: var(--vui-text-dim);
2848
- text-transform: uppercase;
2849
- letter-spacing: 0.6px;
2850
- }
2851
- .vui-pp-body { flex: 1; overflow-y: auto; padding: 6px 0; }
2852
- .vui-pp-section { padding: 8px 12px 4px; }
2853
- .vui-pp-section-label {
2854
- font-size: 10px;
2855
- color: var(--vui-text-dim);
2856
- text-transform: uppercase;
2857
- letter-spacing: 0.5px;
2858
- margin-bottom: 4px;
2859
- }
2860
- .vui-pp-title-row { display: flex; align-items: center; gap: 4px; font-size: 12px; }
2861
- .vui-pp-title-row strong { color: var(--vui-text); }
2862
- .vui-pp-row {
2863
- display: grid;
2864
- grid-template-columns: 60px 1fr;
2865
- gap: 4px;
2866
- padding: 2px 12px;
2867
- font-size: 12px;
2868
- align-items: center;
2869
- }
2870
- .vui-pp-row .label { color: var(--vui-text-dim); font-size: 11px; }
2871
- .vui-pp-row .value {
2872
- color: var(--vui-text);
2873
- font-variant-numeric: tabular-nums;
2874
- overflow: hidden;
2875
- text-overflow: ellipsis;
2876
- white-space: nowrap;
2877
- }
2878
- .vui-readonly-note {
2879
- margin: 0 12px 8px;
2880
- padding: 6px 8px;
2881
- border: 1px solid var(--vui-border);
2882
- border-radius: 4px;
2883
- color: var(--vui-text-dim);
2884
- font-size: 11px;
2885
- }
2886
-
2887
- @media (max-width: 880px) {
2888
- .vui-sidebar { width: 196px; min-width: 176px; }
2889
- .vui-property-panel { width: 264px; }
2890
- }
2891
-
2892
- @media (max-width: 680px) {
2893
- .vui-layer-panel-shell { display: none; }
2894
- .vui-property-panel-shell { display: none; }
2895
- .vui-infobar { right: 8px; bottom: 8px; max-width: calc(100% - 16px); }
2896
- .vui-infobar-content { max-width: calc(100vw - 48px); }
2897
- .vui-infobar-tools .vui-info-text { max-width: min(180px, 45vw); }
2898
- }
2899
- `, yi = {
2900
- dark: `
2901
- :host, .vui-root[data-theme="dark"] {
2902
- --vui-bg: #0A0B0F;
2903
- --vui-bg2: #111217;
2904
- --vui-bg3: #181A21;
2905
- --vui-border: #23262E;
2906
- --vui-border-strong: #2E323B;
2907
- --vui-text: #E6E7EB;
2908
- --vui-text-dim: #8A8F9A;
2909
- --vui-accent: #5B8DEF;
2910
- --vui-accent-h: #7BA3F5;
2911
- --vui-btn-hover: #1D2027;
2912
- --vui-btn-active: #14161C;
2913
- --vui-btn-primary-bg: rgba(91, 141, 239, 0.14);
2914
- --vui-btn-primary-hover: rgba(91, 141, 239, 0.22);
2915
- --vui-btn-primary-text: #A8C2F5;
2916
- --vui-input-bg: #0E0F14;
2917
- --vui-row-hover: #181A21;
2918
- --vui-row-active: rgba(91, 141, 239, 0.10);
2919
- --vui-hud-bg: rgba(10, 11, 15, 0.65);
2920
- --vui-hud-border: rgba(46, 50, 59, 0.6);
2921
- --vui-hud-text: #8A8F9A;
2922
- --vui-hud-file: #B0B5BF;
2923
- --vui-scale-color: #E6E7EB;
2924
- --vui-scrollbar-thumb: #2E323B;
2925
- --vui-count-text: #5A5F69;
2926
- --vui-placeholder-text: #41454D;
2927
- --vui-empty-text: #6C7180;
2928
- --vui-layer-divider: #23262E;
2929
- --vui-layer-all-name: #E6E7EB;
2930
- --vui-layer-eye: #8A8F9A;
2931
- --vui-layer-eye-off: #3A3E47;
2932
- --vui-layer-name: #B0B5BF;
2933
- --vui-layer-control: #8A8F9A;
2934
- --vui-layer-control-off: #41454D;
2935
- --vui-layer-control-mixed: #8AA0D4;
2936
- --vui-layer-control-hover: #C7D6FA;
2937
- --vui-layer-muted: #6C7180;
2938
- --vui-layer-block-name: #9097A3;
2939
- --vui-layer-insert-border: rgba(46, 50, 59, 0.7);
2940
- --vui-layer-active-shadow: rgba(91, 141, 239, 0.32);
2941
- --vui-layer-active-text: #C7D6FA;
2942
- --vui-layer-swatch-border: #2E323B;
2943
- --vui-ok: #4ADE80;
2944
- --vui-warn: #F5A524;
2945
- --vui-fail: #F87171;
2946
- --vui-fps-hi-text: #052E16;
2947
- --vui-fps-hi-border: #22C55E;
2948
- --vui-fps-mid-text: #3A2500;
2949
- --vui-fps-mid-border: #D68B10;
2950
- --vui-fps-low-text: #3D0B0B;
2951
- --vui-fps-low-border: #E05656;
2952
- }
2953
- `,
2954
- light: `
2955
- :host, .vui-root[data-theme="light"] {
2956
- --vui-bg: #FAFBFC;
2957
- --vui-bg2: #FFFFFF;
2958
- --vui-bg3: #F3F5F8;
2959
- --vui-border: #DEE2E7;
2960
- --vui-border-strong: #C7CED7;
2961
- --vui-text: #1A1C20;
2962
- --vui-text-dim: #6A707A;
2963
- --vui-accent: #3B6FE0;
2964
- --vui-accent-h: #2F5FCC;
2965
- --vui-btn-hover: #ECF0F6;
2966
- --vui-btn-active: #DDE5F0;
2967
- --vui-btn-primary-bg: rgba(59, 111, 224, 0.10);
2968
- --vui-btn-primary-hover: rgba(59, 111, 224, 0.16);
2969
- --vui-btn-primary-text: #2756B5;
2970
- --vui-input-bg: #F7F8FB;
2971
- --vui-row-hover: #ECF0F6;
2972
- --vui-row-active: rgba(59, 111, 224, 0.08);
2973
- --vui-hud-bg: rgba(255, 255, 255, 0.75);
2974
- --vui-hud-border: rgba(199, 206, 215, 0.6);
2975
- --vui-hud-text: #6A707A;
2976
- --vui-hud-file: #2E3340;
2977
- --vui-scale-color: #2E3340;
2978
- --vui-scrollbar-thumb: #C7CED7;
2979
- --vui-count-text: #9097A3;
2980
- --vui-placeholder-text: #B6BCC6;
2981
- --vui-empty-text: #6A707A;
2982
- --vui-layer-divider: #DEE2E7;
2983
- --vui-layer-all-name: #1A1C20;
2984
- --vui-layer-eye: #6A707A;
2985
- --vui-layer-eye-off: #B6BCC6;
2986
- --vui-layer-name: #2E3340;
2987
- --vui-layer-control: #6A707A;
2988
- --vui-layer-control-off: #B6BCC6;
2989
- --vui-layer-control-mixed: #5B7AB9;
2990
- --vui-layer-control-hover: #2756B5;
2991
- --vui-layer-muted: #8B92A0;
2992
- --vui-layer-block-name: #475061;
2993
- --vui-layer-insert-border: rgba(199, 206, 215, 0.8);
2994
- --vui-layer-active-shadow: rgba(59, 111, 224, 0.22);
2995
- --vui-layer-active-text: #1F4AA8;
2996
- --vui-layer-swatch-border: #C7CED7;
2997
- --vui-ok: #2BA664;
2998
- --vui-warn: #B6770F;
2999
- --vui-fail: #D04A4A;
3000
- --vui-fps-hi-text: #FFFFFF;
3001
- --vui-fps-hi-border: #248B55;
3002
- --vui-fps-mid-text: #FFFFFF;
3003
- --vui-fps-mid-border: #915F0B;
3004
- --vui-fps-low-text: #FFFFFF;
3005
- --vui-fps-low-border: #AD3E3E;
3006
- }
3007
- `
3008
- };
3009
- function xi(n = "dark") {
3010
- return `${yi[n]}${gi}`;
3011
- }
3012
- const wi = {
3013
- layerPanel: !0,
3014
- propertyPanel: !0,
3015
- hud: !0,
3016
- viewMode: !0,
3017
- fit: !0,
3018
- scaleRuler: !0,
3019
- infobarLineMode: !0,
3020
- infobarPhase: !0,
3021
- infobarDebug: !0
3022
- }, ye = "dxf-parser:reference-property-panel:open", xe = "dxf-parser:reference-layer-panel:open", we = 300, Ce = 180, Ci = 2;
3023
- function T(n, ...e) {
3024
- const t = document.createElement("div");
3025
- t.className = "vui-debug-row";
3026
- const i = document.createElement("span");
3027
- i.textContent = n;
3028
- const r = document.createElement("div");
3029
- return r.className = "vui-debug-value", r.append(...e), t.append(i, r), t;
3030
- }
3031
- function Pe(n, e, t) {
3032
- const i = document.createElement("div");
3033
- i.className = "vui-debug-spread";
3034
- const r = document.createElement("b");
3035
- r.textContent = n;
3036
- const s = document.createElement("span");
3037
- if (s.textContent = e, i.append(r, s), t !== void 0) {
3038
- const o = document.createElement("em");
3039
- o.textContent = t, i.append(o);
3040
- }
3041
- return i;
3042
- }
3043
- function Se(n) {
3044
- const e = document.createElement("div");
3045
- return e.className = "vui-debug-spread-list", e.append(...n), e;
3046
- }
3047
- function Pi(n) {
3048
- switch (n) {
3049
- case "created":
3050
- return "状态 创建";
3051
- case "header-ready":
3052
- return "状态 头部就绪";
3053
- case "initial-loading":
3054
- return "状态 加载中";
3055
- case "interactive":
3056
- return "状态 就绪";
3057
- case "failed":
3058
- return "状态 失败";
3059
- case "disposed":
3060
- return "状态 已关闭";
3061
- default:
3062
- return "";
3063
- }
3064
- }
3065
- class Si {
3066
- constructor(e, t) {
3067
- a(this, "parts");
3068
- a(this, "layerPanelState", new ii());
3069
- a(this, "shadow", null);
3070
- a(this, "layerPanelRefs", null);
3071
- a(this, "propertyPanelRefs", null);
3072
- a(this, "scalebarRefs", null);
3073
- a(this, "infobarRefs", null);
3074
- a(this, "btnFit", null);
3075
- a(this, "btnView2d", null);
3076
- a(this, "btnView3d", null);
3077
- a(this, "layerPanelShell", null);
3078
- a(this, "layerPanelToggle", null);
3079
- a(this, "infobar", null);
3080
- a(this, "infobarToggle", null);
3081
- a(this, "propertyPanelShell", null);
3082
- a(this, "propertyPanelToggle", null);
3083
- a(this, "propertyPanelAutoOpened", !1);
3084
- a(this, "propertyPreviewRenderHandle", null);
3085
- a(this, "propertyPreviewLayoutWaitAbort", null);
3086
- a(this, "propertyPreviewKey", null);
3087
- a(this, "toolbarInfoText", null);
3088
- a(this, "handleKeyDown", (e) => {
3089
- this.disposed || e.defaultPrevented || (e.key === "f" || e.key === "F") && this.host.fitView();
3090
- });
3091
- a(this, "unsubscribers", []);
3092
- a(this, "filterCleanup", null);
3093
- a(this, "disposed", !1);
3094
- a(this, "insertToggleScheduler", new bi({
3095
- onFlush: (e) => {
3096
- var t, i;
3097
- (i = (t = this.host.cad.blocks).batchToggleInserts) == null || i.call(t, e);
3098
- }
3099
- }));
3100
- a(this, "refresh", () => {
3101
- this.disposed || (this.syncToolbarState(), this.layerPanelRefs && this.parts.layerPanel && pe(this.layerPanelRefs, this.layerPanelState, this.createLayerPanelActions()), this.propertyPanelRefs && this.parts.propertyPanel && (be(this.propertyPanelRefs, this.host.cad.inspector), this.syncPropertyPanelSelectionState(), this.syncPropertyPanelPreview()), this.syncHud());
3102
- });
3103
- a(this, "syncHud", () => {
3104
- var e;
3105
- if (!this.disposed) {
3106
- if (this.infobarRefs && this.parts.hud) {
3107
- const t = this.host.cad.hud;
3108
- this.infobarRefs.file.textContent = t.filename ?? "", this.infobarRefs.file.title = t.filename ?? "", this.infobarRefs.file.hidden = !t.filename, this.infobarRefs.zoom.textContent = t.zoomLabel ? `缩放 ${t.zoomLabel}` : "", this.infobarRefs.zoom.hidden = !t.zoomLabel, this.infobarRefs.vertices.textContent = t.viewportVertexCount > 0 ? `顶点 ${t.viewportVertexCount.toLocaleString()}` : "", this.infobarRefs.vertices.hidden = t.viewportVertexCount <= 0, this.infobarRefs.drawCalls.textContent = t.drawCalls > 0 ? `DrawCalls ${t.drawCalls}` : "", this.infobarRefs.drawCalls.hidden = t.drawCalls <= 0, this.infobarRefs.lineMode.textContent = this.parts.infobarLineMode && t.lineRenderMode ? `线模式 ${t.lineRenderMode.toUpperCase()}` : "", this.infobarRefs.lineMode.hidden = !this.infobarRefs.lineMode.textContent, this.infobarRefs.phase.textContent = this.parts.infobarPhase ? Pi(t.phase) : "", this.infobarRefs.phase.hidden = !this.infobarRefs.phase.textContent;
3109
- const i = Math.round(t.fps);
3110
- this.infobarRefs.fps.textContent = i > 0 ? `${i} FPS` : "— FPS", this.infobarRefs.fps.classList.toggle("hi", i >= 55), this.infobarRefs.fps.classList.toggle("mid", i >= 30 && i < 55), this.infobarRefs.fps.classList.toggle("low", i < 30);
3111
- const r = t.blockVariantCount > 0 || t.splitBlockLayerGroupCount > 0 || t.entityCount > 0;
3112
- if (this.infobarRefs.debugCard.hidden = !this.parts.infobarDebug || !r, this.infobarRefs.debugCard.hidden)
3113
- this.infobarRefs.debugCard.open = !1, this.infobarRefs.debugSummary.textContent = "", this.infobarRefs.debugBody.replaceChildren();
3114
- else {
3115
- this.infobarRefs.debugSummary.textContent = `Debug · Block ${t.blockVariantCount} · 跨层 ${t.splitBlockLayerGroupCount}`;
3116
- const s = t.renderDebugBreakdown.map((d) => Pe(
3117
- d.label,
3118
- `${d.renderables.toLocaleString()} call-ish / ${d.submittedVertexRefs.toLocaleString()} vertices`
3119
- )), o = [
3120
- `${t.renderableCount.toLocaleString()} renderables / ${t.drawCalls.toLocaleString()} calls`
3121
- ];
3122
- s.length > 0 && o.push(Se(s));
3123
- const c = t.splitBlockLayerDetails.map((d) => Pe(
3124
- `${d.blockName} #${d.variantId}`,
3125
- `${d.layerCount.toLocaleString()} layer / ${d.instanceCount.toLocaleString()} insert`,
3126
- d.layers.join(", ")
3127
- ));
3128
- if (t.splitBlockLayerDetails.length < t.splitBlockLayerGroupCount) {
3129
- const d = document.createElement("div");
3130
- d.className = "vui-debug-spread vui-debug-spread-muted", d.textContent = `另有 ${(t.splitBlockLayerGroupCount - t.splitBlockLayerDetails.length).toLocaleString()} 个跨层 block`, c.push(d);
3131
- }
3132
- const l = [`跨层 ${t.splitBlockLayerGroupCount.toLocaleString()}`];
3133
- c.length > 0 && l.push(Se(c)), this.infobarRefs.debugBody.replaceChildren(
3134
- T("提交", t.submittedVertexRefs.toLocaleString()),
3135
- ...t.lineRenderMode ? [T("线模式", t.lineRenderMode.toUpperCase())] : [],
3136
- T("图层", `${t.visibleLayerCount.toLocaleString()} / ${t.totalLayerCount.toLocaleString()}`),
3137
- T("DrawCall 来源", ...o),
3138
- T("Block", `${t.blockVariantCount.toLocaleString()} / INSERT ${t.blockInstanceCount.toLocaleString()}`),
3139
- T("分散 layer", ...l)
3140
- );
3141
- }
3142
- }
3143
- if (this.toolbarInfoText) {
3144
- const t = this.host.cad.hud, i = t.fileCount > 0 ? `${t.visibleLayerCount.toLocaleString()} / ${t.totalLayerCount.toLocaleString()} 图层` : "", r = t.statusMessage.trim();
3145
- this.toolbarInfoText.textContent = i && r ? `${i} | ${r}` : i || r;
3146
- }
3147
- this.scalebarRefs && (this.host.viewMode === "2d" && ((e = this.host.cad.hud.scaleRuler) != null && e.visible) ? (this.scalebarRefs.scaleBar.hidden = !1, this.scalebarRefs.scaleLabel.textContent = this.host.cad.hud.scaleRuler.lengthLabel) : this.scalebarRefs.scaleBar.hidden = !0);
3148
- }
3149
- });
3150
- this.host = e, this.options = t, this.parts = { ...wi, ...t.parts };
3151
- }
3152
- mount() {
3153
- if (this.disposed) return;
3154
- this.shadow = this.options.root.attachShadow({ mode: "open" });
3155
- const e = this.options.theme ?? "dark", t = document.createElement("style");
3156
- t.textContent = xi(e);
3157
- const i = document.createElement("div");
3158
- i.className = "vui-root", i.dataset.theme = e, i.dataset.preset = this.options.preset ?? "cad-readonly";
3159
- const r = document.createElement("div");
3160
- r.className = "vui-main", this.parts.layerPanel && r.appendChild(this.buildLayerPanel());
3161
- const s = document.createElement("div");
3162
- s.className = "vui-canvas-space", this.parts.scaleRuler && s.appendChild(this.buildScalebar()), this.parts.hud && s.appendChild(this.buildInfobar()), r.appendChild(s), this.parts.propertyPanel && r.appendChild(this.buildPropertyPanel()), i.appendChild(r), this.shadow.append(t, i), this.bindEvents(), this.setLayerPanelOpen(this.readLayerPanelOpen(), !1), this.setPropertyPanelOpen(this.readPropertyPanelOpen(), !1), this.setInfobarOpen(!0), this.refresh();
3163
- }
3164
- dispose() {
3165
- var e, t;
3166
- if (!this.disposed) {
3167
- this.disposed = !0, this.insertToggleScheduler.clear(), (e = this.filterCleanup) == null || e.call(this), this.filterCleanup = null, (t = globalThis.removeEventListener) == null || t.call(globalThis, "keydown", this.handleKeyDown), this.cancelPropertyPreviewRender();
3168
- for (const i of this.unsubscribers) i();
3169
- this.unsubscribers = [], this.shadow && (this.shadow.replaceChildren(), this.shadow = null);
3170
- }
3171
- }
3172
- get layerState() {
3173
- return this.layerPanelState;
3174
- }
3175
- buildLayerPanel() {
3176
- const e = document.createElement("aside");
3177
- e.className = "vui-layer-panel-shell", e.setAttribute("aria-label", "图层面板");
3178
- const t = document.createElement("div");
3179
- t.className = "vui-sidebar", t.dataset.part = "layer-panel";
3180
- const i = document.createElement("div");
3181
- i.className = "vui-section";
3182
- const r = document.createElement("div");
3183
- r.className = "vui-section-head", r.innerHTML = '<span class="vui-section-title">图层</span><span class="vui-section-count">—</span>';
3184
- const s = document.createElement("div");
3185
- s.className = "vui-section-filter-wrap";
3186
- const o = document.createElement("input");
3187
- o.className = "vui-section-filter", o.type = "text", o.placeholder = "搜索图层…", o.autocomplete = "off", s.appendChild(o);
3188
- const c = document.createElement("div");
3189
- c.className = "vui-layer-quick";
3190
- const l = document.createElement("button");
3191
- l.type = "button", l.className = "vui-btn", l.textContent = "全显", l.addEventListener("click", () => this.host.cad.layers.showAll());
3192
- const d = document.createElement("button");
3193
- d.type = "button", d.className = "vui-btn", d.textContent = "全隐", d.addEventListener("click", () => this.host.cad.layers.hideAll());
3194
- const h = document.createElement("button");
3195
- h.type = "button", h.className = "vui-btn", h.textContent = "折叠", h.title = "折叠全部已展开层级", h.addEventListener("click", () => {
3196
- var b;
3197
- const p = (b = this.host.cad.inspector.selection) == null ? void 0 : b.id, m = p ? `${p.layerIndex}:${p.variantId}:${p.instanceId}` : null;
3198
- this.layerPanelState.collapseAll(m), this.refresh();
3199
- }), c.append(l, d, h);
3200
- const u = document.createElement("div");
3201
- u.className = "vui-section-list", i.append(r, s, c, u), t.appendChild(i), this.layerPanelRefs = {
3202
- list: u,
3203
- count: r.querySelector(".vui-section-count"),
3204
- filterInput: o
3205
- };
3206
- const v = document.createElement("div");
3207
- v.className = "vui-layer-panel-toggle-zone";
3208
- const f = document.createElement("button");
3209
- return f.type = "button", f.className = "vui-layer-panel-toggle", f.addEventListener("click", () => this.setLayerPanelOpen(e.classList.contains("closed"))), v.appendChild(f), e.append(t, v), this.layerPanelShell = e, this.layerPanelToggle = f, e;
3210
- }
3211
- buildScalebar() {
3212
- const e = document.createElement("div");
3213
- e.className = "vui-scalebar", e.title = "当前视图比例尺(按图纸单位)", e.hidden = !this.parts.scaleRuler;
3214
- const t = document.createElement("div");
3215
- t.className = "vui-scalebar-label", t.textContent = "100 mm";
3216
- const i = document.createElement("div");
3217
- return i.className = "vui-scalebar-line", e.append(t, i), this.scalebarRefs = { scaleBar: e, scaleLabel: t, scaleLine: i }, e;
3218
- }
3219
- buildInfobar() {
3220
- const e = document.createElement("div");
3221
- e.className = "vui-infobar", e.dataset.part = "hud";
3222
- const t = document.createElement("button");
3223
- t.type = "button", t.className = "vui-infobar-toggle", t.addEventListener("click", () => this.setInfobarOpen(e.classList.contains("collapsed")));
3224
- const i = document.createElement("div");
3225
- i.className = "vui-infobar-content";
3226
- const r = document.createElement("span");
3227
- r.className = "vui-seg vui-info-file";
3228
- const s = document.createElement("div");
3229
- s.className = "vui-infobar-tools", s.setAttribute("role", "toolbar"), s.setAttribute("aria-label", "视图控制");
3230
- const o = document.createElement("span");
3231
- if (o.className = "vui-info-text", this.toolbarInfoText = o, s.appendChild(o), this.parts.fit && (this.btnFit = document.createElement("button"), this.btnFit.type = "button", this.btnFit.className = "vui-btn", this.btnFit.innerHTML = '适应 <span class="vui-kbd">F</span>', this.btnFit.addEventListener("click", () => this.host.fitView()), s.appendChild(this.btnFit)), this.parts.viewMode) {
3232
- const b = document.createElement("div");
3233
- b.className = "vui-view-toggle", b.setAttribute("role", "group"), b.setAttribute("aria-label", "视图模式"), this.btnView2d = document.createElement("button"), this.btnView2d.type = "button", this.btnView2d.className = "vui-btn", this.btnView2d.textContent = "2D", this.btnView2d.setAttribute("aria-pressed", "false"), this.btnView2d.addEventListener("click", () => this.host.setViewMode("2d")), this.btnView3d = document.createElement("button"), this.btnView3d.type = "button", this.btnView3d.className = "vui-btn", this.btnView3d.textContent = "3D", this.btnView3d.setAttribute("aria-pressed", "false"), this.btnView3d.addEventListener("click", () => this.host.setViewMode("3d")), b.append(this.btnView2d, this.btnView3d), s.appendChild(b);
3234
- }
3235
- const c = document.createElement("span");
3236
- c.className = "vui-seg";
3237
- const l = document.createElement("span");
3238
- l.className = "vui-seg";
3239
- const d = document.createElement("span");
3240
- d.className = "vui-seg";
3241
- const h = document.createElement("span");
3242
- h.className = "vui-seg", h.hidden = !0;
3243
- const u = document.createElement("span");
3244
- u.className = "vui-seg";
3245
- const v = document.createElement("span");
3246
- v.className = "vui-fps";
3247
- const f = document.createElement("details");
3248
- f.className = "vui-debug-card";
3249
- const p = document.createElement("summary");
3250
- p.className = "vui-debug-summary", p.textContent = "详细";
3251
- const m = document.createElement("div");
3252
- return m.className = "vui-debug-body", f.append(p, m), i.append(r, s, c, l, d, h, u, v, f), e.append(t, i), this.infobarRefs = {
3253
- infobar: e,
3254
- file: r,
3255
- tools: s,
3256
- zoom: c,
3257
- vertices: l,
3258
- drawCalls: d,
3259
- lineMode: h,
3260
- phase: u,
3261
- fps: v,
3262
- debugCard: f,
3263
- debugSummary: p,
3264
- debugBody: m
3265
- }, this.infobar = e, this.infobarToggle = t, e;
3266
- }
3267
- buildPropertyPanel() {
3268
- const e = document.createElement("aside");
3269
- e.className = "vui-property-panel-shell", e.setAttribute("aria-label", "属性面板");
3270
- const t = document.createElement("div");
3271
- t.className = "vui-property-panel", t.dataset.part = "property-panel";
3272
- const i = document.createElement("div");
3273
- i.className = "vui-pp-head", i.innerHTML = '<span class="vui-pp-title-text">属性</span>';
3274
- const r = document.createElement("div");
3275
- r.className = "vui-pp-preview-wrap", r.hidden = !0;
3276
- const s = document.createElement("canvas");
3277
- s.className = "vui-pp-preview", s.width = 256, s.height = 256, r.appendChild(s);
3278
- const o = document.createElement("div");
3279
- o.className = "vui-pp-body", t.append(i, r, o);
3280
- const c = document.createElement("div");
3281
- c.className = "vui-property-panel-toggle-zone";
3282
- const l = document.createElement("button");
3283
- return l.type = "button", l.className = "vui-property-panel-toggle", l.addEventListener("click", () => this.setPropertyPanelOpen(e.classList.contains("closed"))), c.appendChild(l), e.append(c, t), this.propertyPanelShell = e, this.propertyPanelToggle = l, this.propertyPanelRefs = { body: o, previewWrap: r, previewCanvas: s }, e;
3284
- }
3285
- bindEvents() {
3286
- var e;
3287
- this.layerPanelRefs && (this.filterCleanup = hi(this.layerPanelRefs.filterInput, (t) => {
3288
- this.layerPanelState.filterText = t, this.layerPanelRefs && oi(this.layerPanelRefs.list, t);
3289
- })), this.unsubscribers.push(
3290
- this.host.cad.on("document-change", () => this.refresh()),
3291
- this.host.cad.on("layer-change", () => this.refresh()),
3292
- this.host.cad.on("block-change", () => this.refresh()),
3293
- this.host.cad.on("inspector-change", () => {
3294
- if (this.layerPanelRefs && this.parts.layerPanel) {
3295
- const t = this.host.cad.inspector.selection, i = t == null ? void 0 : t.id, r = i ? `${i.layerIndex}:${i.variantId}:${i.instanceId}` : null;
3296
- r && this.layerPanelState.needsSelectionReveal(r) ? pe(this.layerPanelRefs, this.layerPanelState, this.createLayerPanelActions()) : ai(this.layerPanelRefs.list, t);
3297
- }
3298
- this.propertyPanelRefs && this.parts.propertyPanel && (be(this.propertyPanelRefs, this.host.cad.inspector), this.syncPropertyPanelSelectionState(), this.syncPropertyPanelPreview());
3299
- }),
3300
- this.host.cad.on("hud-change", () => this.syncHud()),
3301
- this.host.on("view-mode-change", () => {
3302
- this.syncToolbarState(), this.syncHud();
3303
- }),
3304
- this.host.on("interaction-change", () => this.syncHud())
3305
- ), (e = globalThis.addEventListener) == null || e.call(globalThis, "keydown", this.handleKeyDown);
3306
- }
3307
- createLayerPanelActions() {
3308
- var e;
3309
- return {
3310
- layers: this.host.cad.layers,
3311
- blocks: this.host.cad.blocks,
3312
- selection: this.host.cad.inspector.selection,
3313
- scopeKey: ((e = this.host.cad.document) == null ? void 0 : e.documentKey) ?? null,
3314
- toggleLayer: (t) => this.host.cad.layers.toggle(t),
3315
- showAllLayers: () => this.host.cad.layers.showAll(),
3316
- hideAllLayers: () => this.host.cad.layers.hideAll(),
3317
- setLayerColor: (t, i) => this.host.cad.layers.setColor(t, i),
3318
- setAllLayerColor: (t) => {
3319
- var i, r;
3320
- return (r = (i = this.host.cad.layers).setAllColor) == null ? void 0 : r.call(i, t);
3321
- },
3322
- toggleInsert: (t, i) => this.host.cad.blocks.toggleInsert(t, i),
3323
- batchToggleInserts: (t) => this.insertToggleScheduler.enqueueMany(t),
3324
- selectInsert: (t) => {
3325
- var i, r;
3326
- return (r = (i = this.host.cad).selectInsert) == null ? void 0 : r.call(i, t);
3327
- },
3328
- refresh: this.refresh
3329
- };
3330
- }
3331
- syncToolbarState() {
3332
- var t, i, r, s, o;
3333
- const e = this.host.viewMode;
3334
- (t = this.btnView2d) == null || t.classList.toggle("active", e === "2d"), (i = this.btnView3d) == null || i.classList.toggle("active", e === "3d"), (r = this.btnView2d) == null || r.setAttribute("aria-pressed", String(e === "2d")), (s = this.btnView3d) == null || s.setAttribute("aria-pressed", String(e === "3d")), this.btnFit && (this.btnFit.disabled = !((o = this.host.cad.document) != null && o.headerBounds));
3335
- }
3336
- readPropertyPanelOpen() {
3337
- var e;
3338
- try {
3339
- return ((e = globalThis.localStorage) == null ? void 0 : e.getItem(ye)) === "1";
3340
- } catch {
3341
- return !1;
3342
- }
3343
- }
3344
- readLayerPanelOpen() {
3345
- var e;
3346
- try {
3347
- return ((e = globalThis.localStorage) == null ? void 0 : e.getItem(xe)) !== "0";
3348
- } catch {
3349
- return !0;
3350
- }
3351
- }
3352
- setLayerPanelOpen(e, t = !0) {
3353
- var s;
3354
- const i = this.layerPanelShell, r = this.layerPanelToggle;
3355
- if (!(!i || !r) && (i.classList.toggle("closed", !e), r.textContent = e ? "‹" : "›", r.setAttribute("aria-expanded", String(e)), r.setAttribute("aria-label", e ? "收起图层面板" : "展开图层面板"), !!t))
3356
- try {
3357
- (s = globalThis.localStorage) == null || s.setItem(xe, e ? "1" : "0");
3358
- } catch {
3359
- }
3360
- }
3361
- setInfobarOpen(e) {
3362
- const t = this.infobar, i = this.infobarToggle;
3363
- !t || !i || (t.classList.toggle("collapsed", !e), i.textContent = e ? "›" : "‹", i.setAttribute("aria-expanded", String(e)), i.setAttribute("aria-label", e ? "收起信息栏" : "展开信息栏"));
3364
- }
3365
- setPropertyPanelOpen(e, t = !0) {
3366
- var s, o;
3367
- const i = this.propertyPanelShell, r = this.propertyPanelToggle;
3368
- if (!(!i || !r) && (i.classList.toggle("closed", !e), r.textContent = e ? "›" : "‹", r.setAttribute("aria-expanded", String(e)), (s = i.querySelector(".vui-property-panel")) == null || s.setAttribute("aria-hidden", String(!e)), e ? this.syncPropertyPanelPreview(!0) : (this.cancelPropertyPreviewRender(), this.propertyPanelRefs && D(this.propertyPanelRefs, !1)), !!t))
3369
- try {
3370
- (o = globalThis.localStorage) == null || o.setItem(ye, e ? "1" : "0");
3371
- } catch {
3372
- }
3373
- }
3374
- syncPropertyPanelSelectionState() {
3375
- const e = this.propertyPanelShell;
3376
- if (!e) return;
3377
- const t = this.host.cad.inspector;
3378
- if (t.selection || t.textSelection) {
3379
- e.classList.contains("closed") && !this.propertyPanelAutoOpened && (this.setPropertyPanelOpen(!0), this.propertyPanelAutoOpened = !0);
3380
- return;
3381
- }
3382
- this.propertyPanelAutoOpened = !1;
3383
- }
3384
- syncPropertyPanelPreview(e = !1) {
3385
- const t = this.propertyPanelRefs, i = this.host.cad.inspector.selection, r = t == null ? void 0 : t.previewCanvas;
3386
- if (!t || !r || !t.previewWrap || !i || !this.host.cad.inspector.details) {
3387
- this.invalidatePropertyPreview(), t && D(t, !1);
3388
- return;
3389
- }
3390
- const s = i.id, o = `${i.documentKey ?? ""}:${s.layerIndex}:${s.variantId}:${s.instanceId}`;
3391
- if (!e && o === this.propertyPreviewKey) return;
3392
- this.cancelPropertyPreviewRender(), this.propertyPreviewKey = o, D(t, !0), this.clearPropertyPreviewCanvas(r);
3393
- const c = () => {
3394
- this.disposed || this.propertyPreviewKey !== o || !this.isPropertyPreviewLayoutReady(r) || (this.propertyPreviewRenderHandle = requestAnimationFrame(() => {
3395
- this.propertyPreviewRenderHandle = requestAnimationFrame(() => {
3396
- this.propertyPreviewRenderHandle = null, !(this.disposed || this.propertyPreviewKey !== o) && D(t, this.host.cad.renderInsertPreview(r));
3397
- });
3398
- }));
3399
- };
3400
- if (this.shouldWaitForPropertyPreviewLayout(r)) {
3401
- this.waitForPropertyPreviewLayout(r).then(c);
3402
- return;
3403
- }
3404
- c();
3405
- }
3406
- shouldWaitForPropertyPreviewLayout(e) {
3407
- var t;
3408
- return (t = this.propertyPanelShell) != null && t.classList.contains("closed") ? !0 : !this.isPropertyPreviewLayoutReady(e);
3409
- }
3410
- isPropertyPreviewLayoutReady(e) {
3411
- return e.clientWidth >= Ce && e.clientHeight >= Ce;
3412
- }
3413
- waitForPropertyPreviewLayout(e) {
3414
- if (this.isPropertyPreviewLayoutReady(e)) return Promise.resolve();
3415
- const t = this.propertyPanelShell, i = (t == null ? void 0 : t.querySelector(".vui-property-panel")) ?? null;
3416
- return !t || !i ? Promise.resolve() : new Promise((r) => {
3417
- let s = !1, o = 0, c = -1;
3418
- const l = [], d = [];
3419
- let h = 0;
3420
- const u = () => {
3421
- for (const g of l) window.clearTimeout(g);
3422
- for (const g of d) g();
3423
- window.clearTimeout(b), h !== 0 && cancelAnimationFrame(h), this.propertyPreviewLayoutWaitAbort = null;
3424
- }, v = () => {
3425
- s || (s = !0, u(), r());
3426
- }, f = () => {
3427
- const g = e.clientWidth;
3428
- if (!this.isPropertyPreviewLayoutReady(e)) {
3429
- o = 0, c = -1;
3430
- return;
3431
- }
3432
- if (g === c) {
3433
- o += 1, o >= Ci && v();
3434
- return;
3435
- }
3436
- c = g, o = 0;
3437
- }, p = (g) => {
3438
- if (!Ri(g)) return;
3439
- let y = !1;
3440
- const P = (E) => {
3441
- E.target !== g || E.propertyName !== "width" || x();
3442
- }, x = () => {
3443
- y || (y = !0, g.removeEventListener("transitionend", P), requestAnimationFrame(f));
3444
- };
3445
- g.addEventListener("transitionend", P), l.push(window.setTimeout(x, we)), d.push(() => {
3446
- y || (y = !0, g.removeEventListener("transitionend", P));
3447
- });
3448
- }, m = () => {
3449
- f(), s || (h = requestAnimationFrame(m));
3450
- }, b = window.setTimeout(v, we);
3451
- p(i), p(t), h = requestAnimationFrame(m), this.propertyPreviewLayoutWaitAbort = () => {
3452
- s = !0, u();
3453
- };
3454
- });
3455
- }
3456
- clearPropertyPreviewCanvas(e) {
3457
- var t;
3458
- (t = e.getContext("2d")) == null || t.clearRect(0, 0, e.width, e.height);
3459
- }
3460
- invalidatePropertyPreview() {
3461
- this.propertyPreviewKey = null, this.cancelPropertyPreviewRender();
3462
- }
3463
- cancelPropertyPreviewRender() {
3464
- var e;
3465
- this.propertyPreviewRenderHandle !== null && (cancelAnimationFrame(this.propertyPreviewRenderHandle), this.propertyPreviewRenderHandle = null), (e = this.propertyPreviewLayoutWaitAbort) == null || e.call(this), this.propertyPreviewLayoutWaitAbort = null;
3466
- }
3467
- }
3468
- function Ri(n) {
3469
- const e = getComputedStyle(n), t = e.transitionDuration.split(",").map((r) => r.trim()), i = e.transitionProperty.split(",").map((r) => r.trim());
3470
- for (let r = 0; r < i.length; r += 1) {
3471
- const s = i[r] ?? i[0] ?? "all";
3472
- if ((t[r] ?? t[0] ?? "0s") !== "0s" && (s === "all" || s === "width"))
3473
- return !0;
3474
- }
3475
- return !1;
3476
- }
3477
- const B = "viewer:highlight", z = "viewer:cad-selection", H = "viewer:cad-hover", Re = {
3478
- dark: {
3479
- hover: { color: 3528703, fillOpacity: 0, edgeOpacity: 0.85 },
3480
- selected: { color: 5999871, fillOpacity: 0.24, edgeOpacity: 1 }
3481
- },
3482
- light: {
3483
- hover: { color: 24524, fillOpacity: 0, edgeOpacity: 0.95 },
3484
- selected: { color: 24524, fillOpacity: 0.2, edgeOpacity: 1 }
3485
- }
3486
- }, Ei = 6, Ii = 12, Mi = 160, ki = 160, Ti = 50, Ee = 5e3;
3487
- class Di {
3488
- constructor(e, t) {
3489
- a(this, "cad");
3490
- a(this, "mountedObjects", /* @__PURE__ */ new Set());
3491
- a(this, "listeners", /* @__PURE__ */ new Map());
3492
- a(this, "selectedObjectValue", null);
3493
- a(this, "highlightedObjectValue", null);
3494
- a(this, "pointerStart", null);
3495
- a(this, "disposed", !1);
3496
- a(this, "cadRenderRoot", null);
3497
- a(this, "interaction", "idle");
3498
- a(this, "interactionIdleTimer", null);
3499
- a(this, "interactionRenderLastTs", null);
3500
- a(this, "cadHoveredInsert", null);
3501
- a(this, "cadPointerPosition", null);
3502
- a(this, "pointerDragTracker", new ti({
3503
- distancePx: Ei,
3504
- pathPx: Ii
3505
- }));
3506
- a(this, "cadCapability");
3507
- a(this, "cadHost");
3508
- a(this, "ui");
3509
- a(this, "cameraStateStore");
3510
- a(this, "cadInspectorUnsubscribe");
3511
- a(this, "cadDocumentUnsubscribe");
3512
- a(this, "handlePointerDown", (e) => {
3513
- if (this.disposed) return;
3514
- if (this.rememberCadPointer(e.clientX, e.clientY), !(e.isPrimary !== !1) || this.pointerStart !== null) {
3515
- this.pointerStart = null, this.pointerDragTracker.reset(), this.markInteractionActive();
3516
- return;
3517
- }
3518
- this.pointerStart = {
3519
- pointerId: e.pointerId,
3520
- clientX: e.clientX,
3521
- clientY: e.clientY,
3522
- button: e.button
3523
- }, this.pointerDragTracker.begin(e.clientX, e.clientY), this.markInteractionActive();
3524
- });
3525
- a(this, "handleWheel", (e) => {
3526
- if (this.disposed) return;
3527
- const t = e.clientX === 0 && e.clientY === 0 ? this.cadPointerPosition : { clientX: e.clientX, clientY: e.clientY };
3528
- t && this.rememberCadPointer(t.clientX, t.clientY), this.markInteractionActive(), this.scheduleInteractionIdle();
3529
- });
3530
- a(this, "handlePointerMove", (e) => {
3531
- if (!this.disposed) {
3532
- if (this.rememberCadPointer(e.clientX, e.clientY), e.buttons > 0) {
3533
- this.pointerDragTracker.update(e.clientX, e.clientY, e.buttons), this.markInteractionActive(), this.setCadHoveredInsert(null), this.emit("pointer-move", this.toPointerEvent(e));
3534
- return;
3535
- }
3536
- this.updateCadHoveredInsert(e.clientX, e.clientY), this.emit("pointer-move", this.toPointerEvent(e));
3537
- }
3538
- });
3539
- a(this, "handlePointerUp", (e) => {
3540
- const t = this.pointerStart;
3541
- this.pointerStart = null, this.pointerDragTracker.update(e.clientX, e.clientY, e.buttons);
3542
- const i = this.pointerDragTracker.hasExceededDragThreshold;
3543
- if (this.pointerDragTracker.release(), this.rememberCadPointer(e.clientX, e.clientY), this.scheduleInteractionIdle(), this.disposed || !t || t.pointerId !== e.pointerId || t.button !== 0 || e.button !== 0 || i)
3544
- return;
3545
- const r = this.toPointerEvent(e);
3546
- this.emit("pointer-click", r);
3547
- const s = this.cadCapability.pickInsertAtScreen(e.clientX, e.clientY);
3548
- if (s) {
3549
- if (this.isSameCadInsertSelection(s.selection, this.cad.inspector.selection)) {
3550
- this.cadCapability.setInsertSelection(null, null), this.setCadHoveredInsert(s), this.setSelection(null);
3551
- return;
3552
- }
3553
- this.cadCapability.setInsertSelection(s.selection, s.details), this.setCadHoveredInsert(s), this.setSelection(null);
3554
- return;
3555
- }
3556
- this.setCadHoveredInsert(null), this.cadCapability.setInsertSelection(null, null);
3557
- const o = this.cadCapability.pickTextAtScreen(e.clientX, e.clientY);
3558
- if (o) {
3559
- this.cadCapability.setTextSelection(o.selection, o.details), this.setSelection(null);
3560
- return;
3561
- }
3562
- if (this.cadCapability.setTextSelection(null, null), this.options.pointerPick === !1) return;
3563
- const c = this.pick(e.clientX, e.clientY);
3564
- this.emit("pick", {
3565
- ...r,
3566
- result: c
3567
- }), this.options.autoSelectOnPick !== !1 && this.setSelection((c == null ? void 0 : c.object) ?? null);
3568
- });
3569
- a(this, "handlePointerCancel", () => {
3570
- this.pointerStart = null, this.cadPointerPosition = null, this.pointerDragTracker.reset(), this.setCadHoveredInsert(null), this.scheduleInteractionIdle();
3571
- });
3572
- a(this, "handleLostPointerCapture", (e) => {
3573
- var t;
3574
- ((t = this.pointerStart) == null ? void 0 : t.pointerId) === e.pointerId && (this.pointerStart = null, this.cadPointerPosition = null, this.pointerDragTracker.reset(), this.setCadHoveredInsert(null), this.scheduleInteractionIdle());
3575
- });
3576
- a(this, "handleWindowBlur", () => {
3577
- this.pointerStart = null, this.cadPointerPosition = null, this.pointerDragTracker.reset(), this.setCadHoveredInsert(null), this.scheduleInteractionIdle();
3578
- });
3579
- a(this, "handlePointerLeave", (e) => {
3580
- this.pointerStart = null, this.cadPointerPosition = null, this.pointerDragTracker.reset(), this.setCadHoveredInsert(null), this.scheduleInteractionIdle(), !this.disposed && this.emit("pointer-leave", this.toPointerEvent(e));
3581
- });
3582
- var i, r, s, o, c, l, d, h, u, v, f, p, m, b, g;
3583
- this.options = e, this.runtime = t, this.cameraStateStore = e.cameraStateStore === !1 ? void 0 : e.cameraStateStore ?? Xt(), this.cadHost = this.createCadHost(), this.cadCapability = new Qt(this.cadHost), this.cad = this.cadCapability, this.cadInspectorUnsubscribe = this.cad.on("inspector-change", () => {
3584
- this.syncCadSelectionOverlay(), this.syncCadHoverOverlay();
3585
- }), this.cadDocumentUnsubscribe = this.cad.on("document-change", () => {
3586
- this.setCadHoveredInsert(null), this.syncCadSelectionOverlay();
3587
- }), this.ui = e.ui ? new Si(this.createUiHost(), e.ui) : null, (i = this.ui) == null || i.mount(), (s = (r = this.options.canvas).addEventListener) == null || s.call(r, "pointerdown", this.handlePointerDown), (c = (o = this.options.canvas).addEventListener) == null || c.call(o, "pointermove", this.handlePointerMove), (d = (l = this.options.canvas).addEventListener) == null || d.call(l, "pointerup", this.handlePointerUp), (u = (h = this.options.canvas).addEventListener) == null || u.call(h, "pointercancel", this.handlePointerCancel), (f = (v = this.options.canvas).addEventListener) == null || f.call(v, "pointerleave", this.handlePointerLeave), (m = (p = this.options.canvas).addEventListener) == null || m.call(p, "lostpointercapture", this.handleLostPointerCapture), (g = (b = this.options.canvas).addEventListener) == null || g.call(b, "wheel", this.handleWheel, { passive: !0 }), typeof globalThis.window < "u" && globalThis.window.addEventListener("blur", this.handleWindowBlur), this.runtime.startAnimation({
3588
- shouldRender: (y) => this.shouldRenderCurrentFrame(y),
3589
- beforeRender: (y) => {
3590
- this.cadCapability.onBeforeRender(), this.interaction === "active" && (this.interactionRenderLastTs = y);
3591
- },
3592
- onInfoUpdate: () => {
3593
- this.cadCapability.onViewportChanged();
3594
- }
3595
- });
3596
- }
3597
- get viewport() {
3598
- return this.options.viewport;
3599
- }
3600
- get canvas() {
3601
- return this.options.canvas;
3602
- }
3603
- get viewMode() {
3604
- return this.runtime.viewMode;
3605
- }
3606
- get selectedObject() {
3607
- return this.selectedObjectValue;
3608
- }
3609
- get highlightedObject() {
3610
- return this.highlightedObjectValue;
3611
- }
3612
- get isDisposed() {
3613
- return this.disposed;
3614
- }
3615
- addObject(e) {
3616
- this.assertActive(), !this.mountedObjects.has(e) && (this.runtime.addObject(e), this.mountedObjects.add(e));
3617
- }
3618
- removeObject(e) {
3619
- if (this.assertActive(), !this.mountedObjects.has(e)) return !1;
3620
- this.selectedObjectValue && this.isObjectWithinRoot(this.selectedObjectValue, e) && this.setSelection(null), this.highlightedObjectValue && this.isObjectWithinRoot(this.highlightedObjectValue, e) && this.setHighlight(null);
3621
- const t = this.runtime.removeObject(e, { dispose: !1 });
3622
- return t && this.mountedObjects.delete(e), t;
3623
- }
3624
- setVisible(e, t) {
3625
- this.assertActive(), this.assertMounted(e), this.runtime.setObjectVisible(e, t);
3626
- }
3627
- setViewMode(e, t) {
3628
- this.assertActive(), e !== this.runtime.viewMode && (t ? this.runtime.setViewMode(e, t) : this.runtime.setViewMode(e), this.emit("view-mode-change", { viewMode: e }), this.cadCapability.onCameraChanged(), this.cadCapability.onViewportChanged(), this.refreshCadPointerHover());
3629
- }
3630
- fitView(e, t) {
3631
- this.assertActive(), e && !(e instanceof S) && this.assertMounted(e);
3632
- const i = e ?? this.resolveFocusedCadBounds() ?? this.resolveMixedBounds();
3633
- if (!i) return !1;
3634
- const r = this.runtime.fitView(i, t);
3635
- return r && (this.cadCapability.onCameraChanged(), this.cadCapability.onViewportChanged(), this.refreshCadPointerHover()), r;
3636
- }
3637
- screenToWorldOnPlane(e, t, i = 0) {
3638
- var r;
3639
- return this.assertActive(), ((r = this.runtime.screenToWorldOnPlane(e, t, i)) == null ? void 0 : r.clone()) ?? null;
3640
- }
3641
- pick(e, t, i) {
3642
- var s;
3643
- this.assertActive(), (s = i == null ? void 0 : i.roots) == null || s.forEach((o) => this.assertMounted(o));
3644
- const r = this.runtime.pick(e, t, i);
3645
- return r ? {
3646
- object: r.object,
3647
- root: r.root,
3648
- point: r.point.clone(),
3649
- distance: r.distance,
3650
- faceIndex: r.faceIndex,
3651
- ...r.instanceId === void 0 ? {} : { instanceId: r.instanceId }
3652
- } : null;
3653
- }
3654
- setSelection(e) {
3655
- this.assertActive(), e && this.assertMounted(e), e !== this.selectedObjectValue && (this.selectedObjectValue = e, this.runtime.requestRender(), this.emit("selection-change", { object: e }));
3656
- }
3657
- setHighlight(e, t = {}) {
3658
- if (this.assertActive(), e && this.assertMounted(e), !(this.highlightedObjectValue === null && e === null)) {
3659
- if (this.runtime.removeOverlay(B), this.highlightedObjectValue = e, e) {
3660
- const i = new Qe(e, t.color ?? 6000111);
3661
- i.name = B, this.runtime.mountOverlay(B, i, { owned: !0 });
3662
- }
3663
- this.emit("highlight-change", { object: e });
3664
- }
3665
- }
3666
- on(e, t) {
3667
- this.assertActive();
3668
- const i = t, r = this.listeners.get(e) ?? /* @__PURE__ */ new Set();
3669
- r.add(i), this.listeners.set(e, r);
3670
- let s = !0;
3671
- return () => {
3672
- s && (s = !1, r.delete(i), r.size === 0 && this.listeners.delete(e));
3673
- };
3674
- }
3675
- resize() {
3676
- this.assertActive();
3677
- const e = this.runtime.resize();
3678
- return this.emit("resize", { resized: e }), e && (this.cadCapability.onViewportChanged(), this.refreshCadPointerHover()), e;
3679
- }
3680
- requestRender() {
3681
- this.assertActive(), this.runtime.requestRender();
3682
- }
3683
- dispose() {
3684
- var e, t, i, r, s, o, c, l, d, h, u, v, f, p, m;
3685
- if (!this.disposed) {
3686
- this.disposed = !0, this.clearInteractionIdleTimer(), this.pointerDragTracker.reset(), (t = (e = this.options.canvas).removeEventListener) == null || t.call(e, "pointerdown", this.handlePointerDown), (r = (i = this.options.canvas).removeEventListener) == null || r.call(i, "pointermove", this.handlePointerMove), (o = (s = this.options.canvas).removeEventListener) == null || o.call(s, "pointerup", this.handlePointerUp), (l = (c = this.options.canvas).removeEventListener) == null || l.call(c, "pointercancel", this.handlePointerCancel), (h = (d = this.options.canvas).removeEventListener) == null || h.call(d, "pointerleave", this.handlePointerLeave), (v = (u = this.options.canvas).removeEventListener) == null || v.call(u, "lostpointercapture", this.handleLostPointerCapture), (p = (f = this.options.canvas).removeEventListener) == null || p.call(f, "wheel", this.handleWheel), typeof globalThis.window < "u" && globalThis.window.removeEventListener("blur", this.handleWindowBlur), this.pointerStart = null, this.cadPointerPosition = null, this.setCadHoveredInsert(null), this.setCanvasCursor(""), this.highlightedObjectValue && this.runtime.removeOverlay(B), this.runtime.removeOverlay(z), this.runtime.removeOverlay(H), this.cadInspectorUnsubscribe(), this.cadDocumentUnsubscribe(), this.highlightedObjectValue = null, this.selectedObjectValue = null;
3687
- for (const b of this.mountedObjects)
3688
- this.runtime.removeObject(b, { dispose: !1 });
3689
- this.mountedObjects.clear(), (m = this.ui) == null || m.dispose(), this.cadCapability.dispose(), this.listeners.clear(), this.runtime.dispose();
3690
- }
3691
- }
3692
- createCadHost() {
3693
- const e = this;
3694
- return {
3695
- get options() {
3696
- return e.options;
3697
- },
3698
- get runtime() {
3699
- return e.runtime;
3700
- },
3701
- get viewMode() {
3702
- return e.runtime.viewMode;
3703
- },
3704
- get interaction() {
3705
- return e.interaction;
3706
- },
3707
- get visibilityRevision() {
3708
- return e.cadCapability.getVisibilityRevision();
3709
- },
3710
- get viewportDemandCoalesceMs() {
3711
- return e.options.viewportDemandCoalesceMs ?? ki;
3712
- },
3713
- mountCadObject(t) {
3714
- e.unmountCadObject(), e.runtime.addObject(t, { owned: !0 }), e.cadRenderRoot = t;
3715
- },
3716
- unmountCadObject() {
3717
- e.cadRenderRoot && (e.runtime.removeObject(e.cadRenderRoot, { dispose: !0 }), e.cadRenderRoot = null);
3718
- },
3719
- requestRender() {
3720
- e.runtime.requestRender();
3721
- },
3722
- emitContentChange() {
3723
- e.emit("content-change", { source: "cad" });
3724
- },
3725
- emitViewportChange(t) {
3726
- e.emit("viewport-change", t);
3727
- },
3728
- emitCameraChange() {
3729
- e.emit("camera-change", { viewMode: e.runtime.viewMode });
3730
- },
3731
- fitBox3(t, i) {
3732
- return e.runtime.fitView(t, i);
3733
- },
3734
- resolveMixedBounds() {
3735
- return e.resolveMixedBounds();
3736
- },
3737
- resolveCadFitBounds() {
3738
- return e.cadCapability.getMountedBoundsBox() ?? e.cadCapability.getAuthoritativeBoundsBox();
3739
- },
3740
- screenToWorldOnPlane(t, i, r) {
3741
- var s;
3742
- return ((s = e.runtime.screenToWorldOnPlane(t, i, r)) == null ? void 0 : s.clone()) ?? null;
3743
- },
3744
- readCameraSnapshot(t) {
3745
- return qt(e.cameraStateStore, t);
3746
- },
3747
- persistCameraSnapshot(t, i) {
3748
- Wt(e.cameraStateStore, t, i);
3749
- },
3750
- getViewportSize() {
3751
- return {
3752
- width: Math.max(1, e.options.viewport.clientWidth || 1),
3753
- height: Math.max(1, e.options.viewport.clientHeight || 1)
3754
- };
3755
- },
3756
- getVisibleDocumentRect() {
3757
- return e.runtime.getVisibleDocumentRect();
3758
- }
3759
- };
3760
- }
3761
- syncCadSelectionOverlay() {
3762
- var o, c;
3763
- this.runtime.removeOverlay(z);
3764
- const e = this.cad.inspector, t = (o = e.details) != null && o.bbox && [
3765
- e.details.bbox.minX,
3766
- e.details.bbox.minY,
3767
- e.details.bbox.maxX,
3768
- e.details.bbox.maxY
3769
- ].every(Number.isFinite) ? e.details.bbox : null, i = t ? new S(
3770
- new w(t.minX, t.minY, -0.01),
3771
- new w(t.maxX, t.maxY, 0.01)
3772
- ) : e.textDetails ? new S(
3773
- new w(e.textDetails.x, e.textDetails.y, -0.01),
3774
- new w(
3775
- e.textDetails.x + e.textDetails.content.length * e.textDetails.height * 0.6,
3776
- e.textDetails.y + e.textDetails.height,
3777
- 0.01
3778
- )
3779
- ) : null;
3780
- if (!i || i.isEmpty()) return;
3781
- const s = !!((c = e.details) != null && c.bbox) ? this.createCadInteractionOverlay(i, "selected") : new et(i, Re.dark.selected.color);
3782
- s.name = z, this.runtime.mountOverlay(z, s, { owned: !0 });
3783
- }
3784
- syncCadHoverOverlay() {
3785
- var s;
3786
- this.runtime.removeOverlay(H);
3787
- const e = this.cadHoveredInsert, t = this.cad.inspector.selection;
3788
- if (!((s = e == null ? void 0 : e.details) != null && s.bbox) || this.isSameCadInsertSelection(e.selection, t)) return;
3789
- const i = this.cadInsertDetailsToBox(e.details);
3790
- if (!i || i.isEmpty()) return;
3791
- const r = this.createCadInteractionOverlay(i, "hover");
3792
- r.name = H, this.runtime.mountOverlay(H, r, { owned: !0 });
3793
- }
3794
- createCadInteractionOverlay(e, t) {
3795
- var h;
3796
- const i = ((h = this.options.ui) == null ? void 0 : h.theme) === "light" ? "light" : "dark", r = Re[i][t], s = new Ie();
3797
- s.name = t === "hover" ? "cad-insert-hover" : "cad-insert-selected", s.renderOrder = t === "hover" ? 1300 : 1310;
3798
- const o = e.getSize(new w()), c = e.getCenter(new w()), l = new tt(Math.max(o.x, 1e-6), Math.max(o.y, 1e-6), Math.max(o.z, 1e-3));
3799
- if (r.fillOpacity > 0) {
3800
- const u = new it(
3801
- l.clone(),
3802
- new rt({
3803
- color: r.color,
3804
- transparent: !0,
3805
- opacity: r.fillOpacity,
3806
- depthTest: !0,
3807
- depthWrite: !1
3808
- })
3809
- );
3810
- u.name = `${s.name}:fill`, u.position.copy(c), u.renderOrder = 1305, s.add(u);
3811
- }
3812
- const d = new nt(
3813
- new st(l),
3814
- new ot({
3815
- color: r.color,
3816
- transparent: !0,
3817
- opacity: r.edgeOpacity,
3818
- depthTest: !0,
3819
- depthWrite: !1
3820
- })
3821
- );
3822
- return d.name = `${s.name}:edges`, d.position.copy(c), d.renderOrder = t === "hover" ? 1300 : 1310, s.add(d), l.dispose(), s;
3823
- }
3824
- cadInsertDetailsToBox(e) {
3825
- const t = e == null ? void 0 : e.bbox;
3826
- return !t || ![t.minX, t.minY, t.maxX, t.maxY].every(Number.isFinite) ? null : new S(
3827
- new w(t.minX, t.minY, -0.01),
3828
- new w(t.maxX, t.maxY, 0.01)
3829
- );
3830
- }
3831
- isSameCadInsertSelection(e, t) {
3832
- return !!(e && t && e.documentKey === t.documentKey && e.id.layerIndex === t.id.layerIndex && e.id.variantId === t.id.variantId && e.id.instanceId === t.id.instanceId);
3833
- }
3834
- setCadHoveredInsert(e) {
3835
- const t = this.cadHoveredInsert;
3836
- if (!t && !e || t && e && this.isSameCadInsertSelection(t.selection, e.selection)) {
3837
- this.setCanvasCursor(e ? "pointer" : "");
3838
- return;
3839
- }
3840
- this.cadHoveredInsert = e, this.syncCadHoverOverlay(), this.setCanvasCursor(e ? "pointer" : ""), this.runtime.requestRender();
3841
- }
3842
- updateCadHoveredInsert(e, t) {
3843
- this.setCadHoveredInsert(this.cadCapability.pickInsertAtScreen(e, t));
3844
- }
3845
- rememberCadPointer(e, t) {
3846
- !Number.isFinite(e) || !Number.isFinite(t) || (this.cadPointerPosition = { clientX: e, clientY: t });
3847
- }
3848
- refreshCadPointerHover() {
3849
- const e = this.cadPointerPosition;
3850
- e && this.updateCadHoveredInsert(e.clientX, e.clientY);
3851
- }
3852
- setCanvasCursor(e) {
3853
- const t = this.options.canvas.style;
3854
- t && (t.cursor = e);
3855
- }
3856
- createUiHost() {
3857
- const e = this;
3858
- return {
3859
- get cad() {
3860
- return e.cad;
3861
- },
3862
- get viewMode() {
3863
- return e.runtime.viewMode;
3864
- },
3865
- fitView() {
3866
- return e.fitView();
3867
- },
3868
- setViewMode(t) {
3869
- e.setViewMode(t);
3870
- },
3871
- on(t, i) {
3872
- return e.on(t, i);
3873
- }
3874
- };
3875
- }
3876
- unmountCadObject() {
3877
- this.cadHost.unmountCadObject();
3878
- }
3879
- resolveMixedBounds() {
3880
- const e = new S();
3881
- let t = !1;
3882
- for (const r of this.mountedObjects)
3883
- r.visible && (e.expandByObject(r), t = !0);
3884
- const i = this.cadHost.resolveCadFitBounds();
3885
- return i && (e.union(i), t = !0), t && !e.isEmpty() ? e : null;
3886
- }
3887
- resolveFocusedCadBounds() {
3888
- var d;
3889
- const e = (d = this.cad.inspector.details) == null ? void 0 : d.bbox;
3890
- if (!e || ![e.minX, e.minY, e.maxX, e.maxY].every(Number.isFinite)) return null;
3891
- let t = e.minX, i = e.minY, r = e.maxX, s = e.maxY;
3892
- const o = r - t, c = s - i, l = Math.max(o, c);
3893
- if (o > 0 && c > 0 && l < Ee) {
3894
- const h = (t + r) / 2, u = (i + s) / 2, v = Ee / 2;
3895
- t = h - v, i = u - v, r = h + v, s = u + v;
3896
- }
3897
- return new S(
3898
- new w(t, i, -0.01),
3899
- new w(r, s, 0.01)
3900
- );
3901
- }
3902
- setInteraction(e) {
3903
- this.interaction !== e && (this.interaction = e, this.interactionRenderLastTs = null, this.emit("interaction-change", { interaction: e }), this.cadCapability.onInteractionChanged(), e === "idle" && this.runtime.requestRender());
3904
- }
3905
- shouldRenderCurrentFrame(e) {
3906
- if (this.interaction !== "active" || this.interactionRenderLastTs === null) return !0;
3907
- const t = e - this.interactionRenderLastTs;
3908
- return t < 0 || t >= Ti;
3909
- }
3910
- markInteractionActive() {
3911
- this.clearInteractionIdleTimer(), this.setInteraction("active");
3912
- }
3913
- scheduleInteractionIdle() {
3914
- this.clearInteractionIdleTimer(), this.interactionIdleTimer = globalThis.setTimeout(() => {
3915
- this.interactionIdleTimer = null, this.setInteraction("idle"), this.refreshCadPointerHover();
3916
- }, Mi);
3917
- }
3918
- clearInteractionIdleTimer() {
3919
- this.interactionIdleTimer !== null && (globalThis.clearTimeout(this.interactionIdleTimer), this.interactionIdleTimer = null);
3920
- }
3921
- toPointerEvent(e) {
3922
- return {
3923
- clientX: e.clientX,
3924
- clientY: e.clientY,
3925
- buttons: e.buttons,
3926
- shiftKey: e.shiftKey,
3927
- ctrlKey: e.ctrlKey,
3928
- metaKey: e.metaKey
3929
- };
3930
- }
3931
- assertActive() {
3932
- if (this.disposed) throw new Error("Viewer has been disposed.");
3933
- }
3934
- assertMounted(e) {
3935
- if (!(this.cadRenderRoot && this.isObjectWithinRoot(e, this.cadRenderRoot))) {
3936
- for (const t of this.mountedObjects)
3937
- if (this.isObjectWithinRoot(e, t)) return;
3938
- throw new Error("Viewer can only modify mounted objects.");
3939
- }
3940
- }
3941
- isObjectWithinRoot(e, t) {
3942
- let i = e;
3943
- for (; i; ) {
3944
- if (i === t) return !0;
3945
- i = i.parent;
3946
- }
3947
- return !1;
3948
- }
3949
- emit(e, t) {
3950
- for (const i of this.listeners.get(e) ?? []) i(t);
3951
- }
3952
- }
3953
- function Hi(n) {
3954
- const e = new Bt({
3955
- canvasArea: n.viewport,
3956
- canvas: n.canvas,
3957
- clearColor: n.clearColor,
3958
- antialias: n.antialias,
3959
- alpha: n.alpha,
3960
- powerPreference: n.powerPreference,
3961
- initialViewMode: n.initialViewMode,
3962
- environment: n.environment ?? "room",
3963
- navigationPolicy: "viewer"
3964
- });
3965
- return Li(n, e);
3966
- }
3967
- function Li(n, e) {
3968
- return e.initialize(), new Di(n, e);
3969
- }
1
+ import { c as o } from "./index-Lsk3kHNe.js";
2
+ import { configureCadTextBuilder as t } from "@nebula-spatial/cad-loader";
3970
3
  export {
3971
- _i as configureViewerText,
3972
- Hi as createViewer
4
+ t as configureViewerText,
5
+ o as createViewer
3973
6
  };