iflow-engine-base 4.0.15 → 4.0.18

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.
Files changed (148) hide show
  1. package/dist/bim-engine-sdk.es.js +33 -13
  2. package/dist/bim-engine-sdk.umd.js +2811 -342
  3. package/dist/chunks/EngineKernel-BucDRg23.js +20761 -0
  4. package/dist/chunks/engine-2d-BjtuYZUi.js +6119 -0
  5. package/dist/chunks/{engine-3d-DAEOqkkS.js → engine-3d-DnlK2vTK.js} +19422 -18597
  6. package/dist/chunks/engine-720-DJsweyG9.js +1565 -0
  7. package/dist/chunks/engine-gaussian-DklK6vdJ.js +8127 -0
  8. package/dist/chunks/gis-tiles-vendor-DIbUOKJM.js +11781 -0
  9. package/dist/chunks/index-BtsyaVm2.js +2640 -0
  10. package/dist/chunks/index-Ct7RfnLw.js +474 -0
  11. package/dist/chunks/index-Cyv0HpPm.js +438 -0
  12. package/dist/chunks/index-DZmXdy4v.js +717 -0
  13. package/dist/chunks/{sdk-assets-Eo-SBV02.js → sdk-assets-DtcFvuth.js} +10 -10
  14. package/dist/chunks/sdk-runtime-CFM9OAcx.js +1364 -0
  15. package/dist/chunks/sdk-vendor-6wYtfmXR.js +31826 -0
  16. package/dist/chunks/three-vendor-CGmeNMm5.js +81889 -0
  17. package/dist/gis-fixtures/terrain/0/0/0.terrain +0 -0
  18. package/dist/gis-fixtures/terrain/0/1/0.terrain +0 -0
  19. package/dist/gis-fixtures/terrain/1/2/0.terrain +0 -0
  20. package/dist/gis-fixtures/terrain/1/2/1.terrain +0 -0
  21. package/dist/gis-fixtures/terrain/1/3/0.terrain +0 -0
  22. package/dist/gis-fixtures/terrain/1/3/1.terrain +0 -0
  23. package/dist/gis-fixtures/terrain/10/1694/650.terrain +0 -0
  24. package/dist/gis-fixtures/terrain/10/1694/651.terrain +0 -0
  25. package/dist/gis-fixtures/terrain/10/1694/652.terrain +0 -0
  26. package/dist/gis-fixtures/terrain/10/1695/650.terrain +0 -0
  27. package/dist/gis-fixtures/terrain/10/1695/651.terrain +0 -0
  28. package/dist/gis-fixtures/terrain/10/1695/652.terrain +0 -0
  29. package/dist/gis-fixtures/terrain/10/1696/650.terrain +0 -0
  30. package/dist/gis-fixtures/terrain/10/1696/651.terrain +0 -0
  31. package/dist/gis-fixtures/terrain/10/1696/652.terrain +0 -0
  32. package/dist/gis-fixtures/terrain/11/3390/1301.terrain +0 -0
  33. package/dist/gis-fixtures/terrain/11/3390/1302.terrain +0 -0
  34. package/dist/gis-fixtures/terrain/11/3390/1303.terrain +0 -0
  35. package/dist/gis-fixtures/terrain/11/3391/1301.terrain +0 -0
  36. package/dist/gis-fixtures/terrain/11/3391/1302.terrain +0 -0
  37. package/dist/gis-fixtures/terrain/11/3391/1303.terrain +0 -0
  38. package/dist/gis-fixtures/terrain/11/3392/1301.terrain +0 -0
  39. package/dist/gis-fixtures/terrain/11/3392/1302.terrain +0 -0
  40. package/dist/gis-fixtures/terrain/11/3392/1303.terrain +0 -0
  41. package/dist/gis-fixtures/terrain/12/6782/2604.terrain +0 -0
  42. package/dist/gis-fixtures/terrain/12/6782/2605.terrain +0 -0
  43. package/dist/gis-fixtures/terrain/12/6782/2606.terrain +0 -0
  44. package/dist/gis-fixtures/terrain/12/6783/2604.terrain +0 -0
  45. package/dist/gis-fixtures/terrain/12/6783/2605.terrain +0 -0
  46. package/dist/gis-fixtures/terrain/12/6783/2606.terrain +0 -0
  47. package/dist/gis-fixtures/terrain/12/6784/2604.terrain +0 -0
  48. package/dist/gis-fixtures/terrain/12/6784/2605.terrain +0 -0
  49. package/dist/gis-fixtures/terrain/12/6784/2606.terrain +0 -0
  50. package/dist/gis-fixtures/terrain/13/13565/5209.terrain +0 -0
  51. package/dist/gis-fixtures/terrain/13/13565/5210.terrain +0 -0
  52. package/dist/gis-fixtures/terrain/13/13565/5211.terrain +0 -0
  53. package/dist/gis-fixtures/terrain/13/13566/5209.terrain +0 -0
  54. package/dist/gis-fixtures/terrain/13/13566/5210.terrain +0 -0
  55. package/dist/gis-fixtures/terrain/13/13566/5211.terrain +0 -0
  56. package/dist/gis-fixtures/terrain/13/13567/5209.terrain +0 -0
  57. package/dist/gis-fixtures/terrain/13/13567/5210.terrain +0 -0
  58. package/dist/gis-fixtures/terrain/13/13567/5211.terrain +0 -0
  59. package/dist/gis-fixtures/terrain/14/27131/10419.terrain +0 -0
  60. package/dist/gis-fixtures/terrain/14/27131/10420.terrain +0 -0
  61. package/dist/gis-fixtures/terrain/14/27131/10421.terrain +0 -0
  62. package/dist/gis-fixtures/terrain/14/27132/10419.terrain +0 -0
  63. package/dist/gis-fixtures/terrain/14/27132/10420.terrain +0 -0
  64. package/dist/gis-fixtures/terrain/14/27132/10421.terrain +0 -0
  65. package/dist/gis-fixtures/terrain/14/27133/10419.terrain +0 -0
  66. package/dist/gis-fixtures/terrain/14/27133/10420.terrain +0 -0
  67. package/dist/gis-fixtures/terrain/14/27133/10421.terrain +0 -0
  68. package/dist/gis-fixtures/terrain/2/5/1.terrain +0 -0
  69. package/dist/gis-fixtures/terrain/2/5/2.terrain +0 -0
  70. package/dist/gis-fixtures/terrain/2/5/3.terrain +0 -0
  71. package/dist/gis-fixtures/terrain/2/6/1.terrain +0 -0
  72. package/dist/gis-fixtures/terrain/2/6/2.terrain +0 -0
  73. package/dist/gis-fixtures/terrain/2/6/3.terrain +0 -0
  74. package/dist/gis-fixtures/terrain/2/7/1.terrain +0 -0
  75. package/dist/gis-fixtures/terrain/2/7/2.terrain +0 -0
  76. package/dist/gis-fixtures/terrain/2/7/3.terrain +0 -0
  77. package/dist/gis-fixtures/terrain/3/12/4.terrain +0 -0
  78. package/dist/gis-fixtures/terrain/3/12/5.terrain +0 -0
  79. package/dist/gis-fixtures/terrain/3/12/6.terrain +0 -0
  80. package/dist/gis-fixtures/terrain/3/13/4.terrain +0 -0
  81. package/dist/gis-fixtures/terrain/3/13/5.terrain +0 -0
  82. package/dist/gis-fixtures/terrain/3/13/6.terrain +0 -0
  83. package/dist/gis-fixtures/terrain/3/14/4.terrain +0 -0
  84. package/dist/gis-fixtures/terrain/3/14/5.terrain +0 -0
  85. package/dist/gis-fixtures/terrain/3/14/6.terrain +0 -0
  86. package/dist/gis-fixtures/terrain/4/25/10.terrain +0 -0
  87. package/dist/gis-fixtures/terrain/4/25/11.terrain +0 -0
  88. package/dist/gis-fixtures/terrain/4/25/9.terrain +0 -0
  89. package/dist/gis-fixtures/terrain/4/26/10.terrain +0 -0
  90. package/dist/gis-fixtures/terrain/4/26/11.terrain +0 -0
  91. package/dist/gis-fixtures/terrain/4/26/9.terrain +0 -0
  92. package/dist/gis-fixtures/terrain/4/27/10.terrain +0 -0
  93. package/dist/gis-fixtures/terrain/4/27/11.terrain +0 -0
  94. package/dist/gis-fixtures/terrain/4/27/9.terrain +0 -0
  95. package/dist/gis-fixtures/terrain/5/51/19.terrain +0 -0
  96. package/dist/gis-fixtures/terrain/5/51/20.terrain +0 -0
  97. package/dist/gis-fixtures/terrain/5/51/21.terrain +0 -0
  98. package/dist/gis-fixtures/terrain/5/52/19.terrain +0 -0
  99. package/dist/gis-fixtures/terrain/5/52/20.terrain +0 -0
  100. package/dist/gis-fixtures/terrain/5/52/21.terrain +0 -0
  101. package/dist/gis-fixtures/terrain/5/53/19.terrain +0 -0
  102. package/dist/gis-fixtures/terrain/5/53/20.terrain +0 -0
  103. package/dist/gis-fixtures/terrain/5/53/21.terrain +0 -0
  104. package/dist/gis-fixtures/terrain/6/104/39.terrain +0 -0
  105. package/dist/gis-fixtures/terrain/6/104/40.terrain +0 -0
  106. package/dist/gis-fixtures/terrain/6/104/41.terrain +0 -0
  107. package/dist/gis-fixtures/terrain/6/105/39.terrain +0 -0
  108. package/dist/gis-fixtures/terrain/6/105/40.terrain +0 -0
  109. package/dist/gis-fixtures/terrain/6/105/41.terrain +0 -0
  110. package/dist/gis-fixtures/terrain/6/106/39.terrain +0 -0
  111. package/dist/gis-fixtures/terrain/6/106/40.terrain +0 -0
  112. package/dist/gis-fixtures/terrain/6/106/41.terrain +0 -0
  113. package/dist/gis-fixtures/terrain/7/210/80.terrain +0 -0
  114. package/dist/gis-fixtures/terrain/7/210/81.terrain +0 -0
  115. package/dist/gis-fixtures/terrain/7/210/82.terrain +0 -0
  116. package/dist/gis-fixtures/terrain/7/211/80.terrain +0 -0
  117. package/dist/gis-fixtures/terrain/7/211/81.terrain +0 -0
  118. package/dist/gis-fixtures/terrain/7/211/82.terrain +0 -0
  119. package/dist/gis-fixtures/terrain/7/212/80.terrain +0 -0
  120. package/dist/gis-fixtures/terrain/7/212/81.terrain +0 -0
  121. package/dist/gis-fixtures/terrain/7/212/82.terrain +0 -0
  122. package/dist/gis-fixtures/terrain/8/422/161.terrain +0 -0
  123. package/dist/gis-fixtures/terrain/8/422/162.terrain +0 -0
  124. package/dist/gis-fixtures/terrain/8/422/163.terrain +0 -0
  125. package/dist/gis-fixtures/terrain/8/423/161.terrain +0 -0
  126. package/dist/gis-fixtures/terrain/8/423/162.terrain +0 -0
  127. package/dist/gis-fixtures/terrain/8/423/163.terrain +0 -0
  128. package/dist/gis-fixtures/terrain/8/424/161.terrain +0 -0
  129. package/dist/gis-fixtures/terrain/8/424/162.terrain +0 -0
  130. package/dist/gis-fixtures/terrain/8/424/163.terrain +0 -0
  131. package/dist/gis-fixtures/terrain/9/846/324.terrain +0 -0
  132. package/dist/gis-fixtures/terrain/9/846/325.terrain +0 -0
  133. package/dist/gis-fixtures/terrain/9/846/326.terrain +0 -0
  134. package/dist/gis-fixtures/terrain/9/847/324.terrain +0 -0
  135. package/dist/gis-fixtures/terrain/9/847/325.terrain +0 -0
  136. package/dist/gis-fixtures/terrain/9/847/326.terrain +0 -0
  137. package/dist/gis-fixtures/terrain/9/848/324.terrain +0 -0
  138. package/dist/gis-fixtures/terrain/9/848/325.terrain +0 -0
  139. package/dist/gis-fixtures/terrain/9/848/326.terrain +0 -0
  140. package/dist/gis-fixtures/terrain/layer.json +141 -0
  141. package/dist/gis-fixtures/tilesets/xiamen-local/scene.gltf +31 -0
  142. package/dist/gis-fixtures/tilesets/xiamen-local/tileset.json +12 -0
  143. package/dist/iflow-engine-base.css +1 -1
  144. package/dist/index.d.ts +13908 -68
  145. package/package.json +8 -18
  146. package/dist/chunks/sdk-runtime-CB2jwkGL.js +0 -1177
  147. package/dist/chunks/sdk-vendor-DtYOqJWo.js +0 -23208
  148. package/dist/chunks/three-vendor-rBTtPKhu.js +0 -44788
