@fideus-labs/fidnii 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/BufferManager.d.ts.map +1 -1
  2. package/dist/BufferManager.js +2 -5
  3. package/dist/BufferManager.js.map +1 -1
  4. package/dist/ClipPlanes.d.ts.map +1 -1
  5. package/dist/ClipPlanes.js +11 -15
  6. package/dist/ClipPlanes.js.map +1 -1
  7. package/dist/OMEZarrNVImage.d.ts +33 -3
  8. package/dist/OMEZarrNVImage.d.ts.map +1 -1
  9. package/dist/OMEZarrNVImage.js +129 -50
  10. package/dist/OMEZarrNVImage.js.map +1 -1
  11. package/dist/RegionCoalescer.d.ts.map +1 -1
  12. package/dist/RegionCoalescer.js +1 -1
  13. package/dist/RegionCoalescer.js.map +1 -1
  14. package/dist/ResolutionSelector.d.ts.map +1 -1
  15. package/dist/ResolutionSelector.js +2 -4
  16. package/dist/ResolutionSelector.js.map +1 -1
  17. package/dist/ViewportBounds.d.ts.map +1 -1
  18. package/dist/ViewportBounds.js +7 -5
  19. package/dist/ViewportBounds.js.map +1 -1
  20. package/dist/events.d.ts.map +1 -1
  21. package/dist/events.js.map +1 -1
  22. package/dist/index.d.ts +11 -11
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +17 -16
  25. package/dist/index.js.map +1 -1
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js.map +1 -1
  28. package/dist/utils/affine.d.ts +1 -1
  29. package/dist/utils/affine.d.ts.map +1 -1
  30. package/dist/utils/affine.js.map +1 -1
  31. package/dist/utils/coordinates.d.ts +1 -1
  32. package/dist/utils/coordinates.d.ts.map +1 -1
  33. package/dist/utils/coordinates.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/BufferManager.ts +45 -45
  36. package/src/ClipPlanes.ts +131 -130
  37. package/src/OMEZarrNVImage.ts +685 -606
  38. package/src/RegionCoalescer.ts +48 -47
  39. package/src/ResolutionSelector.ts +66 -67
  40. package/src/ViewportBounds.ts +120 -118
  41. package/src/events.ts +36 -35
  42. package/src/index.ts +59 -69
  43. package/src/types.ts +95 -94
  44. package/src/utils/affine.ts +65 -65
  45. package/src/utils/coordinates.ts +70 -70
@@ -1,28 +1,29 @@
1
1
  // SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
- import * as zarr from "zarrita";
5
- import type { NgffImage } from "@fideus-labs/ngff-zarr";
6
- import { zarrGet } from "@fideus-labs/ngff-zarr/browser";
4
+ import type { NgffImage } from "@fideus-labs/ngff-zarr"
5
+ import { zarrGet } from "@fideus-labs/ngff-zarr/browser"
6
+ import * as zarr from "zarrita"
7
+
7
8
  import type {
8
9
  ChunkCache,
9
10
  PixelRegion,
10
11
  RegionFetchResult,
11
12
  TypedArray,
12
- } from "./types.js";
13
+ } from "./types.js"
13
14
 
14
15
  /**
15
16
  * Represents a pending request that may have multiple consumers waiting for the result.
16
17
  */
17
18
  interface PendingRequest {
18
19
  /** The promise that resolves when the request completes */
19
- promise: Promise<RegionFetchResult>;
20
+ promise: Promise<RegionFetchResult>
20
21
  /** Function to resolve the promise with the result */
21
- resolve: (data: RegionFetchResult) => void;
22
+ resolve: (data: RegionFetchResult) => void
22
23
  /** Function to reject the promise with an error */
23
- reject: (error: Error) => void;
24
+ reject: (error: Error) => void
24
25
  /** Set of requester IDs waiting for this result */
25
- requesters: Set<string>;
26
+ requesters: Set<string>
26
27
  }
27
28
 
28
29
  /**
@@ -40,10 +41,10 @@ interface PendingRequest {
40
41
  * overlapping region requests simultaneously.
41
42
  */
