@loaders.gl/tiles 5.0.0-alpha.1 → 5.0.0-alpha.2

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 (115) hide show
  1. package/LICENSE +14 -3
  2. package/dist/dist.dev.js +2396 -274
  3. package/dist/dist.min.js +1 -1
  4. package/dist/index.cjs +2763 -337
  5. package/dist/index.cjs.map +3 -4
  6. package/dist/index.d.ts +7 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/point-cloud/point-cloud-tile.d.ts.map +1 -1
  11. package/dist/point-cloud/point-cloud-tile.js +3 -0
  12. package/dist/point-cloud/point-cloud-tile.js.map +1 -1
  13. package/dist/point-cloud/point-cloud-tileset.d.ts.map +1 -1
  14. package/dist/point-cloud/point-cloud-tileset.js +3 -0
  15. package/dist/point-cloud/point-cloud-tileset.js.map +1 -1
  16. package/dist/point-cloud/types.d.ts.map +1 -1
  17. package/dist/point-cloud/types.js +3 -0
  18. package/dist/point-cloud/types.js.map +1 -1
  19. package/dist/raster-set.d.ts +35 -29
  20. package/dist/raster-set.d.ts.map +1 -1
  21. package/dist/raster-set.js +7 -1
  22. package/dist/raster-set.js.map +1 -1
  23. package/dist/tileset-3d/common/tile-3d.d.ts +98 -3
  24. package/dist/tileset-3d/common/tile-3d.d.ts.map +1 -1
  25. package/dist/tileset-3d/common/tile-3d.js +229 -15
  26. package/dist/tileset-3d/common/tile-3d.js.map +1 -1
  27. package/dist/tileset-3d/common/tileset-3d.d.ts +143 -3
  28. package/dist/tileset-3d/common/tileset-3d.d.ts.map +1 -1
  29. package/dist/tileset-3d/common/tileset-3d.js +257 -16
  30. package/dist/tileset-3d/common/tileset-3d.js.map +1 -1
  31. package/dist/tileset-3d/common/tileset-cache.d.ts +11 -1
  32. package/dist/tileset-3d/common/tileset-cache.d.ts.map +1 -1
  33. package/dist/tileset-3d/common/tileset-cache.js +11 -3
  34. package/dist/tileset-3d/common/tileset-cache.js.map +1 -1
  35. package/dist/tileset-3d/common/tileset-source.d.ts +18 -0
  36. package/dist/tileset-3d/common/tileset-source.d.ts.map +1 -1
  37. package/dist/tileset-3d/common/tileset-source.js.map +1 -1
  38. package/dist/tileset-3d/common/tileset-traverser.d.ts +13 -0
  39. package/dist/tileset-3d/common/tileset-traverser.d.ts.map +1 -1
  40. package/dist/tileset-3d/common/tileset-traverser.js +28 -1
  41. package/dist/tileset-3d/common/tileset-traverser.js.map +1 -1
  42. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts +111 -0
  43. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.d.ts.map +1 -0
  44. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js +340 -0
  45. package/dist/tileset-3d/format-3d-tiles/implicit-tiling.js.map +1 -0
  46. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts +92 -3
  47. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.d.ts.map +1 -1
  48. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js +211 -13
  49. package/dist/tileset-3d/format-3d-tiles/tiles-3d-source.js.map +1 -1
  50. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts +36 -0
  51. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  52. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js +66 -0
  53. package/dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js.map +1 -1
  54. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.d.ts.map +1 -1
  55. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js +3 -0
  56. package/dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js.map +1 -1
  57. package/dist/tileset-3d/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  58. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js +3 -0
  59. package/dist/tileset-3d/format-i3s/i3s-tile-manager.js.map +1 -1
  60. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  61. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js +3 -0
  62. package/dist/tileset-3d/format-i3s/i3s-tileset-traverser.js.map +1 -1
  63. package/dist/tileset-3d/helpers/3d-tiles-options.d.ts.map +1 -1
  64. package/dist/tileset-3d/helpers/3d-tiles-options.js +3 -0
  65. package/dist/tileset-3d/helpers/3d-tiles-options.js.map +1 -1
  66. package/dist/tileset-3d/helpers/bounding-volume.d.ts.map +1 -1
  67. package/dist/tileset-3d/helpers/bounding-volume.js +1 -0
  68. package/dist/tileset-3d/helpers/bounding-volume.js.map +1 -1
  69. package/dist/tileset-3d/helpers/frame-state.d.ts +59 -1
  70. package/dist/tileset-3d/helpers/frame-state.d.ts.map +1 -1
  71. package/dist/tileset-3d/helpers/frame-state.js +84 -9
  72. package/dist/tileset-3d/helpers/frame-state.js.map +1 -1
  73. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts +61 -9
  74. package/dist/tileset-3d/helpers/tiles-3d-lod.d.ts.map +1 -1
  75. package/dist/tileset-3d/helpers/tiles-3d-lod.js +198 -93
  76. package/dist/tileset-3d/helpers/tiles-3d-lod.js.map +1 -1
  77. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts +134 -0
  78. package/dist/tileset-3d/helpers/tiles-3d-request-priority.d.ts.map +1 -0
  79. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js +179 -0
  80. package/dist/tileset-3d/helpers/tiles-3d-request-priority.js.map +1 -0
  81. package/dist/types.d.ts +2 -0
  82. package/dist/types.d.ts.map +1 -1
  83. package/dist/types.js +3 -0
  84. package/dist/types.js.map +1 -1
  85. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  86. package/dist/utils/doubly-linked-list.js +1 -0
  87. package/dist/utils/doubly-linked-list.js.map +1 -1
  88. package/dist/utils/managed-array.d.ts.map +1 -1
  89. package/dist/utils/managed-array.js +1 -0
  90. package/dist/utils/managed-array.js.map +1 -1
  91. package/package.json +5 -5
  92. package/src/index.ts +19 -0
  93. package/src/point-cloud/point-cloud-tile.ts +4 -0
  94. package/src/point-cloud/point-cloud-tileset.ts +4 -0
  95. package/src/point-cloud/types.ts +4 -0
  96. package/src/raster-set.ts +81 -43
  97. package/src/tileset-3d/common/tile-3d.ts +285 -21
  98. package/src/tileset-3d/common/tileset-3d.ts +368 -17
  99. package/src/tileset-3d/common/tileset-cache.ts +15 -7
  100. package/src/tileset-3d/common/tileset-source.ts +21 -0
  101. package/src/tileset-3d/common/tileset-traverser.ts +29 -1
  102. package/src/tileset-3d/format-3d-tiles/implicit-tiling.ts +534 -0
  103. package/src/tileset-3d/format-3d-tiles/tiles-3d-source.ts +266 -13
  104. package/src/tileset-3d/format-3d-tiles/tileset-3d-traverser.ts +77 -0
  105. package/src/tileset-3d/format-i3s/i3s-pending-tiles-register.ts +4 -0
  106. package/src/tileset-3d/format-i3s/i3s-tile-manager.ts +4 -0
  107. package/src/tileset-3d/format-i3s/i3s-tileset-traverser.ts +4 -0
  108. package/src/tileset-3d/helpers/3d-tiles-options.ts +4 -0
  109. package/src/tileset-3d/helpers/bounding-volume.ts +2 -0
  110. package/src/tileset-3d/helpers/frame-state.ts +139 -11
  111. package/src/tileset-3d/helpers/tiles-3d-lod.ts +291 -112
  112. package/src/tileset-3d/helpers/tiles-3d-request-priority.ts +301 -0
  113. package/src/types.ts +6 -0
  114. package/src/utils/doubly-linked-list.ts +2 -0
  115. package/src/utils/managed-array.ts +2 -0
