@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
package/src/ClipPlanes.ts CHANGED
@@ -1,19 +1,20 @@
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 { getChunkShape, getVolumeShape } from "./ResolutionSelector.js"
5
7
  import type {
6
8
  ChunkAlignedRegion,
7
9
  ClipPlane,
8
10
  ClipPlanes,
9
11
  PixelRegion,
10
12
  VolumeBounds,
11
- } from "./types.js";
12
- import { pixelToWorld, worldToPixel } from "./utils/coordinates.js";
13
- import { getChunkShape, getVolumeShape } from "./ResolutionSelector.js";
13
+ } from "./types.js"
14
+ import { pixelToWorld, worldToPixel } from "./utils/coordinates.js"
14
15
 
15
16
  /** Maximum number of clip planes supported by NiiVue */
16
- export const MAX_CLIP_PLANES = 6;
17
+ export const MAX_CLIP_PLANES = 6
17
18
 
18
19
  /**
19
20
  * Normalize a 3D vector to unit length.
@@ -25,11 +26,11 @@ export const MAX_CLIP_PLANES = 6;
25
26
  export function normalizeVector(
26
27
  v: [number, number, number],
27
28
  ): [number, number, number] {
28
- const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
29
+ const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
29
30
  if (length === 0) {
30
- throw new Error("Cannot normalize zero-length vector");
31
+ throw new Error("Cannot normalize zero-length vector")
31
32
  }
32
- return [v[0] / length, v[1] / length, v[2] / length];
33
+ return [v[0] / length, v[1] / length, v[2] / length]
33
34
  }
34
35
 
35
36
  /**
@@ -47,7 +48,7 @@ export function createClipPlane(
47
48
  return {
48
49
  point: [...point],
49
50
  normal: normalizeVector(normal),
50
- };
51
+ }
51
52
  }
52
53
 
53
54
  /**
@@ -57,7 +58,7 @@ export function createClipPlane(
57
58
  * @returns Empty ClipPlanes array
58
59
  */
59
60
  export function createDefaultClipPlanes(_multiscales: Multiscales): ClipPlanes {
60
- return [];
61
+ return []
61
62
  }
62
63
 
63
64
  /**
@@ -70,14 +71,14 @@ export function getVolumeBoundsFromMultiscales(
70
71
  multiscales: Multiscales,
71
72
  ): VolumeBounds {
72
73
  // Use highest resolution for most accurate bounds
73
- const image = multiscales.images[0];
74
- const shape = getVolumeShape(image);
74
+ const image = multiscales.images[0]
75
+ const shape = getVolumeShape(image)
75
76
 
76
- const minPixel: [number, number, number] = [0, 0, 0];
77
- const maxPixel: [number, number, number] = [shape[0], shape[1], shape[2]];
77
+ const minPixel: [number, number, number] = [0, 0, 0]
78
+ const maxPixel: [number, number, number] = [shape[0], shape[1], shape[2]]
78
79
 
79
- const minWorld = pixelToWorld(minPixel, image);
80
- const maxWorld = pixelToWorld(maxPixel, image);
80
+ const minWorld = pixelToWorld(minPixel, image)
81
+ const maxWorld = pixelToWorld(maxPixel, image)
81
82
 
82
83
  return {
83
84
  min: [
@@ -90,7 +91,7 @@ export function getVolumeBoundsFromMultiscales(
90
91
  Math.max(minWorld[1], maxWorld[1]),
91
92
  Math.max(minWorld[2], maxWorld[2]),
92
93
  ],
93
- };
94
+ }
94
95
  }
95
96
 
96
97
  /**
@@ -103,22 +104,23 @@ export function getVolumeBoundsFromMultiscales(
103
104
  * @param normal - Unit normal vector [x, y, z]
104
105
  * @returns Object with azimuth and elevation in degrees
105
106
  */