42
43
  export class RegionCoalescer {
43
- private readonly pending: Map<string, PendingRequest> = new Map();
44
+ private readonly pending: Map<string, PendingRequest> = new Map()
44
45
 
45
46
  /** Optional decoded-chunk cache forwarded to fizarrita's getWorker. */
46
- private readonly _cache: ChunkCache | undefined;
47
+ private readonly _cache: ChunkCache | undefined
47
48
 
48
49
  /**
49
50
  * @param cache - Optional decoded-chunk cache. When provided, `zarrGet`
@@ -51,7 +52,7 @@ export class RegionCoalescer {
51
52
  * or overlapping reads.
52
53
  */
53
54
  constructor(cache?: ChunkCache) {
54
- this._cache = cache;
55
+ this._cache = cache
55
56
  }
56
57
 
57
58
  /**
@@ -62,9 +63,9 @@ export class RegionCoalescer {
62
63
  levelIndex: number,
63
64
  region: PixelRegion,
64
65
  ): string {
65
- const start = region.start.join(",");
66
- const end = region.end.join(",");
67
- return `${imagePath}:${levelIndex}:${start}:${end}`;
66
+ const start = region.start.join(",")
67
+ const end = region.end.join(",")
68
+ return `${imagePath}:${levelIndex}:${start}:${end}`
68
69
  }
69
70
 
70
71
  /**
@@ -82,33 +83,33 @@ export class RegionCoalescer {
82
83
  region: PixelRegion,
83
84
  requesterId: string = "default",
84
85
  ): Promise<RegionFetchResult> {
85
- const key = this.makeKey(ngffImage.data.path, levelIndex, region);
86
+ const key = this.makeKey(ngffImage.data.path, levelIndex, region)
86
87
 
87
88
  // Check if there's already a pending request for this data
88
- const existing = this.pending.get(key);
89
+ const existing = this.pending.get(key)
89
90
  if (existing) {
90
91
  // Add this requester to the waiters and return the existing promise
91
- existing.requesters.add(requesterId);
92
- return existing.promise;
92
+ existing.requesters.add(requesterId)
93
+ return existing.promise
93
94
  }
94
95
 
95
96
  // Create a new pending request
96
- let resolvePromise!: (data: RegionFetchResult) => void;
97
- let rejectPromise!: (error: Error) => void;
97
+ let resolvePromise!: (data: RegionFetchResult) => void
98
+ let rejectPromise!: (error: Error) => void
98
99
 
99
100
  const promise = new Promise<RegionFetchResult>((resolve, reject) => {
100
- resolvePromise = resolve;
101
- rejectPromise = reject;
102
- });
101
+ resolvePromise = resolve
102
+ rejectPromise = reject
103
+ })
103
104
 
104
105
  const pendingRequest: PendingRequest = {
105
106
  promise,
106
107
  resolve: resolvePromise,
107
108
  reject: rejectPromise,
108
109
  requesters: new Set([requesterId]),
109
- };
110
+ }
110
111
 
111
- this.pending.set(key, pendingRequest);
112
+ this.pending.set(key, pendingRequest)
112
113
 
113
114
  // Fetch using fizarrita's worker-accelerated zarrGet
114
115
  try {
@@ -116,28 +117,28 @@ export class RegionCoalescer {
116
117
  zarr.slice(region.start[0], region.end[0]),
117
118
  zarr.slice(region.start[1], region.end[1]),
118
119
  zarr.slice(region.start[2], region.end[2]),
119
- ];
120
+ ]
120
121
  // Pass the chunk cache to fizarrita's getWorker via zarrGet.
121
122
  // The `cache` option is available in @fideus-labs/fizarrita >=1.2.0.
122
123
  const zarrOpts = this._cache
123
- ? { cache: this._cache } as Record<string, unknown>
124
- : undefined;
125
- const result = await zarrGet(ngffImage.data, selection, zarrOpts);
124
+ ? ({ cache: this._cache } as Record<string, unknown>)
125
+ : undefined
126
+ const result = await zarrGet(ngffImage.data, selection, zarrOpts)
126
127
 
127
128
  const fetchResult: RegionFetchResult = {
128
129
  data: result.data as TypedArray,
129
130
  shape: result.shape,
130
131
  stride: result.stride,
131
- };
132
+ }
132
133
 
133
- pendingRequest.resolve(fetchResult);
134
- return fetchResult;
134
+ pendingRequest.resolve(fetchResult)
135
+ return fetchResult
135
136
  } catch (error) {
136
- const err = error instanceof Error ? error : new Error(String(error));
137
- pendingRequest.reject(err);
138
- throw err;
137
+ const err = error instanceof Error ? error : new Error(String(error))
138
+ pendingRequest.reject(err)
139
+ throw err
139
140
  } finally {
140
- this.pending.delete(key);
141
+ this.pending.delete(key)
141
142
  }
142
143
  }
143
144
 
@@ -159,9 +160,9 @@ export class RegionCoalescer {
159
160
  ): Promise<RegionFetchResult[]> {
160
161
  return Promise.all(
161
162
  regions.map((region) =>
162
- this.fetchRegion(ngffImage, levelIndex, region, requesterId)
163
+ this.fetchRegion(ngffImage, levelIndex, region, requesterId),
163
164
  ),
164
- );
165
+ )
165
166
  }
166
167
 
167
168
  /**
@@ -172,8 +173,8 @@ export class RegionCoalescer {
172
173
  levelIndex: number,
173
174
  region: PixelRegion,
174
175
  ): boolean {
175
- const key = this.makeKey(ngffImage.data.path, levelIndex, region);
176
- return this.pending.has(key);
176
+ const key = this.makeKey(ngffImage.data.path, levelIndex, region)
177
+ return this.pending.has(key)
177
178
  }
178
179
 
179
180
  /**
@@ -185,8 +186,8 @@ export class RegionCoalescer {
185
186
  levelIndex: number,
186
187
  region: PixelRegion,
187
188
  ): Set<string> | undefined {
188
- const key = this.makeKey(ngffImage.data.path, levelIndex, region);
189
- return this.pending.get(key)?.requesters;
189
+ const key = this.makeKey(ngffImage.data.path, levelIndex, region)
190
+ return this.pending.get(key)?.requesters
190
191
  }
191
192
 
192
193
  /**
@@ -195,16 +196,16 @@ export class RegionCoalescer {
195
196
  async onIdle(): Promise<void> {
196
197
  // Wait for all in-flight requests to settle
197
198
  const promises = Array.from(this.pending.values()).map((p) =>
198
- p.promise.catch(() => {})
199
- );
200
- await Promise.all(promises);
199
+ p.promise.catch(() => {}),
200
+ )
201
+ await Promise.all(promises)
201
202
  }
202
203
 
203
204
  /**
204
205
  * Get the number of pending requests (unique region requests).
205
206
  */
206
207
  get pendingCount(): number {
207
- return this.pending.size;
208
+ return this.pending.size
208
209
  }
209
210
 
210
211
  /**
@@ -212,6 +213,6 @@ export class RegionCoalescer {
212
213
  * Note: Does not resolve or reject pending promises.
213
214
  */
214
215
  clear(): void {
215
- this.pending.clear();
216
+ this.pending.clear()
216
217
  }
217
218
  }
