@idetik/core 0.4.0 → 0.4.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.
package/dist/index.d.ts CHANGED
@@ -139,11 +139,51 @@ type ChunkLoader = {
139
139
  loadChunkData(chunk: Chunk, signal: AbortSignal): Promise<void>;
140
140
  };
141
141
 
142
+ declare const ALL_CATEGORIES: readonly ["fallbackVisible", "prefetchTime", "visibleCurrent", "fallbackBackground", "prefetchSpace"];
143
+ type PriorityCategory = (typeof ALL_CATEGORIES)[number];
144
+ type ImageSourcePolicyConfig = {
145
+ profile?: string;
146
+ prefetch: {
147
+ x: number;
148
+ y: number;
149
+ z?: number;
150
+ t?: number;
151
+ };
152
+ priorityOrder: PriorityCategory[];
153
+ lod?: {
154
+ min?: number;
155
+ max?: number;
156
+ bias?: number;
157
+ };
158
+ };
159
+ type ImageSourcePolicy = Readonly<{
160
+ profile: string;
161
+ prefetch: {
162
+ x: number;
163
+ y: number;
164
+ z: number;
165
+ t: number;
166
+ };
167
+ priorityOrder: readonly PriorityCategory[];
168
+ priorityMap: Readonly<Record<PriorityCategory, number>>;
169
+ lod: {
170
+ min: number;
171
+ max: number;
172
+ bias: number;
173
+ };
174
+ }>;
175
+ declare function createImageSourcePolicy(config: ImageSourcePolicyConfig): ImageSourcePolicy;
176
+ declare function createExplorationPolicy(overrides?: Partial<ImageSourcePolicyConfig>): ImageSourcePolicy;
177
+ declare function createPlaybackPolicy(overrides?: Partial<ImageSourcePolicyConfig>): ImageSourcePolicy;
178
+ declare function createNoPrefetchPolicy(overrides?: Partial<ImageSourcePolicyConfig>): ImageSourcePolicy;
179
+
142
180
  declare class ChunkStore {
143
181
  private readonly chunks_;
144
182
  private readonly loader_;
145
183
  private readonly lowestResLOD_;
146
184
  private readonly dimensions_;
185
+ private readonly views_;
186
+ private hasHadViews_;
147
187
  constructor(loader: ChunkLoader);
148
188
  getChunksAtTime(timeIndex: number): Chunk[];
149
189
  getTimeIndex(sliceCoords: SliceCoordinates): number;
@@ -151,6 +191,12 @@ declare class ChunkStore {
151
191
  get dimensions(): SourceDimensionMap;
152
192
  getLowestResLOD(): number;
153
193
  loadChunkData(chunk: Chunk, signal: AbortSignal): Promise<void>;
194
+ createView(policy: ImageSourcePolicy): ChunkStoreView;
195
+ removeView(view: ChunkStoreView): void;
196
+ get views(): ReadonlyArray<ChunkStoreView>;
197
+ canDispose(): boolean;
198
+ updateAndCollectChunkChanges(): Set<Chunk>;
199
+ private aggregateChunkViewStates;
154
200
  private validateXYScaleRatios;
155
201
  private getAndValidateTimeDimension;
156
202
  private getAndValidateChannelDimension;
@@ -472,44 +518,6 @@ declare class Viewport {
472
518
  }
473
519
  declare function parseViewportConfigs(viewportConfigs: ViewportConfig[], canvas: HTMLCanvasElement, context: IdetikContext): Viewport[];
474
520
 
475
- declare const ALL_CATEGORIES: readonly ["fallbackVisible", "prefetchTime", "visibleCurrent", "fallbackBackground", "prefetchSpace"];
476
- type PriorityCategory = (typeof ALL_CATEGORIES)[number];
477
- type ImageSourcePolicyConfig = {
478
- profile?: string;
479
- prefetch: {
480
- x: number;
481
- y: number;
482
- z?: number;
483
- t?: number;
484
- };
485
- priorityOrder: PriorityCategory[];
486
- lod?: {
487
- min?: number;
488
- max?: number;
489
- bias?: number;
490
- };
491
- };
492
- type ImageSourcePolicy = Readonly<{
493
- profile: string;
494
- prefetch: {
495
- x: number;
496
- y: number;
497
- z: number;
498
- t: number;
499
- };
500
- priorityOrder: readonly PriorityCategory[];
501
- priorityMap: Readonly<Record<PriorityCategory, number>>;
502
- lod: {
503
- min: number;
504
- max: number;
505
- bias: number;
506
- };
507
- }>;
508
- declare function createImageSourcePolicy(config: ImageSourcePolicyConfig): ImageSourcePolicy;
509
- declare function createExplorationPolicy(overrides?: Partial<ImageSourcePolicyConfig>): ImageSourcePolicy;
510
- declare function createPlaybackPolicy(overrides?: Partial<ImageSourcePolicyConfig>): ImageSourcePolicy;
511
- declare function createNoPrefetchPolicy(overrides?: Partial<ImageSourcePolicyConfig>): ImageSourcePolicy;
512
-
513
521
  declare class ChunkStoreView {
514
522
  private readonly store_;
515
523
  private policy_;
@@ -521,13 +529,16 @@ declare class ChunkStoreView {
521
529
  private sourceMaxSquareDistance2D_;
522
530
  private readonly chunkViewStates_;
523
531
  constructor(store: ChunkStore, policy: ImageSourcePolicy);
524
- get store(): ChunkStore;
525
532
  get chunkViewStates(): ReadonlyMap<Chunk, ChunkViewState>;
533
+ getChunksAtTime(timeIndex: number): Chunk[];
534
+ getTimeIndex(sliceCoords: SliceCoordinates): number;
535
+ get lodCount(): number;
526
536
  getChunksToRender(sliceCoords: SliceCoordinates): Chunk[];
527
537
  updateChunkStates(sliceCoords: SliceCoordinates, viewport: Viewport): void;
528
538
  allVisibleLowestLODLoaded(sliceCoords: SliceCoordinates): boolean;
529
539
  get currentLOD(): number;
530
540
  maybeForgetChunk(chunk: Chunk): void;
541
+ dispose(): void;
531
542
  setImageSourcePolicy(newPolicy: ImageSourcePolicy, key: symbol): void;
532
543
  private setLOD;
533
544
  private updateChunkViewStates;
@@ -546,15 +557,10 @@ declare class ChunkStoreView {
546
557
  declare class ChunkManager {
547
558
  private readonly stores_;
548
559
  private readonly pendingStores_;
549
- private readonly views_;
550
560
  private readonly queue_;
551
561
  addView(source: ChunkSource, policy: ImageSourcePolicy): Promise<ChunkStoreView>;
552
- removeView(view: ChunkStoreView): void;
553
- private addSource;
554
- private getSourceForStore;
562
+ private getOrCreateStore;
555
563
  update(): void;
556
- private updateAndCollectChunkChanges;
557
- private aggregateChunkViewStates;
558
564
  }
559
565
 
560
566
  type Overlay = {
@@ -640,7 +646,6 @@ declare class Idetik {
640
646
  start(): this;
641
647
  private animate;
642
648
  stop(): void;
643
- private updateSize;
644
649
  }
645
650
 
646
651
  declare abstract class Renderer {
@@ -810,7 +815,7 @@ declare class ChunkedImageLayer extends Layer implements ChannelsEnabled {
810
815
  private readonly wireframeColors_;
811
816
  constructor({ source, sliceCoords, policy, channelProps, onPickValue, ...layerOptions }: ChunkedImageLayerProps);
812
817
  onAttached(context: IdetikContext): Promise<void>;
813
- onDetached(context: IdetikContext): void;
818
+ onDetached(_context: IdetikContext): void;
814
819
  update(context?: RenderContext): void;
815
820
  private updateChunks;
816
821
  get lastPresentationTimeCoord(): number | undefined;
package/dist/index.js CHANGED
@@ -2009,136 +2009,11 @@ function xQ(g, A) {
2009
2009
  function jI(g, A, I = 1e-6) {
2010
2010
  return Math.abs(g - A) <= I;
2011
2011
  }
2012
- class bQ {
2013
- chunks_;
2014
- loader_;
2015
- lowestResLOD_;
2016
- dimensions_;
2017
- constructor(A) {
2018
- this.loader_ = A, this.dimensions_ = this.loader_.getSourceDimensionMap(), this.lowestResLOD_ = this.dimensions_.numLods - 1, this.validateXYScaleRatios();
2019
- const { size: I } = this.getAndValidateTimeDimension(), { size: B } = this.getAndValidateChannelDimension();
2020
- this.chunks_ = Array.from({ length: I }, () => []);
2021
- for (let Q = 0; Q < I; ++Q) {
2022
- const C = this.chunks_[Q];
2023
- for (let E = 0; E < this.dimensions_.numLods; ++E) {
2024
- const i = this.dimensions_.x.lods[E], o = this.dimensions_.y.lods[E], a = this.dimensions_.z?.lods[E], D = i.chunkSize, h = o.chunkSize, y = a?.chunkSize ?? 1, t = Math.ceil(i.size / D), F = Math.ceil(o.size / h), r = Math.ceil((a?.size ?? 1) / y);
2025
- for (let S = 0; S < B; ++S)
2026
- for (let e = 0; e < t; ++e) {
2027
- const c = i.translation + e * i.chunkSize * i.scale;
2028
- for (let N = 0; N < F; ++N) {
2029
- const M = o.translation + N * o.chunkSize * o.scale;
2030
- for (let Y = 0; Y < r; ++Y) {
2031
- const J = a !== void 0 ? a.translation + Y * y * a.scale : 0;
2032
- C.push({
2033
- state: "unloaded",
2034
- lod: E,
2035
- visible: !1,
2036
- prefetch: !1,
2037
- priority: null,
2038
- orderKey: null,
2039
- shape: {
2040
- x: Math.min(D, i.size - e * D),
2041
- y: Math.min(h, o.size - N * h),
2042
- z: Math.min(y, (a?.size ?? 1) - Y * y),
2043
- c: 1
2044
- },
2045
- rowAlignmentBytes: 1,
2046
- chunkIndex: { x: e, y: N, z: Y, c: S, t: Q },
2047
- scale: {
2048
- x: i.scale,
2049
- y: o.scale,
2050
- z: a?.scale ?? 1
2051
- },
2052
- offset: {
2053
- x: c,
2054
- y: M,
2055
- z: J
2056
- }
2057
- });
2058
- }
2059
- }
2060
- }
2061
- }
2062
- }
2063
- }
2064
- getChunksAtTime(A) {
2065
- return this.chunks_[A];
2066
- }
2067
- getTimeIndex(A) {
2068
- return A.t === void 0 || this.dimensions_.t === void 0 ? 0 : xQ(this.dimensions_.t.lods[0], A.t);
2069
- }
2070
- get lodCount() {
2071
- return this.lowestResLOD_ + 1;
2072
- }
2073
- get dimensions() {
2074
- return this.dimensions_;
2075
- }
2076
- getLowestResLOD() {
2077
- return this.lowestResLOD_;
2078
- }
2079
- loadChunkData(A, I) {
2080
- return this.loader_.loadChunkData(A, I);
2081
- }
2082
- validateXYScaleRatios() {
2083
- const A = this.dimensions_.x, I = this.dimensions_.y;
2084
- for (let B = 1; B < this.dimensions_.numLods; B++) {
2085
- const Q = A.lods[B].scale / A.lods[B - 1].scale, C = I.lods[B].scale / I.lods[B - 1].scale;
2086
- if (!jI(Q, 2, 0.02) || !jI(C, 2, 0.02))
2087
- throw new Error(
2088
- `Invalid downsampling factor between levels ${B - 1} → ${B}: expected (2× in X and Y), but got (${Q.toFixed(2)}×, ${C.toFixed(2)}×) from scale [${A.lods[B - 1].scale}, ${I.lods[B - 1].scale}] → [${A.lods[B].scale}, ${I.lods[B].scale}]`
2089
- );
2090
- }
2091
- }
2092
- getAndValidateTimeDimension() {
2093
- for (let A = 0; A < this.dimensions_.numLods; ++A) {
2094
- const I = this.dimensions_.t?.lods[A];
2095
- if (!I) continue;
2096
- if (I.chunkSize !== 1)
2097
- throw new Error(
2098
- `ChunkStore only supports a chunk size of 1 in t. Found ${I.chunkSize} at LOD ${A}`
2099
- );
2100
- const B = this.dimensions_.t?.lods[A - 1];
2101
- if (B && I.size !== B.size)
2102
- throw new Error(
2103
- `ChunkStore does not support downsampling in t. Found ${B.size} at LOD ${A - 1} → ${I.size} at LOD ${A}`
2104
- );
2105
- }
2106
- return {
2107
- size: this.dimensions_.t?.lods[0].size ?? 1
2108
- };
2109
- }
2110
- getAndValidateChannelDimension() {
2111
- for (let A = 0; A < this.dimensions_.numLods; ++A) {
2112
- const I = this.dimensions_.c?.lods[A];
2113
- if (!I) continue;
2114
- if (I.chunkSize !== 1)
2115
- throw new Error(
2116
- `ChunkStore only supports a chunk size of 1 in c. Found ${I.chunkSize} at LOD ${A}`
2117
- );
2118
- if (I.scale !== 1)
2119
- throw new Error(
2120
- `ChunkStore does not support scale in c. Found ${I.scale} at LOD ${A}`
2121
- );
2122
- if (I.translation !== 0)
2123
- throw new Error(
2124
- `ChunkStore does not support translation in c. Found ${I.translation} at LOD ${A}`
2125
- );
2126
- const B = this.dimensions_.c?.lods[A - 1];
2127
- if (B && I.size !== B.size)
2128
- throw new Error(
2129
- `ChunkStore does not support downsampling in c. Found ${B.size} at LOD ${A - 1} → ${I.size} at LOD ${A}`
2130
- );
2131
- }
2132
- return {
2133
- size: this.dimensions_.c?.lods[0].size ?? 1
2134
- };
2135
- }
2136
- }
2137
2012
  function OA(g, A, I) {
2138
2013
  return Math.max(A, Math.min(I, g));
2139
2014
  }
2140
2015
  const FB = Symbol("INTERNAL_POLICY_KEY");
2141
- class ZQ {
2016
+ class bQ {
2142
2017
  store_;
2143
2018
  policy_;
2144
2019
  policyChanged_ = !1;
@@ -2159,12 +2034,19 @@ class ZQ {
2159
2034
  u(Q.size * Q.scale, C.size * C.scale)
2160
2035
  );
2161
2036
  }
2162
- get store() {
2163
- return this.store_;
2164
- }
2165
2037
  get chunkViewStates() {
2166
2038
  return this.chunkViewStates_;
2167
2039
  }
2040
+ // forwarding methods for chunk stats overlay while keeping store_ private
2041
+ getChunksAtTime(A) {
2042
+ return this.store_.getChunksAtTime(A);
2043
+ }
2044
+ getTimeIndex(A) {
2045
+ return this.store_.getTimeIndex(A);
2046
+ }
2047
+ get lodCount() {
2048
+ return this.store_.lodCount;
2049
+ }
2168
2050
  getChunksToRender(A) {
2169
2051
  const I = this.store_.getTimeIndex(A), B = this.store_.getChunksAtTime(I), Q = B.filter(
2170
2052
  (i) => i.lod === this.currentLOD_ && this.chunkViewStates_.get(i)?.visible === !0 && i.state === "loaded"
@@ -2195,6 +2077,9 @@ class ZQ {
2195
2077
  const I = this.chunkViewStates_.get(A);
2196
2078
  I && (I.visible || I.prefetch || I.priority !== null) || this.chunkViewStates_.delete(A);
2197
2079
  }
2080
+ dispose() {
2081
+ this.store_.removeView(this);
2082
+ }
2198
2083
  setImageSourcePolicy(A, I) {
2199
2084
  if (I !== FB)
2200
2085
  throw new Error("Unauthorized policy mutation");
@@ -2229,7 +2114,7 @@ class ZQ {
2229
2114
  q(I.min[0], I.min[1], E),
2230
2115
  q(I.max[0], I.max[1], i)
2231
2116
  );
2232
- this.chunkViewStates_.forEach(PQ), this.updateChunksAtTimeIndex(
2117
+ this.chunkViewStates_.forEach(ZQ), this.updateChunksAtTimeIndex(
2233
2118
  B,
2234
2119
  A,
2235
2120
  o,
@@ -2346,80 +2231,118 @@ class ZQ {
2346
2231
  return Q * Q + C * C;
2347
2232
  }
2348
2233
  }
2349
- function PQ(g) {
2234
+ function ZQ(g) {
2350
2235
  g.visible = !1, g.prefetch = !1, g.priority = null, g.orderKey = null;
2351
2236
  }
2352
- class OQ {
2353
- stores_ = /* @__PURE__ */ new Map();
2354
- pendingStores_ = /* @__PURE__ */ new Map();
2355
- views_ = /* @__PURE__ */ new Map();
2356
- queue_ = new WQ();
2357
- async addView(A, I) {
2358
- const B = await this.addSource(A), Q = new ZQ(B, I);
2359
- return this.views_.set(B, (this.views_.get(B) ?? []).concat(Q)), Q;
2237
+ class PQ {
2238
+ chunks_;
2239
+ loader_;
2240
+ lowestResLOD_;
2241
+ dimensions_;
2242
+ views_ = [];
2243
+ hasHadViews_ = !1;
2244
+ constructor(A) {
2245
+ this.loader_ = A, this.dimensions_ = this.loader_.getSourceDimensionMap(), this.lowestResLOD_ = this.dimensions_.numLods - 1, this.validateXYScaleRatios();
2246
+ const { size: I } = this.getAndValidateTimeDimension(), { size: B } = this.getAndValidateChannelDimension();
2247
+ this.chunks_ = Array.from({ length: I }, () => []);
2248
+ for (let Q = 0; Q < I; ++Q) {
2249
+ const C = this.chunks_[Q];
2250
+ for (let E = 0; E < this.dimensions_.numLods; ++E) {
2251
+ const i = this.dimensions_.x.lods[E], o = this.dimensions_.y.lods[E], a = this.dimensions_.z?.lods[E], D = i.chunkSize, h = o.chunkSize, y = a?.chunkSize ?? 1, t = Math.ceil(i.size / D), F = Math.ceil(o.size / h), r = Math.ceil((a?.size ?? 1) / y);
2252
+ for (let S = 0; S < B; ++S)
2253
+ for (let e = 0; e < t; ++e) {
2254
+ const c = i.translation + e * i.chunkSize * i.scale;
2255
+ for (let N = 0; N < F; ++N) {
2256
+ const M = o.translation + N * o.chunkSize * o.scale;
2257
+ for (let Y = 0; Y < r; ++Y) {
2258
+ const J = a !== void 0 ? a.translation + Y * y * a.scale : 0;
2259
+ C.push({
2260
+ state: "unloaded",
2261
+ lod: E,
2262
+ visible: !1,
2263
+ prefetch: !1,
2264
+ priority: null,
2265
+ orderKey: null,
2266
+ shape: {
2267
+ x: Math.min(D, i.size - e * D),
2268
+ y: Math.min(h, o.size - N * h),
2269
+ z: Math.min(y, (a?.size ?? 1) - Y * y),
2270
+ c: 1
2271
+ },
2272
+ rowAlignmentBytes: 1,
2273
+ chunkIndex: { x: e, y: N, z: Y, c: S, t: Q },
2274
+ scale: {
2275
+ x: i.scale,
2276
+ y: o.scale,
2277
+ z: a?.scale ?? 1
2278
+ },
2279
+ offset: {
2280
+ x: c,
2281
+ y: M,
2282
+ z: J
2283
+ }
2284
+ });
2285
+ }
2286
+ }
2287
+ }
2288
+ }
2289
+ }
2290
+ }
2291
+ getChunksAtTime(A) {
2292
+ return this.chunks_[A];
2293
+ }
2294
+ getTimeIndex(A) {
2295
+ return A.t === void 0 || this.dimensions_.t === void 0 ? 0 : xQ(this.dimensions_.t.lods[0], A.t);
2296
+ }
2297
+ get lodCount() {
2298
+ return this.lowestResLOD_ + 1;
2299
+ }
2300
+ get dimensions() {
2301
+ return this.dimensions_;
2302
+ }
2303
+ getLowestResLOD() {
2304
+ return this.lowestResLOD_;
2305
+ }
2306
+ loadChunkData(A, I) {
2307
+ return this.loader_.loadChunkData(A, I);
2308
+ }
2309
+ createView(A) {
2310
+ const I = new bQ(this, A);
2311
+ return this.views_.push(I), this.hasHadViews_ = !0, I;
2360
2312
  }
2361
2313
  removeView(A) {
2362
- const I = A.store, B = this.getSourceForStore(I), Q = this.views_.get(I);
2363
- if (!Q)
2364
- throw new Error("Cannot remove view: store not managed by ChunkManager");
2365
- const C = Q.indexOf(A);
2366
- if (C === -1)
2314
+ const I = this.views_.indexOf(A);
2315
+ if (I === -1)
2367
2316
  throw new Error(
2368
- `Cannot remove view: view not found in store's view list (source: ${B})`
2317
+ "Cannot remove view: view not found in store's view list"
2369
2318
  );
2370
- const E = Array.from(A.chunkViewStates.keys());
2371
- Q.splice(C, 1);
2372
- for (const i of E)
2373
- this.aggregateChunkViewStates(i, I);
2374
- Q.length === 0 && (this.stores_.delete(B), this.views_.delete(I));
2375
- }
2376
- async addSource(A) {
2377
- const I = this.stores_.get(A) ?? this.pendingStores_.get(A);
2378
- if (I)
2379
- return I;
2380
- const Q = (async () => {
2381
- const C = await A.open(), E = new bQ(C);
2382
- return this.stores_.set(A, E), this.pendingStores_.delete(A), E;
2383
- })();
2384
- return this.pendingStores_.set(A, Q), Q;
2319
+ const B = Array.from(A.chunkViewStates.keys());
2320
+ this.views_.splice(I, 1);
2321
+ for (const Q of B)
2322
+ this.aggregateChunkViewStates(Q);
2385
2323
  }
2386
- getSourceForStore(A) {
2387
- for (const [I, B] of this.stores_)
2388
- if (B === A)
2389
- return I;
2390
- throw new Error("Source not found for the given store.");
2324
+ get views() {
2325
+ return this.views_;
2391
2326
  }
2392
- update() {
2393
- for (const [A, I] of this.stores_) {
2394
- const B = this.updateAndCollectChunkChanges(I);
2395
- for (const Q of B)
2396
- Q.priority === null ? this.queue_.cancel(Q) : Q.state === "queued" && this.queue_.enqueue(
2397
- Q,
2398
- (C) => I.loadChunkData(Q, C)
2399
- );
2400
- }
2401
- this.queue_.flush();
2327
+ canDispose() {
2328
+ return this.hasHadViews_ && this.views_.length === 0;
2402
2329
  }
2403
- updateAndCollectChunkChanges(A) {
2404
- const I = this.views_.get(A);
2405
- if (!I) return /* @__PURE__ */ new Set();
2406
- const B = /* @__PURE__ */ new Set();
2407
- for (const Q of I)
2408
- for (const [C, E] of Q.chunkViewStates)
2409
- B.add(C);
2410
- for (const Q of B)
2411
- this.aggregateChunkViewStates(Q, A);
2412
- return B;
2330
+ updateAndCollectChunkChanges() {
2331
+ const A = /* @__PURE__ */ new Set();
2332
+ for (const I of this.views_)
2333
+ for (const [B, Q] of I.chunkViewStates)
2334
+ A.add(B);
2335
+ for (const I of A)
2336
+ this.aggregateChunkViewStates(I);
2337
+ return A;
2413
2338
  }
2414
- aggregateChunkViewStates(A, I) {
2415
- const B = this.views_.get(I);
2416
- if (!B) return;
2417
- let Q = !1, C = !1, E = null, i = null;
2418
- for (const h of B) {
2419
- const y = h.chunkViewStates.get(A);
2420
- y && (y.visible && (Q = !0), y.prefetch && (C = !0), y.priority !== null && (E === null || y.priority < E) && (E = y.priority, i = y.orderKey), !y.visible && !y.prefetch && y.priority === null && h.maybeForgetChunk(A));
2339
+ aggregateChunkViewStates(A) {
2340
+ let I = !1, B = !1, Q = null, C = null;
2341
+ for (const a of this.views_) {
2342
+ const D = a.chunkViewStates.get(A);
2343
+ D && (D.visible && (I = !0), D.prefetch && (B = !0), D.priority !== null && (Q === null || D.priority < Q) && (Q = D.priority, C = D.orderKey), !D.visible && !D.prefetch && D.priority === null && a.maybeForgetChunk(A));
2421
2344
  }
2422
- if (A.visible = Q, A.prefetch = C, A.priority = E, A.orderKey = i, A.priority !== null && A.state === "unloaded") {
2345
+ if (A.visible = I, A.prefetch = B, A.priority = Q, A.orderKey = C, A.priority !== null && A.state === "unloaded") {
2423
2346
  A.state = "queued";
2424
2347
  return;
2425
2348
  }
@@ -2428,10 +2351,100 @@ class OQ {
2428
2351
  return;
2429
2352
  }
2430
2353
  A.state === "loaded" && !A.visible && !A.prefetch && (A.data = void 0, A.state = "unloaded", A.priority = null, A.orderKey = null, A.prefetch = !1, U.debug(
2431
- "ChunkManager",
2354
+ "ChunkStore",
2432
2355
  `Disposing chunk ${JSON.stringify(A.chunkIndex)} in LOD ${A.lod}`
2433
2356
  ));
2434
2357
  }
2358
+ validateXYScaleRatios() {
2359
+ const A = this.dimensions_.x, I = this.dimensions_.y;
2360
+ for (let B = 1; B < this.dimensions_.numLods; B++) {
2361
+ const Q = A.lods[B].scale / A.lods[B - 1].scale, C = I.lods[B].scale / I.lods[B - 1].scale;
2362
+ if (!jI(Q, 2, 0.02) || !jI(C, 2, 0.02))
2363
+ throw new Error(
2364
+ `Invalid downsampling factor between levels ${B - 1} → ${B}: expected (2× in X and Y), but got (${Q.toFixed(2)}×, ${C.toFixed(2)}×) from scale [${A.lods[B - 1].scale}, ${I.lods[B - 1].scale}] → [${A.lods[B].scale}, ${I.lods[B].scale}]`
2365
+ );
2366
+ }
2367
+ }
2368
+ getAndValidateTimeDimension() {
2369
+ for (let A = 0; A < this.dimensions_.numLods; ++A) {
2370
+ const I = this.dimensions_.t?.lods[A];
2371
+ if (!I) continue;
2372
+ if (I.chunkSize !== 1)
2373
+ throw new Error(
2374
+ `ChunkStore only supports a chunk size of 1 in t. Found ${I.chunkSize} at LOD ${A}`
2375
+ );
2376
+ const B = this.dimensions_.t?.lods[A - 1];
2377
+ if (B && I.size !== B.size)
2378
+ throw new Error(
2379
+ `ChunkStore does not support downsampling in t. Found ${B.size} at LOD ${A - 1} → ${I.size} at LOD ${A}`
2380
+ );
2381
+ }
2382
+ return {
2383
+ size: this.dimensions_.t?.lods[0].size ?? 1
2384
+ };
2385
+ }
2386
+ getAndValidateChannelDimension() {
2387
+ for (let A = 0; A < this.dimensions_.numLods; ++A) {
2388
+ const I = this.dimensions_.c?.lods[A];
2389
+ if (!I) continue;
2390
+ if (I.chunkSize !== 1)
2391
+ throw new Error(
2392
+ `ChunkStore only supports a chunk size of 1 in c. Found ${I.chunkSize} at LOD ${A}`
2393
+ );
2394
+ if (I.scale !== 1)
2395
+ throw new Error(
2396
+ `ChunkStore does not support scale in c. Found ${I.scale} at LOD ${A}`
2397
+ );
2398
+ if (I.translation !== 0)
2399
+ throw new Error(
2400
+ `ChunkStore does not support translation in c. Found ${I.translation} at LOD ${A}`
2401
+ );
2402
+ const B = this.dimensions_.c?.lods[A - 1];
2403
+ if (B && I.size !== B.size)
2404
+ throw new Error(
2405
+ `ChunkStore does not support downsampling in c. Found ${B.size} at LOD ${A - 1} → ${I.size} at LOD ${A}`
2406
+ );
2407
+ }
2408
+ return {
2409
+ size: this.dimensions_.c?.lods[0].size ?? 1
2410
+ };
2411
+ }
2412
+ }
2413
+ class OQ {
2414
+ stores_ = /* @__PURE__ */ new Map();
2415
+ pendingStores_ = /* @__PURE__ */ new Map();
2416
+ queue_ = new WQ();
2417
+ async addView(A, I) {
2418
+ return (await this.getOrCreateStore(A)).createView(I);
2419
+ }
2420
+ async getOrCreateStore(A) {
2421
+ const I = this.stores_.get(A);
2422
+ if (I)
2423
+ return I;
2424
+ const B = this.pendingStores_.get(A);
2425
+ if (B)
2426
+ return B;
2427
+ const C = (async () => {
2428
+ const i = await A.open();
2429
+ return new PQ(i);
2430
+ })();
2431
+ this.pendingStores_.set(A, C);
2432
+ const E = await C;
2433
+ return this.stores_.set(A, E), this.pendingStores_.delete(A), E;
2434
+ }
2435
+ update() {
2436
+ for (const [A, I] of this.stores_) {
2437
+ const B = I.updateAndCollectChunkChanges();
2438
+ for (const Q of B)
2439
+ Q.priority === null ? this.queue_.cancel(Q) : Q.state === "queued" && this.queue_.enqueue(
2440
+ Q,
2441
+ (C) => I.loadChunkData(Q, C)
2442
+ );
2443
+ }
2444
+ this.queue_.flush();
2445
+ for (const [A, I] of this.stores_)
2446
+ I.canDispose() && this.stores_.delete(A);
2447
+ }
2435
2448
  }
2436
2449
  var ZA = function(g = 1) {
2437
2450
  var A = 0, I = document.createElement("div");
@@ -2711,9 +2724,9 @@ class IC {
2711
2724
  resizeObserver_;
2712
2725
  mediaQuery_;
2713
2726
  onMediaQueryChange_;
2714
- changed_ = !1;
2715
- constructor(A = []) {
2716
- this.elements_ = A;
2727
+ onChange_;
2728
+ constructor(A = [], I) {
2729
+ this.elements_ = A, this.onChange_ = I;
2717
2730
  }
2718
2731
  connect() {
2719
2732
  if (this.resizeObserver_) {
@@ -2724,21 +2737,17 @@ class IC {
2724
2737
  return;
2725
2738
  }
2726
2739
  this.resizeObserver_ = new ResizeObserver(() => {
2727
- this.changed_ = !0;
2740
+ this.onChange_();
2728
2741
  });
2729
2742
  for (const A of this.elements_)
2730
2743
  this.resizeObserver_.observe(A);
2731
2744
  this.startDevicePixelRatioObserver();
2732
2745
  }
2733
- getAndResetChanged() {
2734
- const A = this.changed_;
2735
- return this.changed_ = !1, A;
2736
- }
2737
2746
  startDevicePixelRatioObserver() {
2738
2747
  this.mediaQuery_ = matchMedia(
2739
2748
  `(resolution: ${window.devicePixelRatio}dppx)`
2740
2749
  ), this.onMediaQueryChange_ = () => {
2741
- this.changed_ = !0, this.startDevicePixelRatioObserver();
2750
+ this.onChange_(), this.startDevicePixelRatioObserver();
2742
2751
  }, this.mediaQuery_.addEventListener("change", this.onMediaQueryChange_, {
2743
2752
  once: !0
2744
2753
  });
@@ -2825,7 +2834,11 @@ class Do {
2825
2834
  const I = [this.canvas];
2826
2835
  for (const B of this.viewports_)
2827
2836
  B.element !== this.canvas && I.push(B.element);
2828
- this.sizeObserver_ = new IC(I);
2837
+ this.sizeObserver_ = new IC(I, () => {
2838
+ this.renderer_.updateSize();
2839
+ for (const B of this.viewports_)
2840
+ B.updateSize(), this.renderer_.render(B);
2841
+ });
2829
2842
  }
2830
2843
  get renderedObjects() {
2831
2844
  return this.renderer_.renderedObjects;
@@ -2855,7 +2868,7 @@ class Do {
2855
2868
  return this;
2856
2869
  }
2857
2870
  animate(A) {
2858
- this.stats_ && this.stats_.begin(), this.sizeObserver_.getAndResetChanged() && this.updateSize();
2871
+ this.stats_ && this.stats_.begin();
2859
2872
  for (const I of this.viewports_)
2860
2873
  this.renderer_.render(I);
2861
2874
  this.chunkManager_.update();
@@ -2875,11 +2888,6 @@ class Do {
2875
2888
  cancelAnimationFrame(this.lastAnimationId_), this.lastAnimationId_ = void 0;
2876
2889
  }
2877
2890
  }
2878
- updateSize() {
2879
- this.renderer_.updateSize();
2880
- for (const A of this.viewports_)
2881
- A.updateSize();
2882
- }
2883
2891
  }
2884
2892
  class gC extends pA {
2885
2893
  constructor(A) {
@@ -3932,7 +3940,7 @@ class NB extends GA {
3932
3940
  );
3933
3941
  }
3934
3942
  onDetached(A) {
3935
- this.releaseAndRemoveChunks(this.visibleChunks_.keys()), this.clearObjects(), this.chunkStoreView_ && (A.chunkManager.removeView(this.chunkStoreView_), this.chunkStoreView_ = void 0);
3943
+ this.releaseAndRemoveChunks(this.visibleChunks_.keys()), this.clearObjects(), this.chunkStoreView_ && (this.chunkStoreView_.dispose(), this.chunkStoreView_ = void 0);
3936
3944
  }
3937
3945
  update(A) {
3938
3946
  !A || !this.chunkStoreView_ || (this.chunkStoreView_.updateChunkStates(this.sliceCoords_, A.viewport), this.updateChunks(), this.resliceIfZChanged());