@@ -0,0 +1,2640 @@
1
+ import { r as te, s as F, a as re, b as ie } from "./sdk-assets-DtcFvuth.js";
2
+ import "./engine-3d-DnlK2vTK.js";
3
+ import { u as z, R as de, a as f, bG as ge, G as O, b as y, aX as G, k as S, bH as me, S as pe, a_ as fe, a4 as H, at as J, aU as ye, aZ as Me, au as be, ai as we, bI as ve, C as Ee } from "./three-vendor-CGmeNMm5.js";
4
+ import { W as Se, a as Ce, r as Ie } from "./sdk-runtime-CFM9OAcx.js";
5
+ class xe {
6
+ constructor(e) {
7
+ this.engine = e;
8
+ }
9
+ engine;
10
+ layers = /* @__PURE__ */ new Map();
11
+ layerSequence = 0;
12
+ /** 登记图层;不同对象不得复用同一个 id,避免产生无法查询的孤立图层。 */
13
+ registerLayer(e) {
14
+ if (!e?.object?.isObject3D)
15
+ throw new TypeError("GIS 图层必须提供有效的 Three.js Object3D。");
16
+ const t = e.id?.trim() || `${e.type}-${++this.layerSequence}`, r = e.name?.trim();
17
+ if (!r) throw new TypeError("GIS 图层名称不能为空。");
18
+ const i = this.layers.get(t);
19
+ if (i && i.object !== e.object)
20
+ throw new RangeError(`GIS 图层 id 已存在:${t}`);
21
+ const s = {
22
+ id: t,
23
+ name: r,
24
+ type: e.type,
25
+ source: e.source,
26
+ object: e.object,
27
+ remove: e.remove
28
+ };
29
+ return this.layers.set(t, s), this.toInfo(s);
30
+ }
31
+ /** 查询全部 GIS 图层,返回快照以避免外部修改内部登记信息。 */
32
+ getLayers() {
33
+ return [...this.layers.values()].map((e) => this.toInfo(e));
34
+ }
35
+ /** 按 id 查询单个 GIS 图层。 */
36
+ getLayer(e) {
37
+ const t = this.layers.get(e);
38
+ return t ? this.toInfo(t) : null;
39
+ }
40
+ /** 仅合并当前可见的业务模型,避免底图、地形和标注放大回家视角。 */
41
+ getModelBoundingBox() {
42
+ const e = new z(), t = /* @__PURE__ */ new Set();
43
+ let r = !1;
44
+ return this.layers.forEach((i) => {
45
+ if (i.type !== "tileset" && i.type !== "model" || !i.object.visible || t.has(i.object))
46
+ return;
47
+ t.add(i.object), i.object.updateWorldMatrix(!0, !0);
48
+ const s = new z().setFromObject(i.object);
49
+ s.isEmpty() || (e.union(s), r = !0);
50
+ }), r ? e : null;
51
+ }
52
+ /** 设置图层显隐;图层不存在时返回 false。 */
53
+ setLayerVisible(e, t) {
54
+ const r = this.layers.get(e);
55
+ return r ? (r.object.visible !== t && (r.object.visible = t, this.engine.requestRender()), !0) : !1;
56
+ }
57
+ /** 开启指定图层。 */
58
+ showLayer(e) {
59
+ return this.setLayerVisible(e, !0);
60
+ }
61
+ /** 关闭指定图层,但保留已加载资源。 */
62
+ hideLayer(e) {
63
+ return this.setLayerVisible(e, !1);
64
+ }
65
+ /** 切换指定图层,返回切换后的显隐状态;图层不存在时返回 null。 */
66
+ toggleLayer(e) {
67
+ const t = this.layers.get(e);
68
+ if (!t) return null;
69
+ const r = !t.object.visible;
70
+ return this.setLayerVisible(e, r), r;
71
+ }
72
+ /** 注销图层登记,不移除或销毁场景资源。 */
73
+ unregisterLayer(e) {
74
+ return this.layers.delete(e);
75
+ }
76
+ /** 调用资源所有者的释放回调并移除图层;无释放回调时返回 false。 */
77
+ removeLayer(e) {
78
+ const t = this.layers.get(e);
79
+ return !t?.remove || t.remove() === !1 ? !1 : (this.layers.delete(e), this.engine.requestRender(), !0);
80
+ }
81
+ /** 清空登记信息;实际资源仍由各自加载器负责释放。 */
82
+ dispose() {
83
+ this.layers.clear();
84
+ }
85
+ toInfo(e) {
86
+ return {
87
+ id: e.id,
88
+ name: e.name,
89
+ type: e.type,
90
+ source: e.source,
91
+ object: e.object,
92
+ visible: e.object.visible
93
+ };
94
+ }
95
+ }
96
+ class Le {
97
+ constructor(e, t, r) {
98
+ this.engine = e, this.coordinateModule = t, this.layerManager = r, this.worldToScreen = new Se(
99
+ e.camera,
100
+ e.renderer,
101
+ e.scene
102
+ ), this.manager = new Ce({
103
+ container: e.container,
104
+ className: "bim-engine-label-container bim-engine-label-container-gis",
105
+ projectCoordinate: (i) => this.projectCoordinate(i),
106
+ pickCoordinate: (i) => this.pickCoordinate(i),
107
+ emitEvent: (i, s) => e.events.trigger(i, s)
108
+ });
109
+ }
110
+ engine;
111
+ coordinateModule;
112
+ layerManager;
113
+ manager;
114
+ worldToScreen;
115
+ raycaster = new de();
116
+ /** 进入一次选点模式,结果同时包含 WGS84 和 Three.js 世界坐标。 */
117
+ pickPoint() {
118
+ return this.manager.pickPoint();
119
+ }
120
+ /** 兼容旧版回调式选点。 */
121
+ startPickPoint(e) {
122
+ return this.manager.startPickPoint(e);
123
+ }
124
+ /** 创建 GIS 标签;经纬度按 WGS84 解释。 */
125
+ createLabel(e) {
126
+ return this.manager.createLabel(e);
127
+ }
128
+ /** 更新指定 GIS 标签。 */
129
+ updateLabel(e, t) {
130
+ return this.manager.updateLabel(e, t);
131
+ }
132
+ /** 删除指定 GIS 标签。 */
133
+ deleteLabel(e) {
134
+ return this.manager.deleteLabel(e);
135
+ }
136
+ /** deleteLabel 的兼容别名。 */
137
+ removeLabel(e) {
138
+ return this.manager.removeLabel(e);
139
+ }
140
+ /** 清空全部 GIS 标签。 */
141
+ clearLabels() {
142
+ this.manager.clearLabels();
143
+ }
144
+ /** 查询单个 GIS 标签。 */
145
+ getLabel(e) {
146
+ return this.manager.getLabel(e);
147
+ }
148
+ /** 查询全部 GIS 标签。 */
149
+ getLabels() {
150
+ return this.manager.getLabels();
151
+ }
152
+ /** 监听标签点击,并返回取消监听函数。 */
153
+ onLabelClick(e) {
154
+ return this.manager.onLabelClick(e);
155
+ }
156
+ /** 取消指定标签点击监听。 */
157
+ offLabelClick(e) {
158
+ this.manager.offLabelClick(e);
159
+ }
160
+ /** 由 GIS 共享帧循环更新标签屏幕位置。 */
161
+ update() {
162
+ this.worldToScreen.updateCamera(this.engine.camera), this.manager.update();
163
+ }
164
+ /** 释放 DOM、选点监听和标签数据。 */
165
+ dispose() {
166
+ this.manager.dispose();
167
+ }
168
+ projectCoordinate(e) {
169
+ const t = this.resolveWorldCoordinate(e);
170
+ if (!t) return null;
171
+ const r = this.worldToScreen.worldToScreenIfVisible(t);
172
+ return r ? { x: r.x, y: r.y, visible: !0 } : null;
173
+ }
174
+ resolveWorldCoordinate(e) {
175
+ return Number.isFinite(e.longitude) && Number.isFinite(e.latitude) ? this.coordinateModule.toWorld({
176
+ longitude: e.longitude,
177
+ latitude: e.latitude,
178
+ height: e.height ?? 0
179
+ }) : [e.x, e.y, e.z].every(Number.isFinite) ? new f(e.x, e.y, e.z) : null;
180
+ }
181
+ pickCoordinate(e) {
182
+ const t = this.engine.renderer.domElement.getBoundingClientRect(), r = this.worldToScreen.screenToNormalized(
183
+ e.clientX - t.left,
184
+ e.clientY - t.top
185
+ );
186
+ this.raycaster.setFromCamera(r, this.engine.camera);
187
+ const i = this.layerManager.getLayers().filter((c) => c.visible).map((c) => c.object), s = this.raycaster.intersectObjects(this.uniqueObjects(i), !0), a = this.engine.octreeBox?.rayInterationModel?.(
188
+ this.raycaster,
189
+ !0,
190
+ !1
191
+ ), o = [...s, ...a ?? []].filter((c) => c?.point instanceof f).sort((c, m) => c.distance - m.distance)[0];
192
+ if (!o) return null;
193
+ const l = this.coordinateModule.toWgs84(o.point);
194
+ return {
195
+ x: o.point.x,
196
+ y: o.point.y,
197
+ z: o.point.z,
198
+ ...l
199
+ };
200
+ }
201
+ uniqueObjects(e) {
202
+ const t = new Set(e);
203
+ return e.filter((r) => {
204
+ let i = r.parent;
205
+ for (; i; ) {
206
+ if (t.has(i)) return !1;
207
+ i = i.parent;
208
+ }
209
+ return !0;
210
+ });
211
+ }
212
+ }
213
+ class Re {
214
+ constructor(e, t) {
215
+ this.cameraModule = e, this.initialView = t;
216
+ }
217
+ cameraModule;
218
+ initialView;
219
+ mainView = null;
220
+ /** 切换底图重建相机模块后更新引用,同时保留已经保存的主视角。 */
221
+ setCameraModule(e) {
222
+ this.cameraModule = e;
223
+ }
224
+ /** 保存用户当前实际相机姿态,而不是上一次 API 写入的缓存。 */
225
+ saveMainViewPort() {
226
+ return this.mainView = this.cameraModule.getView(), { ...this.mainView };
227
+ }
228
+ /** 设置主视角并立即应用;null 表示清除已保存视角。 */
229
+ setMainViewPort(e) {
230
+ if (!e) {
231
+ this.mainView = null;
232
+ return;
233
+ }
234
+ const t = this.cameraModule.getView();
235
+ this.mainView = {
236
+ ...t,
237
+ ...e
238
+ }, this.cameraModule.setView(this.mainView);
239
+ }
240
+ /** 平滑飞行到已保存的主视角;未保存时保持当前视图。 */
241
+ async goToMainPort(e) {
242
+ this.mainView && await this.cameraModule.flyTo(this.mainView, e);
243
+ }
244
+ /** 清除主视角并回到初始化视图。 */
245
+ resetMainViewPort() {
246
+ this.mainView = null, this.cameraModule.setView(this.initialView);
247
+ }
248
+ /** 查询已保存主视角的独立快照。 */
249
+ getMainViewPort() {
250
+ return this.mainView ? { ...this.mainView } : null;
251
+ }
252
+ }
253
+ function Pe() {
254
+ return se(new ge());
255
+ }
256
+ function se(n, e = []) {
257
+ const t = n;
258
+ return t.isClippingGroup !== !0 && Object.defineProperty(t, "isClippingGroup", {
259
+ value: !0,
260
+ enumerable: !0
261
+ }), t.clippingPlanes = e, t.enabled = !0, t.clipIntersection = !1, t.clipShadows = !1, t;
262
+ }
263
+ const U = "__isModelUrlGroup", D = "__modelDepthBias", ne = 4, Te = 5e5;
264
+ function ae(n) {
265
+ return n.__modelGroupMap instanceof Map || (n.__modelGroupMap = /* @__PURE__ */ new Map()), n.__modelGroupMap;
266
+ }
267
+ function oe(n) {
268
+ return n.__modelObjectIndexMap instanceof Map || (n.__modelObjectIndexMap = /* @__PURE__ */ new Map()), n.__modelObjectIndexMap;
269
+ }
270
+ function N(n, e) {
271
+ n.url = e, n.name = e, n.userData = n.userData ?? {}, n.userData[U] = !0;
272
+ }
273
+ function _(n, e) {
274
+ return n instanceof O ? e == null ? n.userData?.[U] === !0 : n.userData?.[U] === !0 && n.url === e : !1;
275
+ }
276
+ function q(n, e) {
277
+ return n?.models?.find?.((t) => t?.url === e);
278
+ }
279
+ function nt(n, e, t) {
280
+ if (typeof t != "string" || t.length === 0)
281
+ return;
282
+ const r = q(n, e);
283
+ r != null && (Array.isArray(r.urls) || (r.urls = []), r.urls.includes(t) || r.urls.push(t));
284
+ }
285
+ function b(n, e) {
286
+ const t = ae(n), r = t.get(e);
287
+ if (r && r.parent != null)
288
+ return r;
289
+ const i = q(n, e);
290
+ if (i?.group instanceof O && i.group.parent != null)
291
+ return N(i.group, e), t.set(e, i.group), i.group;
292
+ const s = n?.scene?.children?.find?.((a) => a instanceof O ? _(a, e) ? !0 : a.name === e && a.url === e : !1);
293
+ return s != null ? (N(s, e), t.set(e, s), i != null && (i.group = s), s) : null;
294
+ }
295
+ function at(n, e) {
296
+ const t = b(n, e);
297
+ return t == null ? new y() : (t.updateWorldMatrix(!0, !1), t.matrixWorld.clone());
298
+ }
299
+ function ot(n, e) {
300
+ const t = typeof n.__getActiveWebGPUClippingPlanes == "function" ? n.__getActiveWebGPUClippingPlanes() : n.sectionPlane, r = b(n, e);
301
+ if (r != null) {
302
+ const o = se(r, t);
303
+ return o.clipShadows = !0, o;
304
+ }
305
+ const i = Pe();
306
+ i.clippingPlanes = Array.isArray(t) ? t : [], i.clipIntersection = !1, i.clipShadows = !0, N(i, e), n.scene.add(i), ae(n).set(e, i);
307
+ const a = q(n, e);
308
+ return a != null && (a.group = i), i;
309
+ }
310
+ function ke(n) {
311
+ const e = [], t = /* @__PURE__ */ new Set();
312
+ return (Array.isArray(n?.models) ? n.models : []).forEach((i) => {
313
+ if (i?.url == null)
314
+ return;
315
+ const s = b(n, i.url);
316
+ s == null || t.has(s.uuid) || (t.add(s.uuid), e.push(s));
317
+ }), n?.scene?.children?.forEach?.((i) => {
318
+ !_(i) || t.has(i.uuid) || (t.add(i.uuid), e.push(i));
319
+ }), e;
320
+ }
321
+ function le(n, e) {
322
+ const t = [], r = /* @__PURE__ */ new Set();
323
+ return ke(n).forEach((s) => {
324
+ s.traverse((a) => {
325
+ a === s || r.has(a.uuid) || e(a) && (r.add(a.uuid), t.push(a));
326
+ });
327
+ }), n?.scene?.children?.forEach?.((s) => {
328
+ if (s.name == "highlightModel")
329
+ r.add(s.uuid), t.push(s);
330
+ else {
331
+ if (r.has(s.uuid) || _(s) || s.url == null)
332
+ return;
333
+ e(s) && (r.add(s.uuid), t.push(s));
334
+ }
335
+ }), t;
336
+ }
337
+ function Ae(n) {
338
+ if (typeof n.__modelObjectIndexSeed == "number")
339
+ return;
340
+ let e = -1;
341
+ n?.scene?.traverse?.((t) => {
342
+ typeof t?.index == "number" && Number.isFinite(t.index) && (e = Math.max(e, t.index));
343
+ }), n.__modelObjectIndexSeed = e + 1;
344
+ }
345
+ function Be(n) {
346
+ Ae(n);
347
+ const e = n.__modelObjectIndexSeed;
348
+ return n.__modelObjectIndexSeed += 1, e;
349
+ }
350
+ function lt(n, e, t) {
351
+ const r = typeof t == "number" ? t : Be(n);
352
+ return e.index = r, oe(n).set(r, e), r;
353
+ }
354
+ function ut(n) {
355
+ n.updateMatrix(), n.matrixAutoUpdate = !1;
356
+ }
357
+ function Ge(n) {
358
+ return Number.isFinite(n) && n >= 3;
359
+ }
360
+ function ht(n, e) {
361
+ return e <= Te && Ge(n);
362
+ }
363
+ function ue(n) {
364
+ return Array.isArray(n) ? n : n != null ? [n] : [];
365
+ }
366
+ function Fe(n) {
367
+ return 1 + (Number.isFinite(n) ? Math.abs(Math.floor(n)) : 0) % 16 * 0.125;
368
+ }
369
+ function ct(n, e, t = !1) {
370
+ const r = typeof e == "number" ? e : n?.index, i = Fe(r), s = t ? -1 : 1;
371
+ ue(n?.material).forEach((a) => {
372
+ a.userData = a.userData ?? {};
373
+ const o = a.userData[D] ?? {};
374
+ a.userData[D] = {
375
+ ...o,
376
+ enabled: !0,
377
+ factor: 1,
378
+ units: i
379
+ }, a.polygonOffset = !0, a.polygonOffsetFactor = s, a.polygonOffsetUnits = s * (i + (o.edgeActive ? ne : 0)), a.needsUpdate = !0;
380
+ });
381
+ }
382
+ function dt(n, e, t = !1) {
383
+ const r = t ? -1 : 1;
384
+ ue(n?.material).forEach((i) => {
385
+ i.userData = i.userData ?? {};
386
+ const s = i.userData[D] ?? {}, a = s.enabled ? s.units : i.polygonOffsetUnits ?? 0, o = s.enabled ? s.factor : i.polygonOffsetFactor ?? 1;
387
+ i.userData[D] = {
388
+ ...s,
389
+ edgeActive: e
390
+ }, s.enabled || e ? (i.polygonOffset = !0, i.polygonOffsetFactor = r * Math.abs(o), i.polygonOffsetUnits = r * (Math.abs(a) + (e ? ne : 0))) : i.polygonOffset = !1, i.needsUpdate = !0;
391
+ });
392
+ }
393
+ function gt(n, e) {
394
+ if (!Number.isFinite(e))
395
+ return;
396
+ const t = oe(n), r = t.get(e);
397
+ if (r && r.parent != null)
398
+ return r;
399
+ r && r.parent == null && t.delete(e);
400
+ const i = le(n, (s) => s.index === e)[0];
401
+ return i != null ? (t.set(e, i), i) : n?.scene?.children?.find?.((s) => s?.index === e);
402
+ }
403
+ class Oe {
404
+ constructor(e, t) {
405
+ this.engine = e, this.coordinateModule = t;
406
+ }
407
+ engine;
408
+ coordinateModule;
409
+ records = /* @__PURE__ */ new Map();
410
+ /** 登记附加 3D Tiles;根节点就绪后再冻结加载完成矩阵作为校准基准。 */
411
+ registerTileset(e, t, r, i, s = !0) {
412
+ const a = this.createRecord(e, t, r, "3dtiles", i.group);
413
+ return a.tiles = i, this.records.set(e, a), s && this.captureBaseState(a), this.cloneItem(a.item);
414
+ }
415
+ /** 高度偏移和根瓦片变换完成后,记录最终加载矩阵。 */
416
+ markTilesetReady(e) {
417
+ const t = [...this.records.values()].find((r) => r.tiles === e);
418
+ return t ? (this.captureBaseState(t), !0) : !1;
419
+ }
420
+ /** 登记普通 BIM 模型;模型 URL 默认同时作为校准 id。 */
421
+ registerBimModel(e, t = {}) {
422
+ const r = b(this.engine, e);
423
+ if (!r) throw new Error(`BIM 模型尚未加载,无法登记校准:${e}`);
424
+ const i = t.id?.trim() || e, s = this.createRecord(i, t.name?.trim() || e, e, "bim", r);
425
+ return s.bimUrl = e, this.records.set(i, s), this.captureBaseState(s), this.cloneItem(s.item);
426
+ }
427
+ /** 查询全部模型校准状态快照。 */
428
+ getItems() {
429
+ return [...this.records.values()].map((e) => this.cloneItem(e.item));
430
+ }
431
+ /** 查询单个模型校准状态。 */
432
+ getItem(e) {
433
+ const t = this.records.get(e);
434
+ return t ? this.cloneItem(t.item) : null;
435
+ }
436
+ /**
437
+ * 从基准矩阵应用 ENU 偏移与旋转。
438
+ * 每次都从 baseMatrix 重算,避免滑块连续调整时累计产生水平漂移。
439
+ */
440
+ setTransform(e) {
441
+ let t = this.records.get(e.id);
442
+ if (!t && b(this.engine, e.id) && (this.registerBimModel(e.id), t = this.records.get(e.id)), !t) return !1;
443
+ if (!t.item.ready || !t.baseMatrix || !t.anchorWorld)
444
+ throw new Error(`模型校准基准尚未就绪:${e.id}`);
445
+ const r = this.resolveOffset(e.offset, t.item.offset), i = this.resolveRotation(e.rotation, t.item.rotation), s = this.buildCalibratedLocalMatrix(t, r, i);
446
+ return this.applyMatrix(t, s) ? (t.item.offset = r, t.item.rotation = i, this.emitChanged(t.item, "apply"), !0) : !1;
447
+ }
448
+ /** 恢复模型登记时的矩阵。 */
449
+ reset(e) {
450
+ const t = this.records.get(e);
451
+ return !t?.baseMatrix || !this.applyMatrix(t, t.baseMatrix) ? !1 : (t.item.offset = this.zeroOffset(), t.item.rotation = this.zeroRotation(), this.emitChanged(t.item, "reset"), !0);
452
+ }
453
+ /** 移除模型的校准记录,不改变模型当前矩阵。 */
454
+ unregister(e) {
455
+ return this.records.delete(e);
456
+ }
457
+ /** 释放内部快照,模型资源仍由各自加载器管理。 */
458
+ dispose() {
459
+ this.records.clear();
460
+ }
461
+ createRecord(e, t, r, i, s) {
462
+ return {
463
+ item: {
464
+ id: e,
465
+ name: t,
466
+ source: r,
467
+ type: i,
468
+ offset: this.zeroOffset(),
469
+ rotation: this.zeroRotation(),
470
+ baseMatrix: new y().toArray(),
471
+ ready: !1
472
+ },
473
+ object: s,
474
+ baseMatrix: null,
475
+ anchorWorld: null
476
+ };
477
+ }
478
+ captureBaseState(e) {
479
+ e.object.matrixAutoUpdate && e.object.updateMatrix(), e.object.updateWorldMatrix(!0, !0), e.baseMatrix = e.object.matrix.clone(), e.anchorWorld = this.readAnchorWorld(e), e.item.baseMatrix = e.baseMatrix.toArray(), e.item.ready = !0, e.item.offset = this.zeroOffset(), e.item.rotation = this.zeroRotation();
480
+ }
481
+ readAnchorWorld(e) {
482
+ if (e.tiles) {
483
+ const r = new G();
484
+ if (e.tiles.getBoundingSphere(r))
485
+ return r.center.applyMatrix4(e.object.matrixWorld);
486
+ }
487
+ const t = new z().setFromObject(e.object);
488
+ return t.isEmpty() ? e.object.getWorldPosition(new f()) : t.getCenter(new f());
489
+ }
490
+ buildCalibratedLocalMatrix(e, t, r) {
491
+ const i = e.anchorWorld, s = this.coordinateModule.toWgs84(i), { east: a, north: o, up: l } = this.coordinateModule.getEastNorthUpAxes(s), c = a.clone().multiplyScalar(t.east).addScaledVector(o, t.north).addScaledVector(l, t.up), m = new y().multiply(new y().makeRotationAxis(l, S.degToRad(r.heading))).multiply(new y().makeRotationAxis(a, S.degToRad(r.pitch))).multiply(new y().makeRotationAxis(o, S.degToRad(r.roll))), u = new y().makeTranslation(...c.toArray()).multiply(new y().makeTranslation(...i.toArray())).multiply(m).multiply(new y().makeTranslation(...i.clone().negate().toArray()));
492
+ e.object.parent?.updateWorldMatrix(!0, !1);
493
+ const g = e.object.parent?.matrixWorld.clone() ?? new y(), h = g.clone().multiply(e.baseMatrix);
494
+ return g.invert().multiply(u).multiply(h);
495
+ }
496
+ applyMatrix(e, t) {
497
+ return e.item.type === "bim" ? this.engine.calibration.apply({
498
+ url: e.bimUrl,
499
+ matrix: t
500
+ }) : (e.object.matrixAutoUpdate = !1, e.object.matrix.copy(t), e.object.matrixWorldNeedsUpdate = !0, e.object.updateMatrixWorld(!0), this.engine.requestRender(), !0);
501
+ }
502
+ resolveOffset(e, t) {
503
+ return {
504
+ east: this.numberOrCurrent(e?.east, t.east),
505
+ north: this.numberOrCurrent(e?.north, t.north),
506
+ up: this.numberOrCurrent(e?.up, t.up)
507
+ };
508
+ }
509
+ resolveRotation(e, t) {
510
+ return {
511
+ heading: this.numberOrCurrent(e?.heading, t.heading),
512
+ pitch: this.numberOrCurrent(e?.pitch, t.pitch),
513
+ roll: this.numberOrCurrent(e?.roll, t.roll)
514
+ };
515
+ }
516
+ numberOrCurrent(e, t) {
517
+ if (e === void 0) return t;
518
+ if (!Number.isFinite(e)) throw new RangeError("模型校准参数必须是有限数值。");
519
+ return Math.abs(e) < 1e-10 ? 0 : e;
520
+ }
521
+ emitChanged(e, t) {
522
+ this.engine.events.trigger("gis:calibration:changed", {
523
+ ...this.cloneItem(e),
524
+ action: t,
525
+ timestamp: Date.now()
526
+ });
527
+ }
528
+ cloneItem(e) {
529
+ return {
530
+ ...e,
531
+ offset: { ...e.offset },
532
+ rotation: { ...e.rotation },
533
+ baseMatrix: [...e.baseMatrix]
534
+ };
535
+ }
536
+ zeroOffset() {
537
+ return { east: 0, north: 0, up: 0 };
538
+ }
539
+ zeroRotation() {
540
+ return { heading: 0, pitch: 0, roll: 0 };
541
+ }
542
+ }
543
+ const I = Math.PI / 180;
544
+ class De {
545
+ constructor(e, t, r, i) {
546
+ this.engine = e, this.tiles = t, this.layerManager = r, this.calibrationManager = i;
547
+ }
548
+ engine;
549
+ tiles;
550
+ layerManager;
551
+ calibrationManager;
552
+ records = /* @__PURE__ */ new Map();
553
+ inverseParentMatrix = new y();
554
+ /** 切换底图后更新地理参考,已有 BIM 模型的世界定位保持不变。 */
555
+ setTiles(e) {
556
+ this.tiles = e;
557
+ }
558
+ /** 定位已加载模型;模型本地 +Y 为上方,heading 从正北向东顺时针增加。 */
559
+ setPosition(e, t) {
560
+ this.assertUrl(e);
561
+ const r = this.resolvePosition(t), i = this.requireModelGroup(e), s = this.records.get(e), a = s?.groupUuid === i.uuid ? s.originalMatrix : i.matrix.clone(), o = this.tiles.ellipsoid.getObjectFrame(
562
+ r.latitude * I,
563
+ r.longitude * I,
564
+ r.height,
565
+ r.heading * I,
566
+ r.pitch * I,
567
+ r.roll * I,
568
+ new y()
569
+ );
570
+ if (this.tiles.group.updateWorldMatrix(!0, !1), o.premultiply(this.tiles.group.matrixWorld), i.parent && (i.parent.updateWorldMatrix(!0, !1), this.inverseParentMatrix.copy(i.parent.matrixWorld).invert(), o.premultiply(this.inverseParentMatrix)), !this.engine.calibration.apply({ url: e, matrix: o }))
571
+ throw new Error(`模型定位失败:${e}`);
572
+ this.records.set(e, {
573
+ groupUuid: i.uuid,
574
+ originalMatrix: a.clone(),
575
+ position: { ...r }
576
+ });
577
+ const l = this.getLayerId(e);
578
+ return this.layerManager?.unregisterLayer(l), this.layerManager?.registerLayer({
579
+ id: l,
580
+ name: e,
581
+ type: "model",
582
+ source: e,
583
+ object: i
584
+ }), this.calibrationManager?.registerBimModel(e, { name: e }), { ...r };
585
+ }
586
+ /** 模型未加载时先通过 v3 Loader 加载,再应用地理定位。 */
587
+ async loadAndPosition(e, t, r) {
588
+ return this.assertUrl(e), b(this.engine, e) || await this.engine.loaderModule.loadModels([e], r), this.setPosition(e, t);
589
+ }
590
+ /** 获取指定模型最近一次由本管理器应用的定位信息。 */
591
+ getPosition(e) {
592
+ const t = this.records.get(e), r = b(this.engine, e);
593
+ return !t || r?.uuid !== t.groupUuid ? null : { ...t.position };
594
+ }
595
+ /** 恢复模型定位前的根分组矩阵。 */
596
+ resetPosition(e) {
597
+ const t = this.records.get(e), r = b(this.engine, e);
598
+ return !t || !r || r.uuid !== t.groupUuid ? (this.records.delete(e), this.layerManager?.unregisterLayer(this.getLayerId(e)), !1) : this.engine.calibration.apply({ url: e, matrix: t.originalMatrix }) ? (this.records.delete(e), this.layerManager?.unregisterLayer(this.getLayerId(e)), this.calibrationManager?.unregister(e), !0) : !1;
599
+ }
600
+ /** 释放内部记录;模型资源由引擎统一管理。 */
601
+ dispose() {
602
+ this.records.forEach((e, t) => {
603
+ this.layerManager?.unregisterLayer(this.getLayerId(t)), this.calibrationManager?.unregister(t);
604
+ }), this.records.clear();
605
+ }
606
+ requireModelGroup(e) {
607
+ const t = b(this.engine, e);
608
+ if (!t) throw new Error(`模型尚未加载,无法定位:${e}`);
609
+ return t;
610
+ }
611
+ resolvePosition(e) {
612
+ if (!e || !Number.isFinite(e.latitude) || e.latitude < -90 || e.latitude > 90)
613
+ throw new RangeError("纬度必须是 -90 到 90 之间的有限数值。");
614
+ if (!Number.isFinite(e.longitude))
615
+ throw new RangeError("经度必须是有限数值。");
616
+ const t = e.height ?? 0, r = e.heading ?? 0, i = e.pitch ?? 0, s = e.roll ?? 0;
617
+ if (![t, r, i, s].every(Number.isFinite))
618
+ throw new RangeError("高度和模型朝向必须是有限数值。");
619
+ if (i < -90 || i > 90)
620
+ throw new RangeError("俯仰角必须是 -90 到 90 之间的有限数值。");
621
+ return {
622
+ longitude: this.normalizeAngle(e.longitude),
623
+ latitude: e.latitude,
624
+ height: t,
625
+ heading: this.normalizeAngle(r),
626
+ pitch: i,
627
+ roll: this.normalizeAngle(s)
628
+ };
629
+ }
630
+ assertUrl(e) {
631
+ if (typeof e != "string" || e.trim().length === 0)
632
+ throw new TypeError("模型 URL 不能为空。");
633
+ }
634
+ normalizeAngle(e) {
635
+ return ((e + 180) % 360 + 360) % 360 - 180;
636
+ }
637
+ getLayerId(e) {
638
+ return `model:${e}`;
639
+ }
640
+ }
641
+ const T = {
642
+ preset: "noon_clear",
643
+ params: {
644
+ cloudSpeed: 0,
645
+ cloudCoverage: 0,
646
+ cloudDensity: 0
647
+ },
648
+ intensity: 0.2
649
+ }, Ve = "4";
650
+ class je {
651
+ constructor(e) {
652
+ this.engine = e;
653
+ }
654
+ engine;
655
+ cubeTextureLoader = new me();
656
+ skyBoxTexture = null;
657
+ savedSceneState = null;
658
+ skyBoxLoadVersion = 0;
659
+ skyBoxType = null;
660
+ /** 应用天空配置;传 false 时关闭并保留资源,便于后续快速恢复。 */
661
+ setSky(e = !0) {
662
+ if (this.clearSkyBox(), e === !1) {
663
+ this.hide();
664
+ return;
665
+ }
666
+ const t = e === !0 ? {} : e, r = {
667
+ preset: t.preset ?? T.preset,
668
+ params: { ...T.params, ...t.params },
669
+ intensity: t.intensity ?? T.intensity
670
+ };
671
+ r.preset && this.engine.skyManager.setPreset(r.preset), r.params && this.engine.skyManager.setParams(r.params), r.intensity !== void 0 && this.setIntensity(r.intensity), this.engine.skyManager.isActive() || this.engine.skyManager.activate();
672
+ }
673
+ /** 显示天空,并可同时覆盖预设、参数和环境光强度。 */
674
+ show(e = {}) {
675
+ this.setSky(e);
676
+ }
677
+ /** 设置内置 HDR 或六面图天空盒,未传配置时使用内置晴空。 */
678
+ async setSkyBox(e = {}) {
679
+ this.validateSkyBoxOptions(e);
680
+ const t = ++this.skyBoxLoadVersion;
681
+ if (!("urls" in e)) {
682
+ this.releaseCubeSkyBox(), this.engine.skyManager.close(!1);
683
+ const o = e.id ?? Ve;
684
+ if (await this.engine.hdr.setHdr(o), t !== this.skyBoxLoadVersion) {
685
+ this.skyBoxType !== "hdr" && this.engine.hdr.getCurrentHdrId() === o && await this.engine.hdr.setHdr("0");
686
+ return;
687
+ }
688
+ this.engine.hdr.setIntensity(e.intensity ?? T.intensity), this.engine.hdr.setBackgroundIntensity(e.backgroundIntensity ?? 1), this.engine.hdr.setVisible(!0), this.skyBoxType = "hdr";
689
+ return;
690
+ }
691
+ const r = await this.cubeTextureLoader.loadAsync([...e.urls]);
692
+ if (t !== this.skyBoxLoadVersion) {
693
+ r.dispose();
694
+ return;
695
+ }
696
+ r.colorSpace = pe, this.skyBoxType === "hdr" && this.engine.hdr.setHdr("0"), this.engine.skyManager.close(!1), this.captureSceneState(), this.skyBoxTexture?.dispose(), this.skyBoxTexture = r, this.skyBoxType = "cube";
697
+ const i = this.engine.scene, s = e.intensity ?? 1, a = S.degToRad(e.rotation ?? 0);
698
+ i.background = r, i.environment = r, i.backgroundIntensity = s, i.environmentIntensity = s, i.backgroundBlurriness = e.blurriness ?? 0, i.backgroundRotation.set(0, a, 0), i.environmentRotation.set(0, a, 0), this.refreshScene();
699
+ }
700
+ /** 清除天空盒并恢复设置前的场景背景和环境光。 */
701
+ clearSkyBox() {
702
+ this.skyBoxLoadVersion += 1, this.skyBoxType === "hdr" && (this.engine.hdr.setHdr("0"), this.skyBoxType = null), this.releaseCubeSkyBox();
703
+ }
704
+ releaseCubeSkyBox() {
705
+ if (!this.savedSceneState && !this.skyBoxTexture) return;
706
+ const e = this.engine.scene;
707
+ this.savedSceneState && (e.background = this.savedSceneState.background, e.environment = this.savedSceneState.environment, e.backgroundIntensity = this.savedSceneState.backgroundIntensity, e.environmentIntensity = this.savedSceneState.environmentIntensity, e.backgroundBlurriness = this.savedSceneState.backgroundBlurriness, e.backgroundRotation.copy(this.savedSceneState.backgroundRotation), e.environmentRotation.copy(this.savedSceneState.environmentRotation)), this.savedSceneState = null, this.skyBoxTexture?.dispose(), this.skyBoxTexture = null, this.skyBoxType = null, this.refreshScene();
708
+ }
709
+ /** 返回 HDR 或六面图天空盒是否已经应用到场景。 */
710
+ isSkyBoxActive() {
711
+ return this.skyBoxType === "hdr" ? this.engine.hdr.getVisible() && this.engine.hdr.getCurrentHdrId() !== "0" : this.skyBoxTexture !== null && this.engine.scene.background === this.skyBoxTexture;
712
+ }
713
+ /** 隐藏当前天空;天空盒纹理会立即释放,物理天空可按需保留资源。 */
714
+ hide(e = !1) {
715
+ if (this.skyBoxType !== null) {
716
+ this.clearSkyBox();
717
+ return;
718
+ }
719
+ this.engine.skyManager.close(e);
720
+ }
721
+ /** 切换 v3 引擎内置天空预设。 */
722
+ setPreset(e) {
723
+ this.clearSkyBox(), this.engine.skyManager.setPreset(e), this.engine.skyManager.isActive() || this.engine.skyManager.activate();
724
+ }
725
+ /** 增量修改太阳、云层和大气参数。 */
726
+ setParams(e) {
727
+ this.clearSkyBox(), this.engine.skyManager.setParams(e), this.engine.skyManager.isActive() || this.engine.skyManager.activate();
728
+ }
729
+ /** 设置天空生成的环境光强度。 */
730
+ setIntensity(e) {
731
+ if (!Number.isFinite(e) || e < 0)
732
+ throw new RangeError("天空环境光强度必须是大于或等于 0 的有限数值。");
733
+ if (this.skyBoxType === "hdr") {
734
+ this.engine.hdr.setIntensity(e), this.engine.hdr.setBackgroundIntensity(e);
735
+ return;
736
+ }
737
+ if (this.skyBoxType === "cube") {
738
+ this.engine.scene.backgroundIntensity = e, this.engine.scene.environmentIntensity = e, this.refreshScene();
739
+ return;
740
+ }
741
+ this.engine.skyManager.setIntensity(e);
742
+ }
743
+ /** 返回当前天空是否可见。 */
744
+ isActive() {
745
+ return this.isSkyBoxActive() || this.engine.skyManager.isActive();
746
+ }
747
+ /** 释放 GIS 天空资源,v3 引擎销毁时仍会执行兜底清理。 */
748
+ dispose() {
749
+ this.clearSkyBox(), this.engine.skyManager.close(!0);
750
+ }
751
+ captureSceneState() {
752
+ if (this.savedSceneState) return;
753
+ const e = this.engine.scene;
754
+ this.savedSceneState = {
755
+ background: e.background,
756
+ environment: e.environment,
757
+ backgroundIntensity: e.backgroundIntensity,
758
+ environmentIntensity: e.environmentIntensity,
759
+ backgroundBlurriness: e.backgroundBlurriness,
760
+ backgroundRotation: e.backgroundRotation.clone(),
761
+ environmentRotation: e.environmentRotation.clone()
762
+ };
763
+ }
764
+ validateSkyBoxOptions(e) {
765
+ if ("urls" in e && (e.urls.length !== 6 || e.urls.some((t) => !t.trim())))
766
+ throw new TypeError("GIS 天空盒必须提供右、左、上、下、前、后共 6 张图片。");
767
+ if (e.intensity !== void 0 && (!Number.isFinite(e.intensity) || e.intensity < 0))
768
+ throw new RangeError("天空盒亮度必须是大于或等于 0 的有限数值。");
769
+ if ("blurriness" in e && e.blurriness !== void 0 && (!Number.isFinite(e.blurriness) || e.blurriness < 0 || e.blurriness > 1))
770
+ throw new RangeError("天空盒模糊度必须是 0 到 1 之间的有限数值。");
771
+ if ("rotation" in e && e.rotation !== void 0 && !Number.isFinite(e.rotation))
772
+ throw new RangeError("天空盒旋转角度必须是有限数值。");
773
+ if ("backgroundIntensity" in e && e.backgroundIntensity !== void 0 && (!Number.isFinite(e.backgroundIntensity) || e.backgroundIntensity < 0))
774
+ throw new RangeError("天空盒背景亮度必须是大于或等于 0 的有限数值。");
775
+ }
776
+ refreshScene() {
777
+ this.engine.composerModule?.rebuild?.(), this.engine.requestComposerRender?.();
778
+ }
779
+ }
780
+ const V = {
781
+ latitude: 35.6812,
782
+ longitude: 139.8,
783
+ height: 500,
784
+ heading: -90,
785
+ pitch: -10,
786
+ roll: 0
787
+ }, x = Math.PI / 180;
788
+ class Y {
789
+ constructor(e, t, r, i, s = V, a) {
790
+ this.camera = e, this.tiles = t, this.cameraFrame = r, this.requestRender = i, this.coordinateModule = a, this.camera.near = 0.1, this.camera.far = 1e7, this.camera.updateProjectionMatrix(), this.currentView = this.resolveView(s), this.applyView(this.currentView);
791
+ }
792
+ camera;
793
+ tiles;
794
+ cameraFrame;
795
+ requestRender;
796
+ coordinateModule;
797
+ controls = null;
798
+ currentView = { ...V };
799
+ inverseParentMatrix = new y();
800
+ flight = null;
801
+ controlsEnabledBeforeFlight = !0;
802
+ disposed = !1;
803
+ /** 绑定地球控制器,使跳转期间禁用输入并在结束后同步控制状态。 */
804
+ setControls(e) {
805
+ this.controls?.removeEventListener("change", this.handleControlsChange), this.controls = e, e.addEventListener("change", this.handleControlsChange), e.resetState();
806
+ }
807
+ /** 立即跳转到指定经纬度。 */
808
+ setView(e) {
809
+ this.cancelFlight("新的视图跳转已替代当前飞行。"), this.currentView = this.resolveView(e), this.applyView(this.currentView), this.controls?.resetState();
810
+ }
811
+ /** 平滑飞行到指定经纬度,默认动画时长为 1.5 秒。 */
812
+ flyTo(e, t = {}) {
813
+ if (this.disposed) return Promise.reject(new Error("GIS 相机模块已销毁。"));
814
+ if (this.cancelFlight("新的视图跳转已替代当前飞行。"), t.signal?.aborted)
815
+ return Promise.reject(new DOMException("GIS 相机飞行已取消。", "AbortError"));
816
+ this.syncViewFromCamera();
817
+ const r = this.resolveView(e), i = Math.max(0, Math.min(6e4, t.durationMs ?? 1500));
818
+ return i === 0 ? (this.setView(r), Promise.resolve()) : (this.controlsEnabledBeforeFlight = this.controls?.enabled ?? !0, this.controls && (this.controls.enabled = !1), new Promise((s, a) => {
819
+ const o = {
820
+ start: { ...this.currentView },
821
+ end: r,
822
+ startedAt: performance.now(),
823
+ durationMs: i,
824
+ resolve: s,
825
+ reject: a,
826
+ signal: t.signal
827
+ };
828
+ t.signal && (o.abortHandler = () => this.cancelFlight("GIS 相机飞行已取消。", !0), t.signal.addEventListener("abort", o.abortHandler, { once: !0 })), this.flight = o, this.requestRender();
829
+ }));
830
+ }
831
+ /** 返回当前已经应用到相机的 WGS84 视图。 */
832
+ getView() {
833
+ return this.syncViewFromCamera(), { ...this.currentView };
834
+ }
835
+ /** 取消当前飞行,并恢复跳转前的控制器状态。 */
836
+ cancelFlight(e = "GIS 相机飞行已取消。", t = !1) {
837
+ const r = this.flight;
838
+ return r ? (this.flight = null, this.cleanupFlight(r), r.reject(t ? new DOMException(e, "AbortError") : new Error(e)), !0) : !1;
839
+ }
840
+ /** 由 GIS 共享渲染循环调用,推进当前相机飞行动画。 */
841
+ update(e) {
842
+ const t = this.flight;
843
+ if (!t) return;
844
+ const r = Math.min(1, Math.max(0, (e - t.startedAt) / t.durationMs)), i = r < 0.5 ? 4 * r * r * r : 1 - Math.pow(-2 * r + 2, 3) / 2;
845
+ this.currentView = {
846
+ latitude: this.lerp(t.start.latitude, t.end.latitude, i),
847
+ longitude: this.lerpAngle(t.start.longitude, t.end.longitude, i),
848
+ height: this.lerp(t.start.height, t.end.height, i),
849
+ heading: this.lerpAngle(t.start.heading, t.end.heading, i),
850
+ pitch: this.lerp(t.start.pitch, t.end.pitch, i),
851
+ roll: this.lerpAngle(t.start.roll, t.end.roll, i)
852
+ }, this.applyView(this.currentView), r === 1 && (this.currentView = { ...t.end }, this.flight = null, this.cleanupFlight(t), this.controls?.resetState(), t.resolve());
853
+ }
854
+ /** 释放飞行任务持有的事件和控制器状态。 */
855
+ dispose() {
856
+ this.disposed || (this.disposed = !0, this.cancelFlight("GIS 相机模块已销毁。"), this.controls?.removeEventListener("change", this.handleControlsChange), this.controls = null);
857
+ }
858
+ /** 用户通过 GlobeControls 改变相机后,同步真实地理视图供保存和后续飞行使用。 */
859
+ handleControlsChange = () => {
860
+ this.flight || this.syncViewFromCamera();
861
+ };
862
+ syncViewFromCamera() {
863
+ !this.coordinateModule || this.flight || (this.currentView = this.coordinateModule.getCameraView(this.camera));
864
+ }
865
+ resolveView(e) {
866
+ if (!Number.isFinite(e.latitude) || e.latitude < -90 || e.latitude > 90)
867
+ throw new RangeError("纬度必须是 -90 到 90 之间的有限数值。");
868
+ if (!Number.isFinite(e.longitude))
869
+ throw new RangeError("经度必须是有限数值。");
870
+ const t = e.height ?? this.currentView.height, r = e.heading ?? this.currentView.heading, i = e.pitch ?? this.currentView.pitch, s = e.roll ?? this.currentView.roll;
871
+ if (![t, r, i, s].every(Number.isFinite))
872
+ throw new RangeError("高度和相机朝向必须是有限数值。");
873
+ return {
874
+ latitude: e.latitude,
875
+ longitude: this.normalizeAngle(e.longitude),
876
+ height: Math.max(1, t),
877
+ heading: this.normalizeAngle(r),
878
+ pitch: Math.max(-90, Math.min(90, i)),
879
+ roll: this.normalizeAngle(s)
880
+ };
881
+ }
882
+ applyView(e) {
883
+ this.tiles.ellipsoid.getObjectFrame(
884
+ e.latitude * x,
885
+ e.longitude * x,
886
+ e.height,
887
+ e.heading * x,
888
+ e.pitch * x,
889
+ e.roll * x,
890
+ this.camera.matrix,
891
+ this.cameraFrame
892
+ ), this.tiles.group.updateWorldMatrix(!0, !1), this.camera.matrix.premultiply(this.tiles.group.matrixWorld), this.camera.parent && (this.camera.parent.updateWorldMatrix(!0, !1), this.inverseParentMatrix.copy(this.camera.parent.matrixWorld).invert(), this.camera.matrix.premultiply(this.inverseParentMatrix)), this.camera.matrix.decompose(this.camera.position, this.camera.quaternion, this.camera.scale), this.camera.updateMatrixWorld(!0), this.requestRender();
893
+ }
894
+ cleanupFlight(e) {
895
+ e.signal && e.abortHandler && e.signal.removeEventListener("abort", e.abortHandler), this.controls && (this.controls.enabled = this.controlsEnabledBeforeFlight);
896
+ }
897
+ lerp(e, t, r) {
898
+ return e + (t - e) * r;
899
+ }
900
+ lerpAngle(e, t, r) {
901
+ const i = this.normalizeAngle(t - e);
902
+ return this.normalizeAngle(e + i * r);
903
+ }
904
+ normalizeAngle(e) {
905
+ return ((e + 180) % 360 + 360) % 360 - 180;
906
+ }
907
+ }
908
+ const k = Math.PI / 180, E = 180 / Math.PI;
909
+ class ze {
910
+ constructor(e, t) {
911
+ this.tiles = e, this.cameraFrame = t;
912
+ }
913
+ tiles;
914
+ cameraFrame;
915
+ inverseTilesMatrix = new y();
916
+ /** 切换底图后更新坐标参考,但保持依赖此模块的业务管理器不变。 */
917
+ setTiles(e, t) {
918
+ this.tiles = e, this.cameraFrame = t;
919
+ }
920
+ /** 将 WGS84 经纬高转换为 Three.js 世界坐标。 */
921
+ toWorld(e, t = new f()) {
922
+ const r = this.normalizeCoordinate(e);
923
+ return this.tiles.ellipsoid.getCartographicToPosition(
924
+ r.latitude * k,
925
+ r.longitude * k,
926
+ r.height,
927
+ t
928
+ ), this.tiles.group.updateWorldMatrix(!0, !1), t.applyMatrix4(this.tiles.group.matrixWorld);
929
+ }
930
+ /** 将 Three.js 世界坐标反算为 WGS84 经纬高。 */
931
+ toWgs84(e) {
932
+ this.tiles.group.updateWorldMatrix(!0, !1), this.inverseTilesMatrix.copy(this.tiles.group.matrixWorld).invert();
933
+ const t = e instanceof f ? e.clone() : new f(e.x, e.y, e.z);
934
+ t.applyMatrix4(this.inverseTilesMatrix);
935
+ const r = this.tiles.ellipsoid.getPositionToCartographic(t, {});
936
+ return {
937
+ longitude: r.lon * E,
938
+ latitude: r.lat * E,
939
+ height: r.height
940
+ };
941
+ }
942
+ /** 从相机当前真实世界矩阵读取 WGS84 位置和姿态。 */
943
+ getCameraView(e) {
944
+ e.updateWorldMatrix(!0, !1), this.tiles.group.updateWorldMatrix(!0, !1);
945
+ const t = this.inverseTilesMatrix.copy(this.tiles.group.matrixWorld).invert().multiply(e.matrixWorld), r = this.tiles.ellipsoid.getCartographicFromObjectFrame(
946
+ t,
947
+ {},
948
+ this.cameraFrame
949
+ );
950
+ return {
951
+ longitude: this.normalizeAngle(r.lon * E),
952
+ latitude: r.lat * E,
953
+ height: r.height,
954
+ heading: this.normalizeAngle(r.azimuth * E),
955
+ pitch: r.elevation * E,
956
+ roll: this.normalizeAngle(r.roll * E)
957
+ };
958
+ }
959
+ /** 获取指定 WGS84 位置在 Three.js 世界空间中的东、北、上单位轴。 */
960
+ getEastNorthUpAxes(e, t = new f(), r = new f(), i = new f()) {
961
+ const s = this.normalizeCoordinate(e);
962
+ return this.tiles.ellipsoid.getEastNorthUpAxes(
963
+ s.latitude * k,
964
+ s.longitude * k,
965
+ t,
966
+ r,
967
+ i
968
+ ), this.tiles.group.updateWorldMatrix(!0, !1), t.transformDirection(this.tiles.group.matrixWorld).normalize(), r.transformDirection(this.tiles.group.matrixWorld).normalize(), i.transformDirection(this.tiles.group.matrixWorld).normalize(), { east: t, north: r, up: i };
969
+ }
970
+ normalizeCoordinate(e) {
971
+ if (!e || !Number.isFinite(e.latitude) || e.latitude < -90 || e.latitude > 90)
972
+ throw new RangeError("纬度必须是 -90 到 90 之间的有限数值。");
973
+ if (!Number.isFinite(e.longitude))
974
+ throw new RangeError("经度必须是有限数值。");
975
+ const t = e.height ?? 0;
976
+ if (!Number.isFinite(t))
977
+ throw new RangeError("高度必须是有限数值。");
978
+ return {
979
+ longitude: this.normalizeAngle(e.longitude),
980
+ latitude: e.latitude,
981
+ height: t
982
+ };
983
+ }
984
+ normalizeAngle(e) {
985
+ return ((e + 180) % 360 + 360) % 360 - 180;
986
+ }
987
+ }
988
+ const v = {
989
+ GIS: "gis",
990
+ Default: "default",
991
+ FirstPerson: "first-person",
992
+ ThirdPerson: "third-person"
993
+ };
994
+ class X {
995
+ constructor(e, t, r, i) {
996
+ this.engine = e, this.gisControls = t, this.requestRender = r, this.layerManager = i, this.ensureViewCubeTargetCompatibility(), this.removeThirdPersonPlacementResolver = this.engine.controlModule.setThirdPersonPlacementResolver?.(
997
+ this.resolveGISPlacement
998
+ ) ?? null, this.removeOrbitCenterResolver = this.engine.controlModule.setOrbitCenterResolver?.(
999
+ this.resolveGISPlacement
1000
+ ) ?? null;
1001
+ }
1002
+ engine;
1003
+ gisControls;
1004
+ requestRender;
1005
+ layerManager;
1006
+ currentMode = null;
1007
+ disposed = !1;
1008
+ removeThirdPersonPlacementResolver = null;
1009
+ removeOrbitCenterResolver = null;
1010
+ /** 切换控制器模式,并把业务配置原样委托给 v3 控制模块。 */
1011
+ switchController(e, t = {}) {
1012
+ if (this.disposed) throw new Error("GIS 控制器模块已销毁。");
1013
+ const r = this.captureCameraRange();
1014
+ try {
1015
+ switch (this.gisControls.enabled = !1, e) {
1016
+ case v.GIS:
1017
+ this.currentMode !== null && this.currentMode !== v.GIS && this.engine.controlModule.switchDefaultMode({ preserveCameraPose: !0 }), this.engine.controlModule.disActive(), this.gisControls.enabled = !0, this.gisControls.resetState(), this.engine.controls = this.gisControls, this.engine.bindControlEvents(this.gisControls);
1018
+ break;
1019
+ case v.Default:
1020
+ this.engine.controlModule.switchDefaultMode({
1021
+ preserveCameraPose: t.preserveCameraPose
1022
+ }), this.engine.controlModule.active(this.engine.controlModule.orbitControls);
1023
+ break;
1024
+ case v.FirstPerson:
1025
+ this.leavePendingThirdPersonMode(), this.engine.controlModule.switchFirstPersonMode(), this.engine.controlModule.active(this.engine.controlModule.firstPersonControls);
1026
+ break;
1027
+ case v.ThirdPerson:
1028
+ this.leavePendingThirdPersonMode(), this.engine.controlModule.switchThirdPersonMode(t.thirdPersonOptions), this.engine.controlModule.active(this.engine.controlModule.orbitControls);
1029
+ break;
1030
+ default:
1031
+ throw new RangeError(`不支持的 GIS 控制器模式:${String(e)}`);
1032
+ }
1033
+ } finally {
1034
+ this.restoreCameraRange(r);
1035
+ }
1036
+ this.currentMode = e, this.requestRender();
1037
+ }
1038
+ /** 获取当前已请求的控制器模式。 */
1039
+ getMode() {
1040
+ return this.currentMode ?? v.GIS;
1041
+ }
1042
+ /** 切换到 3d-tiles-renderer 原生 GIS 控制器。 */
1043
+ switchToGIS() {
1044
+ this.switchController(v.GIS);
1045
+ }
1046
+ /** 切换到 v3 默认轨道控制器。 */
1047
+ switchToDefault(e) {
1048
+ this.switchController(v.Default, e);
1049
+ }
1050
+ /** 切换到 v3 第一人称控制器。 */
1051
+ switchToFirstPerson() {
1052
+ this.switchController(v.FirstPerson);
1053
+ }
1054
+ /** 进入 v3 第三人称控制器已有的落点选择流程。 */
1055
+ switchToThirdPerson(e) {
1056
+ this.switchController(v.ThirdPerson, { thirdPersonOptions: e });
1057
+ }
1058
+ /** 停止当前控制输入,具体控制器资源仍由各自所属模块释放。 */
1059
+ dispose() {
1060
+ this.disposed || (this.disposed = !0, this.gisControls.enabled = !1, this.removeThirdPersonPlacementResolver?.(), this.removeThirdPersonPlacementResolver = null, this.removeOrbitCenterResolver?.(), this.removeOrbitCenterResolver = null, this.engine.controlModule.disActive());
1061
+ }
1062
+ /** 重入或离开第三人称前,借助 v3 默认入口清理等待落点状态。 */
1063
+ leavePendingThirdPersonMode() {
1064
+ this.currentMode === v.ThirdPerson && this.engine.controlModule.switchDefaultMode({ preserveCameraPose: !0 });
1065
+ }
1066
+ /** 将 GlobeControls 的旋转支点适配为 v3 ViewCube 使用的 OrbitControls target。 */
1067
+ ensureViewCubeTargetCompatibility() {
1068
+ const e = this.gisControls;
1069
+ e.target?.clone || !e.pivotPoint || Object.defineProperty(e, "target", {
1070
+ configurable: !0,
1071
+ get: () => e.pivotPoint,
1072
+ set: (t) => e.pivotPoint?.copy(t)
1073
+ });
1074
+ }
1075
+ /** 记录 GIS 当前近远裁剪面,避免底层控制器切换改写地图可见范围。 */
1076
+ captureCameraRange() {
1077
+ const e = this.engine.camera;
1078
+ return typeof e?.near != "number" || typeof e.far != "number" ? null : { near: e.near, far: e.far };
1079
+ }
1080
+ /** 将切换前的裁剪范围应用到切换后的活动相机。 */
1081
+ restoreCameraRange(e) {
1082
+ if (!e) return;
1083
+ const t = this.engine.camera;
1084
+ t && (t.near === e.near && t.far === e.far || (t.near = e.near, t.far = e.far, t.updateProjectionMatrix?.()));
1085
+ }
1086
+ /** 从当前可见 GIS 图层中拾取地图、地形或模型表面。 */
1087
+ resolveGISPlacement = (e) => {
1088
+ const t = this.layerManager?.getLayers().filter((o) => o.visible && o.object.visible).map((o) => o.object) ?? [];
1089
+ if (t.length === 0) return null;
1090
+ const i = e.intersectObjects([...new Set(t)], !0).find((o) => o.object.isMesh === !0 && t.some((l) => this.isVisibleWithinLayer(o.object, l)));
1091
+ if (!i) return null;
1092
+ let s;
1093
+ i.normal instanceof f ? s = i.normal.clone().transformDirection(i.object.matrixWorld) : i.face?.normal instanceof f ? s = i.face.normal.clone().transformDirection(i.object.matrixWorld) : s = e.ray.direction.clone().negate();
1094
+ const a = e.ray.origin.clone().sub(i.point);
1095
+ return s.dot(a) < 0 && s.negate(), {
1096
+ point: i.point.clone(),
1097
+ normal: s.normalize(),
1098
+ distance: i.distance
1099
+ };
1100
+ };
1101
+ isVisibleWithinLayer(e, t) {
1102
+ let r = e;
1103
+ for (; r; ) {
1104
+ if (r.visible === !1) return !1;
1105
+ if (r === t) return !0;
1106
+ r = r.parent;
1107
+ }
1108
+ return !1;
1109
+ }
1110
+ }
1111
+ const R = Math.PI / 180, A = 180 / Math.PI, Z = 6378245, Q = 0.006693421622965943, B = 1e-5, Ue = 1e-9, Ne = 8;
1112
+ function he(n, e) {
1113
+ return n < 72.004 || n > 137.8347 || e < 0.8293 || e > 55.8271;
1114
+ }
1115
+ function We(n, e) {
1116
+ let t = -100 + 2 * n + 3 * e + 0.2 * e * e + 0.1 * n * e + 0.2 * Math.sqrt(Math.abs(n));
1117
+ return t += (20 * Math.sin(6 * n * Math.PI) + 20 * Math.sin(2 * n * Math.PI)) * 2 / 3, t += (20 * Math.sin(e * Math.PI) + 40 * Math.sin(e / 3 * Math.PI)) * 2 / 3, t += (160 * Math.sin(e / 12 * Math.PI) + 320 * Math.sin(e * Math.PI / 30)) * 2 / 3, t;
1118
+ }
1119
+ function _e(n, e) {
1120
+ let t = 300 + n + 2 * e + 0.1 * n * n + 0.1 * n * e + 0.1 * Math.sqrt(Math.abs(n));
1121
+ return t += (20 * Math.sin(6 * n * Math.PI) + 20 * Math.sin(2 * n * Math.PI)) * 2 / 3, t += (20 * Math.sin(n * Math.PI) + 40 * Math.sin(n / 3 * Math.PI)) * 2 / 3, t += (150 * Math.sin(n / 12 * Math.PI) + 300 * Math.sin(n / 30 * Math.PI)) * 2 / 3, t;
1122
+ }
1123
+ function ce(n, e, t = [0, 0]) {
1124
+ if (he(n, e))
1125
+ return t[0] = n, t[1] = e, t;
1126
+ const r = e * R, i = Math.sin(r), s = 1 - Q * i * i, a = Math.sqrt(s), o = We(n - 105, e - 35) * 180 / (Z * (1 - Q) / (s * a) * Math.PI), l = _e(n - 105, e - 35) * 180 / (Z / a * Math.cos(r) * Math.PI);
1127
+ return t[0] = n + l, t[1] = e + o, t;
1128
+ }
1129
+ function qe(n, e, t = [0, 0]) {
1130
+ if (he(n, e))
1131
+ return t[0] = n, t[1] = e, t;
1132
+ let r = n, i = e;
1133
+ const s = [0, 0];
1134
+ for (let a = 0; a < Ne; a += 1) {
1135
+ ce(r, i, s);
1136
+ const o = s[0] - n, l = s[1] - e;
1137
+ if (r -= o, i -= l, Math.max(Math.abs(o), Math.abs(l)) < Ue) break;
1138
+ }
1139
+ return t[0] = r, t[1] = i, t;
1140
+ }
1141
+ class $e {
1142
+ constructor(e) {
1143
+ this.base = e, this.scheme = e.scheme, this.tileCountX = e.tileCountX, this.tileCountY = e.tileCountY;
1144
+ }
1145
+ base;
1146
+ scheme;
1147
+ tileCountX;
1148
+ tileCountY;
1149
+ get isMercator() {
1150
+ return this.base.isMercator;
1151
+ }
1152
+ get isCartographic() {
1153
+ return this.base.isCartographic;
1154
+ }
1155
+ getDerivativeAtNormalizedPoint(e, t, r = [0, 0]) {
1156
+ const i = Math.max(e - B, 0), s = Math.min(e + B, 1), a = Math.max(t - B, 0), o = Math.min(t + B, 1), l = [0, 0], c = this.fromNormalizedToCartographic(i, t, l)[0], m = this.fromNormalizedToCartographic(s, t, l)[0];
1157
+ r[0] = Math.abs(m - c) / (s - i);
1158
+ const u = this.fromNormalizedToCartographic(e, a, l)[1], g = this.fromNormalizedToCartographic(e, o, l)[1];
1159
+ return r[1] = Math.abs(g - u) / (o - a), r;
1160
+ }
1161
+ getBounds() {
1162
+ return this.base.getBounds();
1163
+ }
1164
+ fromCartographicToNormalized(e, t, r = [0, 0]) {
1165
+ return ce(e * A, t * A, r), this.base.fromCartographicToNormalized(
1166
+ r[0] * R,
1167
+ r[1] * R,
1168
+ r
1169
+ );
1170
+ }
1171
+ fromCartographicToNormalizedRange(e) {
1172
+ return this.convertRange(e, (t, r, i) => this.fromCartographicToNormalized(t, r, i));
1173
+ }
1174
+ fromNormalizedToCartographic(e, t, r = [0, 0]) {
1175
+ return this.base.fromNormalizedToCartographic(e, t, r), qe(r[0] * A, r[1] * A, r), r[0] *= R, r[1] *= R, r;
1176
+ }
1177
+ fromNormalizedToCartographicRange(e) {
1178
+ return this.convertRange(e, (t, r, i) => this.fromNormalizedToCartographic(t, r, i));
1179
+ }
1180
+ getProjectedExtents() {
1181
+ return this.base.getProjectedExtents();
1182
+ }
1183
+ clampToBounds(e, t = !1) {
1184
+ return this.base.clampToBounds(e, t);
1185
+ }
1186
+ convertRange(e, t) {
1187
+ const r = [0, 0], i = [1 / 0, 1 / 0, -1 / 0, -1 / 0];
1188
+ return [
1189
+ [e[0], e[1]],
1190
+ [e[0], e[3]],
1191
+ [e[2], e[1]],
1192
+ [e[2], e[3]]
1193
+ ].forEach(([a, o]) => {
1194
+ t(a, o, r), i[0] = Math.min(i[0], r[0]), i[1] = Math.min(i[1], r[1]), i[2] = Math.max(i[2], r[0]), i[3] = Math.max(i[3], r[1]);
1195
+ }), i;
1196
+ }
1197
+ }
1198
+ const L = Math.PI / 180, K = 2, He = 35;
1199
+ class Je {
1200
+ name = "GIS_XYZ_WEBGPU_UV";
1201
+ processTileModel(e) {
1202
+ e.traverse((t) => {
1203
+ const i = t.geometry?.attributes?.uv;
1204
+ if (!(!i || t.userData.gisWebGpuUvCorrected)) {
1205
+ for (let s = 0; s < i.count; s += 1)
1206
+ i.setY(s, 1 - i.getY(s));
1207
+ i.needsUpdate = !0, t.userData.gisWebGpuUvCorrected = !0;
1208
+ }
1209
+ });
1210
+ }
1211
+ }
1212
+ class Ye {
1213
+ constructor(e, t = {}, r = !0, i) {
1214
+ this.engine = e, this.options = t, this.isWebGpuRenderer = r, this.layerManager = i;
1215
+ }
1216
+ engine;
1217
+ options;
1218
+ isWebGpuRenderer;
1219
+ layerManager;
1220
+ managedTilesets = /* @__PURE__ */ new Map();
1221
+ lifecycle = null;
1222
+ disposed = !1;
1223
+ imageConfigs = [
1224
+ {
1225
+ id: "cesium",
1226
+ name: "Cesium",
1227
+ type: "cesium-ion",
1228
+ // CesiumJS 1.145 官方评估 Token,官方声明于 2026-11-01 到期。
1229
+ token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6ImwwendUTkl3UVk5Uk5XdEkiLCJqdGkiOiJjZGFkZDZlNC0wMDI5LTQ5OTMtYWIwMC01ZWY4ZDVlNTBhMzYiLCJpZCI6MjU5LCJzdWIiOiJDZXNpdW1KUyIsImlzcyI6Imh0dHBzOi8vYXBpLmNlc2l1bS5jb20iLCJhdWQiOiIxLjE0NSBSZWxlYXNlIC0gRGVsZXRlIG9uIE5vdmVtYmVyIDEsIDIwMjYiLCJpYXQiOjE3ODc2MzQ3NzV9.3SPieoGxEZ22pnWLETAIFZveR0xXTzvOh8G4NYTxDSo",
1230
+ assetId: 2275207
1231
+ },
1232
+ {
1233
+ id: "gd",
1234
+ name: "高德地图",
1235
+ type: "xyz",
1236
+ url: "https://webrd01.is.autonavi.com/appmaptile?style=7&lang=zh_cn&size=1&scale=1&x={x}&y={y}&z={z}",
1237
+ levels: 19,
1238
+ coordinateSystem: "gcj02"
1239
+ },
1240
+ {
1241
+ id: "gd2",
1242
+ name: "高德卫星地图",
1243
+ type: "xyz",
1244
+ url: "https://webst01.is.autonavi.com/appmaptile?style=6&size=1&scl=1&x={x}&y={y}&z={z}",
1245
+ levels: 19,
1246
+ coordinateSystem: "gcj02"
1247
+ }
1248
+ ];
1249
+ /** 注册附加 Tileset 生命周期回调,业务管理器通过此入口维护校准等状态。 */
1250
+ setTilesetLifecycle(e) {
1251
+ this.lifecycle = e;
1252
+ }
1253
+ /** 加载初始化配置指定的影像,未指定时默认使用 Cesium。 */
1254
+ loadImage() {
1255
+ return this.options.source ? this.load3dtiles(this.options.source) : this.loadImageById(this.options.imageId ?? "cesium");
1256
+ }
1257
+ /** 按内置配置标识加载影像。 */
1258
+ loadImageById(e) {
1259
+ const t = this.imageConfigs.find((r) => r.id === e);
1260
+ return t ? t.type === "cesium-ion" ? this.load3dtiles({ type: "cesium-ion", assetId: t.assetId }) : this.load3dtiles({
1261
+ type: "xyz",
1262
+ url: t.url,
1263
+ levels: t.levels,
1264
+ coordinateSystem: t.coordinateSystem
1265
+ }) : Promise.reject(new RangeError(`未知的影像配置 id: ${e}`));
1266
+ }
1267
+ /** 按 URL 加载自定义 3D Tiles 影像。 */
1268
+ loadImageCustomize(e) {
1269
+ return this.load3dtiles({ type: "url", url: e });
1270
+ }
1271
+ /** 当前地形由 3D Tiles 影像一并提供。 */
1272
+ loadTerrain() {
1273
+ return this.loadImage();
1274
+ }
1275
+ /** 在当前 GIS 底图上叠加通用 3D Tiles,并接入统一渲染生命周期。 */
1276
+ async loadTileset(e, t = {}) {
1277
+ if (this.assertUsable(), typeof e != "string" || e.trim().length === 0)
1278
+ throw new TypeError("3D Tiles URL 不能为空。");
1279
+ const r = t.heightOffset ?? 0;
1280
+ if (!Number.isFinite(r))
1281
+ throw new RangeError("3D Tiles 高度偏移必须是有限数值。");
1282
+ const { tiles: i } = await this.load3dtiles({ type: "url", url: e.trim() });
1283
+ if (this.disposed)
1284
+ throw i.dispose(), new Error("GIS LoaderModule 已销毁。");
1285
+ t.fetchData && i.registerPlugin({
1286
+ name: `GIS_LOCAL_TILESET_${t.id ?? i.group.uuid}`,
1287
+ fetchData: t.fetchData
1288
+ });
1289
+ const s = {
1290
+ onNeedsUpdate: () => this.engine.requestRender(),
1291
+ onLoadError: (o) => {
1292
+ const l = o.error instanceof Error ? o.error : new Error(`3D Tiles 加载失败:${e}`);
1293
+ this.options.onError?.(l);
1294
+ }
1295
+ };
1296
+ let a;
1297
+ try {
1298
+ a = this.layerManager?.registerLayer({
1299
+ id: t.id,
1300
+ name: t.name?.trim() || e.trim(),
1301
+ type: "tileset",
1302
+ source: e.trim(),
1303
+ object: i.group,
1304
+ remove: () => this.removeTileset(i)
1305
+ });
1306
+ } catch (o) {
1307
+ throw i.dispose(), o;
1308
+ }
1309
+ return s.layerId = a?.id, i.group.name = a?.id ?? `gisTileset:${e}`, i.setCamera(this.engine.camera), i.setResolutionFromRenderer(this.engine.camera, this.engine.renderer), i.addEventListener("needs-update", s.onNeedsUpdate), i.addEventListener("load-error", s.onLoadError), this.engine.scene.add(i.group), this.managedTilesets.set(i, s), this.lifecycle?.onTilesetAdded?.(i, {
1310
+ id: s.layerId ?? i.group.name,
1311
+ name: a?.name ?? (t.name?.trim() || e.trim()),
1312
+ source: e.trim()
1313
+ }, !1), r === 0 ? this.registerTilesetReady(i, s) : this.registerHeightOffset(i, s, r), this.engine.requestRender(), i;
1314
+ }
1315
+ /** 移除附加的 3D Tiles,并释放其缓存与 GPU 资源。 */
1316
+ removeTileset(e) {
1317
+ const t = this.managedTilesets.get(e);
1318
+ return t ? (e.removeEventListener("needs-update", t.onNeedsUpdate), e.removeEventListener("load-error", t.onLoadError), t.onRootLoaded && e.removeEventListener("load-root-tileset", t.onRootLoaded), e.deleteCamera(this.engine.camera), this.engine.scene.remove(e.group), this.lifecycle?.onTilesetRemoved?.(e, t.layerId), e.dispose(), t.layerId && this.layerManager?.unregisterLayer(t.layerId), this.managedTilesets.delete(e), this.engine.requestRender(), !0) : !1;
1319
+ }
1320
+ /** 等待附加 Tileset 就绪,并把相机调整到可完整观察模型的位置。 */
1321
+ async flyToTileset(e, t = {}) {
1322
+ if (this.assertUsable(), !this.managedTilesets.has(e))
1323
+ throw new Error("只能跳转到当前 LoaderModule 管理的 3D Tiles。");
1324
+ const r = await this.waitForBoundingSphere(e, t.timeoutMs ?? 15e3), i = Math.max(1, t.padding ?? 1.25), s = this.engine.camera, a = r.center, o = this.engine.controls, l = new f(0, 1, 0);
1325
+ o?.getUpDirection?.(a, l), l.lengthSq() < 1e-8 && l.set(0, 1, 0), l.normalize();
1326
+ const c = s.position.clone().sub(a);
1327
+ if (c.addScaledVector(l, -c.dot(l)), c.lengthSq() < 1e-8 && (s.getWorldDirection(c).negate(), c.addScaledVector(l, -c.dot(l))), c.lengthSq() < 1e-8) {
1328
+ const w = Math.abs(l.y) < 0.9 ? new f(0, 1, 0) : new f(1, 0, 0);
1329
+ c.crossVectors(l, w);
1330
+ }
1331
+ c.normalize();
1332
+ const m = S.degToRad(S.clamp(
1333
+ t.elevation ?? He,
1334
+ 5,
1335
+ 85
1336
+ )), u = c.multiplyScalar(Math.cos(m)).addScaledVector(l, Math.sin(m)).normalize(), g = S.degToRad(s.fov), h = 2 * Math.atan(Math.tan(g / 2) * s.aspect), p = Math.max(1e-4, Math.min(g, h)), d = r.radius * i / Math.sin(p / 2);
1337
+ s.position.copy(a).addScaledVector(u, d), s.up.copy(l), s.lookAt(a), s.updateMatrix(), s.updateMatrixWorld(!0), (o?.target ?? o?.pivotPoint)?.copy(a), o?.resetState?.(), o?.update?.(0), this.engine.requestRender();
1338
+ }
1339
+ /** 更新全部附加瓦片的 LOD 调度。 */
1340
+ update() {
1341
+ this.managedTilesets.forEach((e, t) => {
1342
+ t.group.visible && t.update();
1343
+ });
1344
+ }
1345
+ /** 同步全部附加瓦片使用的屏幕分辨率。 */
1346
+ updateResolution() {
1347
+ this.managedTilesets.forEach((e, t) => {
1348
+ t.setResolutionFromRenderer(this.engine.camera, this.engine.renderer);
1349
+ });
1350
+ }
1351
+ /** 释放 LoaderModule 管理的附加瓦片;主底图仍由 GIS 实例释放。 */
1352
+ dispose() {
1353
+ this.disposed || ([...this.managedTilesets.keys()].forEach((e) => this.removeTileset(e)), this.disposed = !0);
1354
+ }
1355
+ /** 创建 TilesRenderer,并注册官方示例使用的扩展插件。 */
1356
+ async load3dtiles(e = { type: "cesium-ion" }) {
1357
+ this.assertUsable();
1358
+ const [t, r] = await Promise.all([
1359
+ import("./gis-tiles-vendor-DIbUOKJM.js").then((h) => h.i),
1360
+ import("./gis-tiles-vendor-DIbUOKJM.js").then((h) => h.a)
1361
+ ]), i = new t.TilesRenderer(e.type === "url" ? e.url : void 0), s = this.resolveLocalOrigin();
1362
+ if (s) {
1363
+ const h = new r.ReorientationPlugin({
1364
+ lat: s.latitude * L,
1365
+ lon: s.longitude * L,
1366
+ height: s.height,
1367
+ recenter: !0
1368
+ });
1369
+ i.registerPlugin(h), h.transformLatLonHeightToOrigin(
1370
+ s.latitude * L,
1371
+ s.longitude * L,
1372
+ s.height
1373
+ );
1374
+ }
1375
+ if (e.type === "xyz") {
1376
+ const h = e.levels ?? 19, p = new r.XYZTilesOverlay({
1377
+ url: e.url,
1378
+ levels: h,
1379
+ tileDimension: e.tileDimension ?? 256,
1380
+ projection: "EPSG:3857"
1381
+ });
1382
+ e.coordinateSystem === "gcj02" && await this.correctGcj02Overlay(p);
1383
+ const d = r.GeneratedSurfacePlugin;
1384
+ i.registerPlugin(new d({
1385
+ overlay: p,
1386
+ projection: "ellipsoid",
1387
+ applyOverlayTexture: !0
1388
+ })), this.isWebGpuRenderer && i.registerPlugin(new Je()), i.registerPlugin(new r.UpdateOnChangePlugin()), i.errorTarget = this.options.errorTarget ?? K, i.loadSiblings = this.options.loadSiblings ?? !0, i.loadAncestors = !1;
1389
+ const { CAMERA_FRAME: w } = t;
1390
+ return { tiles: i, Controls: t.GlobeControls, cameraFrame: w };
1391
+ }
1392
+ const [a, o, l, c] = await Promise.all([
1393
+ import("./gis-tiles-vendor-DIbUOKJM.js").then((h) => h.b),
1394
+ import("./three-vendor-CGmeNMm5.js").then((h) => h.cr),
1395
+ import("./three-vendor-CGmeNMm5.js").then((h) => h.cs),
1396
+ import("./three-vendor-CGmeNMm5.js").then((h) => h.ct)
1397
+ ]);
1398
+ if (e.type === "cesium-ion") {
1399
+ const h = this.imageConfigs.find(
1400
+ (p) => p.type === "cesium-ion"
1401
+ );
1402
+ i.registerPlugin(new a.CesiumIonAuthPlugin({
1403
+ apiToken: h.token,
1404
+ assetId: String(e.assetId ?? h.assetId),
1405
+ autoRefreshToken: e.autoRefreshToken ?? !0
1406
+ }));
1407
+ }
1408
+ const m = new o.DRACOLoader(), u = this.options.dracoDecoderPath ?? te("draco/", import.meta.url);
1409
+ m.setDecoderPath(u), i.registerPlugin(new r.GLTFExtensionsPlugin({
1410
+ dracoLoader: m,
1411
+ meshoptDecoder: c.MeshoptDecoder,
1412
+ autoDispose: !0
1413
+ })), this.options.creasedNormals !== !1 && i.registerPlugin(new l.TileCreasedNormalsPlugin({
1414
+ creaseAngle: (this.options.creaseAngle ?? 30) * L
1415
+ })), this.options.fade === !0 && i.registerPlugin(new r.TilesFadePlugin()), i.registerPlugin(new r.UpdateOnChangePlugin()), i.errorTarget = this.options.errorTarget ?? K, i.loadSiblings = this.options.loadSiblings ?? !0;
1416
+ const { CAMERA_FRAME: g } = t;
1417
+ return { tiles: i, Controls: t.GlobeControls, cameraFrame: g };
1418
+ }
1419
+ resolveLocalOrigin() {
1420
+ if (this.options.localOrigin === !1) return null;
1421
+ const e = { ...V, ...this.options.initialView }, t = this.options.localOrigin, r = t?.latitude ?? e.latitude, i = t?.longitude ?? e.longitude;
1422
+ if (!Number.isFinite(r) || r < -90 || r > 90)
1423
+ throw new RangeError("GIS 局部原点纬度必须是 -90 到 90 之间的有限数值。");
1424
+ if (!Number.isFinite(i) || i < -180 || i > 180)
1425
+ throw new RangeError("GIS 局部原点经度必须是 -180 到 180 之间的有限数值。");
1426
+ const s = t?.height ?? 0;
1427
+ if (!Number.isFinite(s))
1428
+ throw new RangeError("GIS 局部原点高度必须是有限数值。");
1429
+ return { latitude: r, longitude: i, height: s };
1430
+ }
1431
+ /** 将高德瓦片的 GCJ-02 投影适配到引擎统一使用的 WGS84 地表。 */
1432
+ async correctGcj02Overlay(e) {
1433
+ await e.init();
1434
+ const t = new $e(e.tiling.projection);
1435
+ e.tiling.setProjection(t), e.tiling.setContentBounds(...t.getBounds());
1436
+ }
1437
+ registerHeightOffset(e, t, r) {
1438
+ if (r === 0) return;
1439
+ const i = () => {
1440
+ const s = new G();
1441
+ if (!e.getBoundingSphere(s)) return;
1442
+ if (this.resolveLocalOrigin())
1443
+ e.group.position.y += r;
1444
+ else {
1445
+ const o = e.ellipsoid.getPositionToNormal(s.center, new f());
1446
+ e.group.updateWorldMatrix(!0, !1), o.transformDirection(e.group.matrixWorld), e.group.parent && (e.group.parent.updateWorldMatrix(!0, !1), o.transformDirection(new y().copy(e.group.parent.matrixWorld).invert())), e.group.position.addScaledVector(o, r);
1447
+ }
1448
+ e.group.updateMatrixWorld(!0), e.removeEventListener("load-root-tileset", i), t.onRootLoaded = void 0, this.lifecycle?.onTilesetReady?.(e), this.engine.requestRender();
1449
+ };
1450
+ t.onRootLoaded = i, e.addEventListener("load-root-tileset", i), e.root && i();
1451
+ }
1452
+ /** 根瓦片加载完成后再冻结校准基准,避免使用尚未建立的包围球锚点。 */
1453
+ registerTilesetReady(e, t) {
1454
+ const r = () => {
1455
+ const i = new G();
1456
+ e.getBoundingSphere(i) && (e.removeEventListener("load-root-tileset", r), t.onRootLoaded = void 0, this.lifecycle?.onTilesetReady?.(e));
1457
+ };
1458
+ t.onRootLoaded = r, e.addEventListener("load-root-tileset", r), e.root && r();
1459
+ }
1460
+ waitForBoundingSphere(e, t) {
1461
+ const r = () => {
1462
+ const s = new G();
1463
+ return e.getBoundingSphere(s) ? (e.group.updateWorldMatrix(!0, !1), s.applyMatrix4(e.group.matrixWorld)) : null;
1464
+ }, i = r();
1465
+ return i ? Promise.resolve(i) : new Promise((s, a) => {
1466
+ const o = () => {
1467
+ clearTimeout(m), e.removeEventListener("load-root-tileset", l), e.removeEventListener("load-error", c);
1468
+ }, l = () => {
1469
+ const u = r();
1470
+ u && (o(), s(u));
1471
+ }, c = (u) => {
1472
+ e.root || (o(), a(u.error ?? new Error("3D Tiles 根节点加载失败。")));
1473
+ }, m = setTimeout(() => {
1474
+ o(), a(new Error("等待 3D Tiles 根节点加载超时。"));
1475
+ }, Math.max(1, t));
1476
+ e.addEventListener("load-root-tileset", l), e.addEventListener("load-error", c);
1477
+ });
1478
+ }
1479
+ assertUsable() {
1480
+ if (this.disposed) throw new Error("GIS LoaderModule 已销毁。");
1481
+ }
1482
+ }
1483
+ const ee = "gis-local.invalid";
1484
+ class Xe {
1485
+ constructor(e, t, r, i, s) {
1486
+ this.engine = e, this.tilesLoader = t, this.coordinateModule = r, this.layerManager = i, this.calibrationManager = s;
1487
+ }
1488
+ engine;
1489
+ tilesLoader;
1490
+ coordinateModule;
1491
+ layerManager;
1492
+ calibrationManager;
1493
+ resources = /* @__PURE__ */ new Map();
1494
+ sequence = 0;
1495
+ disposed = !1;
1496
+ /** 按 URL 加载 3D Tiles、glTF/GLB、GeoJSON 或 v3 BIM 模型。 */
1497
+ async loadModel(e, t = {}) {
1498
+ this.assertUsable();
1499
+ const r = this.normalizeUrl(e), i = t.type ?? this.inferUrlType(r);
1500
+ switch (i) {
1501
+ case "3dtiles":
1502
+ return this.loadTileset(r, t);
1503
+ case "gltf":
1504
+ return this.loadGltfUrl(r, t);
1505
+ case "geojson":
1506
+ return this.loadGeoJsonUrl(r, t);
1507
+ case "bim":
1508
+ return this.loadBimUrl(r, t);
1509
+ default:
1510
+ throw new RangeError(`不支持的 GIS 模型类型:${String(i)}`);
1511
+ }
1512
+ }
1513
+ /**
1514
+ * 加载浏览器选择的本地资源文件。
1515
+ * 支持 3D Tiles 文件集、glTF/GLB、GeoJSON 和 v3 BIM 包,不包含 SHP。
1516
+ */
1517
+ async loadModelFile(e, t = {}) {
1518
+ this.assertUsable();
1519
+ const r = Array.from(e ?? []);
1520
+ if (r.length === 0) throw new TypeError("本地模型文件不能为空。");
1521
+ const i = r.find((o) => this.baseName(o).toLowerCase() === "tileset.json");
1522
+ if (i) return [await this.loadLocalTileset(r, i, t)];
1523
+ const s = r.find((o) => /\.(?:glb|gltf)$/i.test(o.name));
1524
+ if (s) return [await this.loadLocalGltf(r, s, t)];
1525
+ const a = r.find((o) => /\.(?:geojson|json)$/i.test(o.name));
1526
+ if (a) {
1527
+ const o = JSON.parse(await a.text());
1528
+ if (this.isGeoJson(o))
1529
+ return [this.createGeoJsonModel(o, a.name, t)];
1530
+ }
1531
+ return this.loadLocalBim(r, t);
1532
+ }
1533
+ /** 通过 SDK ViewToken 解析资源,再进入相同的类型分流和生命周期。 */
1534
+ async loadModelByViewToken(e, t = {}) {
1535
+ if (this.assertUsable(), typeof e != "string" || e.trim().length === 0)
1536
+ throw new TypeError("ViewToken 不能为空。");
1537
+ const r = await Ie(
1538
+ this.engine.options,
1539
+ e.trim(),
1540
+ "3d"
1541
+ ), i = r.urls?.length > 0 ? r.urls : [r.url], s = t.type ?? this.inferResolvedType(
1542
+ r.fileFormat,
1543
+ r.assetCategory,
1544
+ i[0]
1545
+ ), a = [];
1546
+ for (let o = 0; o < i.length; o += 1)
1547
+ a.push(await this.loadModel(i[o], {
1548
+ ...t,
1549
+ id: o === 0 ? t.id : void 0,
1550
+ type: s
1551
+ }));
1552
+ return a;
1553
+ }
1554
+ /** 移除统一资源句柄对应的模型和图层。 */
1555
+ removeModel(e) {
1556
+ const t = typeof e == "string" ? e : e.id, r = this.resources.get(t);
1557
+ return r ? (this.resources.delete(t), this.layerManager.unregisterLayer(t), this.calibrationManager.unregister(t), r.model.type === "bim" && this.calibrationManager.unregister(r.model.source), r.release(), this.engine.events.trigger("gis:model-removed", r.model), this.engine.requestRender(), !0) : !1;
1558
+ }
1559
+ /** 查询由本模块加载的全部资源句柄。 */
1560
+ getModels() {
1561
+ return [...this.resources.values()].map(({ model: e }) => ({ ...e }));
1562
+ }
1563
+ /** 释放独立资源;3D Tiles 与 BIM 分别交还各自底层加载器卸载。 */
1564
+ dispose() {
1565
+ this.disposed || ([...this.resources.keys()].forEach((e) => this.removeModel(e)), this.disposed = !0);
1566
+ }
1567
+ async loadTileset(e, t) {
1568
+ const r = await this.tilesLoader.loadTileset(e, t);
1569
+ t.flyTo !== !1 && await this.tilesLoader.flyToTileset(r);
1570
+ const i = this.layerManager.getLayer(r.group.name), s = {
1571
+ id: i?.id ?? r.group.name,
1572
+ name: i?.name ?? t.name ?? e,
1573
+ type: "3dtiles",
1574
+ source: e,
1575
+ object: r.group,
1576
+ raw: r
1577
+ };
1578
+ return this.track(s, () => {
1579
+ this.tilesLoader.removeTileset(r);
1580
+ });
1581
+ }
1582
+ async loadGltfUrl(e, t) {
1583
+ const r = await this.parseGltf(e);
1584
+ return this.addStandaloneModel(r, e, "gltf", t);
1585
+ }
1586
+ async loadLocalGltf(e, t, r) {
1587
+ const i = [], s = /* @__PURE__ */ new Map();
1588
+ e.forEach((o) => {
1589
+ const l = URL.createObjectURL(o);
1590
+ i.push(l);
1591
+ const c = this.filePath(o);
1592
+ s.set(c, l), s.set(this.baseName(o), l);
1593
+ });
1594
+ const a = new fe();
1595
+ a.setURLModifier((o) => {
1596
+ const l = this.normalizePath(decodeURIComponent(o));
1597
+ return s.get(l) ?? s.get(this.baseNameFromPath(l)) ?? o;
1598
+ });
1599
+ try {
1600
+ const o = s.get(this.filePath(t)), l = await this.parseGltf(o, a);
1601
+ return this.addStandaloneModel(l, t.name, "gltf", r, () => {
1602
+ i.forEach((c) => URL.revokeObjectURL(c));
1603
+ });
1604
+ } catch (o) {
1605
+ throw i.forEach((l) => URL.revokeObjectURL(l)), o;
1606
+ }
1607
+ }
1608
+ async parseGltf(e, t) {
1609
+ const [{ GLTFLoader: r }, { DRACOLoader: i }, { MeshoptDecoder: s }] = await Promise.all([
1610
+ import("./three-vendor-CGmeNMm5.js").then((l) => l.cq),
1611
+ import("./three-vendor-CGmeNMm5.js").then((l) => l.cr),
1612
+ import("./three-vendor-CGmeNMm5.js").then((l) => l.ct)
1613
+ ]), a = new i(t);
1614
+ a.setDecoderPath(te("draco/", import.meta.url));
1615
+ const o = new r(t);
1616
+ o.setDRACOLoader(a), o.setMeshoptDecoder(s);
1617
+ try {
1618
+ return (await o.loadAsync(e)).scene;
1619
+ } finally {
1620
+ a.dispose();
1621
+ }
1622
+ }
1623
+ async loadGeoJsonUrl(e, t) {
1624
+ const r = await fetch(e);
1625
+ if (!r.ok) throw new Error(`GeoJSON 加载失败(${r.status}):${e}`);
1626
+ const i = await r.json();
1627
+ if (!this.isGeoJson(i)) throw new TypeError(`资源不是有效的 GeoJSON:${e}`);
1628
+ return this.createGeoJsonModel(i, e, t);
1629
+ }
1630
+ createGeoJsonModel(e, t, r) {
1631
+ const i = [], s = [], a = (u) => {
1632
+ if (!Array.isArray(u) || u.length < 2) return;
1633
+ const g = Number(u[0]), h = Number(u[1]), p = u.length > 2 ? Number(u[2]) : r.height ?? 0, d = this.coordinateModule.toWorld({ longitude: g, latitude: h, height: p });
1634
+ i.push(d.x, d.y, d.z);
1635
+ }, o = (u, g = !1) => {
1636
+ if (!Array.isArray(u) || u.length < 2) return;
1637
+ const h = u.map((d) => !Array.isArray(d) || d.length < 2 ? null : this.coordinateModule.toWorld({
1638
+ longitude: Number(d[0]),
1639
+ latitude: Number(d[1]),
1640
+ height: d.length > 2 ? Number(d[2]) : r.height ?? 0
1641
+ })).filter((d) => d !== null), p = g ? h.length : h.length - 1;
1642
+ for (let d = 0; d < p; d += 1) {
1643
+ const w = h[d], C = h[(d + 1) % h.length];
1644
+ s.push(w.x, w.y, w.z, C.x, C.y, C.z);
1645
+ }
1646
+ }, l = (u) => {
1647
+ if (!u) return;
1648
+ const g = u.coordinates;
1649
+ switch (u.type) {
1650
+ case "Point":
1651
+ a(g);
1652
+ break;
1653
+ case "MultiPoint":
1654
+ g?.forEach(a);
1655
+ break;
1656
+ case "LineString":
1657
+ o(g);
1658
+ break;
1659
+ case "MultiLineString":
1660
+ g?.forEach((h) => o(h));
1661
+ break;
1662
+ case "Polygon":
1663
+ g?.forEach((h) => o(h, !0));
1664
+ break;
1665
+ case "MultiPolygon":
1666
+ g?.forEach((h) => {
1667
+ Array.isArray(h) && h.forEach((p) => o(p, !0));
1668
+ });
1669
+ break;
1670
+ case "GeometryCollection":
1671
+ u.geometries?.forEach(l);
1672
+ break;
1673
+ }
1674
+ }, c = (u) => {
1675
+ u.type === "FeatureCollection" ? u.features?.forEach(c) : u.type === "Feature" ? l(u.geometry) : l(u);
1676
+ };
1677
+ c(e);
1678
+ const m = new O();
1679
+ if (i.length > 0) {
1680
+ const u = new H();
1681
+ u.setAttribute("position", new J(i, 3)), m.add(new ye(u, new Me({ color: 16763955, size: 6 })));
1682
+ }
1683
+ if (s.length > 0) {
1684
+ const u = new H();
1685
+ u.setAttribute("position", new J(s, 3)), m.add(new be(u, new we({ color: 2598888 })));
1686
+ }
1687
+ if (m.children.length === 0)
1688
+ throw new TypeError(`GeoJSON 不包含可显示的点、线或多边形:${t}`);
1689
+ return this.addStandaloneModel(m, t, "geojson", r);
1690
+ }
1691
+ async loadBimUrl(e, t) {
1692
+ await this.engine.loaderModule.loadModels([e], t);
1693
+ const r = b(this.engine, e);
1694
+ if (!r) throw new Error(`BIM 模型加载完成后未找到根对象:${e}`);
1695
+ return this.addBimModel(e, r, t);
1696
+ }
1697
+ async loadLocalBim(e, t) {
1698
+ const r = new Set(
1699
+ this.engine.models.map((s) => s?.url).filter((s) => typeof s == "string")
1700
+ );
1701
+ await this.engine.loaderModule.loadModelFile(e, t);
1702
+ const i = this.engine.models.map((s) => s?.url).filter((s) => typeof s == "string" && !r.has(s));
1703
+ if (i.length === 0) throw new Error("本地 BIM 已加载,但未发现新的模型根对象。");
1704
+ return i.map((s, a) => {
1705
+ const o = b(this.engine, s);
1706
+ if (!o) throw new Error(`本地 BIM 缺少模型根对象:${s}`);
1707
+ return this.addBimModel(s, o, {
1708
+ ...t,
1709
+ id: a === 0 ? t.id : void 0,
1710
+ name: a === 0 ? t.name : void 0
1711
+ });
1712
+ });
1713
+ }
1714
+ addBimModel(e, t, r) {
1715
+ const i = this.resolveId(r.id, "bim"), s = {
1716
+ id: i,
1717
+ name: r.name?.trim() || this.baseNameFromPath(e) || e,
1718
+ type: "bim",
1719
+ source: e,
1720
+ object: t,
1721
+ raw: t
1722
+ }, a = this.track(s, () => this.engine.loaderModule.unloadModel(e), "model");
1723
+ return this.calibrationManager.registerBimModel(e, { id: i, name: s.name }), a;
1724
+ }
1725
+ addStandaloneModel(e, t, r, i, s) {
1726
+ const a = this.resolveId(i.id, r);
1727
+ e.name = a, this.engine.scene.add(e);
1728
+ const o = {
1729
+ id: a,
1730
+ name: i.name?.trim() || this.baseNameFromPath(t) || t,
1731
+ type: r,
1732
+ source: t,
1733
+ object: e,
1734
+ raw: e
1735
+ };
1736
+ return this.track(o, () => {
1737
+ this.engine.scene.remove(e), this.disposeObject(e), s?.();
1738
+ }, r === "geojson" ? "vector" : "model");
1739
+ }
1740
+ track(e, t, r = "tileset") {
1741
+ if (this.resources.has(e.id))
1742
+ throw t(), new RangeError(`GIS 模型 id 已存在:${e.id}`);
1743
+ return this.layerManager.registerLayer({
1744
+ id: e.id,
1745
+ name: e.name,
1746
+ type: r,
1747
+ source: e.source,
1748
+ object: e.object,
1749
+ remove: () => this.removeModel(e.id)
1750
+ }), this.resources.set(e.id, { model: e, release: t }), this.engine.events.trigger("gis:model-loaded", e), this.engine.requestRender(), e;
1751
+ }
1752
+ async loadLocalTileset(e, t, r) {
1753
+ const i = `tileset-${Date.now()}-${++this.sequence}`, s = `https://${ee}/${i}/`, a = this.filePath(t), o = a.includes("/") ? a.slice(0, a.lastIndexOf("/") + 1) : "", l = /* @__PURE__ */ new Map();
1754
+ e.forEach((p) => {
1755
+ const d = this.filePath(p), w = o && d.startsWith(o) ? d.slice(o.length) : d;
1756
+ l.set(this.normalizePath(w), p);
1757
+ });
1758
+ const c = new URL(this.baseName(t), s).toString(), m = async (p) => {
1759
+ const d = new URL(p);
1760
+ if (d.host !== ee) return fetch(p);
1761
+ const w = `/${i}/`, C = this.normalizePath(decodeURIComponent(d.pathname.split(w)[1] ?? "")), P = l.get(C);
1762
+ return P ? new Response(P, {
1763
+ status: 200,
1764
+ headers: P.type ? { "Content-Type": P.type } : void 0
1765
+ }) : new Response(`本地 3D Tiles 文件不存在:${C}`, { status: 404 });
1766
+ }, u = await this.tilesLoader.loadTileset(c, {
1767
+ id: r.id,
1768
+ name: r.name ?? t.name,
1769
+ fetchData: m
1770
+ }), g = this.layerManager.getLayer(u.group.name), h = {
1771
+ id: g?.id ?? u.group.name,
1772
+ name: g?.name ?? r.name ?? t.name,
1773
+ type: "3dtiles",
1774
+ source: t.name,
1775
+ object: u.group,
1776
+ raw: u
1777
+ };
1778
+ return this.track(h, () => {
1779
+ this.tilesLoader.removeTileset(u);
1780
+ });
1781
+ }
1782
+ disposeObject(e) {
1783
+ e.traverse((t) => {
1784
+ const r = t;
1785
+ r.geometry?.dispose(), (Array.isArray(r.material) ? r.material : r.material ? [r.material] : []).forEach((s) => {
1786
+ Object.values(s).forEach((a) => {
1787
+ const o = a;
1788
+ o?.isTexture && o.dispose();
1789
+ }), s.dispose();
1790
+ });
1791
+ });
1792
+ }
1793
+ inferUrlType(e) {
1794
+ const t = e.split(/[?#]/, 1)[0].toLowerCase();
1795
+ return /(?:^|\/)tileset\.json$/.test(t) ? "3dtiles" : /\.geojson$/.test(t) ? "geojson" : /\.(?:glb|gltf)$/.test(t) ? "gltf" : "bim";
1796
+ }
1797
+ inferResolvedType(e, t, r) {
1798
+ const i = `${e ?? ""} ${t ?? ""} ${r}`.toLowerCase();
1799
+ return /3d.?tiles|tileset\.json/.test(i) ? "3dtiles" : /geojson/.test(i) ? "geojson" : /\bglb\b|\bgltf\b|\.(?:glb|gltf)(?:[?#]|$)/.test(i) ? "gltf" : "bim";
1800
+ }
1801
+ isGeoJson(e) {
1802
+ return !!(e && [
1803
+ "FeatureCollection",
1804
+ "Feature",
1805
+ "GeometryCollection",
1806
+ "Point",
1807
+ "MultiPoint",
1808
+ "LineString",
1809
+ "MultiLineString",
1810
+ "Polygon",
1811
+ "MultiPolygon"
1812
+ ].includes(e.type ?? ""));
1813
+ }
1814
+ resolveId(e, t) {
1815
+ const r = e?.trim() || `${t}-${++this.sequence}`;
1816
+ if (this.resources.has(r) || this.layerManager.getLayer(r))
1817
+ throw new RangeError(`GIS 模型 id 已存在:${r}`);
1818
+ return r;
1819
+ }
1820
+ normalizeUrl(e) {
1821
+ if (typeof e != "string" || e.trim().length === 0)
1822
+ throw new TypeError("模型 URL 不能为空。");
1823
+ return e.trim();
1824
+ }
1825
+ filePath(e) {
1826
+ return this.normalizePath(e.webkitRelativePath || e.name);
1827
+ }
1828
+ baseName(e) {
1829
+ return this.baseNameFromPath(this.filePath(e));
1830
+ }
1831
+ baseNameFromPath(e) {
1832
+ return this.normalizePath(e).split("/").pop() ?? "";
1833
+ }
1834
+ normalizePath(e) {
1835
+ return e.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
1836
+ }
1837
+ assertUsable() {
1838
+ if (this.disposed) throw new Error("GISResourceLoaderModule 已销毁。");
1839
+ }
1840
+ }
1841
+ class Ze {
1842
+ constructor(e, t) {
1843
+ this.options = t, this.engine = e, this.runtimeEngine = e, this.layerManager = new xe(this.runtimeEngine), this.activeBaseSource = t.source ?? t.imageId ?? "cesium";
1844
+ }
1845
+ options;
1846
+ engine;
1847
+ tiles = null;
1848
+ loaderModule;
1849
+ resourceLoaderModule = null;
1850
+ layerManager;
1851
+ coordinateModule = null;
1852
+ controls = null;
1853
+ cameraModule = null;
1854
+ controllerModule = null;
1855
+ modelPositionManager = null;
1856
+ modelCalibrationManager = null;
1857
+ label = null;
1858
+ viewCube = null;
1859
+ skyManager = null;
1860
+ resizeObserver = null;
1861
+ removeFrameCallback = null;
1862
+ previousTime = 0;
1863
+ disposed = !1;
1864
+ baseMapSwitchVersion = 0;
1865
+ activeBaseSource;
1866
+ runtimeEngine;
1867
+ /** 按 Three.js 官方示例注册插件、定位相机并启动瓦片调度。 */
1868
+ async initialize() {
1869
+ this.loaderModule = new Ye(
1870
+ this.runtimeEngine,
1871
+ this.options,
1872
+ this.usesWebGpuBackend(),
1873
+ this.layerManager
1874
+ );
1875
+ const { tiles: e, Controls: t, cameraFrame: r } = await this.loaderModule.loadImage();
1876
+ if (this.disposed) throw new Error("GIS 已在初始化完成前销毁。");
1877
+ return e.setCamera(this.runtimeEngine.camera), e.setResolutionFromRenderer(this.runtimeEngine.camera, this.runtimeEngine.renderer), this.runtimeEngine.scene.add(e.group), this.tiles = e, this.layerManager.registerLayer({
1878
+ id: "base-imagery",
1879
+ name: "GIS 底图",
1880
+ type: "imagery",
1881
+ source: this.getBaseLayerSource(),
1882
+ object: e.group
1883
+ }), this.runtimeEngine.viewCube?.setHomeBoundingBoxProvider?.(
1884
+ () => this.layerManager.getModelBoundingBox()
1885
+ ), this.coordinateModule = new ze(e, r), this.modelCalibrationManager = new Oe(
1886
+ this.runtimeEngine,
1887
+ this.coordinateModule
1888
+ ), this.resourceLoaderModule = new Xe(
1889
+ this.runtimeEngine,
1890
+ this.loaderModule,
1891
+ this.coordinateModule,
1892
+ this.layerManager,
1893
+ this.modelCalibrationManager
1894
+ ), this.loaderModule.setTilesetLifecycle({
1895
+ onTilesetAdded: (i, s, a) => {
1896
+ this.modelCalibrationManager?.registerTileset(
1897
+ s.id,
1898
+ s.name,
1899
+ s.source,
1900
+ i,
1901
+ a
1902
+ );
1903
+ },
1904
+ onTilesetReady: (i) => {
1905
+ this.modelCalibrationManager?.markTilesetReady(i);
1906
+ },
1907
+ onTilesetRemoved: (i, s) => {
1908
+ s && this.modelCalibrationManager?.unregister(s);
1909
+ }
1910
+ }), this.modelPositionManager = new De(
1911
+ this.runtimeEngine,
1912
+ e,
1913
+ this.layerManager,
1914
+ this.modelCalibrationManager
1915
+ ), this.cameraModule = new Y(
1916
+ this.runtimeEngine.camera,
1917
+ e,
1918
+ r,
1919
+ this.requestRender,
1920
+ this.options.initialView,
1921
+ this.coordinateModule
1922
+ ), this.configureControls(t), this.cameraModule.setControls(this.controls), this.controllerModule = new X(
1923
+ this.runtimeEngine,
1924
+ this.controls,
1925
+ this.requestRender,
1926
+ this.layerManager
1927
+ ), this.controllerModule.switchToGIS(), this.viewCube = new Re(
1928
+ this.cameraModule,
1929
+ { ...V, ...this.options.initialView }
1930
+ ), this.label = new Le(
1931
+ this.runtimeEngine,
1932
+ this.coordinateModule,
1933
+ this.layerManager
1934
+ ), this.options.temporalAntialiasing !== !0 && this.runtimeEngine.composerModule.isEffectEnabled?.("traa") && this.runtimeEngine.composerModule.setEffectEnabled("traa", !1), this.skyManager = new je(this.runtimeEngine), this.options.skyBox ? await this.skyManager.setSkyBox(this.options.skyBox) : this.options.sky === void 0 ? await this.skyManager.setSkyBox() : this.skyManager.setSky(this.options.sky), e.addEventListener("needs-update", this.requestRender), e.addEventListener("load-error", this.handleLoadError), this.resizeObserver = new ResizeObserver(this.updateResolution), this.resizeObserver.observe(this.runtimeEngine.container), this.runtimeEngine.setIdleFrameRateExempt("gis-3d-tiles", !0), this.removeFrameCallback = this.runtimeEngine.addFrameCallback(this.update), this;
1935
+ }
1936
+ /** 返回 GIS 是否已完成初始化且尚未销毁。 */
1937
+ isInitialized() {
1938
+ return !this.disposed && this.tiles !== null && this.cameraModule !== null;
1939
+ }
1940
+ /** 兼容旧版业务入口,加载 3D Tiles 并返回稳定模型句柄。 */
1941
+ async load3DTiles(e) {
1942
+ return await this.requireResourceLoader().loadModel(e.url, {
1943
+ ...e,
1944
+ type: "3dtiles"
1945
+ });
1946
+ }
1947
+ /** 统一加载在线 3D Tiles、glTF/GLB、GeoJSON 或 BIM 模型。 */
1948
+ async loadModel(e, t) {
1949
+ return this.requireResourceLoader().loadModel(e, t);
1950
+ }
1951
+ /** 加载本地 3D Tiles 文件集、glTF/GLB、GeoJSON 或 BIM 文件包。 */
1952
+ async loadModelFile(e, t) {
1953
+ return this.requireResourceLoader().loadModelFile(e, t);
1954
+ }
1955
+ /** 解析 ViewToken 并通过统一 GIS 资源加载链路加载。 */
1956
+ async loadModelByViewToken(e, t) {
1957
+ return this.requireResourceLoader().loadModelByViewToken(e, t);
1958
+ }
1959
+ /** 按统一模型句柄或 id 卸载资源。 */
1960
+ removeModel(e) {
1961
+ return this.resourceLoaderModule?.removeModel(e) ?? !1;
1962
+ }
1963
+ /**
1964
+ * 运行时切换 GIS 底图,并保留当前 WGS84 相机视角和业务图层。
1965
+ * 新底图加载失败时不会修改当前场景。
1966
+ */
1967
+ async switchBaseMap(e) {
1968
+ if (!this.tiles || !this.cameraModule || !this.coordinateModule)
1969
+ throw new Error("GIS 尚未完成初始化。");
1970
+ const t = ++this.baseMapSwitchVersion, r = this.cameraModule.getView(), i = this.controllerModule?.getMode(), s = typeof e == "string" ? await this.loaderModule.loadImageById(e) : await this.loaderModule.load3dtiles(e);
1971
+ if (this.disposed || t !== this.baseMapSwitchVersion) {
1972
+ if (s.tiles.dispose(), this.disposed) throw new Error("GIS 已销毁。");
1973
+ return;
1974
+ }
1975
+ const a = this.tiles;
1976
+ a.removeEventListener("needs-update", this.requestRender), a.removeEventListener("load-error", this.handleLoadError), a.deleteCamera(this.runtimeEngine.camera), this.runtimeEngine.scene.remove(a.group), this.layerManager.unregisterLayer("base-imagery"), this.cameraModule.dispose(), this.controllerModule?.dispose(), this.controls?.dispose(), this.tiles = s.tiles, this.activeBaseSource = e, s.tiles.setCamera(this.runtimeEngine.camera), s.tiles.setResolutionFromRenderer(this.runtimeEngine.camera, this.runtimeEngine.renderer), this.runtimeEngine.scene.add(s.tiles.group), this.layerManager.registerLayer({
1977
+ id: "base-imagery",
1978
+ name: "GIS 底图",
1979
+ type: "imagery",
1980
+ source: this.getBaseLayerSource(),
1981
+ object: s.tiles.group
1982
+ }), this.coordinateModule.setTiles(s.tiles, s.cameraFrame), this.modelPositionManager?.setTiles(s.tiles), this.cameraModule = new Y(
1983
+ this.runtimeEngine.camera,
1984
+ s.tiles,
1985
+ s.cameraFrame,
1986
+ this.requestRender,
1987
+ r,
1988
+ this.coordinateModule
1989
+ ), this.configureControls(s.Controls), this.cameraModule.setControls(this.controls), this.controllerModule = new X(
1990
+ this.runtimeEngine,
1991
+ this.controls,
1992
+ this.requestRender,
1993
+ this.layerManager
1994
+ ), this.controllerModule.switchController(i ?? "gis"), this.viewCube?.setCameraModule(this.cameraModule), s.tiles.addEventListener("needs-update", this.requestRender), s.tiles.addEventListener("load-error", this.handleLoadError), a.dispose(), this.runtimeEngine.events.trigger("gis:base-map-changed", {
1995
+ source: e,
1996
+ timestamp: Date.now()
1997
+ }), this.runtimeEngine.requestRender();
1998
+ }
1999
+ /** 同时支持 3D Tiles 图层 id 和 BIM 模型 URL 的 ENU 校准。 */
2000
+ applyCalibration(e) {
2001
+ return this.modelCalibrationManager?.setTransform(e) ?? !1;
2002
+ }
2003
+ /** 返回全部 3D Tiles 与 BIM 校准状态。 */
2004
+ getModelCalibrationItems() {
2005
+ return this.modelCalibrationManager?.getItems() ?? [];
2006
+ }
2007
+ /** 恢复指定 3D Tiles 或 BIM 模型的校准基准矩阵。 */
2008
+ resetModelCalibration(e) {
2009
+ return this.modelCalibrationManager?.reset(e) ?? !1;
2010
+ }
2011
+ /** 释放控制器、瓦片缓存、事件以及 v3 引擎持有的 GPU 资源。 */
2012
+ dispose() {
2013
+ this.disposed || (this.disposed = !0, this.baseMapSwitchVersion += 1, this.removeFrameCallback?.(), this.removeFrameCallback = null, this.runtimeEngine.setIdleFrameRateExempt("gis-3d-tiles", !1), this.resizeObserver?.disconnect(), this.label?.dispose(), this.controllerModule?.dispose(), this.skyManager?.dispose(), this.cameraModule?.dispose(), this.modelPositionManager?.dispose(), this.controls?.dispose(), this.resourceLoaderModule?.dispose(), this.loaderModule?.dispose(), this.modelCalibrationManager?.dispose(), this.runtimeEngine.viewCube?.setHomeBoundingBoxProvider?.(null), this.tiles && (this.tiles.removeEventListener("needs-update", this.requestRender), this.tiles.removeEventListener("load-error", this.handleLoadError), this.tiles.deleteCamera(this.runtimeEngine.camera), this.runtimeEngine.scene.remove(this.tiles.group), this.tiles.dispose()), this.layerManager.dispose(), this.controls = null, this.tiles = null, this.coordinateModule = null, this.cameraModule = null, this.controllerModule = null, this.modelPositionManager = null, this.modelCalibrationManager = null, this.resourceLoaderModule = null, this.label = null, this.viewCube = null, this.skyManager = null, this.runtimeEngine.dispose());
2014
+ }
2015
+ configureControls(e) {
2016
+ this.runtimeEngine.controlModule.disActive();
2017
+ const t = new e(this.tiles.group, this.runtimeEngine.camera, this.runtimeEngine.renderer.domElement);
2018
+ this.disableIncompatiblePivotMarker(t), t.setEllipsoid(this.tiles.ellipsoid, this.tiles.group), t.enableDamping = !0, t.cameraRadius = 0, t.minDistance = 0, t.adjustHeight = !1, t.addEventListener("change", this.requestRender), this.controls = t;
2019
+ }
2020
+ /** WebGPU 不编译 GlobeControls 内置的 GLSL ShaderMaterial,因此禁用该辅助圆圈。 */
2021
+ disableIncompatiblePivotMarker(e) {
2022
+ if (!this.usesWebGpuBackend()) return;
2023
+ const t = e.pivotMesh;
2024
+ if (!t?.material) return;
2025
+ const i = (Array.isArray(t.material) ? t.material : [t.material]).filter((s) => s.isShaderMaterial === !0 && s.isNodeMaterial !== !0);
2026
+ i.length !== 0 && (i.forEach((s) => {
2027
+ s.visible = !1;
2028
+ }), t.visible = !1);
2029
+ }
2030
+ /** WebGPURenderer 可承载 WebGPU 或 WebGL2,兼容逻辑必须按实际后端判断。 */
2031
+ usesWebGpuBackend() {
2032
+ return this.runtimeEngine.renderer.coordinateSystem === ve;
2033
+ }
2034
+ update = (e) => {
2035
+ if (this.disposed) return;
2036
+ const t = this.previousTime === 0 ? 0 : (e - this.previousTime) / 1e3;
2037
+ this.previousTime = e, this.cameraModule?.update(e), this.controls?.enabled && this.controls.update(t), this.tiles?.group.visible && this.tiles.update(), this.loaderModule?.update(), this.label?.update(), this.runtimeEngine.requestRender();
2038
+ };
2039
+ updateResolution = () => {
2040
+ this.tiles?.setResolutionFromRenderer(this.runtimeEngine.camera, this.runtimeEngine.renderer), this.loaderModule?.updateResolution(), this.runtimeEngine.requestRender();
2041
+ };
2042
+ requestRender = () => {
2043
+ this.runtimeEngine.requestRender();
2044
+ };
2045
+ handleLoadError = (e) => {
2046
+ const t = e.error instanceof Error ? e.error : new Error("3D Tiles 加载失败。");
2047
+ this.options.onError?.(t);
2048
+ };
2049
+ getBaseLayerSource() {
2050
+ const e = this.activeBaseSource;
2051
+ return typeof e == "string" ? e : e.type === "url" || e.type === "xyz" ? e.url : `cesium-ion:${e.assetId ?? 2275207}`;
2052
+ }
2053
+ requireResourceLoader() {
2054
+ if (!this.resourceLoaderModule) throw new Error("GIS 尚未完成初始化。");
2055
+ return this.resourceLoaderModule;
2056
+ }
2057
+ }
2058
+ async function Qe(n) {
2059
+ n.assetFiles ? await F(n.assetFiles) : n.assetFile ? await re(n.assetFile) : n.assetBaseUrl && await F(null), n.assetBaseUrl && ie(n.assetBaseUrl);
2060
+ }
2061
+ async function mt(n, e = {}) {
2062
+ await Qe(n);
2063
+ const t = n.engineVersion === "v2" ? new (await import("./engine-3d-DnlK2vTK.js").then((i) => i.e)).EngineKernelV2(n) : new (await import("./EngineKernel-BucDRg23.js")).EngineKernelV3(n), r = new Ze(t, e);
2064
+ try {
2065
+ return "ready" in t && await t.ready, await r.initialize();
2066
+ } catch (i) {
2067
+ throw r.dispose(), i;
2068
+ }
2069
+ }
2070
+ var W = /* @__PURE__ */ ((n) => (n.ModelLoadStart = "model-load-start", n.ModelLoadProgress = "model-load-progress", n.ModelLoaded = "model-loaded", n.ModelError = "model-error", n.SelectionChanged = "selection-changed", n.HoverChanged = "hover-changed", n.Click = "click", n.MouseMove = "mouse-move", n.ModelPlaceSelected = "model-place-selected", n.ModelCalibrationChanged = "model-calibration-changed", n.CameraChanged = "camera-changed", n.CameraIdle = "camera-idle", n.EngineFree = "engine-free", n.EngineBusy = "engine-busy", n.MeasureChanged = "measure-changed", n.MeasureClick = "measure-click", n.SectionMove = "section-move", n.LoadingCompleted = "loading_completed", n))(W || {});
2071
+ class pt {
2072
+ options;
2073
+ snapshots = /* @__PURE__ */ new Map();
2074
+ isDisposed = !1;
2075
+ eventsBound = !1;
2076
+ handleLoadingCompleted = () => {
2077
+ this.options.enabled && this.replaceAll();
2078
+ };
2079
+ engine;
2080
+ constructor(e, t = {}, r = {}) {
2081
+ this.engine = e;
2082
+ const i = this.normalizeInput(t, r);
2083
+ this.options = {
2084
+ enabled: i.enabled ?? this.hasRules(i),
2085
+ rules: this.normalizeRules(i)
2086
+ };
2087
+ }
2088
+ /** 注册模型加载事件并立即处理当前已有模型。 */
2089
+ init(e = {}) {
2090
+ this.isDisposed || (Object.keys(e).length > 0 && (this.options.enabled = e.enabled ?? this.options.enabled, (e.rules != null || e.materialName != null || e.materialParams != null) && (this.options.rules = this.normalizeRules(e))), this.eventsBound || (this.engine.events?.on?.(W.LoadingCompleted, this.handleLoadingCompleted), this.eventsBound = !0), this.options.enabled && this.replaceAll());
2091
+ }
2092
+ isEnabled() {
2093
+ return this.options.enabled;
2094
+ }
2095
+ /** 开关自动替换;关闭时恢复管理器修改过的材质。 */
2096
+ setEnabled(e) {
2097
+ return this.options.enabled = e, e ? this.replaceAll() : (this.restoreAll(), 0);
2098
+ }
2099
+ setMaterialName(e) {
2100
+ return this.restoreAll(), this.options.rules = [{
2101
+ materialName: e,
2102
+ materialParams: {}
2103
+ }], this.options.enabled ? this.replaceAll() : 0;
2104
+ }
2105
+ setMaterialParams(e) {
2106
+ const t = this.options.rules[0] ?? {
2107
+ materialName: "",
2108
+ materialParams: {}
2109
+ };
2110
+ return t.materialParams = {
2111
+ ...t.materialParams,
2112
+ ...e
2113
+ }, this.options.rules = [t, ...this.options.rules.slice(1)], this.options.enabled ? this.replaceAll() : 0;
2114
+ }
2115
+ setRules(e) {
2116
+ return this.restoreAll(), this.options.rules = e.map((t) => ({
2117
+ ...t,
2118
+ materialParams: { ...t.materialParams }
2119
+ })), this.options.enabled ? this.replaceAll() : 0;
2120
+ }
2121
+ addRule(e) {
2122
+ return this.options.rules.push({ ...e, materialParams: { ...e.materialParams } }), this.options.enabled ? this.replaceAll() : 0;
2123
+ }
2124
+ removeRule(e) {
2125
+ return e >= 0 && e < this.options.rules.length && this.options.rules.splice(e, 1), this.options.enabled ? this.replaceAll() : 0;
2126
+ }
2127
+ getRules() {
2128
+ return this.options.rules.map((e) => ({
2129
+ ...e,
2130
+ materialParams: { ...e.materialParams }
2131
+ }));
2132
+ }
2133
+ /** 替换所有已加载模型中匹配名称的材质,返回受影响的材质数量。 */
2134
+ replaceAll() {
2135
+ if (this.isDisposed || !this.options.enabled)
2136
+ return 0;
2137
+ const e = /* @__PURE__ */ new Set();
2138
+ return le(this.engine, (t) => (this.collectObjectMaterials(t, e), !1)), this.applyMaterials(e);
2139
+ }
2140
+ /** 替换指定模型 URL 或模型对象中的材质。 */
2141
+ replaceModel(e) {
2142
+ if (this.isDisposed || !this.options.enabled)
2143
+ return 0;
2144
+ const t = typeof e == "string" ? b(this.engine, e) : e;
2145
+ if (t == null)
2146
+ return 0;
2147
+ const r = /* @__PURE__ */ new Set();
2148
+ return t.traverse((i) => this.collectObjectMaterials(i, r)), this.applyMaterials(r);
2149
+ }
2150
+ /** 对单个材质执行当前规则,供加载器在合批前调用。 */
2151
+ applyMaterial(e) {
2152
+ if (this.isDisposed || !this.options.enabled || e == null)
2153
+ return !1;
2154
+ const t = this.options.rules.filter((s) => this.matches(e, s));
2155
+ if (t.length === 0)
2156
+ return !1;
2157
+ const r = Object.assign({}, ...t.map((s) => s.materialParams)), i = Object.keys(r);
2158
+ if (!this.snapshots.has(e))
2159
+ this.snapshots.set(e, this.captureSnapshot(e, i));
2160
+ else {
2161
+ const s = this.snapshots.get(e);
2162
+ i.forEach((a) => {
2163
+ s.has(a) || s.set(a, this.cloneMaterialValue(e[a]));
2164
+ });
2165
+ }
2166
+ return Object.entries(r).forEach(([s, a]) => this.applyMaterialValue(e, s, a)), e.needsUpdate = !0, !0;
2167
+ }
2168
+ getReplacedMaterialCount() {
2169
+ return this.snapshots.size;
2170
+ }
2171
+ /** 恢复管理器修改过的全部材质。 */
2172
+ restoreAll() {
2173
+ this.snapshots.forEach((e, t) => {
2174
+ e.forEach((r, i) => this.applyMaterialValue(t, i, r)), t.needsUpdate = !0;
2175
+ }), this.snapshots.clear(), this.engine.requestComposerRender?.();
2176
+ }
2177
+ dispose() {
2178
+ this.isDisposed || (this.eventsBound && (this.engine.events?.off?.(W.LoadingCompleted, this.handleLoadingCompleted), this.eventsBound = !1), this.restoreAll(), this.isDisposed = !0);
2179
+ }
2180
+ normalizeRules(e) {
2181
+ return Array.isArray(e.rules) ? e.rules.map((t) => ({
2182
+ ...t,
2183
+ materialParams: { ...t.materialParams }
2184
+ })) : [{
2185
+ materialName: e.materialName,
2186
+ materialParams: { ...e.materialParams ?? {} }
2187
+ }].filter((t) => t.materialName != null);
2188
+ }
2189
+ normalizeInput(e, t) {
2190
+ return Array.isArray(e) ? { enabled: !0, rules: e } : typeof e == "string" || e instanceof RegExp || typeof e == "function" ? { enabled: !0, materialName: e, materialParams: t } : e;
2191
+ }
2192
+ hasRules(e) {
2193
+ return Array.isArray(e.rules) && e.rules.length > 0 || e.materialName != null;
2194
+ }
2195
+ applyMaterials(e) {
2196
+ let t = 0;
2197
+ return e.forEach((r) => {
2198
+ this.applyMaterial(r) && (t += 1);
2199
+ }), t > 0 && this.engine.requestComposerRender?.(), t;
2200
+ }
2201
+ collectObjectMaterials(e, t) {
2202
+ const r = e;
2203
+ (Array.isArray(r.material) ? r.material : r.material != null ? [r.material] : []).filter(Boolean).forEach((s) => t.add(s));
2204
+ }
2205
+ matches(e, t) {
2206
+ const r = t.materialName ?? t.name;
2207
+ return typeof r == "function" ? r(e) : r instanceof RegExp ? (r.lastIndex = 0, r.test(e.name)) : typeof r == "string" && r.length > 0 && e.name.includes(r);
2208
+ }
2209
+ captureSnapshot(e, t) {
2210
+ const r = /* @__PURE__ */ new Map();
2211
+ return t.forEach((i) => r.set(i, this.cloneMaterialValue(e[i]))), r;
2212
+ }
2213
+ cloneMaterialValue(e) {
2214
+ return e?.isColor === !0 && typeof e.clone == "function" ? e.clone() : e;
2215
+ }
2216
+ applyMaterialValue(e, t, r) {
2217
+ const i = e[t];
2218
+ if (t === "color" && i?.isColor === !0) {
2219
+ r instanceof Ee ? i.copy(r) : i.set(r);
2220
+ return;
2221
+ }
2222
+ r !== void 0 && (t in e || t === "envMapIntensity") && (e[t] = r);
2223
+ }
2224
+ }
2225
+ const Ke = {
2226
+ vxgi: {
2227
+ // VXGI 需要体素化整套场景,默认关闭以避免影响现有项目的初始化性能。
2228
+ enabled: !1,
2229
+ resolution: 128,
2230
+ coneCount: 3,
2231
+ coneAngle: 40,
2232
+ giIntensity: 1.5,
2233
+ aoIntensity: 1,
2234
+ aoMinVisibility: 0,
2235
+ aoDistance: 1,
2236
+ normalOffset: 1.5,
2237
+ maxDistance: 50,
2238
+ stepScale: 1,
2239
+ bounces: 0,
2240
+ directionalRadiance: !1,
2241
+ useTemporalFiltering: !0,
2242
+ updateOnChange: !0
2243
+ },
2244
+ colorGrading: {
2245
+ /**
2246
+ * 色温,单位 Kelvin;6500K 为中性白。
2247
+ * 性能:偏离 6500K 时才会创建色彩分级全屏通道;单个色温乘法本身开销很小。
2248
+ */
2249
+ colorTemperature: 5e3,
2250
+ /**
2251
+ * 饱和度倍率;0 为黑白,1 为原始饱和度。
2252
+ * 性能:只增加一次全屏颜色计算;若所有颜色分级参数均为中性,则该通道不会创建。
2253
+ */
2254
+ saturation: 1.05,
2255
+ /**
2256
+ * 对比度倍率;1 为原始对比度。
2257
+ * 性能:只增加一次全屏颜色计算;调节数值不会增加采样次数。
2258
+ */
2259
+ contrast: 1.05,
2260
+ /**
2261
+ * RCAS 锐化强度,范围 0~1;0 关闭,1 最强。
2262
+ * 性能:大于 0 会额外执行锐化全屏通道,并产生邻域纹理采样;通常是本组参数中
2263
+ * 成本最高的项目,移动端建议保持 0 或使用较低值。
2264
+ */
2265
+ sharpen: 0.1
2266
+ },
2267
+ ssgi: {
2268
+ /**
2269
+ * 是否启用屏幕空间间接光照(SSGI),同时包含环境遮蔽贡献。
2270
+ * 性能:启用后增加高成本的全屏光线采样和临时纹理;关闭可直接节省整组开销。
2271
+ */
2272
+ enabled: !0,
2273
+ /**
2274
+ * 每像素的采样方向数(slice)。
2275
+ * 性能:与 `stepCount` 相乘决定主要采样量,成本近似线性增加;优先降低此值
2276
+ * 以换取帧率,过低会出现方向性噪声。
2277
+ */
2278
+ sliceCount: 1,
2279
+ /**
2280
+ * 每个方向的光线步进数。
2281
+ * 性能:与 `sliceCount` 相乘决定主要采样量,成本近似线性增加;提高后会明显
2282
+ * 增加 GPU 时间,默认低步数适合实时场景。
2283
+ */
2284
+ stepCount: 2,
2285
+ /**
2286
+ * 环境遮蔽强度;0 表示不增加遮蔽,1 为常用强度。
2287
+ * 性能:只改变结果合成,不改变采样次数,性能影响很小。
2288
+ */
2289
+ aoIntensity: 1,
2290
+ /**
2291
+ * 间接光照强度;0 表示不叠加间接光,1 为基准强度。
2292
+ * 性能:只改变结果合成,不改变采样次数,性能影响很小。
2293
+ */
2294
+ giIntensity: 2,
2295
+ /**
2296
+ * 屏幕空间采样半径,单位为场景单位。
2297
+ * 性能:主要影响光线覆盖范围和命中结果,不直接增加采样次数;过大可能扩大
2298
+ * 有效搜索区域并增加纹理访问压力。
2299
+ */
2300
+ radius: 1.5,
2301
+ /**
2302
+ * 是否使用屏幕空间半径计算;关闭时使用视图空间计算。
2303
+ * 性能:两种路径采样次数相同,差异主要来自每步坐标换算;通常不是首要调优项。
2304
+ */
2305
+ useScreenSpaceSampling: !0,
2306
+ /**
2307
+ * 遮蔽/光照衰减指数,控制远距离样本的权重。
2308
+ * 性能:只增加少量幂运算,不改变采样数量;主要是画质参数。
2309
+ */
2310
+ expFactor: 1,
2311
+ /**
2312
+ * 深度厚度容差,用于判断样本是否与几何体相交。
2313
+ * 性能:只影响命中判定,不改变采样数量;过大可能造成漏光或光晕。
2314
+ */
2315
+ thickness: 1,
2316
+ /**
2317
+ * 是否使用线性厚度计算,改善不同深度距离下的相交一致性。
2318
+ * 性能:增加少量分支和坐标换算,开销远小于增加步进数。
2319
+ */
2320
+ useLinearThickness: !1,
2321
+ /**
2322
+ * 背面光照权重;0 不使用背面光照。
2323
+ * 性能:主要影响样本合成,增加的计算很小;较高值可能放大背面漏光。
2324
+ */
2325
+ backfaceLighting: 0,
2326
+ /**
2327
+ * 是否启用 SSGI 时域滤波。
2328
+ * 性能:需要维护历史结果并增加时域合成开销和显存占用;可降低噪声,但仅在
2329
+ * 时域渲染条件满足(通常同时启用 TRAA)时生效。
2330
+ */
2331
+ useTemporalFiltering: !0
2332
+ },
2333
+ traa: {
2334
+ /**
2335
+ * 是否启用时域抗锯齿(TRAA)。
2336
+ * 性能:增加一次全屏时域重投影、历史纹理和运动信息处理;关闭可省掉中等开销,
2337
+ * 但 SSGI/GTAO 的时域滤波也会随条件失效。
2338
+ */
2339
+ enabled: !0,
2340
+ /**
2341
+ * 深度相似阈值,用于判断历史样本是否可复用。
2342
+ * 性能:只影响每像素判定,不改变采样次数,性能影响很小。
2343
+ */
2344
+ depthThreshold: 5e-4,
2345
+ /**
2346
+ * 边缘深度差阈值,用于处理几何边缘的历史权重。
2347
+ * 性能:只影响每像素判定,不改变采样次数,性能影响很小。
2348
+ */
2349
+ edgeDepthDiff: 1e-3,
2350
+ /**
2351
+ * 允许的最大速度长度,用于限制运动矢量重投影范围。
2352
+ * 性能:只影响向量限制和历史采样位置,性能影响很小;过大可能增加拖影。
2353
+ */
2354
+ maxVelocityLength: 128,
2355
+ /**
2356
+ * 是否启用亚像素修正。
2357
+ * 性能:增加少量边缘权重计算,开销较小;密集细杆接近单像素宽度时可减少闪烁。
2358
+ */
2359
+ useSubpixelCorrection: !1
2360
+ },
2361
+ ssr: {
2362
+ /**
2363
+ * 是否启用屏幕空间反射(SSR)。
2364
+ * 性能:增加全屏光线步进、命中判定及可能的模糊通道,是最昂贵的效果之一;
2365
+ * 关闭可直接省掉整组 GPU 和临时纹理开销。
2366
+ */
2367
+ enabled: !0,
2368
+ /**
2369
+ * 是否使用随机 GGX 反射方向;关闭时使用单一镜面反射路径。
2370
+ * 性能:随机路径通常需要更多光线步进和去噪,GPU 成本更高;只有在需要粗糙材质
2371
+ * 反射质量时才建议开启。
2372
+ */
2373
+ stochastic: !1,
2374
+ /**
2375
+ * 是否对环境贴图执行重要性采样,用于 SSR 未命中时的环境反射。
2376
+ * 性能:会在环境贴图上预计算亮度分布表,增加初始化/重建时间、显存和采样开销;
2377
+ * 没有有效环境贴图时不会生效。
2378
+ */
2379
+ envImportanceSampling: !1,
2380
+ /**
2381
+ * SSR 未命中时使用的环境节点;null 表示不补充环境反射。
2382
+ * 性能:提供环境节点会增加环境纹理读取;保持 null 时无额外环境采样。
2383
+ */
2384
+ environmentNode: null,
2385
+ /**
2386
+ * 是否反射非金属材质;关闭时仅处理金属表面。
2387
+ * 性能:开启后更多屏幕像素进入反射计算,通常会增加 GPU 时间;建筑场景可按需开启。
2388
+ */
2389
+ reflectNonMetals: !0,
2390
+ /**
2391
+ * 是否对命中位置进行二分细化。
2392
+ * 性能:命中后增加二分搜索步进,成本随命中像素数增加;能改善反射边缘精度。
2393
+ */
2394
+ binaryRefine: !1,
2395
+ /**
2396
+ * SSR 渲染分辨率倍率;1 为全分辨率,0.5 的像素数约为全分辨率的四分之一。
2397
+ * 性能:按宽高平方影响光线步进、纹理带宽和临时目标显存,是 SSR 最有效的调优项。
2398
+ */
2399
+ resolutionScale: 0.5,
2400
+ /**
2401
+ * 最大反射距离,单位为场景单位。
2402
+ * 性能:距离越大,光线覆盖范围越广,命中和纹理访问压力通常越高;不会简单地
2403
+ * 线性增加固定步数,但会增加长距离反射的平均成本。
2404
+ */
2405
+ maxDistance: 8,
2406
+ /**
2407
+ * 深度厚度容差,用于 SSR 命中判定。
2408
+ * 性能:主要改变命中结果,不增加步进次数,性能影响很小。
2409
+ */
2410
+ thickness: 0.15,
2411
+ /**
2412
+ * 反射叠加强度;0 表示不叠加反射。
2413
+ * 性能:只改变最终合成,不改变光线步进数量,性能影响很小。
2414
+ */
2415
+ intensity: 0.85,
2416
+ /**
2417
+ * 屏幕边缘反射淡出宽度,范围通常为 0~1。
2418
+ * 性能:只增加少量边缘计算,不改变光线步进数量。
2419
+ */
2420
+ screenEdgeFade: 0.2,
2421
+ /**
2422
+ * 是否使用黑色边缘淡出路径。
2423
+ * 性能:属于编译期分支,影响很小;修改后需要重建 SSR 节点。
2424
+ */
2425
+ screenEdgeFadeBlack: !1,
2426
+ /**
2427
+ * 反射亮度上限,用于抑制 HDR 过曝。
2428
+ * 性能:只增加亮度限制计算,不改变采样数量。
2429
+ */
2430
+ maxLuminance: 10,
2431
+ /**
2432
+ * SSR 光线步进质量,范围 0~1;越高步进越密。
2433
+ * 性能:直接影响每像素光线步数,成本近似线性增加;是仅次于分辨率的主要调优项。
2434
+ */
2435
+ quality: 0.5,
2436
+ /**
2437
+ * 随机 GGX 反射的镜面偏置,控制采样方向集中程度。
2438
+ * 性能:不改变随机路径的步数,主要影响噪声和反射形状,性能影响很小。
2439
+ */
2440
+ mirrorBias: 0.5,
2441
+ /**
2442
+ * 非随机 SSR 的模糊质量,通常为 1~3。
2443
+ * 性能:仅在 `stochastic=false` 时生效;质量越高模糊邻域采样越多,成本和显存
2444
+ * 带宽越高,并且修改会重建模糊材质。
2445
+ */
2446
+ blurQuality: 2,
2447
+ /**
2448
+ * 光线步进在近端/远端的分布指数。
2449
+ * 性能:通常是编译期常量,不改变总步数;主要改变采样分布和命中质量,修改会重建节点。
2450
+ */
2451
+ stepExponent: 2,
2452
+ /**
2453
+ * 环境反射强度,用于 SSR 未命中时的环境贡献。
2454
+ * 性能:只影响环境结果合成;启用环境节点时会保留对应纹理读取。
2455
+ */
2456
+ environmentIntensity: Math.PI
2457
+ },
2458
+ bloom: {
2459
+ /**
2460
+ * Bloom 会创建多级降采样纹理,默认关闭以保持现有画面和显存占用不变。
2461
+ * 业务可通过 postProcessing.bloom 显式开启。
2462
+ */
2463
+ enabled: !1,
2464
+ /** 辉光叠加强度,只影响最终合成,不改变采样数量。 */
2465
+ strength: 0.05,
2466
+ /** 辉光扩散半径,值越大光晕越宽。 */
2467
+ radius: 0.1,
2468
+ /** 仅让高亮区域产生辉光,避免普通材质整体泛白。 */
2469
+ threshold: 0.1,
2470
+ /** 平滑亮度阈值边缘,减少阈值附近的闪烁和硬边。 */
2471
+ smoothWidth: 0.01,
2472
+ /** 半分辨率可显著降低多级模糊的纹理带宽和显存成本。 */
2473
+ resolutionScale: 0.5
2474
+ },
2475
+ gtao: {
2476
+ /**
2477
+ * 是否启用 GTAO 屏幕空间环境遮蔽。
2478
+ * 性能:增加一次全屏遮蔽采样和合成;关闭可省掉整组 GPU、临时纹理和带宽开销。
2479
+ */
2480
+ enabled: !0,
2481
+ /**
2482
+ * GTAO 渲染分辨率倍率;1 为全分辨率,0.5 的像素数约为全分辨率的四分之一。
2483
+ * 性能:按宽高平方影响采样、带宽和显存,是 GTAO 最有效的性能调优项。
2484
+ */
2485
+ resolutionScale: 1,
2486
+ /**
2487
+ * 遮蔽半径,单位为场景单位。
2488
+ * 性能:主要影响搜索范围和结果,不直接增加固定采样数;过大可能增加有效纹理访问。
2489
+ */
2490
+ radius: 0.15,
2491
+ /**
2492
+ * 深度厚度容差,用于遮蔽命中判断。
2493
+ * 性能:不改变采样数量,主要影响漏光和遮蔽边界,性能影响很小。
2494
+ */
2495
+ thickness: 0.5,
2496
+ /**
2497
+ * 距离衰减指数。
2498
+ * 性能:只影响每像素衰减计算,不增加采样数量。
2499
+ */
2500
+ distanceExponent: 0.5,
2501
+ /**
2502
+ * 距离衰减范围控制。
2503
+ * 性能:只影响每像素衰减计算,不增加采样数量。
2504
+ */
2505
+ distanceFallOff: 1,
2506
+ /**
2507
+ * GTAO 结果缩放;0 表示不增加遮蔽,1 为基准效果。
2508
+ * 性能:只改变结果合成,不改变采样数量。
2509
+ */
2510
+ scale: 0.5,
2511
+ /**
2512
+ * 每像素采样数。
2513
+ * 性能:直接影响 AO 采样循环,成本近似线性增加;提高后画质更稳定,但 GPU 时间同步增加。
2514
+ */
2515
+ samples: 10,
2516
+ /**
2517
+ * 是否启用 GTAO 时域滤波。
2518
+ * 性能:需要历史纹理和额外时域合成开销,并增加显存占用;可降低低采样数下的噪声,
2519
+ * 仅在时域渲染条件满足时实际生效。
2520
+ */
2521
+ useTemporalFiltering: !0
2522
+ }
2523
+ }, $ = ["vxgi", "ssgi", "gtao", "ssr", "traa"];
2524
+ function et() {
2525
+ const n = {};
2526
+ return $.forEach((e) => {
2527
+ const { enabled: t, ...r } = Ke[e];
2528
+ n[e] = { ...r };
2529
+ }), n;
2530
+ }
2531
+ function M(n, e, t, r, i, s = {}) {
2532
+ const a = et();
2533
+ return $.forEach((o) => {
2534
+ Object.assign(a[o], s[o] ?? {});
2535
+ }), {
2536
+ level: n,
2537
+ name: e,
2538
+ shadow: { enabled: t, mapSize: r },
2539
+ effects: {
2540
+ vxgi: !1,
2541
+ ssgi: !1,
2542
+ gtao: !1,
2543
+ ssr: !1,
2544
+ traa: !1,
2545
+ ...i
2546
+ },
2547
+ parameters: a
2548
+ };
2549
+ }
2550
+ const ft = [
2551
+ M(1, "L1", !1, 512, {}),
2552
+ M(2, "L2", !1, 512, { gtao: !0 }, { gtao: { resolutionScale: 0.5, samples: 4, scale: 0.25, useTemporalFiltering: !1 } }),
2553
+ M(3, "L3", !0, 512, { gtao: !0 }, { gtao: { resolutionScale: 0.5, samples: 4, scale: 0.3, useTemporalFiltering: !1 } }),
2554
+ M(4, "L4", !0, 1024, { gtao: !0 }, { gtao: { resolutionScale: 0.5, samples: 6, scale: 0.35, useTemporalFiltering: !1 } }),
2555
+ M(5, "L5", !0, 1024, { gtao: !0, traa: !0 }, { gtao: { resolutionScale: 0.5, samples: 6, scale: 0.4 } }),
2556
+ M(6, "L6", !0, 2048, { gtao: !0, traa: !0 }, { gtao: { resolutionScale: 0.75, samples: 8, scale: 0.45 } }),
2557
+ M(7, "L7", !0, 2048, { gtao: !0, ssgi: !0, traa: !0 }, { gtao: { resolutionScale: 1, samples: 6, scale: 0.25 }, ssgi: { sliceCount: 1, stepCount: 2, aoIntensity: 0.45, giIntensity: 1 } }),
2558
+ // Level 8 完整继承 defaults.ts 的参数,作为十二档画质的中间基准。
2559
+ M(8, "L8", !0, 2048, { gtao: !0, ssgi: !0, traa: !0, ssr: !0 }),
2560
+ M(9, "L9", !0, 2048, { gtao: !0, ssgi: !0, traa: !0, ssr: !0 }, { gtao: { resolutionScale: 1, samples: 12, scale: 0.55 }, ssgi: { sliceCount: 1, stepCount: 3, aoIntensity: 1.05, giIntensity: 2.1 }, ssr: { resolutionScale: 0.6, quality: 0.6, blurQuality: 2 } }),
2561
+ M(10, "L10", !0, 2048, { gtao: !0, ssgi: !0, traa: !0, ssr: !0 }, { gtao: { resolutionScale: 1, samples: 12, scale: 0.6 }, ssgi: { sliceCount: 2, stepCount: 4, aoIntensity: 1.1, giIntensity: 2.2 }, ssr: { resolutionScale: 0.75, quality: 0.7, binaryRefine: !0, blurQuality: 2 } }),
2562
+ M(11, "L11", !0, 4096, { gtao: !0, ssgi: !0, traa: !0, ssr: !0 }, { gtao: { resolutionScale: 1, samples: 16, scale: 0.7 }, ssgi: { sliceCount: 2, stepCount: 6, aoIntensity: 1.2, giIntensity: 2.4 }, ssr: { resolutionScale: 1, quality: 0.8, binaryRefine: !0, blurQuality: 3 } }),
2563
+ M(12, "L12", !0, 4096, { vxgi: !0, gtao: !0, ssgi: !0, traa: !0, ssr: !0 }, { vxgi: { resolution: 128, coneCount: 4, bounces: 1 }, gtao: { resolutionScale: 1, samples: 20, scale: 0.8 }, ssgi: { sliceCount: 3, stepCount: 8, aoIntensity: 1.3, giIntensity: 2.6 }, ssr: { resolutionScale: 1, quality: 0.9, binaryRefine: !0, blurQuality: 3 } })
2564
+ ], yt = [
2565
+ { id: "indoor", name: "Indoor", category: "indoor", hdrId: "11", hdrIntensity: 0.45, ambientIntensity: 0.5, directionalIntensity: 2 },
2566
+ { id: "outdoor", name: "Outdoor", category: "outdoor", hdrId: "1", hdrIntensity: 0.5, ambientIntensity: 0.2, directionalIntensity: 3 }
2567
+ ];
2568
+ function Mt(n) {
2569
+ return {
2570
+ ...n,
2571
+ shadow: { ...n.shadow },
2572
+ effects: { ...n.effects },
2573
+ parameters: Object.fromEntries(
2574
+ $.map((e) => [e, { ...n.parameters[e] }])
2575
+ )
2576
+ };
2577
+ }
2578
+ async function j(n) {
2579
+ n?.assetFiles ? await F(n.assetFiles) : n?.assetFile ? await re(n.assetFile) : n?.assetBaseUrl && await F(null), n?.assetBaseUrl && ie(n.assetBaseUrl);
2580
+ }
2581
+ async function bt(n, e) {
2582
+ if (await j(n), (e ?? (typeof navigator < "u" && "gpu" in navigator ? "webgpu" : "webgl")) === "webgpu") {
2583
+ const { EngineKernelV3: i } = await import("./EngineKernel-BucDRg23.js"), s = new i(n);
2584
+ return await s.ready, s;
2585
+ }
2586
+ const { EngineKernelV2: r } = await import("./engine-3d-DnlK2vTK.js").then((i) => i.e);
2587
+ return new r(n);
2588
+ }
2589
+ async function wt(n) {
2590
+ await j(n);
2591
+ const { EngineKernel2d: e } = await import("./engine-2d-BjtuYZUi.js").then((t) => t.E);
2592
+ return new e(n);
2593
+ }
2594
+ async function vt(n) {
2595
+ await j(n);
2596
+ const { EngineKernel720: e } = await import("./engine-720-DJsweyG9.js");
2597
+ return new e(n);
2598
+ }
2599
+ async function Et(n) {
2600
+ await j(n);
2601
+ const { EngineKernelGaussian: e } = await import("./engine-gaussian-DklK6vdJ.js");
2602
+ return new e(n);
2603
+ }
2604
+ export {
2605
+ wt as A,
2606
+ vt as B,
2607
+ Y as C,
2608
+ Ke as D,
2609
+ W as E,
2610
+ mt as F,
2611
+ v as G,
2612
+ Et as H,
2613
+ pt as M,
2614
+ gt as a,
2615
+ ot as b,
2616
+ lt as c,
2617
+ ct as d,
2618
+ nt as e,
2619
+ ut as f,
2620
+ at as g,
2621
+ b as h,
2622
+ le as i,
2623
+ ft as j,
2624
+ Mt as k,
2625
+ yt as l,
2626
+ se as m,
2627
+ dt as n,
2628
+ Pe as o,
2629
+ X as p,
2630
+ ze as q,
2631
+ Le as r,
2632
+ ht as s,
2633
+ xe as t,
2634
+ Re as u,
2635
+ Oe as v,
2636
+ Xe as w,
2637
+ je as x,
2638
+ De as y,
2639
+ bt as z
2640
+ };