@@ -1,20 +1,21 @@
1
1
  // SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC
2
2
  // SPDX-License-Identifier: MIT
3
3
 
4
- import type { Multiscales, NgffImage } from "@fideus-labs/ngff-zarr";
4
+ import type { Multiscales, NgffImage } from "@fideus-labs/ngff-zarr"
5
+
6
+ import { clipPlanesToPixelRegion } from "./ClipPlanes.js"
5
7
  import type {
6
8
  ClipPlanes,
7
9
  PixelRegion,
8
10
  ResolutionSelection,
9
11
  VolumeBounds,
10
- } from "./types.js";
11
- import { clipPlanesToPixelRegion } from "./ClipPlanes.js";
12
+ } from "./types.js"
12
13
 
13
14
  /**
14
15
  * Orthogonal axis index in [z, y, x] order.
15
16
  * 0 = Z (axial view), 1 = Y (coronal view), 2 = X (sagittal view)
16
17
  */
17
- export type OrthogonalAxis = 0 | 1 | 2;
18
+ export type OrthogonalAxis = 0 | 1 | 2
18
19
 
19
20
  /**
20
21
  * Select the appropriate resolution level based on pixel budget and clip planes.
@@ -38,57 +39,57 @@ export function selectResolution(
38
39
  volumeBounds: VolumeBounds,
39
40
  viewportBounds?: VolumeBounds,
40
41
  ): ResolutionSelection {
41
- const images = multiscales.images;
42
+ const images = multiscales.images
42
43
 
43
44
  // Try each resolution from highest to lowest
44
45
  for (let i = 0; i < images.length; i++) {
45
- const image = images[i];
46
+ const image = images[i]
46
47
  const region = clipPlanesToPixelRegion(
47
48
  clipPlanes,
48
49
  volumeBounds,
49
50
  image,
50
51
  viewportBounds,
51
- );
52
- const alignedRegion = alignRegionToChunks(region, image);
52
+ )
53
+ const alignedRegion = alignRegionToChunks(region, image)
53
54
 
54
55
  const dimensions: [number, number, number] = [
55
56
  alignedRegion.end[0] - alignedRegion.start[0],
56
57
  alignedRegion.end[1] - alignedRegion.start[1],
57
58
  alignedRegion.end[2] - alignedRegion.start[2],
58
- ];
59
+ ]
59
60
 
60
- const pixelCount = dimensions[0] * dimensions[1] * dimensions[2];
61
+ const pixelCount = dimensions[0] * dimensions[1] * dimensions[2]
61
62
 
62
63
  if (pixelCount <= maxPixels) {
63
64
  return {
64
65
  levelIndex: i,
65
66
  dimensions,
66
67
  pixelCount,
67
- };
68
+ }
68
69
  }
69
70
  }
70
71
 
71
72
  // Fall back to lowest resolution
72
- const lowestImage = images[images.length - 1];
73
+ const lowestImage = images[images.length - 1]
73
74
  const region = clipPlanesToPixelRegion(
74
75
  clipPlanes,
75
76
  volumeBounds,
76
77
  lowestImage,
77
78
  viewportBounds,
78
- );
79
- const alignedRegion = alignRegionToChunks(region, lowestImage);
79
+ )
80
+ const alignedRegion = alignRegionToChunks(region, lowestImage)
80
81
 
81
82
  const dimensions: [number, number, number] = [
82
83
  alignedRegion.end[0] - alignedRegion.start[0],
83
84
  alignedRegion.end[1] - alignedRegion.start[1],
84
85
  alignedRegion.end[2] - alignedRegion.start[2],
85
- ];
86
+ ]
86
87
 
87
88
  return {
88
89
  levelIndex: images.length - 1,
89
90
  dimensions,
90
91
  pixelCount: dimensions[0] * dimensions[1] * dimensions[2],
91
- };
92
+ }
92
93
  }
93
94
 
94
95
  /**
@@ -98,21 +99,21 @@ export function selectResolution(
98
99
  * @returns Chunk shape as [z, y, x]
99
100
  */