package/dist/index.cjs CHANGED
@@ -18,8 +18,8 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // dist/index.js
21
- var dist_exports = {};
22
- __export(dist_exports, {
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
23
  I3SSource: () => I3SSource,
24
24
  ImageSet: () => ImageSet,
25
25
  IndexedArchiveTilesetSource: () => IndexedArchiveTilesetSource,
@@ -43,17 +43,1362 @@ __export(dist_exports, {
43
43
  TilesetTraverser: () => TilesetTraverser,
44
44
  calculateTransformProps: () => calculateTransformProps,
45
45
  createBoundingVolume: () => createBoundingVolume,
46
+ createImplicitSubtreeReference: () => createImplicitSubtreeReference,
46
47
  getFrameState: () => getFrameState,
47
48
  getLodStatus: () => getLodStatus,
48
- isTileset3DSource: () => isTileset3DSource
49
+ isTileset3DSource: () => isTileset3DSource,
50
+ materializeImplicitSubtree: () => materializeImplicitSubtree,
51
+ replaceImplicitUrlTemplate: () => replaceImplicitUrlTemplate
49
52
  });
50
- module.exports = __toCommonJS(dist_exports);
53
+ module.exports = __toCommonJS(index_exports);
51
54
 
52
55
  // dist/tileset-3d/common/tileset-3d.js
53
- var import_core3 = require("@math.gl/core");
56
+ var import_core5 = require("@math.gl/core");
54
57
  var import_geospatial3 = require("@math.gl/geospatial");
58
+ var import_stats3 = require("@probe.gl/stats");
59
+
60
+ // ../loader-utils/src/lib/env-utils/assert.ts
61
+ function assert(condition, message) {
62
+ if (!condition) {
63
+ throw new Error(message || "loader assertion failed.");
64
+ }
65
+ }
66
+
67
+ // ../loader-utils/src/lib/request-utils/request-scheduler.ts
55
68
  var import_stats = require("@probe.gl/stats");
56
- var import_loader_utils2 = require("@loaders.gl/loader-utils");
69
+ var STAT_QUEUED_REQUESTS = "Queued Requests";
70
+ var STAT_ACTIVE_REQUESTS = "Active Requests";
71
+ var STAT_CANCELLED_REQUESTS = "Cancelled Requests";
72
+ var STAT_QUEUED_REQUESTS_EVER = "Queued Requests Ever";
73
+ var STAT_ACTIVE_REQUESTS_EVER = "Active Requests Ever";
74
+ var DEFAULT_PROPS = {
75
+ id: "request-scheduler",
76
+ /** Specifies if the request scheduler should throttle incoming requests, mainly for comparative testing. */
77
+ throttleRequests: true,
78
+ /** The maximum number of simultaneous active requests. Un-throttled requests do not observe this limit. */
79
+ maxRequests: 6,
80
+ /**
81
+ * Specifies a debounce time, in milliseconds. All requests are queued, until no new requests have
82
+ * been added to the queue for this amount of time.
83
+ */
84
+ debounceTime: 0
85
+ };
86
+ var RequestScheduler = class {
87
+ props;
88
+ stats;
89
+ activeRequestCount = 0;
90
+ /** Tracks the number of active requests and prioritizes/cancels queued requests. */
91
+ requestQueue = [];
92
+ requestMap = /* @__PURE__ */ new Map();
93
+ updateTimer = null;
94
+ constructor(props = {}) {
95
+ this.props = { ...DEFAULT_PROPS, ...props };
96
+ this.stats = new import_stats.Stats({ id: this.props.id });
97
+ this.stats.get(STAT_QUEUED_REQUESTS);
98
+ this.stats.get(STAT_ACTIVE_REQUESTS);
99
+ this.stats.get(STAT_CANCELLED_REQUESTS);
100
+ this.stats.get(STAT_QUEUED_REQUESTS_EVER);
101
+ this.stats.get(STAT_ACTIVE_REQUESTS_EVER);
102
+ }
103
+ /**
104
+ * Update scheduler props. Preserves active and queued requests.
105
+ */
106
+ setProps(props) {
107
+ if (props.throttleRequests !== void 0) {
108
+ this.props.throttleRequests = props.throttleRequests;
109
+ }
110
+ if (props.maxRequests !== void 0) {
111
+ this.props.maxRequests = props.maxRequests;
112
+ }
113
+ if (props.debounceTime !== void 0) {
114
+ this.props.debounceTime = props.debounceTime;
115
+ }
116
+ }
117
+ /**
118
+ * Called by an application that wants to issue a request, without having it deeply queued by the browser
119
+ *
120
+ * When the returned promise resolved, it is OK for the application to issue a request.
121
+ * The promise resolves to an object that contains a `done` method.
122
+ * When the application's request has completed (or failed), the application must call the `done` function
123
+ *
124
+ * @param handle
125
+ * @param getPriority will be called when request "slots" open up,
126
+ * allowing the caller to update priority or cancel the request
127
+ * Highest priority executes first, priority < 0 cancels the request
128
+ * @returns a promise
129
+ * - resolves to a object (with a `done` field) when the request can be issued without queueing,
130
+ * - resolves to `null` if the request has been cancelled (by the callback return < 0).
131
+ * In this case the application should not issue the request
132
+ */
133
+ scheduleRequest(handle, getPriority = () => 0) {
134
+ if (!this.props.throttleRequests) {
135
+ return Promise.resolve({ done: () => {
136
+ } });
137
+ }
138
+ if (this.requestMap.has(handle)) {
139
+ return this.requestMap.get(handle);
140
+ }
141
+ const request = { handle, priority: 0, getPriority };
142
+ const promise = new Promise((resolve2) => {
143
+ request.resolve = resolve2;
144
+ return request;
145
+ });
146
+ this.requestQueue.push(request);
147
+ this.requestMap.set(handle, promise);
148
+ this._issueNewRequests();
149
+ return promise;
150
+ }
151
+ // PRIVATE
152
+ _issueRequest(request) {
153
+ const { handle, resolve: resolve2 } = request;
154
+ let isDone = false;
155
+ const done = () => {
156
+ if (!isDone) {
157
+ isDone = true;
158
+ this.requestMap.delete(handle);
159
+ this.activeRequestCount--;
160
+ this._issueNewRequests();
161
+ }
162
+ };
163
+ this.activeRequestCount++;
164
+ return resolve2 ? resolve2({ done }) : Promise.resolve({ done });
165
+ }
166
+ /** We check requests asynchronously, to prevent multiple updates */
167
+ _issueNewRequests() {
168
+ if (this.updateTimer !== null) {
169
+ clearTimeout(this.updateTimer);
170
+ }
171
+ this.updateTimer = setTimeout(() => this._issueNewRequestsAsync(), this.props.debounceTime);
172
+ }
173
+ /** Refresh all requests */
174
+ _issueNewRequestsAsync() {
175
+ if (this.updateTimer !== null) {
176
+ clearTimeout(this.updateTimer);
177
+ }
178
+ this.updateTimer = null;
179
+ const freeSlots = Math.max(this.props.maxRequests - this.activeRequestCount, 0);
180
+ if (freeSlots === 0) {
181
+ return;
182
+ }
183
+ this._updateAllRequests();
184
+ for (let i = 0; i < freeSlots; ++i) {
185
+ const request = this.requestQueue.shift();
186
+ if (request) {
187
+ this._issueRequest(request);
188
+ }
189
+ }
190
+ }
191
+ /** Ensure all requests have updated priorities, and that no longer valid requests are cancelled */
192
+ _updateAllRequests() {
193
+ const requestQueue = this.requestQueue;
194
+ for (let i = 0; i < requestQueue.length; ++i) {
195
+ const request = requestQueue[i];
196
+ if (!this._updateRequest(request)) {
197
+ requestQueue.splice(i, 1);
198
+ this.requestMap.delete(request.handle);
199
+ i--;
200
+ }
201
+ }
202
+ requestQueue.sort((a, b) => a.priority - b.priority);
203
+ }
204
+ /** Update a single request by calling the callback */
205
+ _updateRequest(request) {
206
+ request.priority = request.getPriority(request.handle);
207
+ if (request.priority < 0) {
208
+ request.resolve(null);
209
+ return false;
210
+ }
211
+ return true;
212
+ }
213
+ };
214
+
215
+ // ../loader-utils/src/lib/request-utils/range-request-scheduler.ts
216
+ var import_stats2 = require("@probe.gl/stats");
217
+ var RANGE_STATS_KEYS = {
218
+ logicalRanges: "Logical Range Requests",
219
+ rangeBatches: "Range Request Batches",
220
+ transportRanges: "Transport Ranges Created",
221
+ completedTransportRanges: "Range Transport Requests Completed",
222
+ coalescedRanges: "Coalesced Logical Ranges",
223
+ requestedBytes: "Logical Range Request Bytes",
224
+ transportBytes: "Range Transport Bytes Requested",
225
+ responseBytes: "Range Response Bytes",
226
+ networkTimeMs: "Range Network Time",
227
+ overfetchBytes: "Range Overfetch Bytes",
228
+ failedTransportRanges: "Range Transport Requests Failed",
229
+ abortedLogicalRanges: "Aborted Logical Range Requests",
230
+ fullResponseFallbacks: "Full Response Fallbacks"
231
+ };
232
+ var DEFAULT_PROPS2 = {
233
+ batchDelayMs: 50,
234
+ maxGapBytes: 65536,
235
+ rangeExpansionBytes: 65536,
236
+ maxMergedBytes: 8388608,
237
+ stats: createRangeStats("range-request-scheduler-default"),
238
+ onEvent: () => {
239
+ }
240
+ };
241
+ var RangeRequestScheduler = class {
242
+ /** Runtime scheduler configuration with defaults filled in. */
243
+ props;
244
+ /** Runtime counters for range batching and transport behavior. */
245
+ stats;
246
+ /** Optional event callback for range batching diagnostics. */
247
+ onEvent;
248
+ pendingRequests = [];
249
+ flushTimer = null;
250
+ /** Creates a scheduler for one group of byte-range-addressable resources. */
251
+ constructor(props = {}) {
252
+ const rangeExpansionBytes = props.rangeExpansionBytes ?? props.maxGapBytes ?? DEFAULT_PROPS2.rangeExpansionBytes;
253
+ this.props = {
254
+ batchDelayMs: props.batchDelayMs ?? DEFAULT_PROPS2.batchDelayMs,
255
+ maxGapBytes: props.maxGapBytes ?? rangeExpansionBytes,
256
+ rangeExpansionBytes,
257
+ maxMergedBytes: props.maxMergedBytes ?? DEFAULT_PROPS2.maxMergedBytes
258
+ };
259
+ this.stats = props.stats || createRangeStats("range-request-scheduler");
260
+ this.onEvent = props.onEvent;
261
+ initializeStats(this.stats);
262
+ }
263
+ /**
264
+ * Enqueues one byte range request and resolves with the exact requested byte slice.
265
+ */
266
+ scheduleRequest(request) {
267
+ var _a;
268
+ if (request.length < 0) {
269
+ return Promise.reject(new Error("Byte range length cannot be negative"));
270
+ }
271
+ if ((_a = request.signal) == null ? void 0 : _a.aborted) {
272
+ return Promise.reject(createAbortError());
273
+ }
274
+ if (request.length === 0) {
275
+ return Promise.resolve(new ArrayBuffer(0));
276
+ }
277
+ const promise = new Promise((resolve2, reject) => {
278
+ const pendingRequest = { ...request, resolve: resolve2, reject, settled: false };
279
+ if (request.signal) {
280
+ pendingRequest.abortListener = () => {
281
+ var _a2;
282
+ this.abortRequest(pendingRequest);
283
+ (_a2 = pendingRequest.onAbort) == null ? void 0 : _a2.call(pendingRequest);
284
+ };
285
+ request.signal.addEventListener("abort", pendingRequest.abortListener, { once: true });
286
+ }
287
+ this.pendingRequests.push(pendingRequest);
288
+ });
289
+ this.trackEvent({
290
+ type: "queued",
291
+ sourceId: request.sourceId,
292
+ offset: request.offset,
293
+ length: request.length,
294
+ logicalRequestCount: 1,
295
+ logicalBytes: request.length
296
+ });
297
+ this.scheduleFlush();
298
+ return promise;
299
+ }
300
+ /**
301
+ * Enqueues one HTTP byte-range request and resolves with the exact requested byte slice.
302
+ */
303
+ fetch(request) {
304
+ const fetchFunction = request.fetch || globalThis.fetch;
305
+ return this.scheduleRequest({
306
+ sourceId: request.sourceId || request.url,
307
+ offset: request.offset,
308
+ length: request.length,
309
+ signal: request.signal,
310
+ // Different fetch calls may carry different credentials or transport implementations.
311
+ // Sharing is therefore opt-in through an explicit common isolation key.
312
+ isolationKey: request.isolationKey ?? {},
313
+ fetchRange: (offset, length, signal) => fetchHttpRange({
314
+ url: request.url,
315
+ offset,
316
+ length,
317
+ signal,
318
+ fetch: fetchFunction,
319
+ fetchOptions: request.fetchOptions
320
+ })
321
+ });
322
+ }
323
+ /** Immediately starts loading the currently queued requests. */
324
+ flush() {
325
+ if (this.flushTimer) {
326
+ clearTimeout(this.flushTimer);
327
+ this.flushTimer = null;
328
+ }
329
+ const pendingRequests = this.pendingRequests;
330
+ this.pendingRequests = [];
331
+ const activeRequests = pendingRequests.filter((request) => {
332
+ var _a;
333
+ if (request.settled || ((_a = request.signal) == null ? void 0 : _a.aborted)) {
334
+ this.abortRequest(request);
335
+ return false;
336
+ }
337
+ return true;
338
+ });
339
+ const mergedRequests = this.mergeRequests(activeRequests);
340
+ if (activeRequests.length > 0) {
341
+ this.trackBatch(activeRequests, mergedRequests);
342
+ }
343
+ for (const mergedRequest of mergedRequests) {
344
+ this.fetchMergedRequest(mergedRequest);
345
+ }
346
+ }
347
+ /** Schedules the next pending-request flush. */
348
+ scheduleFlush() {
349
+ if (this.flushTimer) {
350
+ return;
351
+ }
352
+ this.flushTimer = setTimeout(() => this.flush(), this.props.batchDelayMs);
353
+ }
354
+ /** Sorts active requests and combines requests for the same source when thresholds allow. */
355
+ mergeRequests(requests) {
356
+ requests.sort((left, right) => {
357
+ if (left.sourceId !== right.sourceId) {
358
+ return left.sourceId < right.sourceId ? -1 : 1;
359
+ }
360
+ return left.offset - right.offset;
361
+ });
362
+ const mergedRequests = [];
363
+ for (const request of requests) {
364
+ const requestEndOffset = request.offset + request.length;
365
+ const lastMergedRequest = mergedRequests[mergedRequests.length - 1];
366
+ if (lastMergedRequest && canMergeRequests(lastMergedRequest, request, this.props)) {
367
+ lastMergedRequest.endOffset = Math.max(lastMergedRequest.endOffset, requestEndOffset);
368
+ lastMergedRequest.requests.push(request);
369
+ } else {
370
+ mergedRequests.push({
371
+ sourceId: request.sourceId,
372
+ isolationKey: request.isolationKey ?? request.sourceId,
373
+ offset: request.offset,
374
+ endOffset: requestEndOffset,
375
+ fetchRange: request.fetchRange,
376
+ requests: [request]
377
+ });
378
+ }
379
+ }
380
+ return mergedRequests;
381
+ }
382
+ /** Loads one merged range and resolves each child request with its sliced bytes. */
383
+ async fetchMergedRequest(mergedRequest) {
384
+ var _a, _b;
385
+ const { offset, endOffset, fetchRange, requests } = mergedRequest;
386
+ const abortController = new AbortController();
387
+ const requestStartTime = getTimestamp();
388
+ const abortTransportIfUnused = () => {
389
+ if (requests.every((request) => {
390
+ var _a2;
391
+ return (_a2 = request.signal) == null ? void 0 : _a2.aborted;
392
+ })) {
393
+ abortController.abort();
394
+ }
395
+ };
396
+ try {
397
+ for (const request of requests) {
398
+ request.onAbort = abortTransportIfUnused;
399
+ }
400
+ abortTransportIfUnused();
401
+ if (requests.every((request) => request.settled)) {
402
+ return;
403
+ }
404
+ const logicalBytes = getLogicalRequestBytes(requests);
405
+ const length = endOffset - offset;
406
+ this.trackEvent({
407
+ type: "request",
408
+ sourceId: mergedRequest.sourceId,
409
+ offset,
410
+ length,
411
+ logicalRequestCount: requests.length,
412
+ logicalBytes,
413
+ transportBytes: length,
414
+ overfetchBytes: Math.max(length - logicalBytes, 0)
415
+ });
416
+ const transportResult = normalizeTransportResult(
417
+ await fetchRange(offset, length, abortController.signal)
418
+ );
419
+ const { arrayBuffer } = transportResult;
420
+ const isClampedFullResponse = transportResult.fullResponse && offset === 0 && arrayBuffer.byteLength < length;
421
+ const isClampedEndOfFile = arrayBuffer.byteLength < length && Number.isSafeInteger(transportResult.sourceByteLength) && offset + arrayBuffer.byteLength === transportResult.sourceByteLength;
422
+ if (arrayBuffer.byteLength !== length && !isClampedFullResponse && !isClampedEndOfFile) {
423
+ throw new Error(
424
+ `Byte-range transport returned ${arrayBuffer.byteLength} bytes; expected ${length}`
425
+ );
426
+ }
427
+ const networkTimeMs = transportResult.networkTimeMs ?? getTimestamp() - requestStartTime;
428
+ this.trackEvent({
429
+ type: "response",
430
+ sourceId: mergedRequest.sourceId,
431
+ offset,
432
+ length,
433
+ logicalRequestCount: requests.length,
434
+ logicalBytes,
435
+ transportBytes: length,
436
+ responseBytes: transportResult.transportBytes ?? arrayBuffer.byteLength,
437
+ networkTimeMs,
438
+ overfetchBytes: Math.max(length - logicalBytes, 0),
439
+ status: transportResult.status,
440
+ fullResponse: transportResult.fullResponse
441
+ });
442
+ for (const request of requests) {
443
+ if (request.settled) {
444
+ continue;
445
+ }
446
+ const start = request.offset - offset;
447
+ request.settled = true;
448
+ request.resolve(arrayBuffer.slice(start, start + request.length));
449
+ }
450
+ } catch (error) {
451
+ this.trackEvent({
452
+ type: "error",
453
+ sourceId: mergedRequest.sourceId,
454
+ offset,
455
+ length: endOffset - offset,
456
+ logicalRequestCount: requests.length,
457
+ logicalBytes: getLogicalRequestBytes(requests),
458
+ networkTimeMs: getTimestamp() - requestStartTime,
459
+ error
460
+ });
461
+ for (const request of requests) {
462
+ if (request.settled) {
463
+ continue;
464
+ }
465
+ if ((_a = request.signal) == null ? void 0 : _a.aborted) {
466
+ this.abortRequest(request);
467
+ } else {
468
+ request.settled = true;
469
+ request.reject(error);
470
+ }
471
+ }
472
+ } finally {
473
+ for (const request of requests) {
474
+ if (request.abortListener) {
475
+ (_b = request.signal) == null ? void 0 : _b.removeEventListener("abort", request.abortListener);
476
+ }
477
+ request.onAbort = void 0;
478
+ }
479
+ }
480
+ }
481
+ /** Rejects and tracks one logical request exactly once when its caller aborts. */
482
+ abortRequest(request) {
483
+ if (request.settled) {
484
+ return;
485
+ }
486
+ request.settled = true;
487
+ request.reject(createAbortError());
488
+ this.trackEvent({
489
+ type: "abort",
490
+ sourceId: request.sourceId,
491
+ offset: request.offset,
492
+ length: request.length,
493
+ logicalRequestCount: 1,
494
+ logicalBytes: request.length
495
+ });
496
+ }
497
+ /** Emits one event to Stats and to the optional callback. */
498
+ trackEvent(event) {
499
+ var _a;
500
+ trackStatsEvent(this.stats, event);
501
+ try {
502
+ (_a = this.onEvent) == null ? void 0 : _a.call(this, event);
503
+ } catch {
504
+ }
505
+ }
506
+ /** Emits one batch event after logical requests have been merged. */
507
+ trackBatch(activeRequests, mergedRequests) {
508
+ const logicalBytes = getLogicalRequestBytes(activeRequests);
509
+ const transportBytes = mergedRequests.reduce(
510
+ (sum, mergedRequest) => sum + mergedRequest.endOffset - mergedRequest.offset,
511
+ 0
512
+ );
513
+ this.trackEvent({
514
+ type: "batch",
515
+ logicalRequestCount: activeRequests.length,
516
+ transportRequestCount: mergedRequests.length,
517
+ logicalBytes,
518
+ transportBytes,
519
+ overfetchBytes: Math.max(transportBytes - logicalBytes, 0)
520
+ });
521
+ }
522
+ };
523
+ function trackStatsEvent(stats, event) {
524
+ if ((event.type === "response" || event.type === "error") && event.networkTimeMs !== void 0) {
525
+ stats.get(RANGE_STATS_KEYS.networkTimeMs, "time").addTime(event.networkTimeMs);
526
+ }
527
+ switch (event.type) {
528
+ case "queued":
529
+ stats.get(RANGE_STATS_KEYS.logicalRanges, "count").incrementCount();
530
+ stats.get(RANGE_STATS_KEYS.requestedBytes, "count").addCount(event.logicalBytes || 0);
531
+ break;
532
+ case "batch":
533
+ stats.get(RANGE_STATS_KEYS.rangeBatches, "count").incrementCount();
534
+ stats.get("Logical Ranges Batched", "count").addCount(event.logicalRequestCount || 0);
535
+ stats.get(RANGE_STATS_KEYS.transportRanges, "count").addCount(event.transportRequestCount || 0);
536
+ stats.get(RANGE_STATS_KEYS.coalescedRanges, "count").addCount(
537
+ Math.max((event.logicalRequestCount || 0) - (event.transportRequestCount || 0), 0)
538
+ );
539
+ stats.get(RANGE_STATS_KEYS.transportBytes, "count").addCount(event.transportBytes || 0);
540
+ stats.get(RANGE_STATS_KEYS.overfetchBytes, "count").addCount(event.overfetchBytes || 0);
541
+ break;
542
+ case "response":
543
+ stats.get(RANGE_STATS_KEYS.completedTransportRanges, "count").incrementCount();
544
+ stats.get(RANGE_STATS_KEYS.responseBytes, "count").addCount(event.responseBytes || 0);
545
+ if (event.fullResponse) {
546
+ stats.get(RANGE_STATS_KEYS.fullResponseFallbacks, "count").incrementCount();
547
+ }
548
+ break;
549
+ case "error":
550
+ stats.get(RANGE_STATS_KEYS.failedTransportRanges, "count").incrementCount();
551
+ break;
552
+ case "abort":
553
+ stats.get(RANGE_STATS_KEYS.abortedLogicalRanges, "count").incrementCount();
554
+ break;
555
+ default:
556
+ }
557
+ }
558
+ function createRangeStats(id = "range-request-scheduler") {
559
+ const stats = new import_stats2.Stats({ id });
560
+ initializeStats(stats);
561
+ return stats;
562
+ }
563
+ async function fetchHttpRange(request) {
564
+ const abortContext = createAbortableFetchContext(request.signal);
565
+ const requestStartTime = getTimestamp();
566
+ try {
567
+ let fullResponse = false;
568
+ let response = await request.fetch(
569
+ request.url,
570
+ createRangeFetchOptions(
571
+ request.fetchOptions,
572
+ request.offset,
573
+ request.length,
574
+ abortContext.signal
575
+ )
576
+ );
577
+ if (response.status === 416 && request.offset === 0) {
578
+ const actualLength = parseUnsatisfiedContentRange(response.headers.get("Content-Range"));
579
+ if (!actualLength) {
580
+ throw new Error("Missing content-length on 416 response");
581
+ }
582
+ response = await request.fetch(
583
+ request.url,
584
+ createRangeFetchOptions(request.fetchOptions, 0, actualLength, abortContext.signal)
585
+ );
586
+ fullResponse = true;
587
+ }
588
+ if (response.status === 200) {
589
+ abortContext.abort();
590
+ await cancelIgnoredRangeResponse(response);
591
+ throw new Error("Byte-range request failed: server returned 200 instead of 206");
592
+ }
593
+ if (response.status !== 206 && !response.ok) {
594
+ throw new Error(`Bad response code: ${response.status}`);
595
+ }
596
+ const arrayBuffer = await response.arrayBuffer();
597
+ const contentRangeHeader = response.headers.get("Content-Range");
598
+ const contentRange = parseSatisfiedContentRange(contentRangeHeader);
599
+ if (contentRangeHeader && !contentRange) {
600
+ throw new Error(`Invalid Content-Range header: ${contentRangeHeader}`);
601
+ }
602
+ if (contentRange) {
603
+ const responseLength = contentRange.endOffset - contentRange.offset + 1;
604
+ const requestedEndOffset = request.offset + request.length - 1;
605
+ if (contentRange.offset !== request.offset || contentRange.endOffset > requestedEndOffset || responseLength !== arrayBuffer.byteLength || contentRange.sourceByteLength !== void 0 && contentRange.endOffset >= contentRange.sourceByteLength) {
606
+ throw new Error(`Content-Range does not match requested range: ${contentRangeHeader}`);
607
+ }
608
+ }
609
+ return {
610
+ arrayBuffer,
611
+ status: response.status,
612
+ sourceByteLength: contentRange == null ? void 0 : contentRange.sourceByteLength,
613
+ transportBytes: arrayBuffer.byteLength,
614
+ networkTimeMs: getTimestamp() - requestStartTime,
615
+ fullResponse
616
+ };
617
+ } finally {
618
+ abortContext.removeAbortListener();
619
+ }
620
+ }
621
+ function canMergeRequests(mergedRequest, request, props) {
622
+ if (mergedRequest.sourceId !== request.sourceId) {
623
+ return false;
624
+ }
625
+ if (mergedRequest.isolationKey !== (request.isolationKey ?? request.sourceId)) {
626
+ return false;
627
+ }
628
+ const requestEndOffset = request.offset + request.length;
629
+ const gapBytes = request.offset - mergedRequest.endOffset;
630
+ const mergedBytes = Math.max(mergedRequest.endOffset, requestEndOffset) - mergedRequest.offset;
631
+ return gapBytes <= props.rangeExpansionBytes && mergedBytes <= props.maxMergedBytes;
632
+ }
633
+ function initializeStats(stats) {
634
+ stats.get(RANGE_STATS_KEYS.logicalRanges, "count");
635
+ stats.get(RANGE_STATS_KEYS.requestedBytes, "count");
636
+ stats.get(RANGE_STATS_KEYS.rangeBatches, "count");
637
+ stats.get("Logical Ranges Batched", "count");
638
+ stats.get(RANGE_STATS_KEYS.transportRanges, "count");
639
+ stats.get(RANGE_STATS_KEYS.coalescedRanges, "count");
640
+ stats.get(RANGE_STATS_KEYS.transportBytes, "count");
641
+ stats.get(RANGE_STATS_KEYS.completedTransportRanges, "count");
642
+ stats.get(RANGE_STATS_KEYS.responseBytes, "count");
643
+ stats.get(RANGE_STATS_KEYS.networkTimeMs, "time");
644
+ stats.get(RANGE_STATS_KEYS.overfetchBytes, "count");
645
+ stats.get(RANGE_STATS_KEYS.fullResponseFallbacks, "count");
646
+ stats.get(RANGE_STATS_KEYS.failedTransportRanges, "count");
647
+ stats.get(RANGE_STATS_KEYS.abortedLogicalRanges, "count");
648
+ }
649
+ function getLogicalRequestBytes(requests) {
650
+ return requests.reduce((sum, request) => sum + request.length, 0);
651
+ }
652
+ function normalizeTransportResult(result) {
653
+ return result instanceof ArrayBuffer ? { arrayBuffer: result } : result;
654
+ }
655
+ function createRangeFetchOptions(fetchOptions, offset, length, signal) {
656
+ return {
657
+ ...fetchOptions,
658
+ signal,
659
+ headers: {
660
+ ...getHeadersObject(fetchOptions == null ? void 0 : fetchOptions.headers),
661
+ Range: `bytes=${offset}-${offset + length - 1}`
662
+ }
663
+ };
664
+ }
665
+ function getHeadersObject(headers) {
666
+ if (!headers) {
667
+ return {};
668
+ }
669
+ if (headers instanceof Headers) {
670
+ return Object.fromEntries(headers.entries());
671
+ }
672
+ if (Array.isArray(headers)) {
673
+ return Object.fromEntries(headers);
674
+ }
675
+ return headers;
676
+ }
677
+ function parseUnsatisfiedContentRange(contentRange) {
678
+ const match = contentRange == null ? void 0 : contentRange.match(/^bytes \*\/(\d+)$/);
679
+ return match ? Number(match[1]) : null;
680
+ }
681
+ function parseSatisfiedContentRange(contentRange) {
682
+ const match = contentRange == null ? void 0 : contentRange.match(/^bytes (\d+)-(\d+)\/(\d+|\*)$/);
683
+ if (!match) {
684
+ return null;
685
+ }
686
+ const offset = Number(match[1]);
687
+ const endOffset = Number(match[2]);
688
+ const sourceByteLength = match[3] === "*" ? void 0 : Number(match[3]);
689
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(endOffset) || offset > endOffset || sourceByteLength !== void 0 && !Number.isSafeInteger(sourceByteLength)) {
690
+ return null;
691
+ }
692
+ return { offset, endOffset, sourceByteLength };
693
+ }
694
+ function createAbortableFetchContext(parentSignal) {
695
+ const abortController = new AbortController();
696
+ const abortListener = () => abortController.abort();
697
+ if (parentSignal == null ? void 0 : parentSignal.aborted) {
698
+ abortController.abort();
699
+ } else {
700
+ parentSignal == null ? void 0 : parentSignal.addEventListener("abort", abortListener, { once: true });
701
+ }
702
+ return {
703
+ signal: abortController.signal,
704
+ abort: () => abortController.abort(),
705
+ removeAbortListener: () => parentSignal == null ? void 0 : parentSignal.removeEventListener("abort", abortListener)
706
+ };
707
+ }
708
+ async function cancelIgnoredRangeResponse(response) {
709
+ var _a;
710
+ await ((_a = response.body) == null ? void 0 : _a.cancel().catch(() => {
711
+ }));
712
+ }
713
+ function getTimestamp() {
714
+ var _a;
715
+ return ((_a = globalThis.performance) == null ? void 0 : _a.now()) ?? Date.now();
716
+ }
717
+ function createAbortError() {
718
+ if (typeof DOMException !== "undefined") {
719
+ return new DOMException("Request aborted", "AbortError");
720
+ }
721
+ const error = new Error("Request aborted");
722
+ error.name = "AbortError";
723
+ return error;
724
+ }
725
+
726
+ // ../loader-utils/src/lib/path-utils/path.ts
727
+ var path_exports = {};
728
+ __export(path_exports, {
729
+ dirname: () => dirname,
730
+ filename: () => filename,
731
+ join: () => join,
732
+ resolve: () => resolve
733
+ });
734
+
735
+ // ../loader-utils/src/lib/path-utils/get-cwd.ts
736
+ function getCWD() {
737
+ var _a;
738
+ if (typeof process !== "undefined" && typeof process.cwd !== "undefined") {
739
+ return process.cwd();
740
+ }
741
+ const pathname = (_a = window.location) == null ? void 0 : _a.pathname;
742
+ return (pathname == null ? void 0 : pathname.slice(0, pathname.lastIndexOf("/") + 1)) || "";
743
+ }
744
+
745
+ // ../loader-utils/src/lib/path-utils/path.ts
746
+ function filename(url) {
747
+ const slashIndex = url ? url.lastIndexOf("/") : -1;
748
+ return slashIndex >= 0 ? url.substr(slashIndex + 1) : url;
749
+ }
750
+ function dirname(url) {
751
+ const slashIndex = url ? url.lastIndexOf("/") : -1;
752
+ return slashIndex >= 0 ? url.substr(0, slashIndex) : "";
753
+ }
754
+ function join(...parts) {
755
+ const separator = "/";
756
+ parts = parts.map((part, index) => {
757
+ if (index) {
758
+ part = part.replace(new RegExp(`^${separator}`), "");
759
+ }
760
+ if (index !== parts.length - 1) {
761
+ part = part.replace(new RegExp(`${separator}$`), "");
762
+ }
763
+ return part;
764
+ });
765
+ return parts.join(separator);
766
+ }
767
+ function resolve(...components) {
768
+ const paths = [];
769
+ for (let _i = 0; _i < components.length; _i++) {
770
+ paths[_i] = components[_i];
771
+ }
772
+ let resolvedPath = "";
773
+ let resolvedAbsolute = false;
774
+ let cwd;
775
+ for (let i = paths.length - 1; i >= -1 && !resolvedAbsolute; i--) {
776
+ let path;
777
+ if (i >= 0) {
778
+ path = paths[i];
779
+ } else {
780
+ if (cwd === void 0) {
781
+ cwd = getCWD();
782
+ }
783
+ path = cwd;
784
+ }
785
+ if (path.length === 0) {
786
+ continue;
787
+ }
788
+ resolvedPath = `${path}/${resolvedPath}`;
789
+ resolvedAbsolute = path.charCodeAt(0) === SLASH;
790
+ }
791
+ resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute);
792
+ if (resolvedAbsolute) {
793
+ return `/${resolvedPath}`;
794
+ } else if (resolvedPath.length > 0) {
795
+ return resolvedPath;
796
+ }
797
+ return ".";
798
+ }
799
+ var SLASH = 47;
800
+ var DOT = 46;
801
+ function normalizeStringPosix(path, allowAboveRoot) {
802
+ let res = "";
803
+ let lastSlash = -1;
804
+ let dots = 0;
805
+ let code;
806
+ let isAboveRoot = false;
807
+ for (let i = 0; i <= path.length; ++i) {
808
+ if (i < path.length) {
809
+ code = path.charCodeAt(i);
810
+ } else if (code === SLASH) {
811
+ break;
812
+ } else {
813
+ code = SLASH;
814
+ }
815
+ if (code === SLASH) {
816
+ if (lastSlash === i - 1 || dots === 1) {
817
+ } else if (lastSlash !== i - 1 && dots === 2) {
818
+ if (res.length < 2 || !isAboveRoot || res.charCodeAt(res.length - 1) !== DOT || res.charCodeAt(res.length - 2) !== DOT) {
819
+ if (res.length > 2) {
820
+ const start = res.length - 1;
821
+ let j = start;
822
+ for (; j >= 0; --j) {
823
+ if (res.charCodeAt(j) === SLASH) {
824
+ break;
825
+ }
826
+ }
827
+ if (j !== start) {
828
+ res = j === -1 ? "" : res.slice(0, j);
829
+ lastSlash = i;
830
+ dots = 0;
831
+ isAboveRoot = false;
832
+ continue;
833
+ }
834
+ } else if (res.length === 2 || res.length === 1) {
835
+ res = "";
836
+ lastSlash = i;
837
+ dots = 0;
838
+ isAboveRoot = false;
839
+ continue;
840
+ }
841
+ }
842
+ if (allowAboveRoot) {
843
+ if (res.length > 0) {
844
+ res += "/..";
845
+ } else {
846
+ res = "..";
847
+ }
848
+ isAboveRoot = true;
849
+ }
850
+ } else {
851
+ const slice = path.slice(lastSlash + 1, i);
852
+ if (res.length > 0) {
853
+ res += `/${slice}`;
854
+ } else {
855
+ res = slice;
856
+ }
857
+ isAboveRoot = false;
858
+ }
859
+ lastSlash = i;
860
+ dots = 0;
861
+ } else if (code === DOT && dots !== -1) {
862
+ ++dots;
863
+ } else {
864
+ dots = -1;
865
+ }
866
+ }
867
+ return res;
868
+ }
869
+
870
+ // ../loader-utils/src/lib/files/blob-file.ts
871
+ var BlobFile = class {
872
+ handle;
873
+ size;
874
+ bigsize;
875
+ url;
876
+ constructor(blob) {
877
+ this.handle = blob instanceof ArrayBuffer ? new Blob([blob]) : blob;
878
+ this.size = blob instanceof ArrayBuffer ? blob.byteLength : blob.size;
879
+ this.bigsize = BigInt(this.size);
880
+ this.url = blob instanceof File ? blob.name : "";
881
+ }
882
+ async close() {
883
+ }
884
+ async stat() {
885
+ return {
886
+ size: this.handle.size,
887
+ bigsize: BigInt(this.handle.size),
888
+ isDirectory: false
889
+ };
890
+ }
891
+ async read(start, length, signal) {
892
+ if (signal == null ? void 0 : signal.aborted) {
893
+ throw new Error("Request aborted");
894
+ }
895
+ const arrayBuffer = await this.handle.slice(Number(start), Number(start) + Number(length)).arrayBuffer();
896
+ if (signal == null ? void 0 : signal.aborted) {
897
+ throw new Error("Request aborted");
898
+ }
899
+ return arrayBuffer;
900
+ }
901
+ };
902
+
903
+ // ../loader-utils/src/lib/files/http-file-transport.ts
904
+ var HttpFileTransport = class {
905
+ /** Remote object URL. */
906
+ url;
907
+ /** Fetch implementation used for range requests. */
908
+ fetchFunction;
909
+ /** Persistent fetch options copied from the caller. */
910
+ fetchOptions;
911
+ /** Validator enforcement mode. */
912
+ consistency;
913
+ /** Caller-supplied identity information. */
914
+ identityHints;
915
+ /** Failure objects already included in telemetry. */
916
+ countedFailures = /* @__PURE__ */ new WeakSet();
917
+ /** Mutable counters backing immutable telemetry snapshots. */
918
+ telemetry = {
919
+ requestedBytes: 0,
920
+ downloadedBytes: 0,
921
+ requestCount: 0,
922
+ networkTimeMs: 0,
923
+ abortCount: 0,
924
+ errorCount: 0
925
+ };
926
+ /** Creates the transport for one remote object. */
927
+ constructor(url, options) {
928
+ validateKnownByteLength(options.byteLength);
929
+ this.url = url;
930
+ this.fetchFunction = options.fetch || ((input, init) => globalThis.fetch(input, init));
931
+ this.fetchOptions = options.fetchOptions ? { ...options.fetchOptions, headers: new Headers(options.fetchOptions.headers) } : void 0;
932
+ this.consistency = options.consistency || "best-effort";
933
+ this.identityHints = {
934
+ byteLength: options.byteLength,
935
+ etag: normalizeHeaderValue(options.etag),
936
+ lastModified: normalizeHeaderValue(options.lastModified)
937
+ };
938
+ }
939
+ /** Returns an identity that is complete enough to avoid an opening request. */
940
+ getInitialIdentity() {
941
+ const { byteLength, etag, lastModified } = this.identityHints;
942
+ if (byteLength !== void 0 && (etag !== void 0 || lastModified !== void 0)) {
943
+ return createIdentity(byteLength, etag, lastModified);
944
+ }
945
+ if (byteLength === 0 && this.consistency === "best-effort") {
946
+ return createIdentity(0);
947
+ }
948
+ return null;
949
+ }
950
+ /** Returns an immutable point-in-time copy of the transport counters. */
951
+ getTelemetry() {
952
+ return Object.freeze({ ...this.telemetry });
953
+ }
954
+ /** Counts each failure object once even when it passes through several async layers. */
955
+ trackFailure(error) {
956
+ if (typeof error === "object" && error !== null) {
957
+ if (this.countedFailures.has(error)) {
958
+ return;
959
+ }
960
+ this.countedFailures.add(error);
961
+ }
962
+ if (isAbortError(error)) {
963
+ this.telemetry.abortCount++;
964
+ } else {
965
+ this.telemetry.errorCount++;
966
+ }
967
+ }
968
+ /** Performs and validates one exact HTTP range request. */
969
+ async requestRange(offset, length, signal, expectedIdentity) {
970
+ var _a;
971
+ const requestStartTime = getTimestamp2();
972
+ const abortContext = createCombinedAbortContext(signal, (_a = this.fetchOptions) == null ? void 0 : _a.signal);
973
+ this.telemetry.requestCount++;
974
+ this.telemetry.requestedBytes += length;
975
+ try {
976
+ const response = await this.fetchFunction(
977
+ this.url,
978
+ createFetchOptions(this.fetchOptions, offset, length, abortContext.signal)
979
+ );
980
+ if (isEmptyIdentityProbeResponse(response, offset, length, expectedIdentity)) {
981
+ return await this.readEmptyIdentityProbe(response, requestStartTime);
982
+ }
983
+ if (response.status !== 206) {
984
+ await cancelResponse(response);
985
+ throw new Error(`HTTP byte-range request expected 206, received ${response.status}`);
986
+ }
987
+ const contentRange = await readContentRange(response);
988
+ const responseIdentity = getResponseIdentity(response, contentRange.byteLength);
989
+ const responseError = getIdentityError(
990
+ expectedIdentity,
991
+ responseIdentity,
992
+ this.identityHints,
993
+ this.consistency
994
+ ) || getRangeError(contentRange, offset, length);
995
+ if (responseError) {
996
+ await cancelResponse(response);
997
+ throw new Error(responseError);
998
+ }
999
+ const arrayBuffer = await response.arrayBuffer();
1000
+ this.telemetry.downloadedBytes += arrayBuffer.byteLength;
1001
+ if (arrayBuffer.byteLength !== length) {
1002
+ throw new Error(
1003
+ `HTTP byte-range response contained ${arrayBuffer.byteLength} bytes; expected ${length}`
1004
+ );
1005
+ }
1006
+ return {
1007
+ arrayBuffer,
1008
+ identity: resolveIdentity(expectedIdentity, responseIdentity, this.identityHints),
1009
+ status: response.status,
1010
+ transportBytes: arrayBuffer.byteLength,
1011
+ networkTimeMs: getTimestamp2() - requestStartTime
1012
+ };
1013
+ } catch (error) {
1014
+ if (expectedIdentity === null || !isAbortError(error)) {
1015
+ this.trackFailure(error);
1016
+ }
1017
+ throw error;
1018
+ } finally {
1019
+ abortContext.removeAbortListeners();
1020
+ this.telemetry.networkTimeMs += getTimestamp2() - requestStartTime;
1021
+ }
1022
+ }
1023
+ /** Validates and consumes the special empty-object identity response. */
1024
+ async readEmptyIdentityProbe(response, requestStartTime) {
1025
+ const responseIdentity = getResponseIdentity(response, 0);
1026
+ const identityError = getIdentityError(
1027
+ null,
1028
+ responseIdentity,
1029
+ this.identityHints,
1030
+ this.consistency
1031
+ );
1032
+ await cancelResponse(response);
1033
+ if (identityError) {
1034
+ throw new Error(identityError);
1035
+ }
1036
+ return {
1037
+ arrayBuffer: new ArrayBuffer(0),
1038
+ identity: resolveIdentity(null, responseIdentity, this.identityHints),
1039
+ status: response.status,
1040
+ transportBytes: 0,
1041
+ networkTimeMs: getTimestamp2() - requestStartTime
1042
+ };
1043
+ }
1044
+ };
1045
+ function createFetchOptions(fetchOptions, offset, length, signal) {
1046
+ const headers = new Headers(fetchOptions == null ? void 0 : fetchOptions.headers);
1047
+ headers.set("Range", `bytes=${offset}-${offset + length - 1}`);
1048
+ return {
1049
+ ...fetchOptions,
1050
+ method: "GET",
1051
+ body: void 0,
1052
+ headers,
1053
+ signal
1054
+ };
1055
+ }
1056
+ function isEmptyIdentityProbeResponse(response, offset, length, expectedIdentity) {
1057
+ return response.status === 416 && expectedIdentity === null && offset === 0 && length === 1 && /^bytes \*\/0$/i.test(response.headers.get("Content-Range") || "");
1058
+ }
1059
+ async function readContentRange(response) {
1060
+ try {
1061
+ return parseContentRange(response.headers.get("Content-Range"));
1062
+ } catch (error) {
1063
+ await cancelResponse(response);
1064
+ throw error;
1065
+ }
1066
+ }
1067
+ function parseContentRange(contentRange) {
1068
+ const match = contentRange == null ? void 0 : contentRange.match(/^bytes (\d+)-(\d+)\/(\d+)$/);
1069
+ if (!match) {
1070
+ throw new Error("HTTP byte-range response has an invalid Content-Range header");
1071
+ }
1072
+ return {
1073
+ offset: Number(match[1]),
1074
+ endOffset: Number(match[2]),
1075
+ byteLength: Number(match[3])
1076
+ };
1077
+ }
1078
+ function getResponseIdentity(response, byteLength) {
1079
+ return createIdentity(
1080
+ byteLength,
1081
+ normalizeHeaderValue(response.headers.get("ETag")),
1082
+ normalizeHeaderValue(response.headers.get("Last-Modified"))
1083
+ );
1084
+ }
1085
+ function resolveIdentity(expectedIdentity, responseIdentity, identityHints) {
1086
+ return expectedIdentity || createIdentity(
1087
+ responseIdentity.byteLength,
1088
+ responseIdentity.etag ?? identityHints.etag,
1089
+ responseIdentity.lastModified ?? identityHints.lastModified
1090
+ );
1091
+ }
1092
+ function getRangeError(contentRange, offset, length) {
1093
+ if (!Number.isSafeInteger(contentRange.offset) || !Number.isSafeInteger(contentRange.endOffset) || !Number.isSafeInteger(contentRange.byteLength) || contentRange.offset < 0 || contentRange.endOffset < contentRange.offset || contentRange.endOffset >= contentRange.byteLength) {
1094
+ return "HTTP byte-range response has invalid range bounds";
1095
+ }
1096
+ if (contentRange.offset !== offset || contentRange.endOffset !== offset + length - 1) {
1097
+ return "HTTP byte-range response does not match the requested range";
1098
+ }
1099
+ return null;
1100
+ }
1101
+ function getIdentityError(expectedIdentity, responseIdentity, identityHints, consistency) {
1102
+ const expectedByteLength = (expectedIdentity == null ? void 0 : expectedIdentity.byteLength) ?? identityHints.byteLength;
1103
+ if (expectedByteLength !== void 0 && responseIdentity.byteLength !== expectedByteLength) {
1104
+ return "HTTP file length changed while the file was open";
1105
+ }
1106
+ const expectedEtag = (expectedIdentity == null ? void 0 : expectedIdentity.etag) ?? identityHints.etag;
1107
+ const expectedLastModified = (expectedIdentity == null ? void 0 : expectedIdentity.lastModified) ?? identityHints.lastModified;
1108
+ if (expectedEtag) {
1109
+ if (responseIdentity.etag) {
1110
+ return expectedEtag === responseIdentity.etag ? null : "HTTP file ETag changed while the file was open";
1111
+ }
1112
+ if (consistency === "strict") {
1113
+ return "HTTP byte-range response is missing the pinned ETag";
1114
+ }
1115
+ if (expectedLastModified && responseIdentity.lastModified && expectedLastModified !== responseIdentity.lastModified) {
1116
+ return "HTTP file Last-Modified value changed while the file was open";
1117
+ }
1118
+ return null;
1119
+ }
1120
+ if (expectedLastModified) {
1121
+ if (responseIdentity.lastModified && expectedLastModified !== responseIdentity.lastModified) {
1122
+ return "HTTP file Last-Modified value changed while the file was open";
1123
+ }
1124
+ if (consistency === "strict" && !responseIdentity.lastModified) {
1125
+ return "HTTP byte-range response is missing the pinned Last-Modified value";
1126
+ }
1127
+ return null;
1128
+ }
1129
+ if (consistency === "strict" && !responseIdentity.etag && !responseIdentity.lastModified) {
1130
+ return "HTTP byte-range response does not provide an ETag or Last-Modified validator";
1131
+ }
1132
+ return null;
1133
+ }
1134
+ function createIdentity(byteLength, etag, lastModified) {
1135
+ return Object.freeze({ byteLength, etag, lastModified });
1136
+ }
1137
+ function validateKnownByteLength(byteLength) {
1138
+ if (byteLength !== void 0 && (!Number.isSafeInteger(byteLength) || byteLength < 0)) {
1139
+ throw new Error("HttpFile byteLength must be a non-negative safe integer");
1140
+ }
1141
+ }
1142
+ async function cancelResponse(response) {
1143
+ var _a;
1144
+ await ((_a = response.body) == null ? void 0 : _a.cancel().catch(() => {
1145
+ }));
1146
+ }
1147
+ function normalizeHeaderValue(value) {
1148
+ return value || void 0;
1149
+ }
1150
+ function getTimestamp2() {
1151
+ var _a;
1152
+ return ((_a = globalThis.performance) == null ? void 0 : _a.now()) ?? Date.now();
1153
+ }
1154
+ function isAbortError(error) {
1155
+ return error instanceof Error && error.name === "AbortError" || error instanceof Error && /aborted/i.test(error.message);
1156
+ }
1157
+ function createCombinedAbortContext(...signals) {
1158
+ const abortController = new AbortController();
1159
+ const uniqueSignals = [
1160
+ ...new Set(signals.filter((signal) => Boolean(signal)))
1161
+ ];
1162
+ const abortListener = () => abortController.abort();
1163
+ for (const signal of uniqueSignals) {
1164
+ if (signal.aborted) {
1165
+ abortController.abort();
1166
+ } else {
1167
+ signal.addEventListener("abort", abortListener, { once: true });
1168
+ }
1169
+ }
1170
+ return {
1171
+ signal: abortController.signal,
1172
+ removeAbortListeners: () => {
1173
+ for (const signal of uniqueSignals) {
1174
+ signal.removeEventListener("abort", abortListener);
1175
+ }
1176
+ }
1177
+ };
1178
+ }
1179
+
1180
+ // ../loader-utils/src/lib/files/http-file.ts
1181
+ var HttpFile = class _HttpFile {
1182
+ /** URL used as the ReadableFile handle. */
1183
+ handle;
1184
+ /** Remote object URL. */
1185
+ url;
1186
+ /** HTTP transport and response validator for this file. */
1187
+ transport;
1188
+ /** Scheduler used to coalesce compatible range reads. */
1189
+ rangeScheduler;
1190
+ /** Unique key that prevents coalescing across transport or credential contexts. */
1191
+ schedulerIsolationKey = {};
1192
+ /** Caller-supplied length available before identity discovery. */
1193
+ suppliedByteLength;
1194
+ /** Pinned remote object identity. */
1195
+ identity;
1196
+ /** Shared opening probe, while identity discovery is in progress. */
1197
+ openPromise = null;
1198
+ /** Whether the file has stopped accepting operations. */
1199
+ closed = false;
1200
+ /** Creates a lazy HTTP file. Use {@link HttpFile.open} when identity must be pinned immediately. */
1201
+ constructor(url, options = {}) {
1202
+ this.handle = url;
1203
+ this.url = url;
1204
+ this.transport = new HttpFileTransport(url, options);
1205
+ this.rangeScheduler = options.rangeScheduler || new RangeRequestScheduler({ batchDelayMs: 0, ...options.rangeSchedulerProps });
1206
+ this.suppliedByteLength = options.byteLength;
1207
+ this.identity = this.transport.getInitialIdentity();
1208
+ }
1209
+ /** Opens a file and pins its byte length and available validators. */
1210
+ static async open(url, options = {}, signal) {
1211
+ const file = new _HttpFile(url, options);
1212
+ await file.open(signal);
1213
+ return file;
1214
+ }
1215
+ /** Pins this file's byte length and available validators. */
1216
+ async open(signal) {
1217
+ this.assertOpen();
1218
+ if (signal == null ? void 0 : signal.aborted) {
1219
+ const error = createAbortError2();
1220
+ this.transport.trackFailure(error);
1221
+ throw error;
1222
+ }
1223
+ await this.getIdentity(signal);
1224
+ return this;
1225
+ }
1226
+ /** Pinned file length, or zero before a lazy file has opened. */
1227
+ get size() {
1228
+ var _a;
1229
+ return ((_a = this.identity) == null ? void 0 : _a.byteLength) ?? this.suppliedByteLength ?? 0;
1230
+ }
1231
+ /** Pinned file length as a bigint, or zero before a lazy file has opened. */
1232
+ get bigsize() {
1233
+ return BigInt(this.size);
1234
+ }
1235
+ /** Returns the pinned identity, or null before a lazy file has opened. */
1236
+ getIdentitySnapshot() {
1237
+ return this.identity;
1238
+ }
1239
+ /** Returns an immutable point-in-time copy of this file's transport counters. */
1240
+ getTelemetry() {
1241
+ return this.transport.getTelemetry();
1242
+ }
1243
+ /** Prevents new operations. Active fetches remain controlled by their read signals. */
1244
+ async close() {
1245
+ this.closed = true;
1246
+ }
1247
+ /** Returns pinned file information without issuing a HEAD request. */
1248
+ async stat() {
1249
+ this.assertOpen();
1250
+ const identity = await this.getIdentity();
1251
+ return {
1252
+ size: identity.byteLength,
1253
+ bigsize: BigInt(identity.byteLength),
1254
+ isDirectory: false
1255
+ };
1256
+ }
1257
+ /** Reads exactly one byte range, with optional cancellation. */
1258
+ async read(offset = 0, length = 0, signal) {
1259
+ this.assertOpen();
1260
+ if (signal == null ? void 0 : signal.aborted) {
1261
+ const error = createAbortError2();
1262
+ this.transport.trackFailure(error);
1263
+ throw error;
1264
+ }
1265
+ const identity = await this.getIdentity(signal);
1266
+ const numericOffset = normalizeOffset(offset);
1267
+ validateRange(numericOffset, length, identity.byteLength);
1268
+ if (length === 0) {
1269
+ return new ArrayBuffer(0);
1270
+ }
1271
+ try {
1272
+ return await this.rangeScheduler.scheduleRequest({
1273
+ sourceId: this.url,
1274
+ isolationKey: this.schedulerIsolationKey,
1275
+ offset: numericOffset,
1276
+ length,
1277
+ signal,
1278
+ fetchRange: this.fetchScheduledRange
1279
+ });
1280
+ } catch (error) {
1281
+ this.transport.trackFailure(error);
1282
+ throw error;
1283
+ }
1284
+ }
1285
+ /** Reads a range and returns a Response for legacy ReadableFile consumers. */
1286
+ async fetchRange(offset, length, signal) {
1287
+ const numericOffset = normalizeOffset(offset);
1288
+ const arrayBuffer = await this.read(numericOffset, length, signal);
1289
+ const headers = createIdentityHeaders(this.identity, numericOffset, length);
1290
+ return new Response(arrayBuffer, { status: 206, headers });
1291
+ }
1292
+ /** Stable scheduler callback used by every read from this file. */
1293
+ fetchScheduledRange = async (offset, length, signal) => {
1294
+ const identity = await this.getIdentity(signal);
1295
+ const result = await this.transport.requestRange(offset, length, signal, identity);
1296
+ return {
1297
+ arrayBuffer: result.arrayBuffer,
1298
+ status: result.status,
1299
+ transportBytes: result.transportBytes,
1300
+ networkTimeMs: result.networkTimeMs
1301
+ };
1302
+ };
1303
+ /** Returns the shared lazy identity promise, retrying after a failed probe. */
1304
+ getIdentity(signal) {
1305
+ if (this.identity) {
1306
+ return Promise.resolve(this.identity);
1307
+ }
1308
+ if (!this.openPromise) {
1309
+ const nextOpenPromise = this.probeIdentity();
1310
+ const cachedOpenPromise = nextOpenPromise.catch((error) => {
1311
+ if (this.openPromise === cachedOpenPromise) {
1312
+ this.openPromise = null;
1313
+ }
1314
+ throw error;
1315
+ });
1316
+ this.openPromise = cachedOpenPromise;
1317
+ }
1318
+ return waitForPromiseWithSignal(this.openPromise, signal).catch((error) => {
1319
+ this.transport.trackFailure(error);
1320
+ throw error;
1321
+ });
1322
+ }
1323
+ /** Uses a one-byte GET range to discover object length and validators. */
1324
+ async probeIdentity() {
1325
+ try {
1326
+ const result = await this.transport.requestRange(0, 1, void 0, null);
1327
+ this.identity = result.identity;
1328
+ return result.identity;
1329
+ } catch (error) {
1330
+ this.transport.trackFailure(error);
1331
+ throw error;
1332
+ }
1333
+ }
1334
+ /** Throws when the file has been closed. */
1335
+ assertOpen() {
1336
+ if (this.closed) {
1337
+ throw new Error("HttpFile is closed");
1338
+ }
1339
+ }
1340
+ };
1341
+ function createIdentityHeaders(identity, offset, length) {
1342
+ const headers = new Headers();
1343
+ if (length > 0 && identity) {
1344
+ headers.set("Content-Range", `bytes ${offset}-${offset + length - 1}/${identity.byteLength}`);
1345
+ }
1346
+ if (identity == null ? void 0 : identity.etag) {
1347
+ headers.set("ETag", identity.etag);
1348
+ }
1349
+ if (identity == null ? void 0 : identity.lastModified) {
1350
+ headers.set("Last-Modified", identity.lastModified);
1351
+ }
1352
+ return headers;
1353
+ }
1354
+ function normalizeOffset(offset) {
1355
+ const numericOffset = Number(offset);
1356
+ if (!Number.isSafeInteger(numericOffset) || numericOffset < 0) {
1357
+ throw new Error("HTTP byte-range offset must be a non-negative safe integer");
1358
+ }
1359
+ return numericOffset;
1360
+ }
1361
+ function validateRange(offset, length, byteLength) {
1362
+ if (!Number.isSafeInteger(length) || length < 0) {
1363
+ throw new Error("HTTP byte-range length must be a non-negative safe integer");
1364
+ }
1365
+ if (offset + length > byteLength) {
1366
+ throw new Error("HTTP byte range extends beyond the end of the file");
1367
+ }
1368
+ }
1369
+ function createAbortError2() {
1370
+ if (typeof DOMException !== "undefined") {
1371
+ return new DOMException("Request aborted", "AbortError");
1372
+ }
1373
+ const error = new Error("Request aborted");
1374
+ error.name = "AbortError";
1375
+ return error;
1376
+ }
1377
+ function waitForPromiseWithSignal(promise, signal) {
1378
+ if (!signal) {
1379
+ return promise;
1380
+ }
1381
+ if (signal.aborted) {
1382
+ return Promise.reject(createAbortError2());
1383
+ }
1384
+ return new Promise((resolve2, reject) => {
1385
+ const abortListener = () => {
1386
+ signal.removeEventListener("abort", abortListener);
1387
+ reject(createAbortError2());
1388
+ };
1389
+ signal.addEventListener("abort", abortListener, { once: true });
1390
+ promise.then(
1391
+ (value) => {
1392
+ signal.removeEventListener("abort", abortListener);
1393
+ resolve2(value);
1394
+ },
1395
+ (error) => {
1396
+ signal.removeEventListener("abort", abortListener);
1397
+ reject(error);
1398
+ }
1399
+ );
1400
+ });
1401
+ }
57
1402
 