106
- export function normalToAzimuthElevation(
107
- normal: [number, number, number],
108
- ): { azimuth: number; elevation: number } {
109
- const [x, y, z] = normal;
107
+ export function normalToAzimuthElevation(normal: [number, number, number]): {
108
+ azimuth: number
109
+ elevation: number
110
+ } {
111
+ const [x, y, z] = normal
110
112
 
111
113
  // Elevation: angle from XY plane (arcsin of z component)
112
114
  // Clamp to [-1, 1] to handle floating point errors
113
- const elevation = Math.asin(Math.max(-1, Math.min(1, z))) * (180 / Math.PI);
115
+ const elevation = Math.asin(Math.max(-1, Math.min(1, z))) * (180 / Math.PI)
114
116
 
115
117
  // Azimuth: angle in XY plane from +Y axis
116
118
  // atan2(x, y) gives angle from +Y, which matches NiiVue's azimuth=0 = posterior (+Y)
117
- let azimuth = Math.atan2(x, y) * (180 / Math.PI);
119
+ let azimuth = Math.atan2(x, y) * (180 / Math.PI)
118
120
  // Normalize to [0, 360)
119
- azimuth = ((azimuth % 360) + 360) % 360;
121
+ azimuth = ((azimuth % 360) + 360) % 360
120
122
 
121
- return { azimuth, elevation };
123
+ return { azimuth, elevation }
122
124
  }
123
125
 
124
126
  /**
@@ -132,15 +134,15 @@ export function azimuthElevationToNormal(
132
134
  azimuth: number,
133
135
  elevation: number,
134
136
  ): [number, number, number] {
135
- const azRad = (azimuth * Math.PI) / 180;
136
- const elRad = (elevation * Math.PI) / 180;
137
+ const azRad = (azimuth * Math.PI) / 180
138
+ const elRad = (elevation * Math.PI) / 180
137
139
 
138
- const cosEl = Math.cos(elRad);
139
- const x = cosEl * Math.sin(azRad);
140
- const y = cosEl * Math.cos(azRad);
141
- const z = Math.sin(elRad);
140
+ const cosEl = Math.cos(elRad)
141
+ const x = cosEl * Math.sin(azRad)
142
+ const y = cosEl * Math.cos(azRad)
143
+ const z = Math.sin(elRad)
142
144
 
143
- return [x, y, z];
145
+ return [x, y, z]
144
146
  }
145
147
 
146
148
  /**
@@ -159,41 +161,43 @@ export function calculateNiivueDepth(
159
161
  plane: ClipPlane,
160
162
  volumeBounds: VolumeBounds,
161
163
  ): number {
162
- const { min, max } = volumeBounds;
164
+ const { min, max } = volumeBounds
163
165
 
164
166
  // Volume center
165
167
  const center: [number, number, number] = [
166
168
  (min[0] + max[0]) / 2,
167
169
  (min[1] + max[1]) / 2,
168
170
  (min[2] + max[2]) / 2,
169
- ];
171
+ ]
170
172
 
171
173
  // Volume extent
172
174
  const extent: [number, number, number] = [
173
175
  max[0] - min[0],
174
176
  max[1] - min[1],
175
177
  max[2] - min[2],
176
- ];
178
+ ]
177
179
 
178
180
  // Signed distance from center to plane along normal
179
- const { point, normal } = plane;
180
- const signedDistance = normal[0] * (point[0] - center[0]) +
181
+ const { point, normal } = plane
182
+ const signedDistance =
183
+ normal[0] * (point[0] - center[0]) +
181
184
  normal[1] * (point[1] - center[1]) +
182
- normal[2] * (point[2] - center[2]);
185
+ normal[2] * (point[2] - center[2])
183
186
 
184
187
  // Full extent along normal direction (using absolute value of each component)
185
188
  // This is the "width" of the bounding box when projected onto the normal direction
186
- const extentAlongNormal = Math.abs(normal[0]) * extent[0] +
189
+ const extentAlongNormal =
190
+ Math.abs(normal[0]) * extent[0] +
187
191
  Math.abs(normal[1]) * extent[1] +
188
- Math.abs(normal[2]) * extent[2];
192
+ Math.abs(normal[2]) * extent[2]
189
193
 
190
194
  // Avoid division by zero
191
195
  if (extentAlongNormal === 0) {
192
- return 0;
196
+ return 0
193
197
  }
194
198
 
195
199
  // Normalize to NiiVue's coordinate system where volume spans -0.5 to 0.5 from center
196
- return signedDistance / extentAlongNormal;
200
+ return signedDistance / extentAlongNormal
197
201
  }
198
202
 
199
203
  /**
@@ -221,7 +225,7 @@ export function clipPlaneToNiivue(
221
225
  plane: ClipPlane,
222
226
  volumeBounds: VolumeBounds,
223
227
  ): [number, number, number] {
224
- const depth = calculateNiivueDepth(plane, volumeBounds);
228
+ const depth = calculateNiivueDepth(plane, volumeBounds)
225
229
 
226
230
  // Negate the normal for azimuth/elevation calculation.
227
231
  // After NiiVue adds 180° to azimuth, the shader will see our original normal.
@@ -229,14 +233,14 @@ export function clipPlaneToNiivue(
229
233
  -plane.normal[0],
230
234
  -plane.normal[1],
231
235
  -plane.normal[2],
232
- ];
233
- const { azimuth, elevation } = normalToAzimuthElevation(negatedNormal);
236
+ ]
237
+ const { azimuth, elevation } = normalToAzimuthElevation(negatedNormal)
234
238
 
235
239
  // Also negate the depth to be consistent with the flipped normal.
236
240
  // The plane equation dot(n, p-center) + d = 0 changes sign when n is negated.
237
- const negatedDepth = -depth;
241
+ const negatedDepth = -depth
238
242
 
239
- return [negatedDepth, azimuth, elevation];
243
+ return [negatedDepth, azimuth, elevation]
240
244
  }
241
245
 
242
246
  /**
@@ -250,7 +254,7 @@ export function clipPlanesToNiivue(
250
254
  clipPlanes: ClipPlanes,
251
255
  volumeBounds: VolumeBounds,
252
256
  ): number[][] {
253
- return clipPlanes.map((plane) => clipPlaneToNiivue(plane, volumeBounds));
257
+ return clipPlanes.map((plane) => clipPlaneToNiivue(plane, volumeBounds))
254
258
  }
255
259
 
256
260
  /**
@@ -267,12 +271,12 @@ export function pointToPlaneDistance(
267
271
  testPoint: [number, number, number],
268
272
  plane: ClipPlane,
269
273
  ): number {
270
- const { point, normal } = plane;
274
+ const { point, normal } = plane
271
275
  return (
272
276
  normal[0] * (testPoint[0] - point[0]) +
273
277
  normal[1] * (testPoint[1] - point[1]) +
274
278
  normal[2] * (testPoint[2] - point[2])
275
- );
279
+ )
276
280
  }
277
281
 
278
282
  /**
@@ -288,10 +292,10 @@ export function isInsideClipPlanes(
288
292
  ): boolean {
289
293
  for (const plane of clipPlanes) {
290
294
  if (pointToPlaneDistance(worldCoord, plane) < 0) {
291
- return false;
295
+ return false
292
296
  }
293
297
  }
294
- return true;
298
+ return true
295
299
  }
296
300
 
297
301
  /**
@@ -315,58 +319,62 @@ export function clipPlanesToBoundingBox(
315
319
  return {
316
320
  min: [...volumeBounds.min],
317
321
  max: [...volumeBounds.max],
318
- };
322
+ }
319
323
  }
320
324
 
321
325
  // Start with full volume bounds
322
- let minX = volumeBounds.min[0];
323
- let maxX = volumeBounds.max[0];
324
- let minY = volumeBounds.min[1];
325
- let maxY = volumeBounds.max[1];
326
- let minZ = volumeBounds.min[2];
327
- let maxZ = volumeBounds.max[2];
326
+ let minX = volumeBounds.min[0]
327
+ let maxX = volumeBounds.max[0]
328
+ let minY = volumeBounds.min[1]
329
+ let maxY = volumeBounds.max[1]
330
+ let minZ = volumeBounds.min[2]
331
+ let maxZ = volumeBounds.max[2]
328
332
 
329
333
  // For each clip plane, constrain the bounding box
330
334
  // This is an approximation: we check the 8 corners and constrain based on
331
335
  // which corners are clipped. For axis-aligned planes, this is exact.
332
336
  // For oblique planes, it's a conservative approximation.
333
337
  for (const plane of clipPlanes) {
334
- const { point, normal } = plane;
338
+ const { point, normal } = plane
335
339
 
336
340
  // For axis-aligned normals, we can compute exact bounds
337
- const absNx = Math.abs(normal[0]);
338
- const absNy = Math.abs(normal[1]);
339
- const absNz = Math.abs(normal[2]);
341
+ const absNx = Math.abs(normal[0])
342
+ const absNy = Math.abs(normal[1])
343
+ const absNz = Math.abs(normal[2])
340
344
 
341
345
  // Check if plane is approximately axis-aligned
342
- const tolerance = 0.001;
346
+ const tolerance = 0.001
343
347
 
344
348
  if (absNx > 1 - tolerance && absNy < tolerance && absNz < tolerance) {
345
349
  // X-aligned plane
346
350
  if (normal[0] > 0) {
347
351
  // Normal points +X, clips -X side
348
- minX = Math.max(minX, point[0]);
352
+ minX = Math.max(minX, point[0])
349
353
  } else {
350
354
  // Normal points -X, clips +X side
351
- maxX = Math.min(maxX, point[0]);
355
+ maxX = Math.min(maxX, point[0])
352
356
  }
353
357
  } else if (
354
- absNy > 1 - tolerance && absNx < tolerance && absNz < tolerance
358
+ absNy > 1 - tolerance &&
359
+ absNx < tolerance &&
360
+ absNz < tolerance
355
361
  ) {
356
362
  // Y-aligned plane
357
363
  if (normal[1] > 0) {
358
- minY = Math.max(minY, point[1]);
364
+ minY = Math.max(minY, point[1])
359
365
  } else {
360
- maxY = Math.min(maxY, point[1]);
366
+ maxY = Math.min(maxY, point[1])
361
367
  }
362
368
  } else if (
363
- absNz > 1 - tolerance && absNx < tolerance && absNy < tolerance
369
+ absNz > 1 - tolerance &&
370
+ absNx < tolerance &&
371
+ absNy < tolerance
364
372
  ) {
365
373
  // Z-aligned plane
366
374
  if (normal[2] > 0) {
367
- minZ = Math.max(minZ, point[2]);
375
+ minZ = Math.max(minZ, point[2])
368
376
  } else {
369
- maxZ = Math.min(maxZ, point[2]);
377
+ maxZ = Math.min(maxZ, point[2])
370
378
  }
371
379
  } else {
372
380
  // Oblique plane - use conservative approximation
@@ -377,38 +385,30 @@ export function clipPlanesToBoundingBox(
377
385
  // Project the plane point onto each axis and use as potential bound
378
386
  // Only constrain if the plane actually intersects that face
379
387
  if (normal[0] > tolerance) {
380
- minX = Math.max(minX, Math.min(point[0], maxX));
388
+ minX = Math.max(minX, Math.min(point[0], maxX))
381
389
  } else if (normal[0] < -tolerance) {
382
- maxX = Math.min(maxX, Math.max(point[0], minX));
390
+ maxX = Math.min(maxX, Math.max(point[0], minX))
383
391
  }
384
392
 
385
393
  if (normal[1] > tolerance) {
386
- minY = Math.max(minY, Math.min(point[1], maxY));
394
+ minY = Math.max(minY, Math.min(point[1], maxY))
387
395
  } else if (normal[1] < -tolerance) {
388
- maxY = Math.min(maxY, Math.max(point[1], minY));
396
+ maxY = Math.min(maxY, Math.max(point[1], minY))
389
397
  }
390
398
 
391
399
  if (normal[2] > tolerance) {
392
- minZ = Math.max(minZ, Math.min(point[2], maxZ));
400
+ minZ = Math.max(minZ, Math.min(point[2], maxZ))
393
401
  } else if (normal[2] < -tolerance) {
394
- maxZ = Math.min(maxZ, Math.max(point[2], minZ));
402
+ maxZ = Math.min(maxZ, Math.max(point[2], minZ))
395
403
  }
396
404
  }
397
405
  }
398
406
 
399
407
  // Ensure valid bounds (min <= max)
400
408
  return {
401
- min: [
402
- Math.min(minX, maxX),
403
- Math.min(minY, maxY),
404
- Math.min(minZ, maxZ),
405
- ],
406
- max: [
407
- Math.max(minX, maxX),
408
- Math.max(minY, maxY),
409
- Math.max(minZ, maxZ),
410
- ],
411
- };
409
+ min: [Math.min(minX, maxX), Math.min(minY, maxY), Math.min(minZ, maxZ)],
410
+ max: [Math.max(minX, maxX), Math.max(minY, maxY), Math.max(minZ, maxZ)],
411
+ }
412
412
  }
413
413
 
414
414
  /**
@@ -431,7 +431,7 @@ export function clipPlanesToPixelRegion(
431
431
  ngffImage: NgffImage,
432
432
  viewportBounds?: VolumeBounds,
433
433
  ): PixelRegion {
434
- let bounds = clipPlanesToBoundingBox(clipPlanes, volumeBounds);
434
+ let bounds = clipPlanesToBoundingBox(clipPlanes, volumeBounds)
435
435
 
436
436
  // If viewport bounds are provided, intersect with them
437
437
  if (viewportBounds) {
@@ -446,46 +446,46 @@ export function clipPlanesToPixelRegion(
446
446
  Math.min(bounds.max[1], viewportBounds.max[1]),
447
447
  Math.min(bounds.max[2], viewportBounds.max[2]),
448
448
  ],
449
- };
449
+ }
450
450
  // Ensure valid bounds
451
451
  for (let i = 0; i < 3; i++) {
452
452
  if (bounds.min[i] > bounds.max[i]) {
453
- bounds.min[i] = bounds.max[i] = (bounds.min[i] + bounds.max[i]) * 0.5;
453
+ bounds.min[i] = bounds.max[i] = (bounds.min[i] + bounds.max[i]) * 0.5
454
454
  }
455
455
  }
456
456
  }
457
457
 
458
- const shape = getVolumeShape(ngffImage);
458
+ const shape = getVolumeShape(ngffImage)
459
459
 
460
460
  // Convert world corners to pixel coordinates
461
461
  const minWorld: [number, number, number] = [
462
462
  bounds.min[0],
463
463
  bounds.min[1],
464
464
  bounds.min[2],
465
- ];
465
+ ]
466
466
  const maxWorld: [number, number, number] = [
467
467
  bounds.max[0],
468
468
  bounds.max[1],
469
469
  bounds.max[2],
470
- ];
470
+ ]
471
471
 
472
- const minPixel = worldToPixel(minWorld, ngffImage);
473
- const maxPixel = worldToPixel(maxWorld, ngffImage);
472
+ const minPixel = worldToPixel(minWorld, ngffImage)
473
+ const maxPixel = worldToPixel(maxWorld, ngffImage)
474
474
 
475
475
  // Ensure proper ordering and clamp to valid range
476
476
  const start: [number, number, number] = [
477
477
  Math.max(0, Math.floor(Math.min(minPixel[0], maxPixel[0]))),
478
478
  Math.max(0, Math.floor(Math.min(minPixel[1], maxPixel[1]))),
479
479
  Math.max(0, Math.floor(Math.min(minPixel[2], maxPixel[2]))),
480
- ];
480
+ ]
481
481
 
482
482
  const end: [number, number, number] = [
483
483
  Math.min(shape[0], Math.ceil(Math.max(minPixel[0], maxPixel[0]))),
484
484
  Math.min(shape[1], Math.ceil(Math.max(minPixel[1], maxPixel[1]))),
485
485
  Math.min(shape[2], Math.ceil(Math.max(minPixel[2], maxPixel[2]))),
486
- ];
486
+ ]
487
487
 
488
- return { start, end };
488
+ return { start, end }
489
489
  }
490
490
 
491
491
  /**
@@ -502,15 +502,15 @@ export function alignToChunks(
502
502
  region: PixelRegion,
503
503
  ngffImage: NgffImage,
504
504
  ): ChunkAlignedRegion {
505
- const chunkShape = getChunkShape(ngffImage);
506
- const volumeShape = getVolumeShape(ngffImage);
505
+ const chunkShape = getChunkShape(ngffImage)
506
+ const volumeShape = getVolumeShape(ngffImage)
507
507
 
508
508
  // Align start down to chunk boundary
509
509
  const chunkAlignedStart: [number, number, number] = [
510
510
  Math.floor(region.start[0] / chunkShape[0]) * chunkShape[0],
511
511
  Math.floor(region.start[1] / chunkShape[1]) * chunkShape[1],
512
512
  Math.floor(region.start[2] / chunkShape[2]) * chunkShape[2],
513
- ];
513
+ ]
514
514
 
515
515
  // Align end up to chunk boundary (but don't exceed volume size)
516
516
  const chunkAlignedEnd: [number, number, number] = [
@@ -526,15 +526,16 @@ export function alignToChunks(
526
526
  Math.ceil(region.end[2] / chunkShape[2]) * chunkShape[2],
527
527
  volumeShape[2],
528
528
  ),
529
- ];
529
+ ]
530
530
 
531
531
  // Check if alignment changed the region
532
- const needsClipping = chunkAlignedStart[0] !== region.start[0] ||
532
+ const needsClipping =
533
+ chunkAlignedStart[0] !== region.start[0] ||
533
534
  chunkAlignedStart[1] !== region.start[1] ||
534
535
  chunkAlignedStart[2] !== region.start[2] ||
535
536
  chunkAlignedEnd[0] !== region.end[0] ||
536
537
  chunkAlignedEnd[1] !== region.end[1] ||
537
- chunkAlignedEnd[2] !== region.end[2];
538
+ chunkAlignedEnd[2] !== region.end[2]
538
539
 
539
540
  return {
540
541
  start: region.start,
@@ -542,7 +543,7 @@ export function alignToChunks(
542
543
  chunkAlignedStart,
543
544
  chunkAlignedEnd,
544
545
  needsClipping,
545
- };
546
+ }
546
547
  }
547
548
 
548
549
  /**
@@ -564,35 +565,35 @@ export function createAxisAlignedClipPlane(
564
565
  direction: "positive" | "negative",
565
566
  volumeBounds: VolumeBounds,
566
567
  ): ClipPlane {
567
- const { min, max } = volumeBounds;
568
+ const { min, max } = volumeBounds
568
569
  const center: [number, number, number] = [
569
570
  (min[0] + max[0]) / 2,
570
571
  (min[1] + max[1]) / 2,
571
572
  (min[2] + max[2]) / 2,
572
- ];
573
+ ]
573
574
 
574
- let point: [number, number, number];
575
- let normal: [number, number, number];
575
+ let point: [number, number, number]
576
+ let normal: [number, number, number]
576
577
 
577
578
  // Normal points toward the visible region
578
- const sign = direction === "positive" ? 1 : -1;
579
+ const sign = direction === "positive" ? 1 : -1
579
580
 
580
581
  switch (axis) {
581
582
  case "x":
582
- point = [position, center[1], center[2]];
583
- normal = [sign, 0, 0];
584
- break;
583
+ point = [position, center[1], center[2]]
584
+ normal = [sign, 0, 0]
585
+ break
585
586
  case "y":
586
- point = [center[0], position, center[2]];
587
- normal = [0, sign, 0];
588
- break;
587
+ point = [center[0], position, center[2]]
588
+ normal = [0, sign, 0]
589
+ break
589
590
  case "z":
590
- point = [center[0], center[1], position];
591
- normal = [0, 0, sign];
592
- break;
591
+ point = [center[0], center[1], position]
592
+ normal = [0, 0, sign]
593
+ break
593
594
  }
594
595
 
595
- return { point, normal };
596
+ return { point, normal }
596
597
  }
597
598
 
598
599
  /**
@@ -605,36 +606,36 @@ export function validateClipPlanes(clipPlanes: ClipPlanes): void {
605
606
  if (clipPlanes.length > MAX_CLIP_PLANES) {
606
607
  throw new Error(
607
608
  `Too many clip planes: ${clipPlanes.length} exceeds maximum of ${MAX_CLIP_PLANES}`,
608
- );
609
+ )
609
610
  }
610
611
 
611
612
  for (let i = 0; i < clipPlanes.length; i++) {
612
- const plane = clipPlanes[i];
613
+ const plane = clipPlanes[i]
613
614
 
614
615
  // Check point is valid
615
616
  if (
616
617
  !Array.isArray(plane.point) ||
617
618
  plane.point.length !== 3 ||
618
- plane.point.some((v) => typeof v !== "number" || !isFinite(v))
619
+ plane.point.some((v) => typeof v !== "number" || !Number.isFinite(v))
619
620
  ) {
620
- throw new Error(`Invalid point in clip plane ${i}`);
621
+ throw new Error(`Invalid point in clip plane ${i}`)
621
622
  }
622
623
 
623
624
  // Check normal is valid
624
625
  if (
625
626
  !Array.isArray(plane.normal) ||
626
627
  plane.normal.length !== 3 ||
627
- plane.normal.some((v) => typeof v !== "number" || !isFinite(v))
628
+ plane.normal.some((v) => typeof v !== "number" || !Number.isFinite(v))
628
629
  ) {
629
- throw new Error(`Invalid normal in clip plane ${i}`);
630
+ throw new Error(`Invalid normal in clip plane ${i}`)
630
631
  }
631
632
 
632
633
  // Check normal is not zero
633
634
  const length = Math.sqrt(
634
635
  plane.normal[0] ** 2 + plane.normal[1] ** 2 + plane.normal[2] ** 2,
635
- );
636
+ )
636
637
  if (length < 0.0001) {
637
- throw new Error(`Zero-length normal in clip plane ${i}`);
638
+ throw new Error(`Zero-length normal in clip plane ${i}`)
638
639
  }
639
640
  }
640
641
  }