100
101
  export function getChunkShape(ngffImage: NgffImage): [number, number, number] {
101
- const chunks = ngffImage.data.chunks;
102
- const dims = ngffImage.dims;
102
+ const chunks = ngffImage.data.chunks
103
+ const dims = ngffImage.dims
103
104
 
104
105
  // Find z, y, x indices in dims
105
- const zIdx = dims.indexOf("z");
106
- const yIdx = dims.indexOf("y");
107
- const xIdx = dims.indexOf("x");
106
+ const zIdx = dims.indexOf("z")
107
+ const yIdx = dims.indexOf("y")
108
+ const xIdx = dims.indexOf("x")
108
109
 
109
110
  if (zIdx === -1 || yIdx === -1 || xIdx === -1) {
110
111
  // Fallback: assume last 3 dimensions are z, y, x
111
- const n = chunks.length;
112
- return [chunks[n - 3] || 1, chunks[n - 2] || 1, chunks[n - 1] || 1];
112
+ const n = chunks.length
113
+ return [chunks[n - 3] || 1, chunks[n - 2] || 1, chunks[n - 1] || 1]
113
114
  }
114
115
 
115
- return [chunks[zIdx], chunks[yIdx], chunks[xIdx]];
116
+ return [chunks[zIdx], chunks[yIdx], chunks[xIdx]]
116
117
  }
117
118
 
118
119
  /**
@@ -122,21 +123,21 @@ export function getChunkShape(ngffImage: NgffImage): [number, number, number] {
122
123
  * @returns Shape as [z, y, x]
123
124
  */