58
1403
  // dist/utils/doubly-linked-list-node.js
59
1404
  var DoublyLinkedListNode = class {
@@ -181,14 +1526,23 @@ var TilesetCache = class {
181
1526
  unloadCallback(tileset, tile);
182
1527
  }
183
1528
  }
1529
+ /**
1530
+ * Unloads least-recently-used tiles until the byte-native cache target is met.
1531
+ *
1532
+ * Tiles touched after the frame sentinel remain protected, so {@link Tileset3D.cacheBytes} is a
1533
+ * soft target rather than a hard cap on the visible working set. Overflow headroom affects
1534
+ * memory-adjusted SSE, not eviction of unused tiles.
1535
+ *
1536
+ * @param tileset - Tileset that owns the cache and reports estimated content memory.
1537
+ * @param unloadCallback - Callback that releases content and decrements the memory estimate.
1538
+ */
184
1539
  unloadTiles(tileset, unloadCallback) {
185
1540
  const trimTiles = this._trimTiles;
186
1541
  this._trimTiles = false;
187
1542
  const list = this._list;
188
- const maximumMemoryUsageInBytes = tileset.maximumMemoryUsage * 1024 * 1024;
189
1543
  const sentinel = this._sentinel;
190
1544
  let node = list.head;
191
- while (node !== sentinel && (tileset.gpuMemoryUsageInBytes > maximumMemoryUsageInBytes || trimTiles)) {
1545
+ while (node !== sentinel && (tileset.gpuMemoryUsageInBytes > tileset.cacheBytes || trimTiles)) {
192
1546
  const tile = node.item;
193
1547
  node = node.next;
194
1548
  this.unloadTile(tileset, tile, unloadCallback);
@@ -202,10 +1556,9 @@ var TilesetCache = class {
202
1556
  // dist/tileset-3d/helpers/transform-utils.js
203
1557
  var import_geospatial = require("@math.gl/geospatial");
204
1558
  var import_core = require("@math.gl/core");
205
- var import_loader_utils = require("@loaders.gl/loader-utils");
206
1559
  function calculateTransformProps(tileHeader, tile) {
207
- (0, import_loader_utils.assert)(tileHeader);
208
- (0, import_loader_utils.assert)(tile);
1560
+ assert(tileHeader);
1561
+ assert(tile);
209
1562
  const { rtcCenter, gltfUpAxis } = tile;
210
1563
  const { computedTransform, boundingVolume: { center } } = tileHeader;
211
1564
  let modelMatrix = new import_core.Matrix4(computedTransform);
@@ -272,6 +1625,8 @@ var import_culling = require("@math.gl/culling");
272
1625
  var import_geospatial2 = require("@math.gl/geospatial");
273
1626
  var scratchVector = new import_core2.Vector3();
274
1627
  var scratchPosition = new import_core2.Vector3();
1628
+ var CAMERA_POSITION_EPSILON = 1e-5;
1629
+ var CAMERA_DIRECTION_EPSILON = 1e-7;
275
1630
  var cullingVolume = new import_culling.CullingVolume([
276
1631
  new import_culling.Plane(),
277
1632
  new import_culling.Plane(),
@@ -280,17 +1635,26 @@ var cullingVolume = new import_culling.CullingVolume([
280
1635
  new import_culling.Plane(),
281
1636
  new import_culling.Plane()
282
1637
  ]);
283
- function getFrameState(viewport, frameNumber) {
284
- const { cameraDirection, cameraUp, height } = viewport;
1638
+ function getFrameState(viewport, frameNumber, options = {}) {
1639
+ const { cameraUp, height } = viewport;
1640
+ const cameraDirection = viewport.cameraDirection;
285
1641
  const { metersPerUnit } = viewport.distanceScales;
286
1642
  const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
287
1643
  const enuToFixedTransform = import_geospatial2.Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
288
1644
  const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
289
1645
  const cameraPositionCartesian2 = import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, new import_core2.Vector3());
290
- const cameraDirectionCartesian = new import_core2.Vector3(
291
- // @ts-ignore
292
- enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraDirection).scale(metersPerUnit))
293
- ).normalize();
1646
+ let cameraDirectionCartesian;
1647
+ if (cameraDirection) {
1648
+ cameraDirectionCartesian = new import_core2.Vector3(
1649
+ // @ts-ignore
1650
+ enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraDirection).scale(metersPerUnit))
1651
+ ).normalize();
1652
+ } else {
1653
+ const nearPlane = viewport.getFrustumPlanes().near;
1654
+ const nearCenterCommon = closestPointOnPlane(nearPlane, viewport.cameraPosition);
1655
+ const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
1656
+ cameraDirectionCartesian = new import_core2.Vector3(nearCenterCartesian).subtract(cameraPositionCartesian2).normalize();
1657
+ }
294
1658
  const cameraUpCartesian = new import_core2.Vector3(
295
1659
  // @ts-ignore
296
1660
  enuToFixedTransform.transformAsVector(new import_core2.Vector3(cameraUp).scale(metersPerUnit))
@@ -311,7 +1675,10 @@ function getFrameState(viewport, frameNumber) {
311
1675
  camera: {
312
1676
  position: cameraPositionCartesian2,
313
1677
  direction: cameraDirectionCartesian,
314
- up: cameraUpCartesian
1678
+ up: cameraUpCartesian,
1679
+ cartographicPosition: cameraPositionCartographic,
1680
+ verticalFieldOfView: Number.isFinite(viewport.fovy) && Number(viewport.fovy) > 0 ? Number(viewport.fovy) * Math.PI / 180 : Math.PI / 3,
1681
+ timeSinceMovement: options.timeSinceCameraMovement ?? Number.POSITIVE_INFINITY
315
1682
  },
316
1683
  viewport,
317
1684
  topDownViewport,
@@ -319,63 +1686,342 @@ function getFrameState(viewport, frameNumber) {
319
1686
  cullingVolume,
320
1687
  frameNumber,
321
1688
  // TODO: This can be the same between updates, what number is unique for between updates?
322
- sseDenominator: 1.15
1689
+ sseDenominator: 1.15,
323
1690
  // Assumes fovy = 60 degrees
1691
+ // Tileset3D fills this immediately before traversal because it depends on the root volume.
1692
+ dynamicScreenSpaceErrorDensity: 0
1693
+ };
1694
+ }
1695
+ function updateCameraMotionState(previousState, position, direction, currentTime) {
1696
+ const positionArray = Array.from(position);
1697
+ const directionArray = Array.from(direction);
1698
+ const positionChanged = previousState ? positionArray.length !== previousState.position.length || positionArray.some((value, index) => Math.abs(value - previousState.position[index]) > CAMERA_POSITION_EPSILON) : false;
1699
+ const directionChanged = previousState ? directionArray.length !== previousState.direction.length || directionArray.some((value, index) => Math.abs(value - previousState.direction[index]) > CAMERA_DIRECTION_EPSILON) : false;
1700
+ const lastMovementTime = positionChanged || directionChanged ? currentTime : (previousState == null ? void 0 : previousState.lastMovementTime) ?? Number.NEGATIVE_INFINITY;
1701
+ return {
1702
+ state: {
1703
+ position: positionArray,
1704
+ direction: directionArray,
1705
+ lastMovementTime
1706
+ },
1707
+ timeSinceMovement: Math.max((currentTime - lastMovementTime) / 1e3, 0)
1708
+ };
1709
+ }
1710
+ function limitSelectedTiles(tiles, frameState, maximumTilesSelected) {
1711
+ if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
1712
+ return [tiles, []];
1713
+ }
1714
+ const tuples = [];
1715
+ const { longitude: viewportLongitude, latitude: viewportLatitude } = frameState.viewport;
1716
+ for (const [index, tile] of tiles.entries()) {
1717
+ const [longitude, latitude] = tile.header.mbs;
1718
+ const deltaLon = Math.abs(viewportLongitude - longitude);
1719
+ const deltaLat = Math.abs(viewportLatitude - latitude);
1720
+ const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
1721
+ tuples.push([index, distance]);
1722
+ }
1723
+ const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
1724
+ const selectedTiles = [];
1725
+ for (let i = 0; i < maximumTilesSelected; i++) {
1726
+ selectedTiles.push(tiles[tuplesSorted[i][0]]);
1727
+ }
1728
+ const unselectedTiles = [];
1729
+ for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
1730
+ unselectedTiles.push(tiles[tuplesSorted[i][0]]);
1731
+ }
1732
+ return [selectedTiles, unselectedTiles];
1733
+ }
1734
+ function commonSpacePlanesToWGS84(viewport) {
1735
+ const frustumPlanes = viewport.getFrustumPlanes();
1736
+ const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
1737
+ const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
1738
+ const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
1739
+ let i = 0;
1740
+ cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
1741
+ for (const dir in frustumPlanes) {
1742
+ if (dir === "near") {
1743
+ continue;
1744
+ }
1745
+ const plane = frustumPlanes[dir];
1746
+ const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
1747
+ const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
1748
+ cullingVolume.planes[i++].fromPointNormal(
1749
+ cartesianPos,
1750
+ // Want the normal to point into the frustum since that's what culling expects
1751
+ scratchVector.copy(nearCenterCartesian).subtract(cartesianPos)
1752
+ );
1753
+ }
1754
+ }
1755
+ function closestPointOnPlane(plane, refPoint, out = new import_core2.Vector3()) {
1756
+ const distanceToRef = plane.normal.dot(refPoint);
1757
+ out.copy(plane.normal).scale(plane.distance - distanceToRef).add(refPoint);
1758
+ return out;
1759
+ }
1760
+ function worldToCartesian(viewport, point, out = new import_core2.Vector3()) {
1761
+ const cartographicPos = viewport.unprojectPosition(point);
1762
+ return import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
1763
+ }
1764
+
1765
+ // dist/tileset-3d/helpers/tiles-3d-lod.js
1766
+ var import_core3 = require("@math.gl/core");
1767
+ var scratchPositionNormal = new import_core3.Vector3();
1768
+ var scratchCartographic = new import_core3.Vector3();
1769
+ var scratchCenter = new import_core3.Vector3();
1770
+ var scratchPosition2 = new import_core3.Vector3();
1771
+ var scratchDirection = new import_core3.Vector3();
1772
+ var scratchBoxXAxis = new import_core3.Vector3();
1773
+ var scratchBoxYAxis = new import_core3.Vector3();
1774
+ var scratchBoxZAxis = new import_core3.Vector3();
1775
+ var scratchBoxQuaternion = new import_core3.Quaternion();
1776
+ var unitZDirection = new import_core3.Vector3(0, 0, 1);
1777
+ function updateRootTransformForDynamicScreenSpaceError(root, modelMatrix) {
1778
+ root._updateTransform(modelMatrix);
1779
+ }
1780
+ function calculateDynamicScreenSpaceErrorDensity(root, frameState, options) {
1781
+ if (!Number.isFinite(options.dynamicScreenSpaceErrorDensity) || options.dynamicScreenSpaceErrorDensity <= 0) {
1782
+ return 0;
1783
+ }
1784
+ const boundingVolumeHeader = getContentBoundingVolumeHeader(root);
1785
+ const heightRange = boundingVolumeHeader.region ? getRegionHeightRange(boundingVolumeHeader.region, frameState) : getCartesianHeightRange(root, boundingVolumeHeader, frameState);
1786
+ if (!heightRange) {
1787
+ return 0;
1788
+ }
1789
+ const heightFalloff = (0, import_core3.clamp)(options.dynamicScreenSpaceErrorHeightFalloff, 0, 1);
1790
+ const heightClose = heightRange.minimumHeight + (heightRange.maximumHeight - heightRange.minimumHeight) * heightFalloff;
1791
+ const heightSpan = heightRange.maximumHeight - heightClose;
1792
+ const heightPercentage = heightSpan > 0 ? (0, import_core3.clamp)((heightRange.cameraHeight - heightClose) / heightSpan, 0, 1) : heightRange.cameraHeight >= heightRange.maximumHeight ? 1 : 0;
1793
+ const verticalAlignment = (0, import_core3.clamp)(Math.abs(heightRange.cameraDirection.dot(heightRange.upDirection)), 0, 1);
1794
+ const horizonFactor = (1 - verticalAlignment) * (1 - heightPercentage);
1795
+ return options.dynamicScreenSpaceErrorDensity * horizonFactor;
1796
+ }
1797
+ function getDynamicScreenSpaceErrorFog(distanceToCamera, density) {
1798
+ const scalar = distanceToCamera * density;
1799
+ return 1 - Math.exp(-(scalar * scalar));
1800
+ }
1801
+ function getDynamicScreenSpaceError(distanceToCamera, density, factor) {
1802
+ if (density <= 0 || factor <= 0 || !Number.isFinite(density) || !Number.isFinite(factor)) {
1803
+ return 0;
1804
+ }
1805
+ return getDynamicScreenSpaceErrorFog(distanceToCamera, density) * factor;
1806
+ }
1807
+ function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric, viewportHeightFraction = 1) {
1808
+ const tileset = tile.tileset;
1809
+ const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
1810
+ const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
1811
+ if (lodMetricValue === 0) {
1812
+ return 0;
1813
+ }
1814
+ const { viewDistanceScale } = tileset.options;
1815
+ const lodScale = viewDistanceScale || 1;
1816
+ const heightFraction = Number.isFinite(viewportHeightFraction) ? Math.max(viewportHeightFraction, 0) : 1;
1817
+ const orthographicError = getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale, heightFraction);
1818
+ if (orthographicError !== null) {
1819
+ return orthographicError;
1820
+ }
1821
+ const distanceToCamera = Math.max(tile._distanceToCamera, 1e-7);
1822
+ const { height, sseDenominator } = frameState;
1823
+ let screenSpaceError = lodMetricValue * height * heightFraction * lodScale / (distanceToCamera * sseDenominator);
1824
+ if (tileset.options.dynamicScreenSpaceError && !frameState.viewport.orthographic) {
1825
+ screenSpaceError -= getDynamicScreenSpaceError(distanceToCamera, frameState.dynamicScreenSpaceErrorDensity, tileset.options.dynamicScreenSpaceErrorFactor);
1826
+ }
1827
+ return screenSpaceError;
1828
+ }
1829
+ function getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale, viewportHeightFraction) {
1830
+ const viewport = frameState.viewport;
1831
+ if (!(viewport == null ? void 0 : viewport.orthographic)) {
1832
+ return null;
1833
+ }
1834
+ const metersPerPixel = viewport.metersPerPixel;
1835
+ if (!Number.isFinite(metersPerPixel) || !metersPerPixel || metersPerPixel <= 0) {
1836
+ return null;
1837
+ }
1838
+ return lodMetricValue * lodScale * viewportHeightFraction / metersPerPixel;
1839
+ }
1840
+ function getContentBoundingVolumeHeader(root) {
1841
+ var _a;
1842
+ return ((_a = root.header.content) == null ? void 0 : _a.boundingVolume) || root.header.boundingVolume;
1843
+ }
1844
+ function getRegionHeightRange(region, frameState) {
1845
+ const upDirection = scratchPositionNormal.copy(frameState.camera.position).normalize();
1846
+ const cameraDirection = scratchDirection.copy(frameState.camera.direction).normalize();
1847
+ return {
1848
+ cameraHeight: frameState.camera.cartographicPosition[2],
1849
+ minimumHeight: region[4],
1850
+ maximumHeight: region[5],
1851
+ cameraDirection,
1852
+ upDirection
1853
+ };
1854
+ }
1855
+ function getCartesianHeightRange(root, boundingVolumeHeader, frameState) {
1856
+ const halfHeight = getBoundingVolumeCenterAndHalfHeight(boundingVolumeHeader, scratchCenter);
1857
+ if (halfHeight === null) {
1858
+ return null;
1859
+ }
1860
+ const localTransform = new import_core3.Matrix4(root.computedTransform).invert();
1861
+ const ellipsoid = root.tileset.ellipsoid;
1862
+ const centerMagnitude = Math.hypot(scratchCenter[0], scratchCenter[1], scratchCenter[2]);
1863
+ if (centerMagnitude > ellipsoid.minimumRadius) {
1864
+ const centerCartographic = ellipsoid.cartesianToCartographic(scratchCenter, scratchCartographic);
1865
+ const upDirection = scratchPositionNormal.copy(frameState.camera.position).normalize();
1866
+ const cameraDirection = scratchDirection.copy(frameState.camera.direction).normalize();
1867
+ return {
1868
+ cameraHeight: frameState.camera.cartographicPosition[2],
1869
+ minimumHeight: 0,
1870
+ maximumHeight: centerCartographic[2] * 2,
1871
+ cameraDirection,
1872
+ upDirection
1873
+ };
1874
+ }
1875
+ localTransform.transformAsPoint(frameState.camera.position, scratchPosition2);
1876
+ localTransform.transformAsVector(frameState.camera.direction, scratchDirection);
1877
+ scratchDirection.normalize();
1878
+ return {
1879
+ cameraHeight: scratchPosition2[2],
1880
+ minimumHeight: scratchCenter[2] - halfHeight,
1881
+ maximumHeight: scratchCenter[2] + halfHeight,
1882
+ cameraDirection: scratchDirection,
1883
+ upDirection: unitZDirection
324
1884
  };
325
1885
  }
