iflow-engine-base 4.0.15 → 4.0.16

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.
@@ -0,0 +1,1565 @@
1
+ import { s as S, av as R, j as E, F as x, M as A, t as b, U as k, Q as F, x as I, W as y, v, d as f, S as P, a2 as m, V as p, e as D } from "./three-vendor-Ds9cgGKb.js";
2
+ import { C as L, W as w, E as T, c as z, r as _ } from "./sdk-runtime-KslKRuwI.js";
3
+ class V {
4
+ scene;
5
+ panoramaSphere = null;
6
+ sphereRadius;
7
+ constructor(e = 500) {
8
+ this.sphereRadius = e, this.scene = new S(), this.init();
9
+ }
10
+ /**
11
+ * 初始化场景
12
+ */
13
+ init() {
14
+ this.panoramaSphere = this.createPanoramaSphere(this.sphereRadius), this.scene.add(this.panoramaSphere);
15
+ }
16
+ /**
17
+ * 创建全景球体
18
+ * @param radius 球体半径(建议 500-1000)
19
+ * @param segments 分段数(建议 64)
20
+ * @returns 球体网格对象
21
+ */
22
+ createPanoramaSphere(e = 500, t = 64) {
23
+ const n = new R(
24
+ e,
25
+ // 半径
26
+ t,
27
+ // 水平分段
28
+ t / 2
29
+ // 垂直分段
30
+ );
31
+ n.scale(-1, 1, 1);
32
+ const o = new E({
33
+ map: null,
34
+ // 纹理后续加载
35
+ side: x,
36
+ // Render inner surface; geometry is flipped via scale(-1,1,1)
37
+ transparent: !0
38
+ }), a = new A(n, o);
39
+ return a.name = "panorama-sphere", a;
40
+ }
41
+ /**
42
+ * 更新全景图纹理
43
+ * @param texture Three.js 纹理对象
44
+ */
45
+ updatePanoramaTexture(e) {
46
+ if (!this.panoramaSphere) {
47
+ console.warn("SceneModule720: Panorama sphere not initialized");
48
+ return;
49
+ }
50
+ const t = this.panoramaSphere.material;
51
+ t.map && t.map.dispose(), t.map = e, t.needsUpdate = !0;
52
+ }
53
+ /**
54
+ * 清除当前全景纹理
55
+ */
56
+ clearPanoramaTexture() {
57
+ if (!this.panoramaSphere)
58
+ return;
59
+ const e = this.panoramaSphere.material;
60
+ e.map && (e.map.dispose(), e.map = null, e.needsUpdate = !0);
61
+ }
62
+ /**
63
+ * 获取场景对象
64
+ */
65
+ getScene() {
66
+ return this.scene;
67
+ }
68
+ /**
69
+ * 获取全景球体对象
70
+ */
71
+ getPanoramaSphere() {
72
+ return this.panoramaSphere;
73
+ }
74
+ /**
75
+ * 设置球体半径(需要重新创建球体)
76
+ * @param radius 新的半径
77
+ */
78
+ setSphereRadius(e) {
79
+ if (this.sphereRadius !== e) {
80
+ if (this.sphereRadius = e, this.panoramaSphere) {
81
+ const t = this.panoramaSphere.material, n = t.map;
82
+ this.scene.remove(this.panoramaSphere), this.panoramaSphere.geometry.dispose(), t.dispose(), n && n.dispose();
83
+ }
84
+ this.panoramaSphere = this.createPanoramaSphere(e), this.scene.add(this.panoramaSphere);
85
+ }
86
+ }
87
+ /**
88
+ * 获取球体半径
89
+ */
90
+ getSphereRadius() {
91
+ return this.sphereRadius;
92
+ }
93
+ /**
94
+ * 销毁模块
95
+ */
96
+ dispose() {
97
+ if (this.panoramaSphere) {
98
+ const e = this.panoramaSphere.material;
99
+ e.map && e.map.dispose(), e.dispose(), this.panoramaSphere.geometry.dispose(), this.scene.remove(this.panoramaSphere), this.panoramaSphere = null;
100
+ }
101
+ this.scene.clear();
102
+ }
103
+ }
104
+ class K {
105
+ camera;
106
+ minFov;
107
+ maxFov;
108
+ container;
109
+ /**
110
+ * 当前相机姿态(球面坐标)
111
+ */
112
+ currentPose;
113
+ constructor(e, t = 75, n = 30, o = 90) {
114
+ this.container = e, this.minFov = n, this.maxFov = o, this.currentPose = {
115
+ theta: 0,
116
+ // 水平角度
117
+ phi: 0
118
+ // 垂直角度
119
+ }, this.camera = this.createCamera(t), this.init();
120
+ }
121
+ /**
122
+ * 创建透视相机
123
+ */
124
+ createCamera(e) {
125
+ const t = this.container.clientWidth / this.container.clientHeight, n = new b(e, t, 0.1, 2e3);
126
+ return n.position.set(0, 0, 0), n;
127
+ }
128
+ /**
129
+ * 初始化相机
130
+ */
131
+ init() {
132
+ this.updateCameraRotation();
133
+ }
134
+ /**
135
+ * 更新相机旋转(基于当前姿态)
136
+ */
137
+ updateCameraRotation() {
138
+ const { theta: e, phi: t } = this.currentPose, n = new k(t, e, 0, "YXZ"), o = new F();
139
+ o.setFromEuler(n), this.camera.quaternion.copy(o);
140
+ }
141
+ /**
142
+ * 设置 FOV(视野角度)
143
+ * @param fov 视野角度(度)
144
+ */
145
+ setFov(e) {
146
+ const t = Math.max(this.minFov, Math.min(this.maxFov, e));
147
+ this.camera.fov = t, this.camera.updateProjectionMatrix();
148
+ }
149
+ /**
150
+ * 获取当前 FOV
151
+ */
152
+ getFov() {
153
+ return this.camera.fov;
154
+ }
155
+ /**
156
+ * 获取相机姿态
157
+ */
158
+ getCameraPose() {
159
+ return { ...this.currentPose };
160
+ }
161
+ /**
162
+ * 设置相机姿态(球面坐标)
163
+ * @param theta 水平角度(弧度)
164
+ * @param phi 垂直角度(弧度)
165
+ */
166
+ setCameraPose(e, t) {
167
+ this.currentPose.theta = e, this.currentPose.phi = t, this.updateCameraRotation();
168
+ }
169
+ /**
170
+ * 更新相机姿态(增量更新)
171
+ * @param deltaTheta 水平角度增量(弧度)
172
+ * @param deltaPhi 垂直角度增量(弧度)
173
+ */
174
+ updatePose(e, t) {
175
+ this.currentPose.theta += e, this.currentPose.phi += t, this.currentPose.theta < 0 ? this.currentPose.theta += 2 * Math.PI : this.currentPose.theta >= 2 * Math.PI && (this.currentPose.theta -= 2 * Math.PI), this.currentPose.phi = Math.max(
176
+ -Math.PI / 2 + 0.01,
177
+ Math.min(Math.PI / 2 - 0.01, this.currentPose.phi)
178
+ ), this.updateCameraRotation();
179
+ }
180
+ /**
181
+ * 获取相机对象
182
+ */
183
+ getCamera() {
184
+ return this.camera;
185
+ }
186
+ /**
187
+ * 调整相机宽高比
188
+ * @param width 容器宽度
189
+ * @param height 容器高度
190
+ */
191
+ updateAspectRatio(e, t) {
192
+ const n = e / t;
193
+ this.camera.aspect = n, this.camera.updateProjectionMatrix();
194
+ }
195
+ /**
196
+ * 销毁相机
197
+ */
198
+ dispose() {
199
+ this.camera = null;
200
+ }
201
+ }
202
+ class O {
203
+ renderer;
204
+ container;
205
+ animationId = null;
206
+ isRenderingPaused = !1;
207
+ watermark;
208
+ // FPS 监控
209
+ fpsFrameCount = 0;
210
+ fpsLastTime = performance.now();
211
+ currentFps = 60;
212
+ // 渲染回调
213
+ onBeforeRenderCallback;
214
+ onAfterRenderCallback;
215
+ constructor(e, t = !0) {
216
+ this.container = e, this.renderer = this.createRenderer(t), this.watermark = new L(I), this.init();
217
+ }
218
+ /**
219
+ * 创建 WebGL 渲染器
220
+ */
221
+ createRenderer(e) {
222
+ const t = new y({
223
+ antialias: e,
224
+ alpha: !0,
225
+ preserveDrawingBuffer: !1
226
+ }), n = this.container.clientWidth, o = this.container.clientHeight;
227
+ return t.setSize(n, o), t.setPixelRatio(Math.min(window.devicePixelRatio, 2)), this.container.appendChild(t.domElement), t;
228
+ }
229
+ /**
230
+ * 初始化渲染器
231
+ */
232
+ init() {
233
+ }
234
+ /**
235
+ * 设置渲染前回调
236
+ */
237
+ setOnBeforeRender(e) {
238
+ this.onBeforeRenderCallback = e;
239
+ }
240
+ /**
241
+ * 设置渲染后回调
242
+ */
243
+ setOnAfterRender(e) {
244
+ this.onAfterRenderCallback = e;
245
+ }
246
+ /**
247
+ * 渲染场景
248
+ */
249
+ render(e, t) {
250
+ this.onBeforeRenderCallback && this.onBeforeRenderCallback(), this.renderer.render(e, t), this.watermark.render(this.renderer), this.onAfterRenderCallback && this.onAfterRenderCallback(), this.updateFps();
251
+ }
252
+ /**
253
+ * 启动渲染循环
254
+ * @param scene Three.js 场景
255
+ * @param camera Three.js 相机
256
+ */
257
+ startRenderLoop(e, t) {
258
+ if (this.animationId !== null) {
259
+ console.warn("RenderModule720: Render loop already running");
260
+ return;
261
+ }
262
+ this.isRenderingPaused = !1, this.animate(e, t);
263
+ }
264
+ /**
265
+ * 动画循环
266
+ */
267
+ animate(e, t) {
268
+ this.isRenderingPaused || (this.animationId = requestAnimationFrame(() => {
269
+ this.animate(e, t);
270
+ }), this.render(e, t));
271
+ }
272
+ /**
273
+ * 暂停渲染
274
+ */
275
+ pauseRendering() {
276
+ this.isRenderingPaused = !0, this.animationId !== null && (cancelAnimationFrame(this.animationId), this.animationId = null);
277
+ }
278
+ /**
279
+ * 恢复渲染
280
+ */
281
+ resumeRendering(e, t) {
282
+ this.isRenderingPaused && (this.isRenderingPaused = !1, this.startRenderLoop(e, t));
283
+ }
284
+ /**
285
+ * 检查渲染是否暂停
286
+ */
287
+ isRenderingPausedState() {
288
+ return this.isRenderingPaused;
289
+ }
290
+ /**
291
+ * 更新 FPS
292
+ */
293
+ updateFps() {
294
+ this.fpsFrameCount++;
295
+ const e = performance.now();
296
+ e >= this.fpsLastTime + 1e3 && (this.currentFps = Math.round(this.fpsFrameCount * 1e3 / (e - this.fpsLastTime)), this.fpsFrameCount = 0, this.fpsLastTime = e, this.currentFps < 30 && console.warn("RenderModule720: Low FPS detected:", this.currentFps));
297
+ }
298
+ /**
299
+ * 获取当前 FPS
300
+ */
301
+ getCurrentFps() {
302
+ return this.currentFps;
303
+ }
304
+ /**
305
+ * 调整渲染器大小
306
+ * @param width 容器宽度
307
+ * @param height 容器高度
308
+ */
309
+ resize(e, t) {
310
+ this.renderer.setSize(e, t), this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
311
+ }
312
+ /**
313
+ * 获取渲染器 DOM 元素
314
+ */
315
+ getDomElement() {
316
+ return this.renderer.domElement;
317
+ }
318
+ /**
319
+ * 获取渲染器对象
320
+ */
321
+ getRenderer() {
322
+ return this.renderer;
323
+ }
324
+ /**
325
+ * 销毁渲染器
326
+ */
327
+ dispose() {
328
+ this.pauseRendering(), this.container.contains(this.renderer.domElement) && this.container.removeChild(this.renderer.domElement), this.watermark.dispose(), this.renderer.dispose(), this.renderer.forceContextLoss(), this.onBeforeRenderCallback = void 0, this.onAfterRenderCallback = void 0;
329
+ }
330
+ }
331
+ class H {
332
+ camera;
333
+ domElement;
334
+ cameraModule;
335
+ // CameraModule720 引用
336
+ // 配置参数
337
+ enableZoom = !0;
338
+ enableRotate = !0;
339
+ minFov = 30;
340
+ // 最小 FOV (最大缩放)
341
+ maxFov = 90;
342
+ // 最大 FOV (最小缩放)
343
+ rotateSpeed = 0.5;
344
+ zoomSpeed = 1;
345
+ enableDamping = !0;
346
+ dampingFactor = 0.05;
347
+ // 内部状态
348
+ isDragging = !1;
349
+ startX = 0;
350
+ startY = 0;
351
+ // 旋转增量
352
+ thetaDelta = 0;
353
+ phiDelta = 0;
354
+ isPinching = !1;
355
+ pinchStartDistance = null;
356
+ pinchStartFov = 75;
357
+ // 事件监听器绑定
358
+ _onMouseDown;
359
+ _onMouseMove;
360
+ _onMouseUp;
361
+ _onWheel;
362
+ _onTouchStart;
363
+ _onTouchMove;
364
+ _onTouchEnd;
365
+ // 视角变化回调
366
+ onViewAngleChange;
367
+ constructor(e, t) {
368
+ this.cameraModule = e, this.camera = e.getCamera(), this.pinchStartFov = this.camera.fov, this.domElement = t, this._onMouseDown = this.onMouseDown.bind(this), this._onMouseMove = this.onMouseMove.bind(this), this._onMouseUp = this.onMouseUp.bind(this), this._onWheel = this.onWheel.bind(this), this._onTouchStart = this.onTouchStart.bind(this), this._onTouchMove = this.onTouchMove.bind(this), this._onTouchEnd = this.onTouchEnd.bind(this), this.init();
369
+ }
370
+ /**
371
+ * 初始化控制器
372
+ */
373
+ init() {
374
+ this.domElement.addEventListener("mousedown", this._onMouseDown), this.domElement.addEventListener("mousemove", this._onMouseMove), this.domElement.addEventListener("mouseup", this._onMouseUp), this.domElement.addEventListener("mouseleave", this._onMouseUp), this.domElement.addEventListener("wheel", this._onWheel, { passive: !1 }), this.domElement.addEventListener("touchstart", this._onTouchStart, { passive: !1 }), this.domElement.addEventListener("touchmove", this._onTouchMove, { passive: !1 }), this.domElement.addEventListener("touchend", this._onTouchEnd);
375
+ }
376
+ /**
377
+ * 鼠标按下
378
+ */
379
+ onMouseDown(e) {
380
+ e.button === 0 && (this.isDragging = !0, this.startX = e.clientX, this.startY = e.clientY);
381
+ }
382
+ /**
383
+ * 鼠标移动
384
+ */
385
+ onMouseMove(e) {
386
+ if (!this.isDragging || !this.enableRotate) return;
387
+ const t = e.clientX - this.startX, n = e.clientY - this.startY, { horizontal: o, vertical: a } = this.getRotationFactors();
388
+ this.thetaDelta += t * o * this.rotateSpeed * 0.06, this.phiDelta += n * a * this.rotateSpeed * 0.06, this.startX = e.clientX, this.startY = e.clientY, this.applyRotation();
389
+ }
390
+ /**
391
+ * 鼠标弹起
392
+ */
393
+ onMouseUp() {
394
+ this.isDragging = !1;
395
+ }
396
+ /**
397
+ * 鼠标滚轮
398
+ */
399
+ onWheel(e) {
400
+ if (!this.enableZoom) return;
401
+ e.preventDefault();
402
+ const n = (e.deltaY > 0 ? 1 : -1) * this.zoomSpeed * 2;
403
+ this.applyZoom(n);
404
+ }
405
+ /**
406
+ * 触摸开始
407
+ */
408
+ onTouchStart(e) {
409
+ e.touches.length === 1 ? (this.isDragging = !0, this.isPinching = !1, this.startX = e.touches[0].clientX, this.startY = e.touches[0].clientY) : e.touches.length === 2 && (e.preventDefault(), this.isDragging = !1, this.isPinching = !0, this.pinchStartDistance = this.getTouchDistance(e.touches), this.pinchStartFov = this.cameraModule.getFov());
410
+ }
411
+ /**
412
+ * 触摸移动
413
+ */
414
+ onTouchMove(e) {
415
+ if (e.touches.length === 1 && this.isDragging && this.enableRotate) {
416
+ const t = e.touches[0].clientX - this.startX, n = e.touches[0].clientY - this.startY, { horizontal: o, vertical: a } = this.getRotationFactors();
417
+ this.thetaDelta -= t * o * this.rotateSpeed, this.phiDelta -= n * a * this.rotateSpeed, this.startX = e.touches[0].clientX, this.startY = e.touches[0].clientY, this.applyRotation();
418
+ } else if (e.touches.length === 2 && this.enableZoom) {
419
+ e.preventDefault(), this.isPinching || (this.isPinching = !0, this.pinchStartDistance = this.getTouchDistance(e.touches), this.pinchStartFov = this.cameraModule.getFov());
420
+ const t = this.getTouchDistance(e.touches);
421
+ if (this.pinchStartDistance && t > 0) {
422
+ const n = this.pinchStartDistance / t, o = v.clamp(
423
+ this.pinchStartFov * n,
424
+ this.minFov,
425
+ this.maxFov
426
+ );
427
+ this.cameraModule.setFov(o);
428
+ }
429
+ }
430
+ }
431
+ /**
432
+ * 触摸结束
433
+ */
434
+ onTouchEnd(e) {
435
+ e.touches && e.touches.length > 0 ? this.isDragging = e.touches.length === 1 : this.isDragging = !1, e.touches.length < 2 && (this.isPinching = !1, this.pinchStartDistance = null);
436
+ }
437
+ /**
438
+ * 应用旋转
439
+ */
440
+ applyRotation() {
441
+ if (this.cameraModule.updatePose(this.thetaDelta, this.phiDelta), this.enableDamping ? (this.thetaDelta *= 1 - this.dampingFactor, this.phiDelta *= 1 - this.dampingFactor) : (this.thetaDelta = 0, this.phiDelta = 0), this.onViewAngleChange) {
442
+ const e = this.cameraModule.getCameraPose();
443
+ this.onViewAngleChange(e.theta, e.phi);
444
+ }
445
+ }
446
+ /**
447
+ * 根据容器尺寸计算旋转灵敏度
448
+ */
449
+ getRotationFactors() {
450
+ const e = Math.max(1, this.domElement.clientWidth), t = Math.max(1, this.domElement.clientHeight);
451
+ return {
452
+ horizontal: Math.PI * 2 / e,
453
+ vertical: Math.PI / t
454
+ };
455
+ }
456
+ /**
457
+ * 统一缩放逻辑
458
+ */
459
+ applyZoom(e) {
460
+ const t = this.cameraModule.getFov(), n = v.clamp(
461
+ t + e,
462
+ this.minFov,
463
+ this.maxFov
464
+ );
465
+ this.cameraModule.setFov(n);
466
+ }
467
+ /**
468
+ * 计算多指距离
469
+ */
470
+ getTouchDistance(e) {
471
+ if (e.length < 2)
472
+ return 0;
473
+ const t = e[0].clientX - e[1].clientX, n = e[0].clientY - e[1].clientY;
474
+ return Math.sqrt(t * t + n * n);
475
+ }
476
+ /**
477
+ * 每帧更新(用于阻尼效果)
478
+ */
479
+ update() {
480
+ this.enableDamping && (Math.abs(this.thetaDelta) > 1e-4 || Math.abs(this.phiDelta) > 1e-4) && this.applyRotation();
481
+ }
482
+ /**
483
+ * 设置相机朝向(通过经纬度)
484
+ * @param theta 水平角度(弧度)
485
+ * @param phi 垂直角度(弧度)
486
+ */
487
+ lookAt(e, t) {
488
+ this.thetaDelta = e, this.phiDelta = Math.max(
489
+ -Math.PI / 2 + 0.01,
490
+ Math.min(Math.PI / 2 - 0.01, t)
491
+ ), this.cameraModule.setCameraPose(e, t), this.thetaDelta = 0, this.phiDelta = 0;
492
+ }
493
+ /**
494
+ * 获取当前视角
495
+ */
496
+ getViewAngle() {
497
+ return this.cameraModule.getCameraPose();
498
+ }
499
+ /**
500
+ * 设置视角变化回调
501
+ */
502
+ setOnViewAngleChange(e) {
503
+ this.onViewAngleChange = e;
504
+ }
505
+ /**
506
+ * 重置控制器
507
+ */
508
+ reset() {
509
+ this.thetaDelta = 0, this.phiDelta = 0, this.lookAt(0, 0);
510
+ }
511
+ /**
512
+ * 更新旋转灵敏度
513
+ */
514
+ setRotateSpeed(e) {
515
+ this.rotateSpeed = Math.max(0.05, e);
516
+ }
517
+ /**
518
+ * 更新缩放灵敏度
519
+ */
520
+ setZoomSpeed(e) {
521
+ this.zoomSpeed = Math.max(0.1, e);
522
+ }
523
+ /**
524
+ * 更新阻尼配置
525
+ */
526
+ setEnableDamping(e) {
527
+ this.enableDamping = e;
528
+ }
529
+ setDampingFactor(e) {
530
+ this.dampingFactor = Math.max(0, Math.min(1, e));
531
+ }
532
+ /**
533
+ * 更新 FOV 限制
534
+ */
535
+ setZoomLimits(e, t) {
536
+ this.minFov = e, this.maxFov = t;
537
+ }
538
+ /**
539
+ * 销毁控制器
540
+ */
541
+ dispose() {
542
+ this.domElement.removeEventListener("mousedown", this._onMouseDown), this.domElement.removeEventListener("mousemove", this._onMouseMove), this.domElement.removeEventListener("mouseup", this._onMouseUp), this.domElement.removeEventListener("mouseleave", this._onMouseUp), this.domElement.removeEventListener("wheel", this._onWheel), this.domElement.removeEventListener("touchstart", this._onTouchStart), this.domElement.removeEventListener("touchmove", this._onTouchMove), this.domElement.removeEventListener("touchend", this._onTouchEnd), this.onViewAngleChange = void 0;
543
+ }
544
+ }
545
+ class U {
546
+ controls;
547
+ domElement;
548
+ cameraModule;
549
+ constructor(e, t) {
550
+ this.cameraModule = e, this.domElement = t, this.controls = new H(e, t);
551
+ }
552
+ /**
553
+ * 初始化控制模块
554
+ */
555
+ init() {
556
+ }
557
+ /**
558
+ * 激活控制器
559
+ */
560
+ active() {
561
+ }
562
+ /**
563
+ * 禁用控制器
564
+ */
565
+ disActive() {
566
+ this.controls.enableRotate = !1, this.controls.enableZoom = !1;
567
+ }
568
+ /**
569
+ * 重新启用控制器
570
+ */
571
+ enable() {
572
+ this.controls.enableRotate = !0, this.controls.enableZoom = !0;
573
+ }
574
+ /**
575
+ * 更新控制器(每帧调用)
576
+ */
577
+ update() {
578
+ this.controls.update();
579
+ }
580
+ /**
581
+ * 设置视角
582
+ */
583
+ lookAt(e, t) {
584
+ this.controls.lookAt(e, t);
585
+ }
586
+ /**
587
+ * 获取当前视角
588
+ */
589
+ getViewAngle() {
590
+ return this.controls.getViewAngle();
591
+ }
592
+ /**
593
+ * 设置视角变化回调
594
+ */
595
+ setOnViewAngleChange(e) {
596
+ this.controls.setOnViewAngleChange(e);
597
+ }
598
+ /**
599
+ * 重置控制器
600
+ */
601
+ reset() {
602
+ this.controls.reset();
603
+ }
604
+ /**
605
+ * 获取控制器对象
606
+ */
607
+ getControls() {
608
+ return this.controls;
609
+ }
610
+ /**
611
+ * 动态更新交互参数
612
+ */
613
+ updateInteractionOptions(e) {
614
+ if (e.rotateSpeed !== void 0 && this.controls.setRotateSpeed(e.rotateSpeed), e.zoomSpeed !== void 0 && this.controls.setZoomSpeed(e.zoomSpeed), e.enableDamping !== void 0 && this.controls.setEnableDamping(e.enableDamping), e.dampingFactor !== void 0 && this.controls.setDampingFactor(e.dampingFactor), e.enableRotate !== void 0 && (this.controls.enableRotate = e.enableRotate), e.enableZoom !== void 0 && (this.controls.enableZoom = e.enableZoom), e.minFov !== void 0 || e.maxFov !== void 0) {
615
+ const t = e.minFov ?? this.controls.minFov, n = e.maxFov ?? this.controls.maxFov;
616
+ this.controls.setZoomLimits(t, n);
617
+ }
618
+ }
619
+ /**
620
+ * 销毁控制模块
621
+ */
622
+ dispose() {
623
+ this.controls.dispose();
624
+ }
625
+ }
626
+ class X {
627
+ cache = /* @__PURE__ */ new Map();
628
+ loadingPromises = /* @__PURE__ */ new Map();
629
+ /**
630
+ * 加载纹理(带缓存)
631
+ * @param url 纹理 URL
632
+ * @returns 纹理对象 Promise
633
+ */
634
+ loadTexture(e) {
635
+ if (this.cache.has(e))
636
+ return Promise.resolve(this.cache.get(e));
637
+ if (this.loadingPromises.has(e))
638
+ return this.loadingPromises.get(e);
639
+ const t = new Promise((n, o) => {
640
+ new f().load(
641
+ e,
642
+ (s) => {
643
+ s.colorSpace = P, s.minFilter = m, s.magFilter = m, this.cache.set(e, s), this.loadingPromises.delete(e), n(s);
644
+ },
645
+ void 0,
646
+ (s) => {
647
+ this.loadingPromises.delete(e), o(s);
648
+ }
649
+ );
650
+ });
651
+ return this.loadingPromises.set(e, t), t;
652
+ }
653
+ /**
654
+ * 预加载多个纹理
655
+ * @param urls 纹理 URL 数组
656
+ * @returns Promise,所有纹理加载完成后 resolve
657
+ */
658
+ async preloadTextures(e) {
659
+ const t = e.map((n) => this.loadTexture(n));
660
+ return Promise.all(t);
661
+ }
662
+ /**
663
+ * 检查纹理是否已缓存
664
+ * @param url 纹理 URL
665
+ */
666
+ has(e) {
667
+ return this.cache.has(e);
668
+ }
669
+ /**
670
+ * 获取缓存的纹理
671
+ * @param url 纹理 URL
672
+ * @returns 纹理对象,如果不存在则返回 undefined
673
+ */
674
+ get(e) {
675
+ return this.cache.get(e);
676
+ }
677
+ /**
678
+ * 清除特定纹理的缓存
679
+ * @param url 纹理 URL
680
+ */
681
+ remove(e) {
682
+ const t = this.cache.get(e);
683
+ t && (t.dispose(), this.cache.delete(e));
684
+ }
685
+ /**
686
+ * 清空所有缓存
687
+ */
688
+ clear() {
689
+ this.cache.forEach((e) => e.dispose()), this.cache.clear(), this.loadingPromises.clear();
690
+ }
691
+ /**
692
+ * 获取缓存大小
693
+ */
694
+ size() {
695
+ return this.cache.size;
696
+ }
697
+ /**
698
+ * 销毁缓存
699
+ */
700
+ dispose() {
701
+ this.clear();
702
+ }
703
+ }
704
+ class Z {
705
+ textureCache;
706
+ currentUrl = null;
707
+ loadProgress = 0;
708
+ onProgressCallback;
709
+ constructor() {
710
+ this.textureCache = new X();
711
+ }
712
+ /**
713
+ * 加载全景图纹理
714
+ * @param url 全景图 URL
715
+ * @returns 纹理对象 Promise
716
+ */
717
+ async loadTexture(e) {
718
+ this.currentUrl = e, this.loadProgress = 0, this.onProgressCallback && this.onProgressCallback(0);
719
+ try {
720
+ const t = await this.loadTextureInternal(e);
721
+ return this.loadProgress = 100, this.onProgressCallback && this.onProgressCallback(100), t;
722
+ } catch (t) {
723
+ throw console.error("PanoramaLoader: Failed to load texture:", t), t;
724
+ }
725
+ }
726
+ /**
727
+ * 内部加载方法(支持进度)
728
+ */
729
+ loadTextureInternal(e) {
730
+ return new Promise((t, n) => {
731
+ if (this.textureCache.has(e)) {
732
+ const a = this.textureCache.get(e);
733
+ if (a) {
734
+ t(a);
735
+ return;
736
+ }
737
+ }
738
+ new f().load(
739
+ e,
740
+ (a) => {
741
+ a.colorSpace = P, a.minFilter = m, a.magFilter = m, this.textureCache.cache.set(e, a), t(a);
742
+ },
743
+ (a) => {
744
+ a.total > 0 && (this.loadProgress = a.loaded / a.total * 100, this.onProgressCallback && this.onProgressCallback(this.loadProgress));
745
+ },
746
+ (a) => {
747
+ n(a);
748
+ }
749
+ );
750
+ });
751
+ }
752
+ /**
753
+ * 预加载多个全景图
754
+ * @param urls 全景图 URL 数组
755
+ * @returns Promise,所有纹理加载完成后 resolve
756
+ */
757
+ async preloadPanoramas(e) {
758
+ try {
759
+ await this.textureCache.preloadTextures(e);
760
+ } catch (t) {
761
+ throw console.error("PanoramaLoader: Failed to preload panoramas:", t), t;
762
+ }
763
+ }
764
+ /**
765
+ * 获取当前加载进度
766
+ * @returns 进度百分比(0-100)
767
+ */
768
+ getLoadProgress() {
769
+ return this.loadProgress;
770
+ }
771
+ /**
772
+ * 设置进度回调
773
+ * @param callback 进度回调函数
774
+ */
775
+ setOnProgress(e) {
776
+ this.onProgressCallback = e;
777
+ }
778
+ /**
779
+ * 获取当前加载的 URL
780
+ */
781
+ getCurrentUrl() {
782
+ return this.currentUrl;
783
+ }
784
+ /**
785
+ * 检查纹理是否已缓存
786
+ * @param url 纹理 URL
787
+ */
788
+ isCached(e) {
789
+ return this.textureCache.has(e);
790
+ }
791
+ /**
792
+ * 获取缓存的纹理
793
+ * @param url 纹理 URL
794
+ */
795
+ getCachedTexture(e) {
796
+ return this.textureCache.get(e);
797
+ }
798
+ /**
799
+ * 清除纹理缓存
800
+ */
801
+ clearCache() {
802
+ this.textureCache.clear();
803
+ }
804
+ /**
805
+ * 移除特定纹理缓存
806
+ * @param url 纹理 URL
807
+ */
808
+ removeCachedTexture(e) {
809
+ this.textureCache.remove(e);
810
+ }
811
+ /**
812
+ * 销毁加载器
813
+ */
814
+ dispose() {
815
+ this.textureCache.dispose(), this.onProgressCallback = void 0, this.currentUrl = null;
816
+ }
817
+ }
818
+ class C {
819
+ /**
820
+ * 球面坐标转笛卡尔坐标
821
+ * @param position 球面坐标 { theta, phi, radius }
822
+ * @returns 笛卡尔坐标 { x, y, z }
823
+ */
824
+ static sphericalToCartesian(e) {
825
+ const t = e.radius || 500, { theta: n, phi: o } = e, a = t * Math.cos(o) * Math.cos(n), s = t * Math.sin(o), h = t * Math.cos(o) * Math.sin(n);
826
+ return { x: a, y: s, z: h };
827
+ }
828
+ /**
829
+ * 笛卡尔坐标转球面坐标
830
+ * @param x X 坐标
831
+ * @param y Y 坐标
832
+ * @param z Z 坐标
833
+ * @returns 球面坐标 { theta, phi, radius }
834
+ */
835
+ static cartesianToSpherical(e, t, n) {
836
+ const o = Math.sqrt(e * e + t * t + n * n), a = Math.asin(t / o);
837
+ let s = Math.atan2(n, e);
838
+ return s < 0 && (s += 2 * Math.PI), { theta: s, phi: a, radius: o };
839
+ }
840
+ /**
841
+ * 规范化角度到指定范围
842
+ * @param angle 角度(弧度)
843
+ * @param min 最小值(默认 -π)
844
+ * @param max 最大值(默认 π)
845
+ * @returns 规范化后的角度
846
+ */
847
+ static normalizeAngle(e, t = -Math.PI, n = Math.PI) {
848
+ const o = n - t;
849
+ let a = e;
850
+ for (; a < t; )
851
+ a += o;
852
+ for (; a > n; )
853
+ a -= o;
854
+ return a;
855
+ }
856
+ /**
857
+ * 限制 phi 角度在有效范围内(避免翻转)
858
+ * @param phi 垂直角度(弧度)
859
+ * @param minPhi 最小值(默认 -π/2 + 0.01)
860
+ * @param maxPhi 最大值(默认 π/2 - 0.01)
861
+ * @returns 限制后的角度
862
+ */
863
+ static clampPhi(e, t = -Math.PI / 2 + 0.01, n = Math.PI / 2 - 0.01) {
864
+ return Math.max(t, Math.min(n, e));
865
+ }
866
+ /**
867
+ * 计算两个球面坐标之间的角度距离
868
+ * @param pos1 第一个球面坐标
869
+ * @param pos2 第二个球面坐标
870
+ * @returns 角度距离(弧度)
871
+ */
872
+ static sphericalDistance(e, t) {
873
+ const n = Math.sin(e.phi) * Math.sin(t.phi) + Math.cos(e.phi) * Math.cos(t.phi) * Math.cos(e.theta - t.theta);
874
+ return Math.acos(Math.max(-1, Math.min(1, n)));
875
+ }
876
+ /**
877
+ * 线性插值两个球面坐标
878
+ * @param pos1 起始球面坐标
879
+ * @param pos2 目标球面坐标
880
+ * @param t 插值因子(0-1)
881
+ * @returns 插值后的球面坐标
882
+ */
883
+ static lerpSpherical(e, t, n) {
884
+ let o = t.theta - e.theta;
885
+ o > Math.PI ? o -= 2 * Math.PI : o < -Math.PI && (o += 2 * Math.PI);
886
+ const a = e.theta + o * n, s = e.phi + (t.phi - e.phi) * n, h = e.radius || 500, l = t.radius || 500, c = h + (l - h) * n;
887
+ return { theta: a, phi: s, radius: c };
888
+ }
889
+ }
890
+ var d = /* @__PURE__ */ ((i) => (i.ICON = "icon", i.TEXT = "text", i.LINK = "link", i))(d || {});
891
+ class W {
892
+ annotations = /* @__PURE__ */ new Map();
893
+ container;
894
+ annotationContainer;
895
+ worldToScreen;
896
+ sphereRadius;
897
+ camera;
898
+ renderer;
899
+ // 事件监听器
900
+ onClickAnnotation;
901
+ onHoverAnnotation;
902
+ constructor(e, t, n, o) {
903
+ this.container = e, this.camera = t, this.renderer = n, this.sphereRadius = o, this.worldToScreen = new w(t, n), this.createAnnotationContainer();
904
+ }
905
+ /**
906
+ * 创建标注容器
907
+ */
908
+ createAnnotationContainer() {
909
+ this.annotationContainer = document.createElement("div"), this.annotationContainer.className = "panorama-annotation-container", this.annotationContainer.style.cssText = `
910
+ position: absolute;
911
+ top: 0;
912
+ left: 0;
913
+ width: 100%;
914
+ height: 100%;
915
+ pointer-events: none;
916
+ z-index: 1000;
917
+ overflow: hidden;
918
+ `, this.container.appendChild(this.annotationContainer);
919
+ }
920
+ /**
921
+ * 添加标注
922
+ * @param config 标注配置
923
+ * @returns 标注 ID
924
+ */
925
+ addAnnotation(e) {
926
+ const t = this.generateId(), n = this.sphericalToCartesian(e.position), o = this.createAnnotationElement(e, t), a = {
927
+ id: t,
928
+ config: e,
929
+ element: o,
930
+ worldPosition: n,
931
+ screenPosition: { x: 0, y: 0 },
932
+ visible: !1
933
+ };
934
+ return this.annotationContainer.appendChild(o), this.annotations.set(t, a), this.updateAnnotationPosition(a), t;
935
+ }
936
+ /**
937
+ * 批量添加标注
938
+ * @param configs 标注配置数组
939
+ * @returns 标注 ID 数组
940
+ */
941
+ addAnnotations(e) {
942
+ return e.map((t) => this.addAnnotation(t));
943
+ }
944
+ /**
945
+ * 创建标注 DOM 元素
946
+ */
947
+ createAnnotationElement(e, t) {
948
+ const n = document.createElement("div");
949
+ switch (n.className = `panorama-annotation panorama-annotation-${e.type}`, n.dataset.annotationId = t, n.style.cssText = `
950
+ position: absolute;
951
+ pointer-events: auto;
952
+ cursor: pointer;
953
+ transform: translate(-50%, -50%);
954
+ transition: opacity 0.2s ease;
955
+ `, e.type) {
956
+ case d.ICON:
957
+ this.createIconAnnotation(n, e);
958
+ break;
959
+ case d.TEXT:
960
+ this.createTextAnnotation(n, e);
961
+ break;
962
+ case d.LINK:
963
+ this.createLinkAnnotation(n, e);
964
+ break;
965
+ }
966
+ return this.attachAnnotationEvents(n, t), n;
967
+ }
968
+ /**
969
+ * 创建图标标注
970
+ */
971
+ createIconAnnotation(e, t) {
972
+ const n = t.size || { width: 40, height: 40 };
973
+ e.style.width = `${n.width}px`, e.style.height = `${n.height}px`;
974
+ const o = document.createElement("img");
975
+ o.src = t.iconUrl, o.style.cssText = `
976
+ width: 100%;
977
+ height: 100%;
978
+ object-fit: contain;
979
+ display: block;
980
+ `, o.onerror = () => {
981
+ console.error(`Failed to load annotation icon: ${t.iconUrl}`);
982
+ }, e.appendChild(o), t.tooltip && (e.title = t.tooltip);
983
+ }
984
+ /**
985
+ * 创建文字标注
986
+ */
987
+ createTextAnnotation(e, t) {
988
+ const n = t.style || {};
989
+ e.style.cssText += `
990
+ padding: ${n.padding || "8px 12px"};
991
+ background-color: ${n.backgroundColor || "rgba(0, 0, 0, 0.7)"};
992
+ color: ${n.color || "#ffffff"};
993
+ font-size: ${n.fontSize || 14}px';
994
+ border-radius: 4px;
995
+ white-space: nowrap;
996
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
997
+ `, e.textContent = t.text, t.tooltip && (e.title = t.tooltip);
998
+ }
999
+ /**
1000
+ * 创建超链接标注
1001
+ */
1002
+ createLinkAnnotation(e, t) {
1003
+ const n = t.style || {};
1004
+ if (e.style.cssText += `
1005
+ padding: 8px 12px;
1006
+ background-color: rgba(59, 130, 246, 0.9);
1007
+ color: ${n.color || "#ffffff"};
1008
+ font-size: ${n.fontSize || 14}px';
1009
+ border-radius: 4px;
1010
+ text-decoration: ${n.textDecoration || "none"};
1011
+ display: flex;
1012
+ align-items: center;
1013
+ gap: 8px;
1014
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1015
+ transition: background-color 0.2s ease;
1016
+ `, e.addEventListener("mouseenter", () => {
1017
+ e.style.backgroundColor = "rgba(37, 99, 235, 0.95)";
1018
+ }), e.addEventListener("mouseleave", () => {
1019
+ e.style.backgroundColor = "rgba(59, 130, 246, 0.9)";
1020
+ }), t.iconUrl) {
1021
+ const a = document.createElement("img");
1022
+ a.src = t.iconUrl, a.style.cssText = `
1023
+ width: 16px;
1024
+ height: 16px;
1025
+ display: block;
1026
+ `, e.appendChild(a);
1027
+ }
1028
+ const o = document.createElement("span");
1029
+ o.textContent = t.text || t.url, e.appendChild(o), t.tooltip && (e.title = t.tooltip);
1030
+ }
1031
+ /**
1032
+ * 附加标注事件
1033
+ */
1034
+ attachAnnotationEvents(e, t) {
1035
+ e.addEventListener("click", (n) => {
1036
+ n.stopPropagation();
1037
+ const o = this.annotations.get(t);
1038
+ o && this.handleClickAnnotation(o);
1039
+ }), e.addEventListener("mouseenter", () => {
1040
+ const n = this.annotations.get(t);
1041
+ n && this.handleHoverAnnotation(n);
1042
+ }), e.addEventListener("mouseleave", () => {
1043
+ this.onHoverAnnotation && this.onHoverAnnotation(null);
1044
+ });
1045
+ }
1046
+ /**
1047
+ * 球面坐标转笛卡尔坐标
1048
+ */
1049
+ sphericalToCartesian(e) {
1050
+ const t = e.radius ?? this.sphereRadius;
1051
+ return C.sphericalToCartesian({
1052
+ ...e,
1053
+ radius: t
1054
+ });
1055
+ }
1056
+ /**
1057
+ * 更新标注位置(单个标注)
1058
+ */
1059
+ updateAnnotationPosition(e) {
1060
+ const t = new p(
1061
+ e.worldPosition.x,
1062
+ e.worldPosition.y,
1063
+ e.worldPosition.z
1064
+ ), n = this.worldToScreen.worldToScreen(t);
1065
+ e.screenPosition = { x: n.x, y: n.y };
1066
+ const o = this.isPositionInFront(t), a = this.isPositionInView(n);
1067
+ e.visible = o && a, e.visible ? (e.element.style.display = "block", e.element.style.left = `${n.x}px`, e.element.style.top = `${n.y}px`) : e.element.style.display = "none";
1068
+ }
1069
+ /**
1070
+ * 检查点是否在相机前方
1071
+ */
1072
+ isPositionInFront(e) {
1073
+ const t = new p();
1074
+ return this.camera.getWorldDirection(t), e.clone().normalize().dot(t) > 0;
1075
+ }
1076
+ /**
1077
+ * 检查点是否在视野内
1078
+ */
1079
+ isPositionInView(e) {
1080
+ const t = this.renderer.domElement.clientWidth, n = this.renderer.domElement.clientHeight;
1081
+ return e.x >= 0 && e.x <= t && e.y >= 0 && e.y <= n;
1082
+ }
1083
+ /**
1084
+ * 更新所有标注(每帧调用)
1085
+ */
1086
+ update() {
1087
+ this.annotations.forEach((e) => {
1088
+ this.updateAnnotationPosition(e);
1089
+ });
1090
+ }
1091
+ /**
1092
+ * 处理标注点击
1093
+ */
1094
+ handleClickAnnotation(e) {
1095
+ if (e.config.type === d.LINK) {
1096
+ const t = e.config;
1097
+ window.open(t.url, "_blank");
1098
+ }
1099
+ this.onClickAnnotation && this.onClickAnnotation(e);
1100
+ }
1101
+ /**
1102
+ * 处理标注悬停
1103
+ */
1104
+ handleHoverAnnotation(e) {
1105
+ this.onHoverAnnotation && this.onHoverAnnotation(e);
1106
+ }
1107
+ /**
1108
+ * 设置点击回调
1109
+ */
1110
+ setOnClickAnnotation(e) {
1111
+ this.onClickAnnotation = e;
1112
+ }
1113
+ /**
1114
+ * 设置悬停回调
1115
+ */
1116
+ setOnHoverAnnotation(e) {
1117
+ this.onHoverAnnotation = e;
1118
+ }
1119
+ /**
1120
+ * 移除标注
1121
+ * @param id 标注 ID
1122
+ */
1123
+ removeAnnotation(e) {
1124
+ const t = this.annotations.get(e);
1125
+ t && (t.element.remove(), this.annotations.delete(e));
1126
+ }
1127
+ /**
1128
+ * 清空所有标注
1129
+ */
1130
+ clearAll() {
1131
+ this.annotations.forEach((e) => {
1132
+ e.element.remove();
1133
+ }), this.annotations.clear();
1134
+ }
1135
+ /**
1136
+ * 获取所有标注
1137
+ */
1138
+ getAnnotations() {
1139
+ return Array.from(this.annotations.values());
1140
+ }
1141
+ /**
1142
+ * 获取标注
1143
+ * @param id 标注 ID
1144
+ */
1145
+ getAnnotation(e) {
1146
+ return this.annotations.get(e);
1147
+ }
1148
+ /**
1149
+ * 更新相机引用(当相机发生变化时调用)
1150
+ */
1151
+ updateCamera(e) {
1152
+ this.camera = e, this.worldToScreen.updateCamera(e);
1153
+ }
1154
+ /**
1155
+ * 销毁管理器
1156
+ */
1157
+ dispose() {
1158
+ this.clearAll(), this.annotationContainer.remove(), this.onClickAnnotation = void 0, this.onHoverAnnotation = void 0;
1159
+ }
1160
+ /**
1161
+ * 生成唯一 ID
1162
+ */
1163
+ generateId() {
1164
+ return `annotation-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1165
+ }
1166
+ }
1167
+ class Y {
1168
+ listeners = /* @__PURE__ */ new Map();
1169
+ /**
1170
+ * 订阅事件
1171
+ * @param event 事件类型(字符串或枚举)
1172
+ * @param callback 回调函数
1173
+ */
1174
+ on(e, t) {
1175
+ const n = String(e);
1176
+ this.listeners.has(n) || this.listeners.set(n, []), this.listeners.get(n).push(t);
1177
+ }
1178
+ /**
1179
+ * 取消订阅事件
1180
+ * @param event 事件类型
1181
+ * @param callback 要移除的回调函数
1182
+ */
1183
+ off(e, t) {
1184
+ const n = String(e), o = this.listeners.get(n);
1185
+ if (o) {
1186
+ const a = o.indexOf(t);
1187
+ a !== -1 && o.splice(a, 1), o.length === 0 && this.listeners.delete(n);
1188
+ }
1189
+ }
1190
+ /**
1191
+ * 触发事件
1192
+ * @param event 事件类型
1193
+ * @param data 事件数据
1194
+ */
1195
+ trigger(e, t) {
1196
+ const n = String(e), o = this.listeners.get(n);
1197
+ if (o && o.length > 0) {
1198
+ const a = [...o];
1199
+ for (const s of a)
1200
+ try {
1201
+ s(t);
1202
+ } catch (h) {
1203
+ console.error(`EventModule: Error in event handler for "${n}":`, h);
1204
+ }
1205
+ }
1206
+ }
1207
+ /**
1208
+ * 移除某个事件的所有监听器
1209
+ * @param event 事件类型
1210
+ */
1211
+ removeAllListeners(e) {
1212
+ const t = String(e);
1213
+ this.listeners.delete(t);
1214
+ }
1215
+ /**
1216
+ * 清空所有事件监听器
1217
+ */
1218
+ clear() {
1219
+ this.listeners.clear();
1220
+ }
1221
+ /**
1222
+ * 获取某个事件的监听器数量
1223
+ * @param event 事件类型
1224
+ */
1225
+ listenerCount(e) {
1226
+ const t = String(e), n = this.listeners.get(t);
1227
+ return n ? n.length : 0;
1228
+ }
1229
+ /**
1230
+ * 销毁事件模块
1231
+ */
1232
+ dispose() {
1233
+ this.clear();
1234
+ }
1235
+ }
1236
+ var r = /* @__PURE__ */ ((i) => (i.PanoramaLoadStart = "panorama-load-start", i.PanoramaLoadProgress = "panorama-load-progress", i.PanoramaLoaded = "panorama-loaded", i.PanoramaLoadError = "panorama-load-error", i.AnnotationClick = "annotation-click", i.AnnotationHover = "annotation-hover", i.AnnotationAdd = "annotation-add", i.AnnotationRemove = "annotation-remove", i.ViewAngleChanged = "view-angle-changed", i))(r || {});
1237
+ class N {
1238
+ options;
1239
+ // 核心模块(使用 definite assignment assertions)
1240
+ sceneModule;
1241
+ cameraModule;
1242
+ renderModule;
1243
+ controlModule;
1244
+ eventModule;
1245
+ // 功能管理器
1246
+ panoramaLoader;
1247
+ annotationManager;
1248
+ worldToScreen;
1249
+ // 内部状态
1250
+ isInitialized = !1;
1251
+ version = T;
1252
+ localPanoramaResource = null;
1253
+ constructor(e) {
1254
+ this.options = e, this.initialize(e);
1255
+ }
1256
+ /**
1257
+ * 初始化引擎
1258
+ */
1259
+ initialize(e) {
1260
+ const t = {
1261
+ fov: 75,
1262
+ enableZoom: !0,
1263
+ enableRotate: !0,
1264
+ minFov: 30,
1265
+ maxFov: 90,
1266
+ sphereRadius: 500,
1267
+ rotateSpeed: 0.35,
1268
+ zoomSpeed: 0.8,
1269
+ enableDamping: !0,
1270
+ dampingFactor: 0.08,
1271
+ ...e
1272
+ }, n = document.getElementById(t.containerId);
1273
+ if (!n)
1274
+ throw new Error(`Container with id "${t.containerId}" not found`);
1275
+ try {
1276
+ this.eventModule = new Y(), this.sceneModule = new V(t.sphereRadius), this.cameraModule = new K(
1277
+ n,
1278
+ t.fov,
1279
+ t.minFov,
1280
+ t.maxFov
1281
+ ), this.renderModule = new O(n, !0), this.controlModule = new U(
1282
+ this.cameraModule,
1283
+ this.renderModule.getDomElement()
1284
+ );
1285
+ const o = this.controlModule.getControls();
1286
+ o.enableZoom = t.enableZoom, o.enableRotate = t.enableRotate, o.setRotateSpeed(t.rotateSpeed), o.setZoomSpeed(t.zoomSpeed), o.setEnableDamping(t.enableDamping), o.setDampingFactor(t.dampingFactor), o.setZoomLimits(t.minFov, t.maxFov), o.setOnViewAngleChange((a, s) => {
1287
+ this.eventModule.trigger(r.ViewAngleChanged, { theta: a, phi: s });
1288
+ }), this.panoramaLoader = new Z(), this.worldToScreen = new w(
1289
+ this.cameraModule.getCamera(),
1290
+ this.renderModule.getRenderer(),
1291
+ this.sceneModule.getScene()
1292
+ ), this.annotationManager = new W(
1293
+ n,
1294
+ this.cameraModule.getCamera(),
1295
+ this.renderModule.getRenderer(),
1296
+ t.sphereRadius
1297
+ ), this.annotationManager.setOnClickAnnotation((a) => {
1298
+ this.eventModule.trigger(r.AnnotationClick, {
1299
+ id: a.id,
1300
+ config: a.config,
1301
+ metadata: a.config.metadata
1302
+ });
1303
+ }), this.annotationManager.setOnHoverAnnotation((a) => {
1304
+ this.eventModule.trigger(r.AnnotationHover, {
1305
+ annotation: a ? {
1306
+ id: a.id,
1307
+ config: a.config,
1308
+ metadata: a.config.metadata
1309
+ } : null
1310
+ });
1311
+ }), this.setupRenderLoop(), this.isInitialized = !0, console.log("EngineKernel720: Initialized successfully");
1312
+ } catch (o) {
1313
+ throw console.error("EngineKernel720: Initialization failed", o), o;
1314
+ }
1315
+ }
1316
+ /**
1317
+ * 设置渲染循环
1318
+ */
1319
+ setupRenderLoop() {
1320
+ this.renderModule.setOnBeforeRender(() => {
1321
+ this.controlModule.update(), this.annotationManager.update();
1322
+ }), this.renderModule.startRenderLoop(
1323
+ this.sceneModule.getScene(),
1324
+ this.cameraModule.getCamera()
1325
+ );
1326
+ }
1327
+ // --- 全景图加载 ---
1328
+ /**
1329
+ * 加载全景图
1330
+ * @param imageUrl 全景图 URL(png, jpg)
1331
+ */
1332
+ async loadPanorama(e) {
1333
+ this.checkInitialized(), this.eventModule.trigger(r.PanoramaLoadStart, { url: e });
1334
+ try {
1335
+ this.panoramaLoader.setOnProgress((n) => {
1336
+ this.eventModule.trigger(r.PanoramaLoadProgress, {
1337
+ url: e,
1338
+ progress: n
1339
+ });
1340
+ });
1341
+ const t = await this.panoramaLoader.loadTexture(e);
1342
+ this.sceneModule.updatePanoramaTexture(t), this.eventModule.trigger(r.PanoramaLoaded, {
1343
+ url: e,
1344
+ progress: 100
1345
+ }), console.log("EngineKernel720: Panorama loaded successfully");
1346
+ } catch (t) {
1347
+ throw console.error("EngineKernel720: Failed to load panorama", t), this.eventModule.trigger(r.PanoramaLoadError, {
1348
+ url: e,
1349
+ error: t
1350
+ }), t;
1351
+ }
1352
+ }
1353
+ async loadPanoramaFile(e) {
1354
+ this.checkInitialized(), this.revokeLocalPanoramaResource();
1355
+ const t = z(e);
1356
+ this.localPanoramaResource = t;
1357
+ try {
1358
+ await this.loadPanorama(t.url);
1359
+ } catch (n) {
1360
+ throw this.revokeLocalPanoramaResource(), n;
1361
+ }
1362
+ }
1363
+ async loadModelFile(e) {
1364
+ if (e.length === 0)
1365
+ throw new Error("EngineKernel720: no file selected");
1366
+ await this.loadPanoramaFile(e[0]);
1367
+ }
1368
+ /**
1369
+ * 通过 viewToken 解析全景资源地址,再复用现有全景加载逻辑。
1370
+ */
1371
+ async loadPanoramaByViewToken(e) {
1372
+ const t = await _(
1373
+ this.options,
1374
+ e,
1375
+ "720"
1376
+ );
1377
+ if (t.assetCategory === "composite_model" && t.urls.length > 1)
1378
+ throw new Error("EngineKernel720: composite_model viewToken is not supported");
1379
+ await this.loadPanorama(t.url);
1380
+ }
1381
+ /**
1382
+ * 预加载多个全景图
1383
+ * @param urls 全景图 URL 数组
1384
+ */
1385
+ async preloadPanoramas(e) {
1386
+ this.checkInitialized();
1387
+ try {
1388
+ await this.panoramaLoader.preloadPanoramas(e), console.log(`EngineKernel720: Preloaded ${e.length} panoramas`);
1389
+ } catch (t) {
1390
+ throw console.error("EngineKernel720: Failed to preload panoramas", t), t;
1391
+ }
1392
+ }
1393
+ // --- 标注管理 ---
1394
+ /**
1395
+ * 添加标注
1396
+ * @param config 标注配置
1397
+ * @returns 标注 ID
1398
+ */
1399
+ addAnnotation(e) {
1400
+ this.checkInitialized();
1401
+ const t = this.annotationManager.addAnnotation(e);
1402
+ return this.eventModule.trigger(r.AnnotationAdd, { id: t, config: e }), t;
1403
+ }
1404
+ /**
1405
+ * 批量添加标注
1406
+ * @param configs 标注配置数组
1407
+ * @returns 标注 ID 数组
1408
+ */
1409
+ addAnnotations(e) {
1410
+ return this.checkInitialized(), e.map((t) => this.addAnnotation(t));
1411
+ }
1412
+ /**
1413
+ * 移除标注
1414
+ * @param id 标注 ID
1415
+ */
1416
+ removeAnnotation(e) {
1417
+ this.checkInitialized(), this.annotationManager.removeAnnotation(e), this.eventModule.trigger(r.AnnotationRemove, { id: e });
1418
+ }
1419
+ /**
1420
+ * 清空所有标注
1421
+ */
1422
+ clearAnnotations() {
1423
+ this.checkInitialized(), this.annotationManager.clearAll();
1424
+ }
1425
+ /**
1426
+ * 获取所有标注
1427
+ */
1428
+ getAnnotations() {
1429
+ return this.checkInitialized(), this.annotationManager.getAnnotations();
1430
+ }
1431
+ /**
1432
+ * 更新交互参数(旋转/缩放/阻尼等)
1433
+ */
1434
+ updateInteractionOptions(e) {
1435
+ this.checkInitialized(), this.controlModule.updateInteractionOptions(e);
1436
+ }
1437
+ /**
1438
+ * 根据屏幕坐标拾取全景球面坐标
1439
+ */
1440
+ pickPanoramaPoint(e, t) {
1441
+ this.checkInitialized();
1442
+ const o = this.renderModule.getRenderer().domElement, a = o.clientWidth, s = o.clientHeight;
1443
+ if (a === 0 || s === 0)
1444
+ return null;
1445
+ const h = new D(
1446
+ e / a * 2 - 1,
1447
+ -(t / s * 2 - 1)
1448
+ ), l = this.cameraModule.getCamera(), c = new p(h.x, h.y, 0.5);
1449
+ c.unproject(l), c.sub(l.position).normalize();
1450
+ const g = this.sceneModule.getSphereRadius(), u = l.position.clone().add(c.multiplyScalar(g)), M = C.cartesianToSpherical(u.x, u.y, u.z);
1451
+ return {
1452
+ theta: M.theta,
1453
+ phi: M.phi,
1454
+ radius: g
1455
+ };
1456
+ }
1457
+ // --- 视角控制 ---
1458
+ /**
1459
+ * 设置视角(通过经纬度)
1460
+ * @param theta 水平角度(弧度)
1461
+ * @param phi 垂直角度(弧度)
1462
+ * @param animate 是否动画过渡(暂未实现)
1463
+ */
1464
+ lookAt(e, t, n) {
1465
+ this.checkInitialized(), n && console.warn("EngineKernel720: Animated lookAt not implemented yet"), this.controlModule.lookAt(e, t);
1466
+ }
1467
+ /**
1468
+ * 获取当前视角
1469
+ */
1470
+ getViewAngle() {
1471
+ return this.checkInitialized(), this.controlModule.getViewAngle();
1472
+ }
1473
+ /**
1474
+ * 设置 FOV
1475
+ * @param fov 视野角度(30-90)
1476
+ */
1477
+ setFov(e) {
1478
+ this.checkInitialized(), this.cameraModule.setFov(e);
1479
+ }
1480
+ /**
1481
+ * 获取当前 FOV
1482
+ */
1483
+ getFov() {
1484
+ return this.checkInitialized(), this.cameraModule.getFov();
1485
+ }
1486
+ // --- 事件监听 ---
1487
+ /**
1488
+ * 监听事件
1489
+ * @param event 事件类型
1490
+ * @param callback 回调函数
1491
+ */
1492
+ on(e, t) {
1493
+ this.checkInitialized(), this.eventModule.on(e, t);
1494
+ }
1495
+ /**
1496
+ * 取消监听
1497
+ * @param event 事件类型
1498
+ * @param callback 回调函数
1499
+ */
1500
+ off(e, t) {
1501
+ this.checkInitialized(), this.eventModule.off(e, t);
1502
+ }
1503
+ // --- 生命周期 ---
1504
+ /**
1505
+ * 调整尺寸
1506
+ * @param width 容器宽度(可选,默认使用容器尺寸)
1507
+ * @param height 容器高度(可选,默认使用容器尺寸)
1508
+ */
1509
+ resize(e, t) {
1510
+ this.checkInitialized();
1511
+ const n = e ?? this.renderModule.getRenderer().domElement.clientWidth, o = t ?? this.renderModule.getRenderer().domElement.clientHeight;
1512
+ this.renderModule.resize(n, o), this.cameraModule.updateAspectRatio(n, o), console.log("EngineKernel720: Resized to", n, "x", o);
1513
+ }
1514
+ /**
1515
+ * 暂停渲染
1516
+ */
1517
+ pauseRendering() {
1518
+ this.checkInitialized(), this.renderModule.pauseRendering();
1519
+ }
1520
+ /**
1521
+ * 恢复渲染
1522
+ */
1523
+ resumeRendering() {
1524
+ this.checkInitialized(), this.renderModule.resumeRendering(
1525
+ this.sceneModule.getScene(),
1526
+ this.cameraModule.getCamera()
1527
+ );
1528
+ }
1529
+ /**
1530
+ * 检查渲染是否暂停
1531
+ */
1532
+ isRenderingPausedState() {
1533
+ return this.isInitialized ? this.renderModule.isRenderingPausedState() : !0;
1534
+ }
1535
+ /**
1536
+ * 获取版本
1537
+ */
1538
+ getVersion() {
1539
+ return this.version;
1540
+ }
1541
+ /**
1542
+ * 销毁引擎
1543
+ */
1544
+ dispose() {
1545
+ if (this.isInitialized)
1546
+ try {
1547
+ this.renderModule.pauseRendering(), this.annotationManager.dispose(), this.panoramaLoader.dispose(), this.controlModule.dispose(), this.cameraModule.dispose(), this.renderModule.dispose(), this.sceneModule.dispose(), this.eventModule.dispose(), this.isInitialized = !1, this.revokeLocalPanoramaResource(), console.log("EngineKernel720: Disposed successfully");
1548
+ } catch (e) {
1549
+ console.error("EngineKernel720: Error during dispose", e);
1550
+ }
1551
+ }
1552
+ /**
1553
+ * 检查是否已初始化
1554
+ */
1555
+ checkInitialized() {
1556
+ if (!this.isInitialized)
1557
+ throw new Error("EngineKernel720: Not initialized");
1558
+ }
1559
+ revokeLocalPanoramaResource() {
1560
+ this.localPanoramaResource?.revoke(), this.localPanoramaResource = null;
1561
+ }
1562
+ }
1563
+ export {
1564
+ N as EngineKernel720
1565
+ };