124
125
  export function getVolumeShape(ngffImage: NgffImage): [number, number, number] {
125
- const shape = ngffImage.data.shape;
126
- const dims = ngffImage.dims;
126
+ const shape = ngffImage.data.shape
127
+ const dims = ngffImage.dims
127
128
 
128
129
  // Find z, y, x indices in dims
129
- const zIdx = dims.indexOf("z");
130
- const yIdx = dims.indexOf("y");
131
- const xIdx = dims.indexOf("x");
130
+ const zIdx = dims.indexOf("z")
131
+ const yIdx = dims.indexOf("y")
132
+ const xIdx = dims.indexOf("x")
132
133
 
133
134
  if (zIdx === -1 || yIdx === -1 || xIdx === -1) {
134
135
  // Fallback: assume last 3 dimensions are z, y, x
135
- const n = shape.length;
136
- return [shape[n - 3] || 1, shape[n - 2] || 1, shape[n - 1] || 1];
136
+ const n = shape.length
137
+ return [shape[n - 3] || 1, shape[n - 2] || 1, shape[n - 1] || 1]
137
138
  }
138
139
 
139
- return [shape[zIdx], shape[yIdx], shape[xIdx]];
140
+ return [shape[zIdx], shape[yIdx], shape[xIdx]]
140
141
  }
141
142
 
142
143
  /**
@@ -151,15 +152,15 @@ export function alignRegionToChunks(
151
152
  region: PixelRegion,
152
153
  ngffImage: NgffImage,
153
154
  ): PixelRegion {
154
- const chunkShape = getChunkShape(ngffImage);
155
- const volumeShape = getVolumeShape(ngffImage);
155
+ const chunkShape = getChunkShape(ngffImage)
156
+ const volumeShape = getVolumeShape(ngffImage)
156
157
 
157
158
  // Align start down to chunk boundary
158
159
  const alignedStart: [number, number, number] = [
159
160
  Math.floor(region.start[0] / chunkShape[0]) * chunkShape[0],
160
161
  Math.floor(region.start[1] / chunkShape[1]) * chunkShape[1],
161
162
  Math.floor(region.start[2] / chunkShape[2]) * chunkShape[2],
162
- ];
163
+ ]
163
164
 
164
165
  // Align end up to chunk boundary (but don't exceed volume size)
165
166
  const alignedEnd: [number, number, number] = [
@@ -175,12 +176,12 @@ export function alignRegionToChunks(
175
176
  Math.ceil(region.end[2] / chunkShape[2]) * chunkShape[2],
176
177
  volumeShape[2],
177
178
  ),
178
- ];
179
+ ]
179
180
 
180
181
  return {
181
182
  start: alignedStart,
182
183
  end: alignedEnd,
183
- };
184
+ }
184
185
  }
185
186
 
186
187
  /**
@@ -190,7 +191,7 @@ export function alignRegionToChunks(
190
191
  * @returns Middle resolution level index
191
192
  */
192
193
  export function getMiddleResolutionIndex(multiscales: Multiscales): number {
193
- return Math.floor(multiscales.images.length / 2);
194
+ return Math.floor(multiscales.images.length / 2)
194
195
  }
195
196
 