326
- function limitSelectedTiles(tiles, frameState, maximumTilesSelected) {
327
- if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
328
- return [tiles, []];
1886
+ function getBoundingVolumeCenterAndHalfHeight(boundingVolumeHeader, result) {
1887
+ if (boundingVolumeHeader.box) {
1888
+ const box = boundingVolumeHeader.box;
1889
+ result.set(box[0], box[1], box[2]);
1890
+ return getBoxHalfHeight(box);
329
1891
  }
330
- const tuples = [];
331
- const { longitude: viewportLongitude, latitude: viewportLatitude } = frameState.viewport;
332
- for (const [index, tile] of tiles.entries()) {
333
- const [longitude, latitude] = tile.header.mbs;
334
- const deltaLon = Math.abs(viewportLongitude - longitude);
335
- const deltaLat = Math.abs(viewportLatitude - latitude);
336
- const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
337
- tuples.push([index, distance]);
1892
+ if (boundingVolumeHeader.sphere) {
1893
+ const sphere = boundingVolumeHeader.sphere;
1894
+ result.set(sphere[0], sphere[1], sphere[2]);
1895
+ return sphere[3];
338
1896
  }
339
- const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
340
- const selectedTiles = [];
341
- for (let i = 0; i < maximumTilesSelected; i++) {
342
- selectedTiles.push(tiles[tuplesSorted[i][0]]);
1897
+ return null;
1898
+ }
1899
+ function getBoxHalfHeight(box) {
1900
+ if (box.length === 10) {
1901
+ scratchBoxQuaternion.fromArray(box, 6);
1902
+ scratchBoxXAxis.set(box[3], 0, 0).transformByQuaternion(scratchBoxQuaternion);
1903
+ scratchBoxYAxis.set(0, box[4], 0).transformByQuaternion(scratchBoxQuaternion);
1904
+ scratchBoxZAxis.set(0, 0, box[5]).transformByQuaternion(scratchBoxQuaternion);
1905
+ return Math.abs(scratchBoxXAxis[2]) + Math.abs(scratchBoxYAxis[2]) + Math.abs(scratchBoxZAxis[2]);
343
1906
  }
344
- const unselectedTiles = [];
345
- for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
346
- unselectedTiles.push(tiles[tuplesSorted[i][0]]);
1907
+ return Math.abs(box[5]) + Math.abs(box[8]) + Math.abs(box[11]);
1908
+ }
1909
+
1910
+ // dist/tileset-3d/helpers/tiles-3d-request-priority.js
1911
+ var import_core4 = require("@math.gl/core");
1912
+ var import_culling2 = require("@math.gl/culling");
1913
+
1914
+ // dist/constants.js
1915
+ var TILE_CONTENT_STATE = {
1916
+ UNLOADED: 0,
1917
+ // Has never been requested
1918
+ LOADING: 1,
1919
+ // Is waiting on a pending request
1920
+ PROCESSING: 2,
1921
+ // Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
1922
+ READY: 3,
1923
+ // Ready to render.
1924
+ EXPIRED: 4,
1925
+ // Is expired and will be unloaded once new content is loaded.
1926
+ FAILED: 5
1927
+ // Request failed.
1928
+ };
1929
+ var TILE_REFINEMENT;
1930
+ (function(TILE_REFINEMENT2) {
1931
+ TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
1932
+ TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
1933
+ })(TILE_REFINEMENT || (TILE_REFINEMENT = {}));
1934
+ var TILE_TYPE;
1935
+ (function(TILE_TYPE2) {
1936
+ TILE_TYPE2["EMPTY"] = "empty";
1937
+ TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
1938
+ TILE_TYPE2["POINTCLOUD"] = "pointcloud";
1939
+ TILE_TYPE2["MESH"] = "mesh";
1940
+ })(TILE_TYPE || (TILE_TYPE = {}));
1941
+ var TILESET_TYPE;
1942
+ (function(TILESET_TYPE2) {
1943
+ TILESET_TYPE2["I3S"] = "I3S";
1944
+ TILESET_TYPE2["TILES3D"] = "TILES3D";
1945
+ })(TILESET_TYPE || (TILESET_TYPE = {}));
1946
+ var LOD_METRIC_TYPE;
1947
+ (function(LOD_METRIC_TYPE2) {
1948
+ LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
1949
+ LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
1950
+ })(LOD_METRIC_TYPE || (LOD_METRIC_TYPE = {}));
1951
+ var TILE3D_OPTIMIZATION_HINT = {
1952
+ NOT_COMPUTED: -1,
1953
+ USE_OPTIMIZATION: 1,
1954
+ SKIP_OPTIMIZATION: 0
1955
+ };
1956
+
1957
+ // dist/tileset-3d/helpers/tiles-3d-request-priority.js
1958
+ var PREFERRED_SORTING_DIGITS = 1e4;
1959
+ var FOVEATED_SORTING_SCALE = 1e4;
1960
+ var PROGRESSIVE_RESOLUTION_PENALTY = 1e8;
1961
+ var scratchBoundingSphere = new import_culling2.BoundingSphere();
1962
+ var scratchCenterOffset = new import_core4.Vector3();
1963
+ var scratchClosestPointOnLine = new import_core4.Vector3();
1964
+ var scratchVectorToLine = new import_core4.Vector3();
1965
+ var scratchClosestPointOnSphere = new import_core4.Vector3();
1966
+ function interpolateLinearly(minimumValue, maximumValue, interpolationAmount) {
1967
+ return minimumValue + (maximumValue - minimumValue) * interpolationAmount;
1968
+ }
1969
+ function calculateFoveatedFactor(boundingVolume, camera) {
1970
+ const boundingSphere = boundingVolume instanceof import_culling2.BoundingSphere ? boundingVolume : boundingVolume.getBoundingSphere(scratchBoundingSphere);
1971
+ const { center, radius } = boundingSphere;
1972
+ scratchCenterOffset.copy(center).subtract(camera.position);
1973
+ const centerZDepth = Math.max(scratchCenterOffset.dot(camera.direction), 0);
1974
+ scratchClosestPointOnLine.copy(camera.direction).scale(centerZDepth).add(camera.position);
1975
+ scratchVectorToLine.copy(scratchClosestPointOnLine).subtract(center);
1976
+ const distanceToCenterLine = scratchVectorToLine.magnitude();
1977
+ if (distanceToCenterLine <= radius) {
1978
+ return 0;
347
1979
  }
348
- return [selectedTiles, unselectedTiles];
1980
+ scratchClosestPointOnSphere.copy(scratchVectorToLine).normalize().scale(radius).add(center).subtract(camera.position);
1981
+ if (scratchClosestPointOnSphere.magnitude() === 0) {
1982
+ return 0;
1983
+ }
1984
+ scratchClosestPointOnSphere.normalize();
1985
+ return Math.min(Math.max(1 - scratchClosestPointOnSphere.dot(camera.direction), 0), 1);
349
1986
  }
350
- function commonSpacePlanesToWGS84(viewport) {
351
- const frustumPlanes = viewport.getFrustumPlanes();
352
- const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
353
- const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
354
- const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
355
- let i = 0;
356
- cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
357
- for (const dir in frustumPlanes) {
358
- if (dir === "near") {
359
- continue;
360
- }
361
- const plane = frustumPlanes[dir];
362
- const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
363
- const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
364
- cullingVolume.planes[i++].fromPointNormal(
365
- cartesianPos,
366
- // Want the normal to point into the frustum since that's what culling expects
367
- scratchVector.copy(nearCenterCartesian).subtract(cartesianPos)
368
- );
1987
+ function isProgressiveResolutionPriority(screenSpaceError, parentScreenSpaceError, maximumScreenSpaceError, progressiveResolutionHeightFraction) {
1988
+ if (!Number.isFinite(progressiveResolutionHeightFraction) || progressiveResolutionHeightFraction <= 0 || progressiveResolutionHeightFraction > 0.5) {
1989
+ return false;
1990
+ }
1991
+ if (screenSpaceError > maximumScreenSpaceError) {
1992
+ return true;
369
1993
  }
1994
+ return parentScreenSpaceError !== void 0 && parentScreenSpaceError > maximumScreenSpaceError;
370
1995
  }
371
- function closestPointOnPlane(plane, refPoint, out = new import_core2.Vector3()) {
372
- const distanceToRef = plane.normal.dot(refPoint);
373
- out.copy(plane.normal).scale(plane.distance - distanceToRef).add(refPoint);
374
- return out;
1996
+ function isFoveatedRequestDeferred(parameters) {
1997
+ const coneSize = Number.isFinite(parameters.foveatedConeSize) ? Math.min(Math.max(parameters.foveatedConeSize, 0), 1) : 1;
1998
+ const replacementRefinement = parameters.refinement === TILE_REFINEMENT.REPLACE;
1999
+ if (replacementRefinement && !parameters.skipLevelOfDetail || !parameters.foveatedScreenSpaceError || coneSize >= 1 || replacementRefinement && parameters.skipLevelOfDetail && parameters.priorityProgressiveResolution) {
2000
+ return false;
2001
+ }
2002
+ const verticalFieldOfView = Number.isFinite(parameters.verticalFieldOfView) && parameters.verticalFieldOfView > 0 ? parameters.verticalFieldOfView : Math.PI / 3;
2003
+ const maximumFoveatedFactor = 1 - Math.cos(verticalFieldOfView * 0.5);
2004
+ const coneFactor = coneSize * maximumFoveatedFactor;
2005
+ if (parameters.foveatedFactor <= coneFactor || maximumFoveatedFactor <= coneFactor) {
2006
+ return false;
2007
+ }
2008
+ const interpolationAmount = Math.min(Math.max((parameters.foveatedFactor - coneFactor) / (maximumFoveatedFactor - coneFactor), 0), 1);
2009
+ const interpolationCallback = typeof parameters.interpolationCallback === "function" ? parameters.interpolationCallback : interpolateLinearly;
2010
+ const interpolatedRelaxation = interpolationCallback(parameters.minimumScreenSpaceErrorRelaxation, parameters.maximumScreenSpaceError, interpolationAmount);
2011
+ const screenSpaceErrorRelaxation = Number.isFinite(interpolatedRelaxation) ? Math.min(Math.max(interpolatedRelaxation, 0), parameters.maximumScreenSpaceError) : 0;
2012
+ const screenSpaceError = parameters.screenSpaceError === 0 && parameters.parentScreenSpaceError !== void 0 ? parameters.parentScreenSpaceError * 0.5 : parameters.screenSpaceError;
2013
+ return parameters.maximumScreenSpaceError - screenSpaceErrorRelaxation <= screenSpaceError;
375
2014
  }
376
- function worldToCartesian(viewport, point, out = new import_core2.Vector3()) {
377
- const cartographicPos = viewport.unprojectPosition(point);
378
- return import_geospatial2.Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
2015
+ function isFoveatedRequestDelayActive(deferralEligible, timeSinceMovement, foveatedTimeDelay) {
2016
+ return deferralEligible && Number.isFinite(timeSinceMovement) && Number.isFinite(foveatedTimeDelay) && foveatedTimeDelay > 0 && timeSinceMovement < foveatedTimeDelay;
2017
+ }
2018
+ function calculateTileRequestPriority(parameters) {
2019
+ const normalizedReverseScreenSpaceError = parameters.rootScreenSpaceError > 0 ? Math.min(Math.max(parameters.reverseScreenSpaceError / parameters.rootScreenSpaceError, 0), 1) : 0;
2020
+ const preferredSortingDigits = Math.floor(normalizedReverseScreenSpaceError * (PREFERRED_SORTING_DIGITS - 1));
2021
+ const normalizedFoveatedFactor = Number.isFinite(parameters.foveatedFactor) ? Math.min(Math.max(parameters.foveatedFactor, 0), 1) : 0;
2022
+ const foveatedSortingDigits = Math.floor(normalizedFoveatedFactor * (PREFERRED_SORTING_DIGITS - 1)) * FOVEATED_SORTING_SCALE;
2023
+ const progressiveResolutionDigits = parameters.priorityProgressiveResolution ? 0 : PROGRESSIVE_RESOLUTION_PENALTY;
2024
+ return progressiveResolutionDigits + foveatedSortingDigits + preferredSortingDigits;
379
2025
  }
380
2026
 
381
2027
  // dist/tileset-3d/common/tileset-source.js
@@ -384,14 +2030,35 @@ function isTileset3DSource(value) {
384
2030
  }
385
2031
 
386
2032
  // dist/tileset-3d/common/tileset-3d.js
387
- var DEFAULT_PROPS = {
2033
+ var BYTES_PER_MEBIBYTE = 1024 * 1024;
2034
+ var DEFAULT_CACHE_BYTES = 512 * BYTES_PER_MEBIBYTE;
2035
+ var DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES = 512 * BYTES_PER_MEBIBYTE;
2036
+ var DEFAULT_I3S_CACHE_BYTES = 32 * BYTES_PER_MEBIBYTE;
2037
+ var DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES = BYTES_PER_MEBIBYTE;
2038
+ function validateCacheByteLength(value, optionName) {
2039
+ if (!Number.isFinite(value) || value < 0) {
2040
+ throw new Error(`${optionName} must be a finite number greater than or equal to 0`);
2041
+ }
2042
+ return value;
2043
+ }
2044
+ function resolveCacheBytes(options, fallbackCacheBytes) {
2045
+ const cacheBytes = options.cacheBytes ?? (options.maximumMemoryUsage !== void 0 ? options.maximumMemoryUsage * BYTES_PER_MEBIBYTE : fallbackCacheBytes);
2046
+ return validateCacheByteLength(cacheBytes, "cacheBytes");
2047
+ }
2048
+ function resolveMaximumCacheOverflowBytes(options, fallbackOverflowBytes) {
2049
+ const maximumCacheOverflowBytes = options.maximumCacheOverflowBytes ?? (options.memoryCacheOverflow !== void 0 ? options.memoryCacheOverflow * BYTES_PER_MEBIBYTE : fallbackOverflowBytes);
2050
+ return validateCacheByteLength(maximumCacheOverflowBytes, "maximumCacheOverflowBytes");
2051
+ }
2052
+ var DEFAULT_PROPS3 = {
388
2053
  description: "",
389
2054
  ellipsoid: import_geospatial3.Ellipsoid.WGS84,
390
- modelMatrix: new import_core3.Matrix4(),
2055
+ modelMatrix: new import_core5.Matrix4(),
391
2056
  throttleRequests: true,
392
2057
  maxRequests: 64,
393
- maximumMemoryUsage: 32,
394
- memoryCacheOverflow: 1,
2058
+ cacheBytes: DEFAULT_CACHE_BYTES,
2059
+ maximumCacheOverflowBytes: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES,
2060
+ maximumMemoryUsage: DEFAULT_CACHE_BYTES / BYTES_PER_MEBIBYTE,
2061
+ memoryCacheOverflow: DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES / BYTES_PER_MEBIBYTE,
395
2062
  maximumTilesSelected: 0,
396
2063
  debounceTime: 0,
397
2064
  onTileLoad: () => {
@@ -409,8 +2076,18 @@ var DEFAULT_PROPS = {
409
2076
  },
410
2077
  contentLoader: void 0,
411
2078
  viewDistanceScale: 1,
2079
+ progressiveResolutionHeightFraction: 0.3,
2080
+ foveatedScreenSpaceError: true,
2081
+ foveatedConeSize: 0.1,
2082
+ foveatedMinimumScreenSpaceErrorRelaxation: 0,
2083
+ foveatedInterpolationCallback: interpolateLinearly,
2084
+ foveatedTimeDelay: 0.2,
412
2085
  maximumScreenSpaceError: 8,
413
- memoryAdjustedScreenSpaceError: false,
2086
+ dynamicScreenSpaceError: true,
2087
+ dynamicScreenSpaceErrorDensity: 2e-4,
2088
+ dynamicScreenSpaceErrorFactor: 24,
2089
+ dynamicScreenSpaceErrorHeightFalloff: 0.25,
2090
+ memoryAdjustedScreenSpaceError: true,
414
2091
  loadTiles: true,
415
2092
  updateTransforms: true,
416
2093
  viewportTraversersMap: null,
@@ -459,12 +2136,14 @@ var Tileset3D = class {
459
2136
  cartesianCenter = null;
460
2137
  zoom = 1;
461
2138
  boundingVolume = null;
2139
+ /** Effective dynamic SSE density calculated for the most recently traversed viewport. */
462
2140
  dynamicScreenSpaceErrorComputedDensity = 0;
463
- maximumMemoryUsage = 32;
2141
+ /** Estimated bytes currently retained by loaded tile content. */
464
2142
  gpuMemoryUsageInBytes = 0;
2143
+ /** Active SSE threshold after optional cache-pressure adjustment, in logical/CSS pixels. */
465
2144
  memoryAdjustedScreenSpaceError = 0;
466
- _cacheBytes = 0;
467
- _cacheOverflowBytes = 0;
2145
+ _cacheBytes = DEFAULT_CACHE_BYTES;
2146
+ _maximumCacheOverflowBytes = DEFAULT_MAXIMUM_CACHE_OVERFLOW_BYTES;
468
2147
  _frameNumber = 0;
469
2148
  _tiles = {};
470
2149
  _pendingCount = 0;
@@ -475,6 +2154,10 @@ var Tileset3D = class {
475
2154
  _requestedTiles = [];
476
2155
  _emptyTiles = [];
477
2156
  frameStateData = {};
2157
+ /** Last camera pose retained independently for each viewport. */
2158
+ _cameraMotionStates = {};
2159
+ /** Pending traversal that releases requests after camera motion settles. */
2160
+ _deferredTraversalTimeout = null;
478
2161
  _traverser;
479
2162
  _cache = new TilesetCache();
480
2163
  _requestScheduler;
@@ -487,11 +2170,25 @@ var Tileset3D = class {
487
2170
  * @param options Traversal and runtime options.
488
2171
  */
489
2172
  constructor(source, options) {
490
- this.options = { ...DEFAULT_PROPS, ...options };
491
- this.loadOptions = this.options.loadOptions || {};
492
2173
  if (!isTileset3DSource(source)) {
493
2174
  throw new Error("Tileset3D requires a Tileset3DSource instance");
494
2175
  }
2176
+ const suppliedOptions = options || {};
2177
+ const usesTiles3DCacheDefaults = source.type === TILESET_TYPE.TILES3D;
2178
+ const defaultCacheBytes = usesTiles3DCacheDefaults ? DEFAULT_PROPS3.cacheBytes : DEFAULT_I3S_CACHE_BYTES;
2179
+ const defaultMaximumCacheOverflowBytes = usesTiles3DCacheDefaults ? DEFAULT_PROPS3.maximumCacheOverflowBytes : DEFAULT_I3S_MAXIMUM_CACHE_OVERFLOW_BYTES;
2180
+ const cacheBytes = resolveCacheBytes(suppliedOptions, defaultCacheBytes);
2181
+ const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(suppliedOptions, defaultMaximumCacheOverflowBytes);
2182
+ this.options = {
2183
+ ...DEFAULT_PROPS3,
2184
+ ...suppliedOptions,
2185
+ cacheBytes,
2186
+ maximumCacheOverflowBytes,
2187
+ maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
2188
+ memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE,
2189
+ memoryAdjustedScreenSpaceError: suppliedOptions.memoryAdjustedScreenSpaceError ?? usesTiles3DCacheDefaults
2190
+ };
2191
+ this.loadOptions = this.options.loadOptions || {};
495
2192
  this.source = source;
496
2193
  this.tileset = null;
497
2194
  this.loader = this.source.loader;
@@ -505,18 +2202,21 @@ var Tileset3D = class {
505
2202
  this.refine = "";
506
2203
  this.contentFormats = this.source.contentFormats;
507
2204
  this._traverser = this._initializeTraverser();
508
- this._requestScheduler = new import_loader_utils2.RequestScheduler({
2205
+ this._requestScheduler = new RequestScheduler({
509
2206
  throttleRequests: this.options.throttleRequests,
510
2207
  maxRequests: this.options.maxRequests
511
2208
  });
512
2209
  this.memoryAdjustedScreenSpaceError = this.options.maximumScreenSpaceError;
513
- this._cacheBytes = this.options.maximumMemoryUsage * 1024 * 1024;
514
- this._cacheOverflowBytes = this.options.memoryCacheOverflow * 1024 * 1024;
515
- this.stats = new import_stats.Stats({ id: this.url });
2210
+ this._cacheBytes = cacheBytes;
2211
+ this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
2212
+ this.stats = new import_stats3.Stats({ id: this.url });
516
2213
  this._initializeStats();
517
2214
  this.tilesetInitializationPromise = this._initializeTileSet();
518
2215
  }
519
2216
  destroy() {
2217
+ var _a, _b;
2218
+ this._clearDeferredTraversal();
2219
+ (_b = (_a = this.source).destroy) == null ? void 0 : _b.call(_a);
520
2220
  this._destroy();
521
2221
  }
522
2222
  isLoaded() {
@@ -528,13 +2228,73 @@ var Tileset3D = class {
528
2228
  get frameNumber() {
529
2229
  return this._frameNumber;
530
2230
  }
2231
+ /**
2232
+ * Gets or sets the soft target, in bytes, for cached tile content not needed this frame.
2233
+ *
2234
+ * Current-frame tiles remain protected even when their estimated memory exceeds this target.
2235
+ * Reducing the value makes unused least-recently-used tiles eligible for eviction on the next
2236
+ * traversal.
2237
+ * @default 536870912 for 3D Tiles; 33554432 for I3S
2238
+ */
2239
+ get cacheBytes() {
2240
+ return this._cacheBytes;
2241
+ }
2242
+ set cacheBytes(value) {
2243
+ this._cacheBytes = validateCacheByteLength(value, "cacheBytes");
2244
+ this.options.cacheBytes = this._cacheBytes;
2245
+ this.options.maximumMemoryUsage = this._cacheBytes / BYTES_PER_MEBIBYTE;
2246
+ }
2247
+ /**
2248
+ * Gets or sets the additional current-frame headroom, in bytes, before memory-adjusted SSE rises.
2249
+ *
2250
+ * The pressure ceiling is `cacheBytes + maximumCacheOverflowBytes`. This value does not change
2251
+ * the base target used to evict tiles that were not touched in the current frame.
2252
+ * @default 536870912 for 3D Tiles; 1048576 for I3S
2253
+ */
2254
+ get maximumCacheOverflowBytes() {
2255
+ return this._maximumCacheOverflowBytes;
2256
+ }
2257
+ set maximumCacheOverflowBytes(value) {
2258
+ this._maximumCacheOverflowBytes = validateCacheByteLength(value, "maximumCacheOverflowBytes");
2259
+ this.options.maximumCacheOverflowBytes = this._maximumCacheOverflowBytes;
2260
+ this.options.memoryCacheOverflow = this._maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE;
2261
+ }
2262
+ /**
2263
+ * Gets or sets the soft cache target in mebibytes.
2264
+ * @deprecated Use {@link Tileset3D.cacheBytes}. Assignments remain synchronized for compatibility.
2265
+ */
2266
+ get maximumMemoryUsage() {
2267
+ return this.cacheBytes / BYTES_PER_MEBIBYTE;
2268
+ }
2269
+ set maximumMemoryUsage(value) {
2270
+ this.cacheBytes = validateCacheByteLength(value * BYTES_PER_MEBIBYTE, "maximumMemoryUsage");
2271
+ }
531
2272
  get queryParams() {
532
2273
  const rootUrl = this.source.getTileUrl(this.url);
533
2274
  const queryIndex = rootUrl.indexOf("?");
534
2275
  return queryIndex >= 0 ? rootUrl.slice(queryIndex + 1) : "";
535
2276
  }
2277
+ /**
2278
+ * Updates traversal and runtime options, synchronizing byte-native and deprecated cache values.
2279
+ *
2280
+ * For each budget, the byte-native option takes precedence when both unit forms are supplied.
2281
+ * Unspecified budgets retain their current byte values, avoiding repeated MiB conversion.
2282
+ *
2283
+ * @param props - Partial runtime options to apply.
2284
+ */
536
2285
  setProps(props) {
537
- this.options = { ...this.options, ...props };
2286
+ const cacheBytes = resolveCacheBytes(props, this.cacheBytes);
2287
+ const maximumCacheOverflowBytes = resolveMaximumCacheOverflowBytes(props, this.maximumCacheOverflowBytes);
2288
+ this.options = {
2289
+ ...this.options,
2290
+ ...props,
2291
+ cacheBytes,
2292
+ maximumCacheOverflowBytes,
2293
+ maximumMemoryUsage: cacheBytes / BYTES_PER_MEBIBYTE,
2294
+ memoryCacheOverflow: maximumCacheOverflowBytes / BYTES_PER_MEBIBYTE
2295
+ };
2296
+ this._cacheBytes = cacheBytes;
2297
+ this._maximumCacheOverflowBytes = maximumCacheOverflowBytes;
538
2298
  }
539
2299
  getTileUrl(tilePath) {
540
2300
  return this.source.getTileUrl(tilePath);
@@ -561,22 +2321,29 @@ var Tileset3D = class {
561
2321
  this.lastUpdatedVieports = viewports;
562
2322
  }
563
2323
  if (!this.updatePromise) {
564
- this.updatePromise = new Promise((resolve) => {
2324
+ this.updatePromise = new Promise((resolve2) => {
565
2325
  setTimeout(() => {
566
2326
  if (this.lastUpdatedVieports) {
567
2327
  this.doUpdate(this.lastUpdatedVieports);
568
2328
  }
569
- resolve(this._frameNumber);
2329
+ resolve2(this._frameNumber);
570
2330
  this.updatePromise = null;
571
2331
  }, this.options.debounceTime);
572
2332
  });
573
2333
  }
574
2334
  return this.updatePromise;
575
2335
  }
2336
+ /**
2337
+ * Moves the active LOD threshold toward the configured SSE or away from cache pressure.
2338
+ *
2339
+ * Usage below {@link Tileset3D.cacheBytes} restores quality in two-percent steps. Usage above
2340
+ * `cacheBytes + maximumCacheOverflowBytes` reduces refinement in two-percent steps. Values in
2341
+ * the overflow window leave the threshold unchanged, preventing rapid load/evict oscillation.
2342
+ */
576
2343
  adjustScreenSpaceError() {
577
- if (this.gpuMemoryUsageInBytes < this._cacheBytes) {
2344
+ if (this.gpuMemoryUsageInBytes < this.cacheBytes) {
578
2345
  this.memoryAdjustedScreenSpaceError = Math.max(this.memoryAdjustedScreenSpaceError / 1.02, this.options.maximumScreenSpaceError);
579
- } else if (this.gpuMemoryUsageInBytes > this._cacheBytes + this._cacheOverflowBytes) {
2346
+ } else if (this.gpuMemoryUsageInBytes > this.cacheBytes + this.maximumCacheOverflowBytes) {
580
2347
  this.memoryAdjustedScreenSpaceError *= 1.02;
581
2348
  }
582
2349
  }
@@ -591,6 +2358,8 @@ var Tileset3D = class {
591
2358
  this._cache.reset();
592
2359
  this._frameNumber++;
593
2360
  this.traverseCounter = preparedViewports.length;
2361
+ const currentTime = Date.now();
2362
+ let minimumDeferredTraversalDelay = Number.POSITIVE_INFINITY;
594
2363
  const viewportsToTraverse = [];
595
2364
  for (const viewport of preparedViewports) {
596
2365
  const id = viewport.id;
@@ -609,7 +2378,47 @@ var Tileset3D = class {
609
2378
  continue;
610
2379
  }
611
2380
  const frameState = getFrameState(viewport, this._frameNumber);
612
- this._traverser.traverse(this.roots[id], frameState, this.options);
2381
+ const root = this.roots[id];
2382
+ const cameraMotionUpdate = updateCameraMotionState(this._cameraMotionStates[id], frameState.camera.position, frameState.camera.direction, currentTime);
2383
+ this._cameraMotionStates[id] = cameraMotionUpdate.state;
2384
+ frameState.camera.timeSinceMovement = cameraMotionUpdate.timeSinceMovement;
2385
+ updateRootTransformForDynamicScreenSpaceError(root, this.modelMatrix);
2386
+ frameState.dynamicScreenSpaceErrorDensity = this.type === TILESET_TYPE.TILES3D && this.options.dynamicScreenSpaceError && !frameState.viewport.orthographic ? calculateDynamicScreenSpaceErrorDensity(root, frameState, this.options) : 0;
2387
+ this.dynamicScreenSpaceErrorComputedDensity = frameState.dynamicScreenSpaceErrorDensity;
2388
+ this._traverser.traverse(root, frameState, this.options);
2389
+ if (Object.keys(this._traverser.deferredTiles).length > 0) {
2390
+ minimumDeferredTraversalDelay = Math.min(minimumDeferredTraversalDelay, Math.max(this.options.foveatedTimeDelay - cameraMotionUpdate.timeSinceMovement, 0));
2391
+ }
2392
+ }
2393
+ if (Number.isFinite(minimumDeferredTraversalDelay)) {
2394
+ this._scheduleDeferredTraversal(minimumDeferredTraversalDelay);
2395
+ } else {
2396
+ this._clearDeferredTraversal();
2397
+ }
2398
+ }
2399
+ /**
2400
+ * Schedules one follow-up traversal when the moving-camera deferral window expires.
2401
+ *
2402
+ * Replacing an existing timer ensures continuing camera movement pushes the retry window forward
2403
+ * instead of creating a queue of redundant traversals.
2404
+ *
2405
+ * @param delayInSeconds - Remaining stationary delay before deferred requests become eligible.
2406
+ */
2407
+ _scheduleDeferredTraversal(delayInSeconds) {
2408
+ this._clearDeferredTraversal();
2409
+ this._deferredTraversalTimeout = setTimeout(() => {
2410
+ this._deferredTraversalTimeout = null;
2411
+ this.selectTiles().catch((error) => {
2412
+ const traversalError = error instanceof Error ? error : new Error(String(error));
2413
+ this._onTilesetError(traversalError);
2414
+ });
2415
+ }, Math.max(delayInSeconds * 1e3, 0));
2416
+ }
2417
+ /** Clears a pending moving-camera follow-up traversal. */
2418
+ _clearDeferredTraversal() {
2419
+ if (this._deferredTraversalTimeout !== null) {
2420
+ clearTimeout(this._deferredTraversalTimeout);
2421
+ this._deferredTraversalTimeout = null;
613
2422
  }
614
2423
  }
615
2424
  _needTraverse(viewportId) {
@@ -708,6 +2517,9 @@ var Tileset3D = class {
708
2517
  }
709
2518
  _unloadTiles() {
710
2519
  this._cache.unloadTiles(this, (tileset, tile) => tileset._unloadTile(tile));
2520
+ if (this.options.memoryAdjustedScreenSpaceError) {
2521
+ this.adjustScreenSpaceError();
2522
+ }
711
2523
  }
712
2524
  _updateStats() {
713
2525
  let tilesRenderable = 0;
@@ -792,6 +2604,27 @@ var Tileset3D = class {
792
2604
  this._onTileLoad(tile, loadResult);
793
2605
  }
794
2606
  }
2607
+ /**
2608
+ * Loads a source-managed lazy child-header group and reports failures through tile callbacks.
2609
+ *
2610
+ * Subtree metadata shares the normal loading counters but is not reported as render content and
2611
+ * is not added to the GPU cache. Traversal is notified separately after the promise settles.
2612
+ *
2613
+ * @param tile - Runtime tile that owns the lazy subtree reference.
2614
+ * @param frameState - View state that made the subtree eligible for refinement.
2615
+ */
2616
+ async _loadTileChildren(tile, frameState) {
2617
+ try {
2618
+ this._onStartTileLoading();
2619
+ await tile.loadChildren(frameState);
2620
+ } catch (error) {
2621
+ const tileError = error instanceof Error ? error : new Error("subtree load failed");
2622
+ this._onSourceError(tileError, tile);
2623
+ this._onTileLoadError(tile, tileError);
2624
+ } finally {
2625
+ this._onEndTileLoading();
2626
+ }
2627
+ }
795
2628
  _onTileLoadError(tile, error) {
796
2629
  this.stats.get(TILES_LOAD_FAILED).incrementCount();
797
2630
  const message = error.message || error.toString();
@@ -913,7 +2746,7 @@ var Tileset3D = class {
913
2746
  this.credits = viewState.credits || this.source.credits || this.credits || { attributions: this.options.attributions || [] };
914
2747
  this.description = viewState.description || this.options.description || "";
915
2748
  this.boundingVolume = viewState.boundingVolume || ((_a = this.root) == null ? void 0 : _a.boundingVolume) || null;
916
- this.cartographicCenter = viewState.cartographicCenter || new import_core3.Vector3();
2749
+ this.cartographicCenter = viewState.cartographicCenter || new import_core5.Vector3();
917
2750
  this.cartesianCenter = viewState.cartesianCenter || null;
918
2751
  this.zoom = viewState.zoom || 1;
919
2752
  this.geometricError = ((_b = this.tileset) == null ? void 0 : _b.geometricError) || 0;
@@ -931,7 +2764,6 @@ var Tileset3D = class {
931
2764
  };
932
2765
 
933
2766
  // dist/tileset-3d/common/indexed-archive-tileset-source.js
934
- var import_loader_utils3 = require("@loaders.gl/loader-utils");
935
2767
  var IndexedArchiveTilesetSource = class {
936
2768
  /** URL exposed to the tileset source that owns this archive source. */
937
2769
  sourceUrl;
@@ -998,30 +2830,30 @@ var IndexedArchiveTilesetSource = class {
998
2830
  async openArchiveReadableFile() {
999
2831
  const { data } = this.options;
1000
2832
  if (typeof data !== "string") {
1001
- return new import_loader_utils3.BlobFile(data);
2833
+ return new BlobFile(data);
1002
2834
  }
1003
2835
  if (/^https?:\/\//i.test(data)) {
1004
- return new import_loader_utils3.HttpFile(data);
2836
+ return new HttpFile(data);
1005
2837
  }
1006
2838
  const coreApi = this.options.getCoreApi();
1007
2839
  if (!coreApi) {
1008
2840
  throw new Error(this.options.missingCoreApiMessage);
1009
2841
  }
1010
2842
  const response = await coreApi.fetchFile(data);
1011
- return new import_loader_utils3.BlobFile(await response.arrayBuffer());
2843
+ return new BlobFile(await response.arrayBuffer());
1012
2844
  }
1013
2845
  resolveArchivePath(request, baseUrl) {
1014
2846
  const requestWithoutQuery = stripQueryString(request);
1015
2847
  const archiveRoot = stripQueryString(this.sourceUrl);
1016
2848
  const baseWithoutQuery = stripQueryString(baseUrl);
1017
- const basePath = baseWithoutQuery === archiveRoot ? archiveRoot : import_loader_utils3.path.dirname(baseWithoutQuery);
2849
+ const basePath = baseWithoutQuery === archiveRoot ? archiveRoot : path_exports.dirname(baseWithoutQuery);
1018
2850
  const markerExpression = new RegExp(`\\.${this.options.archiveExtension}/(.*)$`, "i");
1019
2851
  const markerMatch = requestWithoutQuery.match(markerExpression);
1020
2852
  if (markerMatch == null ? void 0 : markerMatch[1]) {
1021
2853
  return markerMatch[1];
1022
2854
  }
1023
2855
  const isAbsoluteRequest = /^[a-z][a-z0-9+.-]*:/i.test(requestWithoutQuery) || requestWithoutQuery.startsWith("/");
1024
- const absoluteRequest = requestWithoutQuery.startsWith(archiveRoot) ? requestWithoutQuery : baseWithoutQuery === archiveRoot && !isAbsoluteRequest ? `${archiveRoot}/${requestWithoutQuery}` : import_loader_utils3.path.resolve(basePath, requestWithoutQuery);
2856
+ const absoluteRequest = requestWithoutQuery.startsWith(archiveRoot) ? requestWithoutQuery : baseWithoutQuery === archiveRoot && !isAbsoluteRequest ? `${archiveRoot}/${requestWithoutQuery}` : path_exports.resolve(basePath, requestWithoutQuery);
1025
2857
  if (absoluteRequest === archiveRoot) {
1026
2858
  return "";
1027
2859
  }
@@ -1044,70 +2876,26 @@ function stripQueryString(url) {
1044
2876
  }
1045
2877
 
1046
2878
  // dist/tileset-3d/common/tile-3d.js
1047
- var import_core7 = require("@math.gl/core");
1048
- var import_culling3 = require("@math.gl/culling");
1049
-
1050
- // dist/constants.js
1051
- var TILE_CONTENT_STATE = {
1052
- UNLOADED: 0,
1053
- // Has never been requested
1054
- LOADING: 1,
1055
- // Is waiting on a pending request
1056
- PROCESSING: 2,
1057
- // Request received. Contents are being processed for rendering. Depending on the content, it might make its own requests for external data.
1058
- READY: 3,
1059
- // Ready to render.
1060
- EXPIRED: 4,
1061
- // Is expired and will be unloaded once new content is loaded.
1062
- FAILED: 5
1063
- // Request failed.
1064
- };
1065
- var TILE_REFINEMENT;
1066
- (function(TILE_REFINEMENT2) {
1067
- TILE_REFINEMENT2[TILE_REFINEMENT2["ADD"] = 1] = "ADD";
1068
- TILE_REFINEMENT2[TILE_REFINEMENT2["REPLACE"] = 2] = "REPLACE";
1069
- })(TILE_REFINEMENT || (TILE_REFINEMENT = {}));
1070
- var TILE_TYPE;
1071
- (function(TILE_TYPE2) {
1072
- TILE_TYPE2["EMPTY"] = "empty";
1073
- TILE_TYPE2["SCENEGRAPH"] = "scenegraph";
1074
- TILE_TYPE2["POINTCLOUD"] = "pointcloud";
1075
- TILE_TYPE2["MESH"] = "mesh";
1076
- })(TILE_TYPE || (TILE_TYPE = {}));
1077
- var TILESET_TYPE;
1078
- (function(TILESET_TYPE2) {
1079
- TILESET_TYPE2["I3S"] = "I3S";
1080
- TILESET_TYPE2["TILES3D"] = "TILES3D";
1081
- })(TILESET_TYPE || (TILESET_TYPE = {}));
1082
- var LOD_METRIC_TYPE;
1083
- (function(LOD_METRIC_TYPE2) {
1084
- LOD_METRIC_TYPE2["GEOMETRIC_ERROR"] = "geometricError";
1085
- LOD_METRIC_TYPE2["MAX_SCREEN_THRESHOLD"] = "maxScreenThreshold";
1086
- })(LOD_METRIC_TYPE || (LOD_METRIC_TYPE = {}));
1087
- var TILE3D_OPTIMIZATION_HINT = {
1088
- NOT_COMPUTED: -1,
1089
- USE_OPTIMIZATION: 1,
1090
- SKIP_OPTIMIZATION: 0
1091
- };
2879
+ var import_core8 = require("@math.gl/core");
2880
+ var import_culling4 = require("@math.gl/culling");
1092
2881
 
1093
2882
  // dist/tileset-3d/helpers/bounding-volume.js
1094
- var import_core4 = require("@math.gl/core");
1095
- var import_culling2 = require("@math.gl/culling");
2883
+ var import_core6 = require("@math.gl/core");
2884
+ var import_culling3 = require("@math.gl/culling");
1096
2885
  var import_geospatial4 = require("@math.gl/geospatial");
1097
- var import_loader_utils4 = require("@loaders.gl/loader-utils");
1098
2886
  function defined(x) {
1099
2887
  return x !== void 0 && x !== null;
1100
2888
  }
1101
- var scratchPoint = new import_core4.Vector3();
1102
- var scratchScale = new import_core4.Vector3();
1103
- var scratchNorthWest = new import_core4.Vector3();
1104
- var scratchSouthEast = new import_core4.Vector3();
1105
- var scratchCenter = new import_core4.Vector3();
1106
- var scratchXAxis = new import_core4.Vector3();
1107
- var scratchYAxis = new import_core4.Vector3();
1108
- var scratchZAxis = new import_core4.Vector3();
2889
+ var scratchPoint = new import_core6.Vector3();
2890
+ var scratchScale = new import_core6.Vector3();
2891
+ var scratchNorthWest = new import_core6.Vector3();
2892
+ var scratchSouthEast = new import_core6.Vector3();
2893
+ var scratchCenter2 = new import_core6.Vector3();
2894
+ var scratchXAxis = new import_core6.Vector3();
2895
+ var scratchYAxis = new import_core6.Vector3();
2896
+ var scratchZAxis = new import_core6.Vector3();
1109
2897
  function createBoundingVolume(boundingVolumeHeader, transform, result) {
1110
- (0, import_loader_utils4.assert)(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
2898
+ assert(boundingVolumeHeader, "3D Tile: boundingVolume must be defined");
1111
2899
  if (boundingVolumeHeader.box) {
1112
2900
  return createBox(boundingVolumeHeader.box, transform, result);
1113
2901
  }
@@ -1126,8 +2914,8 @@ function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
1126
2914
  if (boundingVolumeHeader.region) {
1127
2915
  const [west, south, east, north, minHeight, maxHeight] = boundingVolumeHeader.region;
1128
2916
  return [
1129
- [(0, import_core4.degrees)(west), (0, import_core4.degrees)(south), minHeight],
1130
- [(0, import_core4.degrees)(east), (0, import_core4.degrees)(north), maxHeight]
2917
+ [(0, import_core6.degrees)(west), (0, import_core6.degrees)(south), minHeight],
2918
+ [(0, import_core6.degrees)(east), (0, import_core6.degrees)(north), maxHeight]
1131
2919
  ];
1132
2920
  }
1133
2921
  if (boundingVolumeHeader.sphere) {
@@ -1136,16 +2924,16 @@ function getCartographicBounds(boundingVolumeHeader, boundingVolume) {
1136
2924
  throw new Error("Unkown boundingVolume type");
1137
2925
  }
1138
2926
  function createBox(box, transform, result) {
1139
- const center = new import_core4.Vector3(box[0], box[1], box[2]);
2927
+ const center = new import_core6.Vector3(box[0], box[1], box[2]);
1140
2928
  transform.transform(center, center);
1141
2929
  let origin = [];
1142
2930
  if (box.length === 10) {
1143
2931
  const halfSize = box.slice(3, 6);
1144
- const quaternion = new import_core4.Quaternion();
2932
+ const quaternion = new import_core6.Quaternion();
1145
2933
  quaternion.fromArray(box, 6);
1146
- const x = new import_core4.Vector3([1, 0, 0]);
1147
- const y = new import_core4.Vector3([0, 1, 0]);
1148
- const z = new import_core4.Vector3([0, 0, 1]);
2934
+ const x = new import_core6.Vector3([1, 0, 0]);
2935
+ const y = new import_core6.Vector3([0, 1, 0]);
2936
+ const z = new import_core6.Vector3([0, 0, 1]);
1149
2937
  x.transformByQuaternion(quaternion);
1150
2938
  x.scale(halfSize[0]);
1151
2939
  y.transformByQuaternion(quaternion);
@@ -1159,7 +2947,7 @@ function createBox(box, transform, result) {
1159
2947
  const xAxis = transform.transformAsVector(origin.slice(0, 3));
1160
2948
  const yAxis = transform.transformAsVector(origin.slice(3, 6));
1161
2949
  const zAxis = transform.transformAsVector(origin.slice(6, 9));
1162
- const halfAxes = new import_core4.Matrix3([
2950
+ const halfAxes = new import_core6.Matrix3([
1163
2951
  xAxis[0],
1164
2952
  xAxis[1],
1165
2953
  xAxis[2],
@@ -1175,10 +2963,10 @@ function createBox(box, transform, result) {
1175
2963
  result.halfAxes = halfAxes;
1176
2964
  return result;
1177
2965
  }
1178
- return new import_culling2.OrientedBoundingBox(center, halfAxes);
2966
+ return new import_culling3.OrientedBoundingBox(center, halfAxes);
1179
2967
  }
1180
2968
  function createSphere(sphere, transform, result) {
1181
- const center = new import_core4.Vector3(sphere[0], sphere[1], sphere[2]);
2969
+ const center = new import_core6.Vector3(sphere[0], sphere[1], sphere[2]);
1182
2970
  transform.transform(center, center);
1183
2971
  const scale = transform.getScale(scratchScale);
1184
2972
  const uniformScale = Math.max(Math.max(scale[0], scale[1]), scale[2]);
@@ -1188,30 +2976,30 @@ function createSphere(sphere, transform, result) {
1188
2976
  result.radius = radius;
1189
2977
  return result;
1190
2978
  }
1191
- return new import_culling2.BoundingSphere(center, radius);
2979
+ return new import_culling3.BoundingSphere(center, radius);
1192
2980
  }
1193
2981
  function createObbFromRegion(region) {
1194
2982
  const [west, south, east, north, minHeight, maxHeight] = region;
1195
- const northWest = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core4.degrees)(west), (0, import_core4.degrees)(north), minHeight], scratchNorthWest);
1196
- const southEast = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core4.degrees)(east), (0, import_core4.degrees)(south), maxHeight], scratchSouthEast);
1197
- const centerInCartesian = new import_core4.Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5);
1198
- import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter);
1199
- import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core4.degrees)(east), scratchCenter[1], scratchCenter[2]], scratchXAxis);
1200
- import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter[0], (0, import_core4.degrees)(north), scratchCenter[2]], scratchYAxis);
1201
- import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter[0], scratchCenter[1], maxHeight], scratchZAxis);
2983
+ const northWest = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core6.degrees)(west), (0, import_core6.degrees)(north), minHeight], scratchNorthWest);
2984
+ const southEast = import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core6.degrees)(east), (0, import_core6.degrees)(south), maxHeight], scratchSouthEast);
2985
+ const centerInCartesian = new import_core6.Vector3().addVectors(northWest, southEast).multiplyByScalar(0.5);
2986
+ import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(centerInCartesian, scratchCenter2);
2987
+ import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([(0, import_core6.degrees)(east), scratchCenter2[1], scratchCenter2[2]], scratchXAxis);
2988
+ import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter2[0], (0, import_core6.degrees)(north), scratchCenter2[2]], scratchYAxis);
2989
+ import_geospatial4.Ellipsoid.WGS84.cartographicToCartesian([scratchCenter2[0], scratchCenter2[1], maxHeight], scratchZAxis);
1202
2990
  return createBox([
1203
2991
  ...centerInCartesian,
1204
2992
  ...scratchXAxis.subtract(centerInCartesian),
1205
2993
  ...scratchYAxis.subtract(centerInCartesian),
1206
2994
  ...scratchZAxis.subtract(centerInCartesian)
1207
- ], new import_core4.Matrix4());
2995
+ ], new import_core6.Matrix4());
1208
2996
  }
1209
2997
  function orientedBoundingBoxToCartographicBounds(boundingVolume) {
1210
2998
  const result = emptyCartographicBounds();
1211
2999
  const { halfAxes } = boundingVolume;
1212
- const xAxis = new import_core4.Vector3(halfAxes.getColumn(0));
1213
- const yAxis = new import_core4.Vector3(halfAxes.getColumn(1));
1214
- const zAxis = new import_core4.Vector3(halfAxes.getColumn(2));
3000
+ const xAxis = new import_core6.Vector3(halfAxes.getColumn(0));
3001
+ const yAxis = new import_core6.Vector3(halfAxes.getColumn(1));
3002
+ const zAxis = new import_core6.Vector3(halfAxes.getColumn(2));
1215
3003
  for (let x = 0; x < 2; x++) {
1216
3004
  for (let y = 0; y < 2; y++) {
1217
3005
  for (let z = 0; z < 2; z++) {
@@ -1236,13 +3024,13 @@ function boundingSphereToCartographicBounds(boundingVolume) {
1236
3024
  if (point) {
1237
3025
  zAxis = import_geospatial4.Ellipsoid.WGS84.geodeticSurfaceNormal(point);
1238
3026
  } else {
1239
- zAxis = new import_core4.Vector3(0, 0, 1);
3027
+ zAxis = new import_core6.Vector3(0, 0, 1);
1240
3028
  }
1241
- let xAxis = new import_core4.Vector3(zAxis[2], -zAxis[1], 0);
3029
+ let xAxis = new import_core6.Vector3(zAxis[2], -zAxis[1], 0);
1242
3030
  if (xAxis.len() > 0) {
1243
3031
  xAxis.normalize();
1244
3032
  } else {
1245
- xAxis = new import_core4.Vector3(0, 1, 0);
3033
+ xAxis = new import_core6.Vector3(0, 1, 0);
1246
3034
  }
1247
3035
  const yAxis = xAxis.clone().cross(zAxis);
1248
3036
  for (const axis of [xAxis, yAxis, zAxis]) {
@@ -1263,77 +3051,25 @@ function emptyCartographicBounds() {
1263
3051
  ];
1264
3052
  }
1265
3053
  function addToCartographicBounds(target, cartesian) {
1266
- import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(cartesian, scratchPoint);
1267
- target[0][0] = Math.min(target[0][0], scratchPoint[0]);
1268
- target[0][1] = Math.min(target[0][1], scratchPoint[1]);
1269
- target[0][2] = Math.min(target[0][2], scratchPoint[2]);
1270
- target[1][0] = Math.max(target[1][0], scratchPoint[0]);
1271
- target[1][1] = Math.max(target[1][1], scratchPoint[1]);
1272
- target[1][2] = Math.max(target[1][2], scratchPoint[2]);
1273
- }
1274
-
1275
- // dist/tileset-3d/helpers/tiles-3d-lod.js
1276
- var import_core5 = require("@math.gl/core");
1277
- var scratchPositionNormal = new import_core5.Vector3();
1278
- var scratchCartographic = new import_core5.Vector3();
1279
- var scratchMatrix = new import_core5.Matrix4();
1280
- var scratchCenter2 = new import_core5.Vector3();
1281
- var scratchPosition2 = new import_core5.Vector3();
1282
- var scratchDirection = new import_core5.Vector3();
1283
- function fog(distanceToCamera, density) {
1284
- const scalar = distanceToCamera * density;
1285
- return 1 - Math.exp(-(scalar * scalar));
1286
- }
1287
- function getDynamicScreenSpaceError(tileset, distanceToCamera) {
1288
- if (tileset.dynamicScreenSpaceError && tileset.dynamicScreenSpaceErrorComputedDensity) {
1289
- const density = tileset.dynamicScreenSpaceErrorComputedDensity;
1290
- const factor = tileset.dynamicScreenSpaceErrorFactor;
1291
- const dynamicError = fog(distanceToCamera, density) * factor;
1292
- return dynamicError;
1293
- }
1294
- return 0;
1295
- }
1296
- function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric) {
1297
- const tileset = tile.tileset;
1298
- const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
1299
- const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
1300
- if (lodMetricValue === 0) {
1301
- return 0;
1302
- }
1303
- const { viewDistanceScale } = tileset.options;
1304
- const lodScale = viewDistanceScale || 1;
1305
- const orthographicError = getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale);
1306
- if (orthographicError !== null) {
1307
- return orthographicError;
1308
- }
1309
- const distance = Math.max(tile._distanceToCamera, 1e-7);
1310
- const { height, sseDenominator } = frameState;
1311
- let error = lodMetricValue * height * lodScale / (distance * sseDenominator);
1312
- error -= getDynamicScreenSpaceError(tileset, distance);
1313
- return error;
1314
- }
1315
- function getOrthographicScreenSpaceError(lodMetricValue, frameState, lodScale) {
1316
- const viewport = frameState.viewport;
1317
- if (!(viewport == null ? void 0 : viewport.orthographic)) {
1318
- return null;
1319
- }
1320
- const metersPerPixel = viewport.metersPerPixel;
1321
- if (!Number.isFinite(metersPerPixel) || metersPerPixel <= 0) {
1322
- return null;
1323
- }
1324
- return lodMetricValue * lodScale / metersPerPixel;
3054
+ import_geospatial4.Ellipsoid.WGS84.cartesianToCartographic(cartesian, scratchPoint);
3055
+ target[0][0] = Math.min(target[0][0], scratchPoint[0]);
3056
+ target[0][1] = Math.min(target[0][1], scratchPoint[1]);
3057
+ target[0][2] = Math.min(target[0][2], scratchPoint[2]);
3058
+ target[1][0] = Math.max(target[1][0], scratchPoint[0]);
3059
+ target[1][1] = Math.max(target[1][1], scratchPoint[1]);
3060
+ target[1][2] = Math.max(target[1][2], scratchPoint[2]);
1325
3061
  }
1326
3062
 
1327
3063
  // dist/tileset-3d/helpers/i3s-lod.js
1328
- var import_core6 = require("@math.gl/core");
3064
+ var import_core7 = require("@math.gl/core");
1329
3065
  var import_geospatial5 = require("@math.gl/geospatial");
1330
- var cameraPositionCartesian = new import_core6.Vector3();
1331
- var toEye = new import_core6.Vector3();
1332
- var cameraPositionEnu = new import_core6.Vector3();
1333
- var extraVertexEnu = new import_core6.Vector3();
1334
- var projectedOriginVector = new import_core6.Vector3();
1335
- var enuToCartesianMatrix = new import_core6.Matrix4();
1336
- var cartesianToEnuMatrix = new import_core6.Matrix4();
3066
+ var cameraPositionCartesian = new import_core7.Vector3();
3067
+ var toEye = new import_core7.Vector3();
3068
+ var cameraPositionEnu = new import_core7.Vector3();
3069
+ var extraVertexEnu = new import_core7.Vector3();
3070
+ var projectedOriginVector = new import_core7.Vector3();
3071
+ var enuToCartesianMatrix = new import_core7.Matrix4();
3072
+ var cartesianToEnuMatrix = new import_core7.Matrix4();
1337
3073
  function getLodStatus(tile, frameState) {
1338
3074
  if (tile.lodMetricValue === 0 || Number.isNaN(tile.lodMetricValue)) {
1339
3075
  return "DIG";
@@ -1377,7 +3113,6 @@ function getProjectedRadius(tile, frameState) {
1377
3113
  }
1378
3114
 
1379
3115
  // dist/utils/managed-array.js
1380
- var import_loader_utils5 = require("@loaders.gl/loader-utils");
1381
3116
  var ManagedArray = class {
1382
3117
  _map = /* @__PURE__ */ new Map();
1383
3118
  _array;
@@ -1418,7 +3153,7 @@ var ManagedArray = class {
1418
3153
  * @param {Number} index The index to get.
1419
3154
  */
1420
3155
  get(index) {
1421
- (0, import_loader_utils5.assert)(index < this._array.length);
3156
+ assert(index < this._array.length);
1422
3157
  return this._array[index];
1423
3158
  }
1424
3159
  /**
@@ -1428,7 +3163,7 @@ var ManagedArray = class {
1428
3163
  * @param {*} element The element to set at index.
1429
3164
  */
1430
3165
  set(index, element) {
1431
- (0, import_loader_utils5.assert)(index >= 0);
3166
+ assert(index >= 0);
1432
3167
  if (index >= this.length) {
1433
3168
  this.length = index + 1;
1434
3169
  }
@@ -1482,7 +3217,7 @@ var ManagedArray = class {
1482
3217
  * @param {Number} length The length.
1483
3218
  */
1484
3219
  reserve(length) {
1485
- (0, import_loader_utils5.assert)(length >= 0);
3220
+ assert(length >= 0);
1486
3221
  if (length > this._array.length) {
1487
3222
  this._array.length = length;
1488
3223
  }
@@ -1493,7 +3228,7 @@ var ManagedArray = class {
1493
3228
  * @param {Number} length The length.
1494
3229
  */
1495
3230
  resize(length) {
1496
- (0, import_loader_utils5.assert)(length >= 0);
3231
+ assert(length >= 0);
1497
3232
  this.length = length;
1498
3233
  }
1499
3234
  /**
@@ -1518,7 +3253,7 @@ var ManagedArray = class {
1518
3253
  };
1519
3254
 
1520
3255
  // dist/tileset-3d/common/tileset-traverser.js
1521
- var DEFAULT_PROPS2 = {
3256
+ var DEFAULT_PROPS4 = {
1522
3257
  loadSiblings: false,
1523
3258
  skipLevelOfDetail: false,
1524
3259
  updateTransforms: true,
@@ -1535,9 +3270,11 @@ var TilesetTraverser = class {
1535
3270
  selectedTiles = {};
1536
3271
  // tiles should be loaded from server
1537
3272
  requestedTiles = {};
3273
+ /** Tiles intentionally held until the camera has remained still for the configured delay. */
3274
+ deferredTiles = {};
1538
3275
  // tiles does not have render content
1539
3276
  emptyTiles = {};
1540
- lastUpdate = new Date().getTime();
3277
+ lastUpdate = (/* @__PURE__ */ new Date()).getTime();
1541
3278
  updateDebounceTime = 1e3;
1542
3279
  /** temporary storage to hold the traversed tiles during a traversal */
1543
3280
  _traversalStack = new ManagedArray();
@@ -1550,7 +3287,7 @@ var TilesetTraverser = class {
1550
3287
  }
1551
3288
  // TODO nested props
1552
3289
  constructor(options) {
1553
- this.options = { ...DEFAULT_PROPS2, ...options };
3290
+ this.options = { ...DEFAULT_PROPS4, ...options };
1554
3291
  }
1555
3292
  // tiles should be visible
1556
3293
  traverse(root, frameState, options) {
@@ -1563,6 +3300,7 @@ var TilesetTraverser = class {
1563
3300
  }
1564
3301
  reset() {
1565
3302
  this.requestedTiles = {};
3303
+ this.deferredTiles = {};
1566
3304
  this.selectedTiles = {};
1567
3305
  this.emptyTiles = {};
1568
3306
  this._traversalStack.reset();
@@ -1610,7 +3348,7 @@ var TilesetTraverser = class {
1610
3348
  this.touchTile(tile, frameState);
1611
3349
  tile._shouldRefine = shouldRefine && parentRefines;
1612
3350
  }
1613
- const newTime = new Date().getTime();
3351
+ const newTime = (/* @__PURE__ */ new Date()).getTime();
1614
3352
  if (this.traversalFinished(frameState) || newTime - this.lastUpdate > this.updateDebounceTime) {
1615
3353
  this.lastUpdate = newTime;
1616
3354
  this.options.onTraversalEnd(frameState);
@@ -1676,11 +3414,28 @@ var TilesetTraverser = class {
1676
3414
  // tile to load from server
1677
3415
  loadTile(tile, frameState) {
1678
3416
  if (this.shouldLoadTile(tile)) {
3417
+ if (this.shouldDeferTileRequest(tile)) {
3418
+ this.deferredTiles[tile.id] = tile;
3419
+ return;
3420
+ }
1679
3421
  tile._requestedFrame = frameState.frameNumber;
1680
3422
  tile._priority = tile._getPriority();
1681
3423
  this.requestedTiles[tile.id] = tile;
1682
3424
  }
1683
3425
  }
3426
+ /**
3427
+ * Returns whether an eligible peripheral request should wait for camera motion to settle.
3428
+ *
3429
+ * Eligibility and the current time window are calculated together when tile visibility updates.
3430
+ * Keeping the active state on the tile also lets a queued scheduler callback cancel work that
3431
+ * became deferred after its original traversal.
3432
+ *
3433
+ * @param tile - Tile considered for loading.
3434
+ * @returns `true` when the request should be retried after the remaining delay.
3435
+ */
3436
+ shouldDeferTileRequest(tile) {
3437
+ return tile.priorityDeferred;
3438
+ }
1684
3439
  // cache tile
1685
3440
  touchTile(tile, frameState) {
1686
3441
  tile.tileset._cache.touch(tile);
@@ -1742,12 +3497,17 @@ var TilesetTraverser = class {
1742
3497
  // Ignores visibility.
1743
3498
  executeEmptyTraversal(root, frameState) {
1744
3499
  let allDescendantsLoaded = true;
3500
+ let hasPendingLazyChildren = false;
1745
3501
  const stack = this._emptyTraversalStack;
1746
3502
  stack.push(root);
1747
3503
  while (stack.length > 0) {
1748
3504
  const tile = stack.pop();
1749
3505
  const traverse = !tile.hasRenderContent && this.canTraverse(tile, frameState);
1750
3506
  const emptyLeaf = !tile.hasRenderContent && tile.children.length === 0;
3507
+ if (tile.header.implicitSubtree) {
3508
+ hasPendingLazyChildren = true;
3509
+ allDescendantsLoaded = false;
3510
+ }
1751
3511
  if (!traverse && !tile.contentAvailable && !emptyLeaf) {
1752
3512
  allDescendantsLoaded = false;
1753
3513
  }
@@ -1763,12 +3523,12 @@ var TilesetTraverser = class {
1763
3523
  }
1764
3524
  }
1765
3525
  }
1766
- return root.hasEmptyContent || allDescendantsLoaded;
3526
+ return root.hasEmptyContent && !hasPendingLazyChildren || allDescendantsLoaded;
1767
3527
  }
1768
3528
  };
1769
3529
 
1770
3530
  // dist/tileset-3d/common/tile-3d.js
1771
- var scratchVector2 = new import_core7.Vector3();
3531
+ var scratchVector2 = new import_core8.Vector3();
1772
3532
  function defined2(x) {
1773
3533
  return x !== void 0 && x !== null;
1774
3534
  }
@@ -1805,9 +3565,11 @@ var Tile3D = class {
1805
3565
  gpuMemoryUsageInBytes = 0;
1806
3566
  /** The tile's children - an array of Tile3D objects. */
1807
3567
  children = [];
3568
+ /** Current lifecycle state for a lazy implicit subtree rooted at this tile. */
3569
+ childrenState = "ready";
1808
3570
  depth = 0;
1809
3571
  viewportIds = [];
1810
- transform = new import_core7.Matrix4();
3572
+ transform = new import_core8.Matrix4();
1811
3573
  extensions = null;
1812
3574
  /** TODO Cesium 3d tiles specific */
1813
3575
  implicitTiling = null;
@@ -1823,15 +3585,25 @@ var Tile3D = class {
1823
3585
  // TODO Cesium 3d tiles specific
1824
3586
  _expireDate = null;
1825
3587
  _expiredContent = null;
3588
+ /** In-flight source request shared by repeated traversal frames. */
3589
+ _childrenPromise = null;
1826
3590
  _boundingBox = void 0;
1827
3591
  /** updated every frame for tree traversal and rendering optimizations: */
1828
3592
  _distanceToCamera = 0;
1829
3593
  _screenSpaceError = 0;
3594
+ /** SSE calculated at the reduced viewport height used to prioritize coarse coverage. */
3595
+ _screenSpaceErrorProgressiveResolution = 0;
3596
+ /** Whether this tile should be promoted into the progressive-resolution request pass. */
3597
+ _priorityProgressiveResolution = false;
3598
+ /** Angular distance from the camera view axis used to prioritize center tiles. */
3599
+ _foveatedFactor = 0;
3600
+ /** Whether this peripheral request is currently inside the camera-motion deferral window. */
3601
+ priorityDeferred = false;
1830
3602
  _visibilityPlaneMask;
1831
3603
  _visible = void 0;
1832
3604
  _contentBoundingVolume;
1833
3605
  _viewerRequestVolume;
1834
- _initialTransform = new import_core7.Matrix4();
3606
+ _initialTransform = new import_core8.Matrix4();
1835
3607
  // Used by traverser, cannot be marked private
1836
3608
  _priority = 0;
1837
3609
  _selectedFrame = 0;
@@ -1871,6 +3643,7 @@ var Tile3D = class {
1871
3643
  this.refine = this._getRefine(header.refine);
1872
3644
  this.type = header.type;
1873
3645
  this.contentUrl = header.contentUrl;
3646
+ this.childrenState = header.implicitSubtree ? "unloaded" : "ready";
1874
3647
  this._initializeLodMetric(header);
1875
3648
  this._initializeTransforms(header);
1876
3649
  this._updateLodMetricScale();
@@ -1900,7 +3673,15 @@ var Tile3D = class {
1900
3673
  }
1901
3674
  /** Returns true if tile has children */
1902
3675
  get hasChildren() {
1903
- return this.children.length > 0 || this.header.children && this.header.children.length > 0;
3676
+ return this.children.length > 0 || this.header.children && this.header.children.length > 0 || Boolean(this.header.implicitSubtree && this.childrenState !== "ready");
3677
+ }
3678
+ /** Returns whether traversal may request an unresolved lazy child-header group. */
3679
+ get hasUnloadedChildren() {
3680
+ return Boolean(this.header.implicitSubtree && (this.childrenState === "unloaded" || this.childrenState === "failed"));
3681
+ }
3682
+ /** Returns whether a lazy child-header request is currently in flight. */
3683
+ get childrenLoading() {
3684
+ return this.childrenState === "loading";
1904
3685
  }
1905
3686
  /**
1906
3687
  * Determines if the tile's content is ready. This is automatically `true` for
@@ -1961,13 +3742,23 @@ var Tile3D = class {
1961
3742
  }
1962
3743
  return this._boundingBox;
1963
3744
  }
1964
- /** Get the tile's screen space error. */
1965
- getScreenSpaceError(frameState, useParentLodMetric) {
3745
+ /**
3746
+ * Gets the tile's screen-space error for normal refinement or a reduced-height priority pass.
3747
+ *
3748
+ * I3S already supplies a screen-oriented LOD metric and intentionally ignores the optional 3D
3749
+ * Tiles progressive-resolution height fraction.
3750
+ *
3751
+ * @param frameState - Current camera and viewport measurements.
3752
+ * @param useParentLodMetric - Whether to substitute the parent LOD metric.
3753
+ * @param viewportHeightFraction - Fraction of viewport height represented by the priority pass.
3754
+ * @returns Screen-space LOD error for the current tileset format.
3755
+ */
3756
+ getScreenSpaceError(frameState, useParentLodMetric, viewportHeightFraction = 1) {
1966
3757
  switch (this.tileset.type) {
1967
3758
  case TILESET_TYPE.I3S:
1968
3759
  return getProjectedRadius(this, frameState);
1969
3760
  case TILESET_TYPE.TILES3D:
1970
- return getTiles3DScreenSpaceError(this, frameState, useParentLodMetric);
3761
+ return getTiles3DScreenSpaceError(this, frameState, useParentLodMetric, viewportHeightFraction);
1971
3762
  default:
1972
3763
  throw new Error("Unsupported tileset type");
1973
3764
  }
@@ -1985,12 +3776,34 @@ var Tile3D = class {
1985
3776
  _getGpuMemoryUsageInBytes() {
1986
3777
  return this.content.gpuMemoryUsageInBytes || this.content.byteLength || 0;
1987
3778
  }
1988
- /*
1989
- * If skipLevelOfDetail is off try to load child tiles as soon as possible so that their parent can refine sooner.
1990
- * Tiles are prioritized by screen space error.
3779
+ /**
3780
+ * Returns scheduler priority for render-content requests.
3781
+ *
3782
+ * @returns Numeric priority, or a negative value when the queued request should be cancelled.
1991
3783
  */
1992
- // eslint-disable-next-line complexity
1993
3784
  _getPriority() {
3785
+ return this._calculateRequestPriority(false);
3786
+ }
3787
+ /**
3788
+ * Returns scheduler priority for lazy child-header requests.
3789
+ *
3790
+ * Contentless implicit placeholders intentionally retain `UNLOADED` content state, so metadata
3791
+ * scheduling skips only that render-content cancellation check and preserves every visibility,
3792
+ * recency, progressive, foveated, and motion-deferral rule.
3793
+ *
3794
+ * @returns Numeric priority, or a negative value when the queued request should be cancelled.
3795
+ */
3796
+ _getChildrenPriority() {
3797
+ return this._calculateRequestPriority(true);
3798
+ }
3799
+ /**
3800
+ * Calculates the common scheduler priority for content or hierarchy metadata.
3801
+ *
3802
+ * @param allowUnloadedContent - Whether an empty placeholder may request child metadata.
3803
+ * @returns Numeric priority, or a negative value when the queued request should be cancelled.
3804
+ */
3805
+ // eslint-disable-next-line complexity
3806
+ _calculateRequestPriority(allowUnloadedContent) {
1994
3807
  const traverser = this.tileset._traverser;
1995
3808
  const { skipLevelOfDetail } = traverser.options;
1996
3809
  const maySkipTile = this.refine === TILE_REFINEMENT.ADD || skipLevelOfDetail;
@@ -2000,14 +3813,23 @@ var Tile3D = class {
2000
3813
  if (this.tileset._frameNumber - this._touchedFrame >= 1) {
2001
3814
  return -1;
2002
3815
  }
2003
- if (this.contentState === TILE_CONTENT_STATE.UNLOADED) {
3816
+ if (this.priorityDeferred) {
3817
+ return -1;
3818
+ }
3819
+ if (!allowUnloadedContent && this.contentState === TILE_CONTENT_STATE.UNLOADED) {
2004
3820
  return -1;
2005
3821
  }
2006
3822
  const parent = this.parent;
2007
3823
  const useParentScreenSpaceError = parent && (!maySkipTile || this._screenSpaceError === 0 || parent.hasTilesetContent);
2008
3824
  const screenSpaceError = useParentScreenSpaceError ? parent._screenSpaceError : this._screenSpaceError;
2009
3825
  const rootScreenSpaceError = traverser.root ? traverser.root._screenSpaceError : 0;
2010
- return Math.max(rootScreenSpaceError - screenSpaceError, 0);
3826
+ const reverseScreenSpaceError = Math.max(rootScreenSpaceError - screenSpaceError, 0);
3827
+ return calculateTileRequestPriority({
3828
+ priorityProgressiveResolution: this._priorityProgressiveResolution,
3829
+ foveatedFactor: this._foveatedFactor,
3830
+ reverseScreenSpaceError,
3831
+ rootScreenSpaceError
3832
+ });
2011
3833
  }
2012
3834
  /**
2013
3835
  * Requests the tile's content.
@@ -2046,6 +3868,84 @@ var Tile3D = class {
2046
3868
  requestToken.done();
2047
3869
  }
2048
3870
  }
3871
+ /**
3872
+ * Requests and installs this tile's lazy child-header group through its source.
3873
+ *
3874
+ * The same request scheduler and SSE/foveated priority value used for render content are reused
3875
+ * for subtree metadata. Repeated traversal frames share one promise, and scheduler cancellation
3876
+ * restores the unloaded state so a later frame can retry.
3877
+ *
3878
+ * @param frameState - View state that made the subtree eligible for refinement.
3879
+ * @returns Source load result, or an unloaded result if scheduling was cancelled.
3880
+ */
3881
+ async loadChildren(frameState) {
3882
+ if (!this.header.implicitSubtree || this.childrenState === "ready") {
3883
+ return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
3884
+ }
3885
+ if (this._childrenPromise) {
3886
+ return await this._childrenPromise;
3887
+ }
3888
+ if (!this.tileset.source.loadTileChildren) {
3889
+ throw new Error("Tileset source does not support lazy tile children");
3890
+ }
3891
+ this.childrenState = "loading";
3892
+ this._childrenPromise = this._loadChildrenWithScheduler(frameState);
3893
+ try {
3894
+ return await this._childrenPromise;
3895
+ } finally {
3896
+ this._childrenPromise = null;
3897
+ }
3898
+ }
3899
+ /**
3900
+ * Runs one lazy child-header load after obtaining a scheduler token.
3901
+ *
3902
+ * @param frameState - View state forwarded to the source implementation.
3903
+ * @returns Source load result.
3904
+ */
3905
+ async _loadChildrenWithScheduler(frameState) {
3906
+ const requestToken = await this.tileset._requestScheduler.scheduleRequest(`${this.id}:implicit-subtree`, this._getChildrenPriority.bind(this));
3907
+ if (!requestToken) {
3908
+ this.childrenState = "unloaded";
3909
+ return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
3910
+ }
3911
+ try {
3912
+ const loadResult = await this.tileset.source.loadTileChildren(this, frameState);
3913
+ this.childrenState = loadResult.loaded ? "ready" : "unloaded";
3914
+ return loadResult;
3915
+ } catch (error) {
3916
+ this.childrenState = "failed";
3917
+ throw error;
3918
+ } finally {
3919
+ requestToken.done();
3920
+ }
3921
+ }
3922
+ /**
3923
+ * Replaces a contentless implicit placeholder with its materialized subtree-root header.
3924
+ *
3925
+ * Transform state is intentionally preserved: the placeholder already owns the composed
3926
+ * transform, and reapplying the root transform here would double-transform bounds and geometric
3927
+ * error. Content and raw geometric error are reinitialized from the newly available header.
3928
+ *
3929
+ * @param materializedHeader - Available root header produced from one subtree resource.
3930
+ */
3931
+ applyImplicitSubtreeHeader(materializedHeader) {
3932
+ const existingTransform = this.header.transform;
3933
+ const existingTransformMatrix = this.header.transformMatrix;
3934
+ this.header = {
3935
+ ...this.header,
3936
+ ...materializedHeader,
3937
+ transform: existingTransform,
3938
+ transformMatrix: existingTransformMatrix,
3939
+ implicitSubtree: void 0
3940
+ };
3941
+ this.refine = this._getRefine(materializedHeader.refine);
3942
+ this.type = materializedHeader.type;
3943
+ this.contentUrl = materializedHeader.contentUrl;
3944
+ this._initializeLodMetric(materializedHeader);
3945
+ this._updateLodMetricScale();
3946
+ this._initializeBoundingVolumes(this.header);
3947
+ this._initializeContent(this.header);
3948
+ }
2049
3949
  // Unloads the tile's content.
2050
3950
  unloadContent() {
2051
3951
  if (this.content && this.content.destroy) {
@@ -2071,19 +3971,64 @@ var Tile3D = class {
2071
3971
  return;
2072
3972
  }
2073
3973
  const parent = this.parent;
2074
- const parentVisibilityPlaneMask = parent ? parent._visibilityPlaneMask : import_culling3.CullingVolume.MASK_INDETERMINATE;
3974
+ const parentVisibilityPlaneMask = parent ? parent._visibilityPlaneMask : import_culling4.CullingVolume.MASK_INDETERMINATE;
2075
3975
  if (this.tileset._traverser.options.updateTransforms) {
2076
3976
  const parentTransform = parent ? parent.computedTransform : this.tileset.modelMatrix;
2077
3977
  this._updateTransform(parentTransform);
2078
3978
  }
2079
3979
  this._distanceToCamera = this.distanceToTile(frameState);
2080
3980
  this._screenSpaceError = this.getScreenSpaceError(frameState, false);
3981
+ this._updateRequestPriorityMetrics(frameState);
2081
3982
  this._visibilityPlaneMask = this.visibility(frameState, parentVisibilityPlaneMask);
2082
- this._visible = this._visibilityPlaneMask !== import_culling3.CullingVolume.MASK_OUTSIDE;
3983
+ this._visible = this._visibilityPlaneMask !== import_culling4.CullingVolume.MASK_OUTSIDE;
2083
3984
  this._inRequestVolume = this.insideViewerRequestVolume(frameState);
2084
3985
  this._frameNumber = frameState.frameNumber;
2085
3986
  this.viewportIds = viewportIds;
2086
3987
  }
3988
+ /**
3989
+ * Updates progressive-resolution and foveated request metrics for a 3D Tiles traversal frame.
3990
+ *
3991
+ * I3S retains its format-specific screen-threshold ordering and is explicitly isolated from
3992
+ * geometric-error scheduling. Orthographic viewports still receive progressive coverage, but
3993
+ * angular foveation is disabled because an orthographic frustum has no perspective field of view.
3994
+ *
3995
+ * @param frameState - Current camera and viewport measurements.
3996
+ */
3997
+ _updateRequestPriorityMetrics(frameState) {
3998
+ var _a, _b, _c, _d, _e;
3999
+ if (this.tileset.type !== TILESET_TYPE.TILES3D) {
4000
+ this._screenSpaceErrorProgressiveResolution = this._screenSpaceError;
4001
+ this._priorityProgressiveResolution = false;
4002
+ this._foveatedFactor = 0;
4003
+ this.priorityDeferred = false;
4004
+ return;
4005
+ }
4006
+ const options = this.tileset.options;
4007
+ const progressiveResolutionHeightFraction = options.progressiveResolutionHeightFraction;
4008
+ this._screenSpaceErrorProgressiveResolution = this.getScreenSpaceError(frameState, false, progressiveResolutionHeightFraction);
4009
+ this._priorityProgressiveResolution = isProgressiveResolutionPriority(this._screenSpaceErrorProgressiveResolution, (_a = this.parent) == null ? void 0 : _a._screenSpaceErrorProgressiveResolution, this.tileset.memoryAdjustedScreenSpaceError, progressiveResolutionHeightFraction);
4010
+ if (!options.foveatedScreenSpaceError || ((_b = frameState.viewport) == null ? void 0 : _b.orthographic) || !((_c = frameState.camera) == null ? void 0 : _c.position) || !((_d = frameState.camera) == null ? void 0 : _d.direction)) {
4011
+ this._foveatedFactor = 0;
4012
+ this.priorityDeferred = false;
4013
+ return;
4014
+ }
4015
+ this._foveatedFactor = calculateFoveatedFactor(this.boundingVolume, frameState.camera);
4016
+ const deferralEligible = isFoveatedRequestDeferred({
4017
+ refinement: this.refine,
4018
+ skipLevelOfDetail: this.tileset._traverser.options.skipLevelOfDetail,
4019
+ foveatedScreenSpaceError: options.foveatedScreenSpaceError,
4020
+ foveatedConeSize: options.foveatedConeSize,
4021
+ minimumScreenSpaceErrorRelaxation: options.foveatedMinimumScreenSpaceErrorRelaxation,
4022
+ interpolationCallback: options.foveatedInterpolationCallback,
4023
+ foveatedFactor: this._foveatedFactor,
4024
+ verticalFieldOfView: frameState.camera.verticalFieldOfView,
4025
+ screenSpaceError: this._screenSpaceError,
4026
+ parentScreenSpaceError: (_e = this.parent) == null ? void 0 : _e._screenSpaceError,
4027
+ maximumScreenSpaceError: this.tileset.memoryAdjustedScreenSpaceError,
4028
+ priorityProgressiveResolution: this._priorityProgressiveResolution
4029
+ });
4030
+ this.priorityDeferred = isFoveatedRequestDelayActive(deferralEligible, frameState.camera.timeSinceMovement, options.foveatedTimeDelay);
4031
+ }
2087
4032
  // Determines whether the tile's bounding volume intersects the culling volume.
2088
4033
  // @param {FrameState} frameState The frame state.
2089
4034
  // @param {Number} parentVisibilityPlaneMask The parent's plane mask to speed up the visibility check.
@@ -2160,13 +4105,13 @@ var Tile3D = class {
2160
4105
  this.lodMetricValue = this._unscaledLodMetricValue;
2161
4106
  }
2162
4107
  _initializeTransforms(tileHeader) {
2163
- this.transform = tileHeader.transform ? new import_core7.Matrix4(tileHeader.transform) : new import_core7.Matrix4();
4108
+ this.transform = tileHeader.transform ? new import_core8.Matrix4(tileHeader.transform) : new import_core8.Matrix4();
2164
4109
  const parent = this.parent;
2165
4110
  const tileset = this.tileset;
2166
4111
  const parentTransform = parent && parent.computedTransform ? parent.computedTransform.clone() : tileset.modelMatrix.clone();
2167
- this.computedTransform = new import_core7.Matrix4(parentTransform).multiplyRight(this.transform);
2168
- const parentInitialTransform = parent && parent._initialTransform ? parent._initialTransform.clone() : new import_core7.Matrix4();
2169
- this._initialTransform = new import_core7.Matrix4(parentInitialTransform).multiplyRight(this.transform);
4112
+ this.computedTransform = new import_core8.Matrix4(parentTransform).multiplyRight(this.transform);
4113
+ const parentInitialTransform = parent && parent._initialTransform ? parent._initialTransform.clone() : new import_core8.Matrix4();
4114
+ this._initialTransform = new import_core8.Matrix4(parentInitialTransform).multiplyRight(this.transform);
2170
4115
  }
2171
4116
  /**
2172
4117
  * Updates the world-space LOD metric from the source metric and the complete tile transform.
@@ -2210,7 +4155,11 @@ var Tile3D = class {
2210
4155
  this._distanceToCamera = 0;
2211
4156
  this._centerZDepth = 0;
2212
4157
  this._screenSpaceError = 0;
2213
- this._visibilityPlaneMask = import_culling3.CullingVolume.MASK_INDETERMINATE;
4158
+ this._screenSpaceErrorProgressiveResolution = 0;
4159
+ this._priorityProgressiveResolution = false;
4160
+ this._foveatedFactor = 0;
4161
+ this.priorityDeferred = false;
4162
+ this._visibilityPlaneMask = import_culling4.CullingVolume.MASK_INDETERMINATE;
2214
4163
  this._visible = void 0;
2215
4164
  this._inRequestVolume = false;
2216
4165
  this._stackLength = 0;
@@ -2225,8 +4174,16 @@ var Tile3D = class {
2225
4174
  _getRefine(refine) {
2226
4175
  return refine || this.parent && this.parent.refine || TILE_REFINEMENT.REPLACE;
2227
4176
  }
4177
+ /**
4178
+ * Returns whether loaded content is an external tileset.
4179
+ *
4180
+ * Parsed shape is authoritative because valid external resources may be extensionless, signed,
4181
+ * or use a misleading filename. This method is called only after source loading has populated
4182
+ * `content`.
4183
+ */
2228
4184
  _isTileset() {
2229
- return this.contentUrl.indexOf(".json") !== -1;
4185
+ var _a;
4186
+ return ((_a = this.content) == null ? void 0 : _a.shape) === "tileset3d";
2230
4187
  }
2231
4188
  _onContentLoaded() {
2232
4189
  switch (this.content && this.content.type) {
@@ -2244,6 +4201,9 @@ var Tile3D = class {
2244
4201
  }
2245
4202
  _updateBoundingVolume(header) {
2246
4203
  this.boundingVolume = createBoundingVolume(header.boundingVolume, this.computedTransform, this.boundingVolume);
4204
+ if (header.viewerRequestVolume) {
4205
+ this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
4206
+ }
2247
4207
  const content = header.content;
2248
4208
  if (!content) {
2249
4209
  return;
@@ -2251,15 +4211,12 @@ var Tile3D = class {
2251
4211
  if (content.boundingVolume) {
2252
4212
  this._contentBoundingVolume = createBoundingVolume(content.boundingVolume, this.computedTransform, this._contentBoundingVolume);
2253
4213
  }
2254
- if (header.viewerRequestVolume) {
2255
- this._viewerRequestVolume = createBoundingVolume(header.viewerRequestVolume, this.computedTransform, this._viewerRequestVolume);
2256
- }
2257
4214
  }
2258
4215
  /**
2259
4216
  * Updates the tile's computed transform, world-space geometric error, and bounding volumes.
2260
4217
  * @param parentTransform - Current computed transform of the parent tile or tileset.
2261
4218
  */
2262
- _updateTransform(parentTransform = new import_core7.Matrix4()) {
4219
+ _updateTransform(parentTransform = new import_core8.Matrix4()) {
2263
4220
  const computedTransform = parentTransform.clone().multiplyRight(this.transform);
2264
4221
  const didTransformChange = !computedTransform.equals(this.computedTransform);
2265
4222
  if (!didTransformChange) {
@@ -2272,15 +4229,77 @@ var Tile3D = class {
2272
4229
  };
2273
4230
 
2274
4231
  // dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
2275
- var import_loader_utils6 = require("@loaders.gl/loader-utils");
2276
4232
  var import_geospatial7 = require("@math.gl/geospatial");
2277
- var import_core9 = require("@math.gl/core");
4233
+ var import_core10 = require("@math.gl/core");
2278
4234
 
2279
4235
  // dist/tileset-3d/format-3d-tiles/tileset-3d-traverser.js
2280
4236
  var Tileset3DTraverser = class extends TilesetTraverser {
4237
+ /** Pending implicit subtree requests keyed by viewport and tile identity. */
4238
+ pendingImplicitSubtrees = /* @__PURE__ */ new Map();
4239
+ /**
4240
+ * Reports whether every implicit subtree request started by this viewport traversal has settled.
4241
+ *
4242
+ * @param frameState - Traversal frame whose completion callback is being considered.
4243
+ * @returns `true` when no matching subtree metadata request remains pending.
4244
+ */
4245
+ traversalFinished(frameState) {
4246
+ const viewportPrefix = `${frameState.viewport.id}:`;
4247
+ for (const [requestKey, frameNumber] of this.pendingImplicitSubtrees) {
4248
+ if (requestKey.startsWith(viewportPrefix) && frameNumber === frameState.frameNumber) {
4249
+ return false;
4250
+ }
4251
+ }
4252
+ return true;
4253
+ }
2281
4254
  compareDistanceToCamera(a, b) {
2282
4255
  return b._distanceToCamera === 0 && a._distanceToCamera === 0 ? b._centerZDepth - a._centerZDepth : b._distanceToCamera - a._distanceToCamera;
2283
4256
  }
4257
+ /**
4258
+ * Starts an implicit subtree request only after culling, request-volume, and SSE eligibility.
4259
+ *
4260
+ * The current parent remains the traversal boundary while metadata is loading, which preserves
4261
+ * REPLACE coverage and ADD accumulation. Request scheduling reads the tile's existing
4262
+ * progressive-resolution and foveated metrics, so subtree metadata follows the same priority
4263
+ * policy as render content. A completion notification lets the owning tileset publish the newly
4264
+ * materialized traversal result on the next update.
4265
+ *
4266
+ * @param tile - Tile whose existing or lazy children should be updated.
4267
+ * @param frameState - Current culling and LOD state.
4268
+ */
4269
+ updateChildTiles(tile, frameState) {
4270
+ if (tile.hasUnloadedChildren && tile.isVisibleAndInRequestVolume && this.shouldRefine(tile, frameState)) {
4271
+ const requestKey = `${frameState.viewport.id}:${tile.id}`;
4272
+ if (!this.pendingImplicitSubtrees.has(requestKey)) {
4273
+ this.pendingImplicitSubtrees.set(requestKey, frameState.frameNumber);
4274
+ void tile.tileset._loadTileChildren(tile, frameState).finally(() => {
4275
+ this.handleImplicitSubtreeLoad(tile, frameState, requestKey);
4276
+ });
4277
+ }
4278
+ }
4279
+ super.updateChildTiles(tile, frameState);
4280
+ }
4281
+ /**
4282
+ * Resumes the same traversal frame after one lazy subtree request settles.
4283
+ *
4284
+ * Successful materialization traverses the new headers immediately. Failed or scheduler-cancelled
4285
+ * requests finish the current traversal without retrying in a tight loop; a later application
4286
+ * update may retry them.
4287
+ *
4288
+ * @param tile - Subtree-root tile whose request settled.
4289
+ * @param frameState - Frame that started the request.
4290
+ * @param requestKey - Viewport-scoped pending-request identity.
4291
+ */
4292
+ handleImplicitSubtreeLoad(tile, frameState, requestKey) {
4293
+ this.pendingImplicitSubtrees.delete(requestKey);
4294
+ if (this._frameNumber !== frameState.frameNumber) {
4295
+ return;
4296
+ }
4297
+ if (tile.childrenState === "ready") {
4298
+ this.executeTraversal(tile, frameState);
4299
+ } else if (this.traversalFinished(frameState)) {
4300
+ this.options.onTraversalEnd(frameState);
4301
+ }
4302
+ }
2284
4303
  updateTileVisibility(tile, frameState) {
2285
4304
  super.updateTileVisibility(tile, frameState);
2286
4305
  if (!tile.isVisibleAndInRequestVolume) {
@@ -2316,19 +4335,19 @@ var Tileset3DTraverser = class extends TilesetTraverser {
2316
4335
  };
2317
4336
 
2318
4337
  // dist/tileset-3d/helpers/zoom.js
2319
- var import_core8 = require("@math.gl/core");
2320
- var import_culling4 = require("@math.gl/culling");
4338
+ var import_core9 = require("@math.gl/core");
4339
+ var import_culling5 = require("@math.gl/culling");
2321
4340
  var import_geospatial6 = require("@math.gl/geospatial");
2322
4341
  var WGS84_RADIUS_X = 6378137;
2323
4342
  var WGS84_RADIUS_Y = 6378137;
2324
4343
  var WGS84_RADIUS_Z = 6356752314245179e-9;
2325
- var scratchVector3 = new import_core8.Vector3();
4344
+ var scratchVector3 = new import_core9.Vector3();
2326
4345
  function getZoomFromBoundingVolume(boundingVolume, cartorgraphicCenter) {
2327
- if (boundingVolume instanceof import_culling4.OrientedBoundingBox) {
4346
+ if (boundingVolume instanceof import_culling5.OrientedBoundingBox) {
2328
4347
  const { halfAxes } = boundingVolume;
2329
4348
  const obbSize = getObbSize(halfAxes);
2330
4349
  return Math.log2(WGS84_RADIUS_Z / (obbSize + cartorgraphicCenter[2]));
2331
- } else if (boundingVolume instanceof import_culling4.BoundingSphere) {
4350
+ } else if (boundingVolume instanceof import_culling5.BoundingSphere) {
2332
4351
  const { radius } = boundingVolume;
2333
4352
  return Math.log2(WGS84_RADIUS_Z / (radius + cartorgraphicCenter[2]));
2334
4353
  } else if (boundingVolume.width && boundingVolume.height) {
@@ -2357,6 +4376,220 @@ function getObbSize(halfAxes) {
2357
4376
  return size;
2358
4377
  }
2359
4378
 
4379
+ // dist/tileset-3d/format-3d-tiles/implicit-tiling.js
4380
+ var QUADTREE_CHILD_COUNT = 4;
4381
+ var OCTREE_CHILD_COUNT = 8;
4382
+ function createImplicitSubtreeReference(descriptor, coordinates) {
4383
+ return {
4384
+ coordinates,
4385
+ subtreeUrl: replaceImplicitUrlTemplate(descriptor.subtreesUrlTemplate, coordinates),
4386
+ descriptor
4387
+ };
4388
+ }
4389
+ function materializeImplicitSubtree(subtree, reference) {
4390
+ const { descriptor } = reference;
4391
+ validateImplicitDescriptor(descriptor);
4392
+ const counters = { tileCount: 0, childSubtreeCount: 0 };
4393
+ const root = materializeAvailableTile(subtree, reference, { localLevel: 0, mortonIndex: 0 }, reference.coordinates, counters);
4394
+ if (!root) {
4395
+ throw new Error(`Invalid 3D Tiles subtree: root tile is unavailable at ${reference.subtreeUrl}`);
4396
+ }
4397
+ return { root, ...counters };
4398
+ }
4399
+ function replaceImplicitUrlTemplate(templateUrl, coordinates) {
4400
+ const values = {
4401
+ level: coordinates.level,
4402
+ x: coordinates.x,
4403
+ y: coordinates.y,
4404
+ z: coordinates.z
4405
+ };
4406
+ return templateUrl.replace(/{(level|x|y|z)}/gi, (_match, coordinateName) => String(values[coordinateName.toLowerCase()]));
4407
+ }
4408
+ function materializeAvailableTile(subtree, reference, localCoordinates, globalCoordinates, counters) {
4409
+ const { descriptor } = reference;
4410
+ const childCount = getImplicitChildCount(descriptor.subdivisionScheme);
4411
+ const availabilityIndex = getLevelOffset(childCount, localCoordinates.localLevel) + localCoordinates.mortonIndex;
4412
+ if (!getAvailabilityValue(subtree.tileAvailability, availabilityIndex)) {
4413
+ return null;
4414
+ }
4415
+ counters.tileCount++;
4416
+ const contentAvailable = getAvailabilityValue(getPrimaryContentAvailability(subtree.contentAvailability), availabilityIndex);
4417
+ const children = [];
4418
+ if (globalCoordinates.level < descriptor.maximumLevel) {
4419
+ if (localCoordinates.localLevel + 1 < descriptor.subtreeLevels) {
4420
+ for (let childIndex = 0; childIndex < childCount; childIndex++) {
4421
+ const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
4422
+ const childHeader = materializeAvailableTile(subtree, reference, {
4423
+ localLevel: localCoordinates.localLevel + 1,
4424
+ mortonIndex: localCoordinates.mortonIndex * childCount + childIndex
4425
+ }, childCoordinates, counters);
4426
+ if (childHeader) {
4427
+ children.push(childHeader);
4428
+ }
4429
+ }
4430
+ } else {
4431
+ for (let childIndex = 0; childIndex < childCount; childIndex++) {
4432
+ const childSubtreeIndex = localCoordinates.mortonIndex * childCount + childIndex;
4433
+ if (getAvailabilityValue(subtree.childSubtreeAvailability, childSubtreeIndex)) {
4434
+ const childCoordinates = getChildCoordinates(globalCoordinates, childIndex, descriptor.subdivisionScheme);
4435
+ children.push(createLazyImplicitTileHeader(descriptor, childCoordinates));
4436
+ counters.childSubtreeCount++;
4437
+ }
4438
+ }
4439
+ }
4440
+ }
4441
+ return formatImplicitTileHeader(descriptor, reference, globalCoordinates, contentAvailable, children);
4442
+ }
4443
+ function createLazyImplicitTileHeader(descriptor, coordinates) {
4444
+ const reference = createImplicitSubtreeReference(descriptor, coordinates);
4445
+ return {
4446
+ id: getImplicitTileId(reference, coordinates),
4447
+ children: [],
4448
+ implicitSubtree: reference,
4449
+ boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme),
4450
+ geometricError: descriptor.rootLodMetricValue / 2 ** coordinates.level,
4451
+ lodMetricType: descriptor.lodMetricType,
4452
+ lodMetricValue: descriptor.rootLodMetricValue / 2 ** coordinates.level,
4453
+ refine: descriptor.refine,
4454
+ type: TILE_TYPE.EMPTY
4455
+ };
4456
+ }
4457
+ function formatImplicitTileHeader(descriptor, reference, coordinates, contentAvailable, children) {
4458
+ const contentUrl = contentAvailable && descriptor.contentUrlTemplate ? replaceImplicitUrlTemplate(descriptor.contentUrlTemplate, coordinates) : void 0;
4459
+ const lodMetricValue = descriptor.rootLodMetricValue / 2 ** coordinates.level;
4460
+ return {
4461
+ id: getImplicitTileId(reference, coordinates),
4462
+ children,
4463
+ contentUrl,
4464
+ content: contentUrl ? { ...descriptor.contentHeader, uri: contentUrl } : void 0,
4465
+ refine: descriptor.refine,
4466
+ type: getImplicitTileType(contentUrl),
4467
+ lodMetricType: descriptor.lodMetricType,
4468
+ lodMetricValue,
4469
+ geometricError: lodMetricValue,
4470
+ boundingVolume: calculateImplicitBoundingVolume(descriptor.rootBoundingVolume, coordinates, descriptor.subdivisionScheme)
4471
+ };
4472
+ }
4473
+ function getImplicitTileId(reference, coordinates) {
4474
+ return `${reference.subtreeUrl}#implicit=${coordinates.level}/${coordinates.x}/${coordinates.y}/${coordinates.z}`;
4475
+ }
4476
+ function getAvailabilityValue(availability, index) {
4477
+ if (typeof (availability == null ? void 0 : availability.constant) === "number") {
4478
+ return Boolean(availability.constant);
4479
+ }
4480
+ const bitstream = availability == null ? void 0 : availability.explicitBitstream;
4481
+ if (!bitstream) {
4482
+ return false;
4483
+ }
4484
+ const byteIndex = Math.floor(index / 8);
4485
+ const bitIndex = index % 8;
4486
+ return Boolean(bitstream[byteIndex] >> bitIndex & 1);
4487
+ }
4488
+ function getPrimaryContentAvailability(availability) {
4489
+ return Array.isArray(availability) ? availability[0] || { constant: 0 } : availability || { constant: 0 };
4490
+ }
4491
+ function getLevelOffset(childCount, localLevel) {
4492
+ return (childCount ** localLevel - 1) / (childCount - 1);
4493
+ }
4494
+ function getChildCoordinates(parent, childIndex, subdivisionScheme) {
4495
+ return {
4496
+ level: parent.level + 1,
4497
+ x: parent.x * 2 + (childIndex & 1),
4498
+ y: parent.y * 2 + (childIndex >> 1 & 1),
4499
+ z: subdivisionScheme === "OCTREE" ? parent.z * 2 + (childIndex >> 2 & 1) : 0
4500
+ };
4501
+ }
4502
+ function getImplicitChildCount(subdivisionScheme) {
4503
+ return subdivisionScheme === "OCTREE" ? OCTREE_CHILD_COUNT : QUADTREE_CHILD_COUNT;
4504
+ }
4505
+ function calculateImplicitBoundingVolume(rootBoundingVolume, coordinates, subdivisionScheme) {
4506
+ if (rootBoundingVolume.region) {
4507
+ const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
4508
+ const divisionCount = 2 ** coordinates.level;
4509
+ const longitudeSize = (east - west) / divisionCount;
4510
+ const latitudeSize = (north - south) / divisionCount;
4511
+ const childWest = west + longitudeSize * coordinates.x;
4512
+ const childSouth = south + latitudeSize * coordinates.y;
4513
+ let childMinimumHeight = minimumHeight;
4514
+ let childMaximumHeight = maximumHeight;
4515
+ if (subdivisionScheme === "OCTREE") {
4516
+ const heightSize = (maximumHeight - minimumHeight) / divisionCount;
4517
+ childMinimumHeight = minimumHeight + heightSize * coordinates.z;
4518
+ childMaximumHeight = childMinimumHeight + heightSize;
4519
+ }
4520
+ return {
4521
+ region: [
4522
+ childWest,
4523
+ childSouth,
4524
+ childWest + longitudeSize,
4525
+ childSouth + latitudeSize,
4526
+ childMinimumHeight,
4527
+ childMaximumHeight
4528
+ ]
4529
+ };
4530
+ }
4531
+ if (rootBoundingVolume.box) {
4532
+ if (rootBoundingVolume.s2VolumeInfo) {
4533
+ return { box: [...rootBoundingVolume.box], s2VolumeInfo: rootBoundingVolume.s2VolumeInfo };
4534
+ }
4535
+ const box = rootBoundingVolume.box;
4536
+ const divisionCount = 2 ** coordinates.level;
4537
+ const xOffset = -1 + (2 * coordinates.x + 1) / divisionCount;
4538
+ const yOffset = -1 + (2 * coordinates.y + 1) / divisionCount;
4539
+ const zOffset = -1 + (2 * coordinates.z + 1) / divisionCount;
4540
+ const divideHeight = subdivisionScheme === "OCTREE";
4541
+ const center = [
4542
+ box[0] + box[3] * xOffset + box[6] * yOffset + (divideHeight ? box[9] * zOffset : 0),
4543
+ box[1] + box[4] * xOffset + box[7] * yOffset + (divideHeight ? box[10] * zOffset : 0),
4544
+ box[2] + box[5] * xOffset + box[8] * yOffset + (divideHeight ? box[11] * zOffset : 0)
4545
+ ];
4546
+ return {
4547
+ box: [
4548
+ ...center,
4549
+ box[3] / divisionCount,
4550
+ box[4] / divisionCount,
4551
+ box[5] / divisionCount,
4552
+ box[6] / divisionCount,
4553
+ box[7] / divisionCount,
4554
+ box[8] / divisionCount,
4555
+ divideHeight ? box[9] / divisionCount : box[9],
4556
+ divideHeight ? box[10] / divisionCount : box[10],
4557
+ divideHeight ? box[11] / divisionCount : box[11]
4558
+ ]
4559
+ };
4560
+ }
4561
+ throw new Error(`Unsupported implicit 3D Tiles bounding volume: ${JSON.stringify(rootBoundingVolume)}`);
4562
+ }
4563
+ function getImplicitTileType(contentUrl) {
4564
+ var _a;
4565
+ if (!contentUrl) {
4566
+ return TILE_TYPE.EMPTY;
4567
+ }
4568
+ const extension = (_a = contentUrl.split("?")[0].split(".").pop()) == null ? void 0 : _a.toLowerCase();
4569
+ switch (extension) {
4570
+ case "pnts":
4571
+ return TILE_TYPE.POINTCLOUD;
4572
+ case "i3dm":
4573
+ case "b3dm":
4574
+ case "glb":
4575
+ case "gltf":
4576
+ return TILE_TYPE.SCENEGRAPH;
4577
+ default:
4578
+ return extension || TILE_TYPE.EMPTY;
4579
+ }
4580
+ }
4581
+ function validateImplicitDescriptor(descriptor) {
4582
+ if (descriptor.subdivisionScheme !== "QUADTREE" && descriptor.subdivisionScheme !== "OCTREE") {
4583
+ throw new Error(`Unsupported implicit subdivision scheme: ${descriptor.subdivisionScheme}`);
4584
+ }
4585
+ if (!Number.isInteger(descriptor.subtreeLevels) || descriptor.subtreeLevels < 1) {
4586
+ throw new Error("Implicit subtreeLevels must be a positive integer");
4587
+ }
4588
+ if (!Number.isInteger(descriptor.maximumLevel) || descriptor.maximumLevel < 0) {
4589
+ throw new Error("Implicit availableLevels must describe at least the root level");
4590
+ }
4591
+ }
4592
+
2360
4593
  // dist/tileset-3d/format-3d-tiles/tiles-3d-source.js
2361
4594
  var EMPTY_CONTENT_FORMATS = {
2362
4595
  draco: false,
@@ -2364,6 +4597,7 @@ var EMPTY_CONTENT_FORMATS = {
2364
4597
  dds: false,
2365
4598
  ktx2: false
2366
4599
  };
4600
+ var DEFAULT_MAXIMUM_CACHED_SUBTREES = 32;
2367
4601
  var Tiles3DSource = class {
2368
4602
  /** 3D Tiles format discriminator. */
2369
4603
  type = TILESET_TYPE.TILES3D;
@@ -2392,6 +4626,23 @@ var Tiles3DSource = class {
2392
4626
  /** Normalized source metadata after initialization. */
2393
4627
  metadata;
2394
4628
  queryParams = {};
4629
+ /** Final request URLs cached by unmodified tile content URL. */
4630
+ tileUrlCache = /* @__PURE__ */ new Map();
4631
+ /** Parsed subtree promises keyed by final source URL for request deduplication and LRU reuse. */
4632
+ implicitSubtreeCache = /* @__PURE__ */ new Map();
4633
+ /** URLs whose subtree resource requests have not settled. */
4634
+ pendingImplicitSubtreeUrls = /* @__PURE__ */ new Set();
4635
+ /** Maximum number of settled parsed subtrees retained by this source. */
4636
+ maximumCachedSubtrees;
4637
+ /** Mutable counters exposed as a defensive snapshot through {@link getImplicitTilingStats}. */
4638
+ implicitTilingStats = {
4639
+ requestedSubtrees: 0,
4640
+ loadedSubtrees: 0,
4641
+ cacheHits: 0,
4642
+ materializedTiles: 0
4643
+ };
4644
+ /** Whether the owning tileset has released this source. */
4645
+ destroyed = false;
2395
4646
  extensionsUsed = [];
2396
4647
  resolver;
2397
4648
  rootTileset;
@@ -2401,15 +4652,30 @@ var Tiles3DSource = class {
2401
4652
  * @param loadOptions Loader options forwarded to tile requests.
2402
4653
  */
2403
4654
  constructor(input, loadOptions = {}) {
4655
+ var _a;
2404
4656
  const request = normalizeTiles3DRequest(input);
2405
4657
  this.rootTileset = isTilesetRequest(input) ? null : input;
2406
4658
  this.tileset = this.rootTileset;
2407
4659
  this.loader = request.loader;
2408
4660
  this.url = request.url;
2409
- this.basePath = request.basePath || import_loader_utils6.path.dirname(request.url);
4661
+ this.basePath = request.basePath || path_exports.dirname(request.url);
2410
4662
  this.resolver = request.resolver;
2411
4663
  this.coreApi = request.coreApi;
2412
4664
  this.loadOptions = loadOptions;
4665
+ const maximumCachedSubtrees = Number((_a = loadOptions["3d-tiles"]) == null ? void 0 : _a.maximumCachedSubtrees);
4666
+ this.maximumCachedSubtrees = Number.isFinite(maximumCachedSubtrees) ? Math.max(0, Math.floor(maximumCachedSubtrees)) : DEFAULT_MAXIMUM_CACHED_SUBTREES;
4667
+ }
4668
+ /**
4669
+ * Releases URL and parsed-subtree caches and blocks late subtree installation.
4670
+ *
4671
+ * The injected resolver or core API remains responsible for transport-level abort signals. A
4672
+ * request that cannot be aborted may finish parsing, but it will not mutate destroyed tiles.
4673
+ */
4674
+ destroy() {
4675
+ this.destroyed = true;
4676
+ this.tileUrlCache.clear();
4677
+ this.implicitSubtreeCache.clear();
4678
+ this.pendingImplicitSubtreeUrls.clear();
2413
4679
  }
2414
4680
  /**
2415
4681
  * Fetches and validates top-level metadata, then normalizes common fields used by {@link Tileset3D}.
@@ -2429,7 +4695,9 @@ var Tiles3DSource = class {
2429
4695
  this.tileset = this.rootTileset;
2430
4696
  if (this.rootTileset.queryString) {
2431
4697
  const searchParams = new URLSearchParams(this.rootTileset.queryString);
2432
- Object.assign(this.queryParams, Object.fromEntries(searchParams.entries()));
4698
+ for (const [parameterName, parameterValue] of searchParams.entries()) {
4699
+ this.setQueryParameter(parameterName, parameterValue);
4700
+ }
2433
4701
  }
2434
4702
  this.asset = this.rootTileset.asset;
2435
4703
  if (!this.asset) {
@@ -2439,7 +4707,7 @@ var Tiles3DSource = class {
2439
4707
  throw new Error("The tileset must be 3D Tiles version either 0.0 or 1.0 or 1.1.");
2440
4708
  }
2441
4709
  if ("tilesetVersion" in this.asset) {
2442
- this.queryParams.v = this.asset.tilesetVersion;
4710
+ this.setQueryParameter("v", this.asset.tilesetVersion);
2443
4711
  }
2444
4712
  this.properties = this.rootTileset.properties;
2445
4713
  this.extras = this.rootTileset.extras;
@@ -2472,7 +4740,7 @@ var Tiles3DSource = class {
2472
4740
  return this.getMetadata().tileset;
2473
4741
  }
2474
4742
  /**
2475
- * Builds the eager runtime tile tree for a 3D Tiles subtree.
4743
+ * Builds explicit runtime headers while leaving implicit subtree references lazy.
2476
4744
  */
2477
4745
  initializeTileHeaders(tileset, tilesetJson, parentTile) {
2478
4746
  var _a;
@@ -2492,7 +4760,7 @@ var Tiles3DSource = class {
2492
4760
  const url = new URL(childTile.contentUrl);
2493
4761
  const session = url.searchParams.get("session");
2494
4762
  if (session) {
2495
- this.queryParams.session = session;
4763
+ this.setQueryParameter("session", session);
2496
4764
  }
2497
4765
  }
2498
4766
  tile.children.push(childTile);
@@ -2518,7 +4786,9 @@ var Tiles3DSource = class {
2518
4786
  ...this.loadOptions,
2519
4787
  [this.loader.id]: {
2520
4788
  ...tilesetLoaderOptions,
2521
- isTileset: tile.type === "json",
4789
+ // Content bytes, rather than URL suffixes, distinguish external tilesets from renderable
4790
+ // payloads. This is required for signed and extensionless resources.
4791
+ isTileset: "auto",
2522
4792
  assetGltfUpAxis: this.asset && this.asset.gltfUpAxis || "Y"
2523
4793
  }
2524
4794
  };
@@ -2526,17 +4796,77 @@ var Tiles3DSource = class {
2526
4796
  tile.content = content;
2527
4797
  return {
2528
4798
  loaded: true,
2529
- nestedTileset: tile.contentUrl.includes(".json") ? content : void 0
4799
+ nestedTileset: (content == null ? void 0 : content.shape) === "tileset3d" ? content : void 0
4800
+ };
4801
+ }
4802
+ /**
4803
+ * Loads, materializes, and installs exactly one implicit subtree.
4804
+ *
4805
+ * Final URLs pass through the same query inheritance and archive/custom resolver path as render
4806
+ * content. Parsed resources are deduplicated by final URL, while the pure materializer creates
4807
+ * lazy placeholders for every available child subtree instead of recursively fetching them.
4808
+ *
4809
+ * @param tile - Existing contentless subtree-root placeholder.
4810
+ * @param frameState - View state that made this request eligible; priority is consumed earlier.
4811
+ * @returns Counts describing the installed subtree.
4812
+ */
4813
+ async loadTileChildren(tile, frameState) {
4814
+ void frameState;
4815
+ const reference = tile.header.implicitSubtree;
4816
+ if (!reference) {
4817
+ return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
4818
+ }
4819
+ const subtreeUrl = this.getTileUrl(reference.subtreeUrl);
4820
+ const subtree = await this.loadImplicitSubtreeResource(subtreeUrl);
4821
+ if (this.destroyed || tile.isDestroyed()) {
4822
+ return { loaded: false, tileCount: 0, childSubtreeCount: 0 };
4823
+ }
4824
+ const materializedSubtree = materializeImplicitSubtree(subtree, {
4825
+ ...reference,
4826
+ subtreeUrl
4827
+ });
4828
+ tile.applyImplicitSubtreeHeader(materializedSubtree.root);
4829
+ const materializedTileCount = this.initializeMaterializedChildren(tile.tileset, tile, materializedSubtree.root.children);
4830
+ this.implicitTilingStats.loadedSubtrees++;
4831
+ this.implicitTilingStats.materializedTiles += materializedTileCount;
4832
+ return {
4833
+ loaded: true,
4834
+ tileCount: materializedSubtree.tileCount,
4835
+ childSubtreeCount: materializedSubtree.childSubtreeCount
4836
+ };
4837
+ }
4838
+ /**
4839
+ * Returns a snapshot of implicit-subtree request, cache, and materialization counters.
4840
+ *
4841
+ * @returns Immutable-by-convention diagnostic values for runtime inspection.
4842
+ */
4843
+ getImplicitTilingStats() {
4844
+ return {
4845
+ ...this.implicitTilingStats,
4846
+ cachedSubtrees: this.implicitSubtreeCache.size - this.pendingImplicitSubtreeUrls.size,
4847
+ pendingSubtrees: this.pendingImplicitSubtreeUrls.size
2530
4848
  };
2531
4849
  }
2532
4850
  /**
2533
4851
  * Resolves a tile content URL with source-managed query parameters.
4852
+ *
4853
+ * Existing per-resource parameters take precedence over inherited root, version, and session
4854
+ * values. Completed URLs are cached by the original tile path; {@link setQueryParameter}
4855
+ * invalidates the cache before changed source state can be observed.
4856
+ *
4857
+ * @param tilePath - Unmodified absolute content URL or data URL from the tile header.
4858
+ * @returns Content URL with any missing source parameters appended.
2534
4859
  */
2535
4860
  getTileUrl(tilePath) {
2536
4861
  if (tilePath.startsWith("data:")) {
2537
4862
  return tilePath;
2538
4863
  }
4864
+ const cachedTileUrl = this.tileUrlCache.get(tilePath);
4865
+ if (cachedTileUrl) {
4866
+ return cachedTileUrl;
4867
+ }
2539
4868
  if (!Object.keys(this.queryParams).length) {
4869
+ this.tileUrlCache.set(tilePath, tilePath);
2540
4870
  return tilePath;
2541
4871
  }
2542
4872
  const [pathWithoutQuery, existingQuery = ""] = tilePath.split("?");
@@ -2547,7 +4877,26 @@ var Tiles3DSource = class {
2547
4877
  }
2548
4878
  }
2549
4879
  const queryParams = mergedQueryParams.toString();
2550
- return queryParams ? `${pathWithoutQuery}?${queryParams}` : pathWithoutQuery;
4880
+ const tileUrl = queryParams ? `${pathWithoutQuery}?${queryParams}` : pathWithoutQuery;
4881
+ this.tileUrlCache.set(tilePath, tileUrl);
4882
+ return tileUrl;
4883
+ }
4884
+ /**
4885
+ * Updates an inherited source query parameter and invalidates derived request URLs.
4886
+ *
4887
+ * Root tokens and tileset versions normally settle during initialization. Some providers expose
4888
+ * a session parameter on a child URL, so invalidation is required to prevent URLs cached earlier
4889
+ * in header construction from retaining stale authentication state.
4890
+ *
4891
+ * @param parameterName - Query parameter name.
4892
+ * @param parameterValue - Query parameter value.
4893
+ */
4894
+ setQueryParameter(parameterName, parameterValue) {
4895
+ if (this.queryParams[parameterName] === parameterValue) {
4896
+ return;
4897
+ }
4898
+ this.queryParams[parameterName] = parameterValue;
4899
+ this.tileUrlCache.clear();
2551
4900
  }
2552
4901
  /**
2553
4902
  * Derives the default view state from the root bounding volume.
@@ -2564,10 +4913,10 @@ var Tiles3DSource = class {
2564
4913
  const { center } = rootTile.boundingVolume;
2565
4914
  let cartographicCenter;
2566
4915
  if (center && (center[0] !== 0 || center[1] !== 0 || center[2] !== 0)) {
2567
- cartographicCenter = new import_core9.Vector3();
4916
+ cartographicCenter = new import_core10.Vector3();
2568
4917
  import_geospatial7.Ellipsoid.WGS84.cartesianToCartographic(center, cartographicCenter);
2569
4918
  } else {
2570
- cartographicCenter = new import_core9.Vector3(0, 0, -import_geospatial7.Ellipsoid.WGS84.radii[0]);
4919
+ cartographicCenter = new import_core10.Vector3(0, 0, -import_geospatial7.Ellipsoid.WGS84.radii[0]);
2571
4920
  }
2572
4921
  return {
2573
4922
  asset: this.asset,
@@ -2608,11 +4957,11 @@ var Tiles3DSource = class {
2608
4957
  /**
2609
4958
  * Loads data through injected core APIs so this module stays independent from `@loaders.gl/core`.
2610
4959
  */
2611
- async loadWithCoreApi(url, options) {
4960
+ async loadWithCoreApi(url, options, loader = this.loader) {
2612
4961
  if (!this.coreApi) {
2613
4962
  throw new Error("Tiles3DSource requires an injected coreApi to load tileset data");
2614
4963
  }
2615
- return await this.coreApi.load(url, this.loader, options);
4964
+ return await this.coreApi.load(url, loader, options);
2616
4965
  }
2617
4966
  /**
2618
4967
  * Loads data through an injected resolver when present, otherwise through the injected core API.
@@ -2624,13 +4973,92 @@ var Tiles3DSource = class {
2624
4973
  return await this.loadWithCoreApi(url, options);
2625
4974
  }
2626
4975
  /**
2627
- * Loads tile content through an injected resolver when present, otherwise through the injected core API.
4976
+ * Loads an arbitrary source resource through an injected resolver or core API.
2628
4977
  */
2629
- async loadResourceData(url, options) {
4978
+ async loadResourceData(url, options, loader = this.loader) {
2630
4979
  if (this.resolver) {
2631
- return await this.resolver.loadResource(url, this.loader, options);
4980
+ return await this.resolver.loadResource(url, loader, options);
4981
+ }
4982
+ return await this.loadWithCoreApi(url, options, loader);
4983
+ }
4984
+ /**
4985
+ * Returns a parsed subtree from the LRU cache or starts one source-managed request.
4986
+ *
4987
+ * @param subtreeUrl - Final subtree URL after query inheritance.
4988
+ * @returns Parsed subtree availability data.
4989
+ */
4990
+ async loadImplicitSubtreeResource(subtreeUrl) {
4991
+ const cachedSubtree = this.implicitSubtreeCache.get(subtreeUrl);
4992
+ if (cachedSubtree) {
4993
+ this.implicitTilingStats.cacheHits++;
4994
+ this.implicitSubtreeCache.delete(subtreeUrl);
4995
+ this.implicitSubtreeCache.set(subtreeUrl, cachedSubtree);
4996
+ return await cachedSubtree;
4997
+ }
4998
+ this.implicitTilingStats.requestedSubtrees++;
4999
+ this.pendingImplicitSubtreeUrls.add(subtreeUrl);
5000
+ const loaderOptions = this.loadOptions[this.loader.id] || {};
5001
+ const subtreePromise = this.loadResourceData(subtreeUrl, {
5002
+ ...this.loadOptions,
5003
+ [this.loader.id]: {
5004
+ ...loaderOptions,
5005
+ isTileset: false,
5006
+ isSubtree: true
5007
+ }
5008
+ });
5009
+ this.implicitSubtreeCache.set(subtreeUrl, subtreePromise);
5010
+ try {
5011
+ return await subtreePromise;
5012
+ } catch (error) {
5013
+ this.implicitSubtreeCache.delete(subtreeUrl);
5014
+ throw error;
5015
+ } finally {
5016
+ this.pendingImplicitSubtreeUrls.delete(subtreeUrl);
5017
+ this.trimImplicitSubtreeCache();
5018
+ }
5019
+ }
5020
+ /**
5021
+ * Instantiates every header represented by one materialized subtree.
5022
+ *
5023
+ * @param tileset - Owning runtime tileset.
5024
+ * @param parentTile - Existing materialized parent.
5025
+ * @param childHeaders - Headers to install below the parent.
5026
+ * @returns Number of newly allocated runtime tile nodes.
5027
+ */
5028
+ initializeMaterializedChildren(tileset, parentTile, childHeaders) {
5029
+ var _a;
5030
+ let materializedTileCount = 0;
5031
+ const stack = [
5032
+ { parentTile, childHeaders }
5033
+ ];
5034
+ while (stack.length > 0) {
5035
+ const entry = stack.pop();
5036
+ for (const childHeader of entry.childHeaders) {
5037
+ const childTile = new Tile3D(tileset, childHeader, entry.parentTile);
5038
+ entry.parentTile.children.push(childTile);
5039
+ childTile.depth = entry.parentTile.depth + 1;
5040
+ tileset.stats.get("Tiles In Tileset(s)").incrementCount();
5041
+ materializedTileCount++;
5042
+ if ((_a = childHeader.children) == null ? void 0 : _a.length) {
5043
+ stack.push({ parentTile: childTile, childHeaders: childHeader.children });
5044
+ }
5045
+ }
5046
+ }
5047
+ return materializedTileCount;
5048
+ }
5049
+ /** Evicts least-recently-used settled subtree entries until the configured bound is met. */
5050
+ trimImplicitSubtreeCache() {
5051
+ if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
5052
+ return;
5053
+ }
5054
+ for (const subtreeUrl of this.implicitSubtreeCache.keys()) {
5055
+ if (!this.pendingImplicitSubtreeUrls.has(subtreeUrl)) {
5056
+ this.implicitSubtreeCache.delete(subtreeUrl);
5057
+ }
5058
+ if (this.implicitSubtreeCache.size <= this.maximumCachedSubtrees) {
5059
+ break;
5060
+ }
2632
5061
  }
2633
- return await this.loadWithCoreApi(url, options);
2634
5062
  }
2635
5063
  };
2636
5064
  function isTilesetRequest(input) {
@@ -2640,22 +5068,21 @@ function normalizeTiles3DRequest(input) {
2640
5068
  if (isTilesetRequest(input)) {
2641
5069
  return {
2642
5070
  ...input,
2643
- basePath: input.basePath || import_loader_utils6.path.dirname(input.url)
5071
+ basePath: input.basePath || path_exports.dirname(input.url)
2644
5072
  };
2645
5073
  }
2646
5074
  return {
2647
5075
  url: input.url,
2648
5076
  loader: input.loader,
2649
- basePath: input.basePath || import_loader_utils6.path.dirname(input.url),
5077
+ basePath: input.basePath || path_exports.dirname(input.url),
2650
5078
  resolver: input.resolver,
2651
5079
  coreApi: input.coreApi
2652
5080
  };
2653
5081
  }
2654
5082
 
2655
5083
  // dist/tileset-3d/format-i3s/i3s-source.js
2656
- var import_loader_utils7 = require("@loaders.gl/loader-utils");
2657
5084
  var import_geospatial8 = require("@math.gl/geospatial");
2658
- var import_core10 = require("@math.gl/core");
5085
+ var import_core11 = require("@math.gl/core");
2659
5086
 
2660
5087
  // dist/tileset-3d/format-i3s/i3s-pending-tiles-register.js
2661
5088
  var I3SPendingTilesRegister = class {
@@ -2829,7 +5256,7 @@ var I3STilesetTraverser = class extends TilesetTraverser {
2829
5256
  tile.children.push(childTile);
2830
5257
  const frameState = this._tileManager.find(childTile.id).frameState;
2831
5258
  this.updateTile(childTile, frameState);
2832
- if (this._frameNumber === frameState.frameNumber && (this.traversalFinished(frameState) || new Date().getTime() - this.lastUpdate > this.updateDebounceTime)) {
5259
+ if (this._frameNumber === frameState.frameNumber && (this.traversalFinished(frameState) || (/* @__PURE__ */ new Date()).getTime() - this.lastUpdate > this.updateDebounceTime)) {
2833
5260
  this.executeTraversal(childTile, frameState);
2834
5261
  }
2835
5262
  }
@@ -2878,7 +5305,7 @@ var I3SSource = class {
2878
5305
  this.tileset = this.rootTileset;
2879
5306
  this.loader = request.loader;
2880
5307
  this.url = request.url;
2881
- this.basePath = request.basePath || import_loader_utils7.path.dirname(request.url);
5308
+ this.basePath = request.basePath || path_exports.dirname(request.url);
2882
5309
  this.resolver = request.resolver;
2883
5310
  this.coreApi = request.coreApi;
2884
5311
  this.loadOptions = loadOptions;
@@ -3019,8 +5446,8 @@ var I3SSource = class {
3019
5446
  const fullExtent = metadata.tileset.fullExtent;
3020
5447
  if (fullExtent) {
3021
5448
  const { xmin, xmax, ymin, ymax, zmin, zmax } = fullExtent;
3022
- const cartographicCenter = new import_core10.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
3023
- const cartesianCenter = new import_core10.Vector3();
5449
+ const cartographicCenter = new import_core11.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, zmin + (zmax - zmin) / 2);
5450
+ const cartesianCenter = new import_core11.Vector3();
3024
5451
  import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
3025
5452
  return {
3026
5453
  cartographicCenter,
@@ -3031,8 +5458,8 @@ var I3SSource = class {
3031
5458
  const extent = (_a = metadata.tileset.store) == null ? void 0 : _a.extent;
3032
5459
  if (extent) {
3033
5460
  const [xmin, ymin, xmax, ymax] = extent;
3034
- const cartographicCenter = new import_core10.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
3035
- const cartesianCenter = new import_core10.Vector3();
5461
+ const cartographicCenter = new import_core11.Vector3(xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2, 0);
5462
+ const cartesianCenter = new import_core11.Vector3();
3036
5463
  import_geospatial8.Ellipsoid.WGS84.cartographicToCartesian(cartographicCenter, cartesianCenter);
3037
5464
  return {
3038
5465
  cartographicCenter,
@@ -3042,7 +5469,7 @@ var I3SSource = class {
3042
5469
  }
3043
5470
  console.warn("Extent is not defined in the tileset header");
3044
5471
  return {
3045
- cartographicCenter: new import_core10.Vector3(),
5472
+ cartographicCenter: new import_core11.Vector3(),
3046
5473
  zoom: 1
3047
5474
  };
3048
5475
  }
@@ -3106,13 +5533,13 @@ function normalizeI3SRequest(input) {
3106
5533
  if (isTilesetRequest2(input)) {
3107
5534
  return {
3108
5535
  ...input,
3109
- basePath: input.basePath || import_loader_utils7.path.dirname(input.url)
5536
+ basePath: input.basePath || path_exports.dirname(input.url)
3110
5537
  };
3111
5538
  }
3112
5539
  return {
3113
5540
  url: input.url,
3114
5541
  loader: input.loader,
3115
- basePath: input.basePath || import_loader_utils7.path.dirname(input.url),
5542
+ basePath: input.basePath || path_exports.dirname(input.url),
3116
5543
  resolver: input.resolver,
3117
5544
  coreApi: input.coreApi
3118
5545
  };
@@ -3294,8 +5721,7 @@ function normalizeTileError(error) {
3294
5721
  }
3295
5722
 
3296
5723
  // dist/tileset-2d/tileset-2d.js
3297
- var import_loader_utils8 = require("@loaders.gl/loader-utils");
3298
- var import_stats2 = require("@probe.gl/stats");
5724
+ var import_stats4 = require("@probe.gl/stats");
3299
5725
  var STRATEGY_NEVER = "never";
3300
5726
  var STRATEGY_REPLACE = "no-overlap";
3301
5727
  var STRATEGY_DEFAULT = "best-available";
@@ -3320,7 +5746,7 @@ var DEFAULT_TILESET2D_PROPS = {
3320
5746
  },
3321
5747
  tileSource: null
3322
5748
  };
3323
- var Tileset2D = class {
5749
+ var Tileset2D = class _Tileset2D {
3324
5750
  /** Live counters describing shared tileset state. */
3325
5751
  stats;
3326
5752
  /** Cached metadata returned by the backing TileSource, if any. */
@@ -3342,7 +5768,7 @@ var Tileset2D = class {
3342
5768
  _lastTileContext = null;
3343
5769
  /** Creates a tileset from either `getTileData` or a loaders.gl `TileSource`. */
3344
5770
  constructor(opts) {
3345
- this.stats = new import_stats2.Stats({
5771
+ this.stats = new import_stats4.Stats({
3346
5772
  id: "Tileset2D",
3347
5773
  stats: [
3348
5774
  { name: "Tiles In Cache" },
@@ -3360,7 +5786,7 @@ var Tileset2D = class {
3360
5786
  getTileData: opts.getTileData || (() => null),
3361
5787
  tileSource: opts.tileSource
3362
5788
  };
3363
- this._requestScheduler = new import_loader_utils8.RequestScheduler({
5789
+ this._requestScheduler = new RequestScheduler({
3364
5790
  throttleRequests: this.opts.maxRequests > 0 || this.opts.debounceTime > 0,
3365
5791
  maxRequests: this.opts.maxRequests,
3366
5792
  debounceTime: this.opts.debounceTime
@@ -3382,7 +5808,7 @@ var Tileset2D = class {
3382
5808
  }
3383
5809
  /** Convenience factory for wrapping a loaders.gl `TileSource`. */
3384
5810
  static fromTileSource(tileSource, opts = {}) {
3385
- return new Tileset2D({ ...opts, tileSource });
5811
+ return new _Tileset2D({ ...opts, tileSource });
3386
5812
  }
3387
5813
  /** All tiles currently present in the shared cache. */
3388
5814
  get tiles() {
@@ -3453,7 +5879,7 @@ var Tileset2D = class {
3453
5879
  const previousDebounceTime = this.opts.debounceTime;
3454
5880
  this._applyResolvedOptions();
3455
5881
  if (previousMaxRequests !== this.opts.maxRequests || previousDebounceTime !== this.opts.debounceTime) {
3456
- this._requestScheduler = new import_loader_utils8.RequestScheduler({
5882
+ this._requestScheduler = new RequestScheduler({
3457
5883
  throttleRequests: this.opts.maxRequests > 0 || this.opts.debounceTime > 0,
3458
5884
  maxRequests: this.opts.maxRequests,
3459
5885
  debounceTime: this.opts.debounceTime
@@ -3823,7 +6249,7 @@ var DEFAULT_IMAGESET_PROPS = {
3823
6249
  getImage: async () => null,
3824
6250
  debounceTime: 0
3825
6251
  };
3826
- var ImageSet = class {
6252
+ var ImageSet = class _ImageSet {
3827
6253
  /** Cached metadata returned by the backing source, if any. */
3828
6254
  metadata = null;
3829
6255
  _opts;
@@ -3844,7 +6270,7 @@ var ImageSet = class {
3844
6270
  }
3845
6271
  /** Convenience factory for wrapping a loaders.gl {@link ImageSource}. */
3846
6272
  static fromImageSource(imageSource, opts = {}) {
3847
- return new ImageSet({ ...opts, imageSource });
6273
+ return new _ImageSet({ ...opts, imageSource });
3848
6274
  }
3849
6275
  /** Whether the manager has no requests in flight and a current image. */
3850
6276
  get isLoaded() {
@@ -3956,7 +6382,7 @@ var ImageSet = class {
3956
6382
  }
3957
6383
  this._emitUpdate();
3958
6384
  } catch (error) {
3959
- if (isAbortError(error)) {
6385
+ if (isAbortError2(error)) {
3960
6386
  return;
3961
6387
  }
3962
6388
  const normalizedError = normalizeImageSetError(error, "Image request failed");
@@ -4040,7 +6466,7 @@ var ImageSet = class {
4040
6466
  this._abortController = null;
4041
6467
  }
4042
6468
  };
4043
- function isAbortError(error) {
6469
+ function isAbortError2(error) {
4044
6470
  return error instanceof Error && error.name === "AbortError";
4045
6471
  }
4046
6472
  function normalizeImageSetError(error, message) {
@@ -4071,7 +6497,7 @@ var DEFAULT_RASTERSET_PROPS = {
4071
6497
  debounceTime: 0,
4072
6498
  shouldRefetch: () => true
4073
6499
  };
4074
- var RasterSet = class {
6500
+ var RasterSet = class _RasterSet {
4075
6501
  /** Cached metadata returned by the backing source, if any. */
4076
6502
  metadata = null;
4077
6503
  _opts;
@@ -4092,7 +6518,7 @@ var RasterSet = class {
4092
6518
  }
4093
6519
  /** Convenience factory for wrapping a loaders.gl {@link RasterSource}. */
4094
6520
  static fromRasterSource(rasterSource, opts = {}) {
4095
- return new RasterSet({ ...opts, rasterSource });
6521
+ return new _RasterSet({ ...opts, rasterSource });
4096
6522
  }
4097
6523
  /** Whether the manager has no requests in flight and a current raster. */
4098
6524
  get isLoaded() {
@@ -4215,7 +6641,7 @@ var RasterSet = class {
4215
6641
  }
4216
6642
  this._emitUpdate();
4217
6643
  } catch (error) {
4218
- if (isAbortError2(error)) {
6644
+ if (isAbortError3(error)) {
4219
6645
  return;
4220
6646
  }
4221
6647
  const normalizedError = normalizeRasterSetError(error, "Raster request failed");
@@ -4307,13 +6733,13 @@ function normalizeRasterSetError(error, message) {
4307
6733
  }
4308
6734
  return new Error(message);
4309
6735
  }
4310
- function isAbortError2(error) {
6736
+ function isAbortError3(error) {
4311
6737
  return error instanceof Error && error.name === "AbortError";
4312
6738
  }
4313
6739
 
4314
6740
  // dist/point-cloud/point-cloud-tileset.js
4315
- var import_core11 = require("@math.gl/core");
4316
- var import_culling5 = require("@math.gl/culling");
6741
+ var import_core12 = require("@math.gl/core");
6742
+ var import_culling6 = require("@math.gl/culling");
4317
6743
 
4318
6744
  // dist/point-cloud/point-cloud-tile.js
4319
6745
  var PointCloudTile = class {
@@ -4381,7 +6807,7 @@ var PointCloudTile = class {
4381
6807
  };
4382
6808
 
4383
6809
  // dist/point-cloud/point-cloud-tileset.js
4384
- var DEFAULT_PROPS3 = {
6810
+ var DEFAULT_PROPS5 = {
4385
6811
  debounceTime: 0,
4386
6812
  minimumNodePixelSize: 150,
4387
6813
  maximumScreenSpaceError: 1,
@@ -4411,7 +6837,7 @@ var PointCloudTileset = class {
4411
6837
  tilesMap = /* @__PURE__ */ new Map();
4412
6838
  constructor(dataSource, options = {}) {
4413
6839
  this.dataSource = dataSource;
4414
- this.options = { ...DEFAULT_PROPS3, ...options };
6840
+ this.options = { ...DEFAULT_PROPS5, ...options };
4415
6841
  this.tilesetInitializationPromise = this.initialize();
4416
6842
  }
4417
6843
  tilesetInitializationPromise;
@@ -4474,7 +6900,7 @@ var PointCloudTileset = class {
4474
6900
  const viewState = await ((_b = (_a = this.dataSource).getViewState) == null ? void 0 : _b.call(_a));
4475
6901
  this.boundingVolume = (viewState == null ? void 0 : viewState.boundingVolume) || this.root.boundingVolume;
4476
6902
  const center = (viewState == null ? void 0 : viewState.cartographicCenter) || this.root.boundingVolume.center;
4477
- this.cartographicCenter = new import_core11.Vector3(center);
6903
+ this.cartographicCenter = new import_core12.Vector3(center);
4478
6904
  this.zoom = (viewState == null ? void 0 : viewState.zoom) || this.estimateZoom(this.boundingVolume);
4479
6905
  }
4480
6906
  /**
@@ -4486,9 +6912,9 @@ var PointCloudTileset = class {
4486
6912
  this.lastUpdatedViewports = viewports;
4487
6913
  }
4488
6914
  if (!this.updatePromise) {
4489
- this.updatePromise = new Promise((resolve, reject) => {
6915
+ this.updatePromise = new Promise((resolve2, reject) => {
4490
6916
  setTimeout(() => {
4491
- this.doSelectTiles().then(() => resolve(this.frameNumberValue)).catch(reject).finally(() => {
6917
+ this.doSelectTiles().then(() => resolve2(this.frameNumberValue)).catch(reject).finally(() => {
4492
6918
  this.updatePromise = null;
4493
6919
  });
4494
6920
  }, this.options.debounceTime);
@@ -4617,7 +7043,7 @@ var PointCloudTileset = class {
4617
7043
  * Checks whether a common-space bounding sphere intersects a culling volume.
4618
7044
  */
4619
7045
  isBoundingSphereInsideCullingVolume(boundingSphere, cullingVolume2) {
4620
- return cullingVolume2.computeVisibilityWithPlaneMask(boundingSphere, import_culling5.CullingVolume.MASK_INDETERMINATE) !== import_culling5.CullingVolume.MASK_OUTSIDE;
7046
+ return cullingVolume2.computeVisibilityWithPlaneMask(boundingSphere, import_culling6.CullingVolume.MASK_INDETERMINATE) !== import_culling6.CullingVolume.MASK_OUTSIDE;
4621
7047
  }
4622
7048
  /**
4623
7049
  * Checks a projected screen-space footprint against the viewport rectangle.
@@ -4813,9 +7239,9 @@ var PointCloudTileset = class {
4813
7239
  if (!plane) {
4814
7240
  return null;
4815
7241
  }
4816
- planes.push(new import_culling5.Plane(plane.normal, -plane.distance));
7242
+ planes.push(new import_culling6.Plane(plane.normal, -plane.distance));
4817
7243
  }
4818
- return new import_culling5.CullingVolume(planes);
7244
+ return new import_culling6.CullingVolume(planes);
4819
7245
  }
4820
7246
  /**
4821
7247
  * Approximate a tile AABB by a common-space bounding sphere for frustum culling.
@@ -4852,7 +7278,7 @@ var PointCloudTileset = class {
4852
7278
  if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(minZ) || !Number.isFinite(maxX) || !Number.isFinite(maxY) || !Number.isFinite(maxZ)) {
4853
7279
  return null;
4854
7280
  }
4855
- return new import_culling5.BoundingSphere().fromCornerPoints([minX, minY, minZ], [maxX, maxY, maxZ]);
7281
+ return new import_culling6.BoundingSphere().fromCornerPoints([minX, minY, minZ], [maxX, maxY, maxZ]);
4856
7282
  }
4857
7283
  };
4858
7284
  //# sourceMappingURL=index.cjs.map