196
197
  /**
@@ -206,17 +207,17 @@ export function calculateUpsampleFactor(
206
207
  fromLevel: number,
207
208
  toLevel: number,
208
209
  ): [number, number, number] {
209
- const fromImage = multiscales.images[fromLevel];
210
- const toImage = multiscales.images[toLevel];
210
+ const fromImage = multiscales.images[fromLevel]
211
+ const toImage = multiscales.images[toLevel]
211
212
 
212
- const fromShape = getVolumeShape(fromImage);
213
- const toShape = getVolumeShape(toImage);
213
+ const fromShape = getVolumeShape(fromImage)
214
+ const toShape = getVolumeShape(toImage)
214
215
 
215
216
  return [
216
217
  toShape[0] / fromShape[0],
217
218
  toShape[1] / fromShape[1],
218
219
  toShape[2] / fromShape[2],
219
- ];
220
+ ]
220
221
  }
221
222
 
222
223
  /**
@@ -226,7 +227,7 @@ export function getFullVolumeDimensions(
226
227
  multiscales: Multiscales,
227
228
  levelIndex: number,
228
229
  ): [number, number, number] {
229
- return getVolumeShape(multiscales.images[levelIndex]);
230
+ return getVolumeShape(multiscales.images[levelIndex])
230
231
  }
231
232
 
232
233
  /**
@@ -257,69 +258,67 @@ export function select2DResolution(
257
258
  orthogonalAxis: OrthogonalAxis,
258
259
  viewportBounds?: VolumeBounds,
259
260
  ): ResolutionSelection {
260
- const images = multiscales.images;
261
+ const images = multiscales.images
261
262
 
262
263
  // Try each resolution from highest to lowest
263
264
  for (let i = 0; i < images.length; i++) {
264
- const image = images[i];
265
+ const image = images[i]
265
266
  const region = clipPlanesToPixelRegion(
266
267
  clipPlanes,
267
268
  volumeBounds,
268
269
  image,
269
270
  viewportBounds,
270
- );
271
- const alignedRegion = alignRegionToChunks(region, image);
271
+ )
272
+ const alignedRegion = alignRegionToChunks(region, image)
272
273
 
273
274
  const dimensions: [number, number, number] = [
274
275
  alignedRegion.end[0] - alignedRegion.start[0],
275
276
  alignedRegion.end[1] - alignedRegion.start[1],
276
277
  alignedRegion.end[2] - alignedRegion.start[2],
277
- ];
278
+ ]
278
279
 
279
280
  // Count the full slab volume: in-plane area × chunk depth along the
280
281
  // orthogonal axis. The slab is always one chunk thick, so we use the
281
282
  // chunk shape rather than the full volume extent in that axis.
282
- const chunkShape = getChunkShape(image);
283
- const slabDepth = chunkShape[orthogonalAxis];
284
- const inPlaneAxes = ([0, 1, 2] as const).filter((a) =>
285
- a !== orthogonalAxis
286
- );
287
- const slabVoxelCount = dimensions[inPlaneAxes[0]] *
288
- dimensions[inPlaneAxes[1]] * slabDepth;
283
+ const chunkShape = getChunkShape(image)
284
+ const slabDepth = chunkShape[orthogonalAxis]
285
+ const inPlaneAxes = ([0, 1, 2] as const).filter((a) => a !== orthogonalAxis)
286
+ const slabVoxelCount =
287
+ dimensions[inPlaneAxes[0]] * dimensions[inPlaneAxes[1]] * slabDepth
289
288
 
290
289
  if (slabVoxelCount <= maxPixels) {
291
290
  return {
292
291
  levelIndex: i,
293
292
  dimensions,
294
293
  pixelCount: slabVoxelCount,
295
- };
294
+ }
296
295
  }
297
296
  }
298
297
 
299
298
  // Fall back to lowest resolution
300
- const lowestImage = images[images.length - 1];
299
+ const lowestImage = images[images.length - 1]
301
300
  const region = clipPlanesToPixelRegion(
302
301
  clipPlanes,
303
302
  volumeBounds,
304
303
  lowestImage,
305
304
  viewportBounds,
306
- );
307
- const alignedRegion = alignRegionToChunks(region, lowestImage);
305
+ )
306
+ const alignedRegion = alignRegionToChunks(region, lowestImage)
308
307
 
309
308
  const dimensions: [number, number, number] = [
310
309
  alignedRegion.end[0] - alignedRegion.start[0],
311
310
  alignedRegion.end[1] - alignedRegion.start[1],
312
311
  alignedRegion.end[2] - alignedRegion.start[2],
313
- ];
312
+ ]
314
313
 
315
- const chunkShape = getChunkShape(lowestImage);
316
- const slabDepth = chunkShape[orthogonalAxis];
317
- const inPlaneAxes = ([0, 1, 2] as const).filter((a) => a !== orthogonalAxis);
314
+ const chunkShape = getChunkShape(lowestImage)
315
+ const slabDepth = chunkShape[orthogonalAxis]
316
+ const inPlaneAxes = ([0, 1, 2] as const).filter((a) => a !== orthogonalAxis)
318
317
 
319
318
  return {
320
319
  levelIndex: images.length - 1,
321
320
  dimensions,
322
- pixelCount: dimensions[inPlaneAxes[0]] * dimensions[inPlaneAxes[1]] *
323
- slabDepth,
324
- };
321
+ pixelCount:
322
+ dimensions[inPlaneAxes[0]] * dimensions[inPlaneAxes[1]] * slabDepth,
323
+ }
325
324
  }