@fideus-labs/fidnii 0.1.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 (58) hide show
  1. package/LICENSE.txt +9 -0
  2. package/README.md +180 -0
  3. package/dist/BufferManager.d.ts +86 -0
  4. package/dist/BufferManager.d.ts.map +1 -0
  5. package/dist/BufferManager.js +146 -0
  6. package/dist/BufferManager.js.map +1 -0
  7. package/dist/ClipPlanes.d.ts +180 -0
  8. package/dist/ClipPlanes.d.ts.map +1 -0
  9. package/dist/ClipPlanes.js +513 -0
  10. package/dist/ClipPlanes.js.map +1 -0
  11. package/dist/OMEZarrNVImage.d.ts +545 -0
  12. package/dist/OMEZarrNVImage.d.ts.map +1 -0
  13. package/dist/OMEZarrNVImage.js +1799 -0
  14. package/dist/OMEZarrNVImage.js.map +1 -0
  15. package/dist/RegionCoalescer.d.ts +75 -0
  16. package/dist/RegionCoalescer.d.ts.map +1 -0
  17. package/dist/RegionCoalescer.js +151 -0
  18. package/dist/RegionCoalescer.js.map +1 -0
  19. package/dist/ResolutionSelector.d.ts +88 -0
  20. package/dist/ResolutionSelector.d.ts.map +1 -0
  21. package/dist/ResolutionSelector.js +224 -0
  22. package/dist/ResolutionSelector.js.map +1 -0
  23. package/dist/ViewportBounds.d.ts +50 -0
  24. package/dist/ViewportBounds.d.ts.map +1 -0
  25. package/dist/ViewportBounds.js +325 -0
  26. package/dist/ViewportBounds.js.map +1 -0
  27. package/dist/events.d.ts +122 -0
  28. package/dist/events.d.ts.map +1 -0
  29. package/dist/events.js +12 -0
  30. package/dist/events.js.map +1 -0
  31. package/dist/index.d.ts +48 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +59 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/types.d.ts +273 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +126 -0
  38. package/dist/types.js.map +1 -0
  39. package/dist/utils/affine.d.ts +72 -0
  40. package/dist/utils/affine.d.ts.map +1 -0
  41. package/dist/utils/affine.js +173 -0
  42. package/dist/utils/affine.js.map +1 -0
  43. package/dist/utils/coordinates.d.ts +80 -0
  44. package/dist/utils/coordinates.d.ts.map +1 -0
  45. package/dist/utils/coordinates.js +207 -0
  46. package/dist/utils/coordinates.js.map +1 -0
  47. package/package.json +61 -0
  48. package/src/BufferManager.ts +176 -0
  49. package/src/ClipPlanes.ts +640 -0
  50. package/src/OMEZarrNVImage.ts +2286 -0
  51. package/src/RegionCoalescer.ts +217 -0
  52. package/src/ResolutionSelector.ts +325 -0
  53. package/src/ViewportBounds.ts +369 -0
  54. package/src/events.ts +146 -0
  55. package/src/index.ts +153 -0
  56. package/src/types.ts +429 -0
  57. package/src/utils/affine.ts +218 -0
  58. package/src/utils/coordinates.ts +271 -0
@@ -0,0 +1,513 @@
1
+ // SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC
2
+ // SPDX-License-Identifier: MIT
3
+ import { pixelToWorld, worldToPixel } from "./utils/coordinates.js";
4
+ import { getChunkShape, getVolumeShape } from "./ResolutionSelector.js";
5
+ /** Maximum number of clip planes supported by NiiVue */
6
+ export const MAX_CLIP_PLANES = 6;
7
+ /**
8
+ * Normalize a 3D vector to unit length.
9
+ *
10
+ * @param v - Vector to normalize [x, y, z]
11
+ * @returns Normalized vector [x, y, z]
12
+ * @throws Error if vector has zero length
13
+ */
14
+ export function normalizeVector(v) {
15
+ const length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
16
+ if (length === 0) {
17
+ throw new Error("Cannot normalize zero-length vector");
18
+ }
19
+ return [v[0] / length, v[1] / length, v[2] / length];
20
+ }
21
+ /**
22
+ * Create a clip plane from a point and normal vector.
23
+ * The normal is automatically normalized to unit length.
24
+ *
25
+ * @param point - A point on the plane [x, y, z] in world coordinates
26
+ * @param normal - Normal vector pointing toward visible region [x, y, z]
27
+ * @returns ClipPlane with normalized normal
28
+ */
29
+ export function createClipPlane(point, normal) {
30
+ return {
31
+ point: [...point],
32
+ normal: normalizeVector(normal),
33
+ };
34
+ }
35
+ /**
36
+ * Create default clip planes (empty array = full volume visible).
37
+ *
38
+ * @param _multiscales - The OME-Zarr multiscales data (unused, kept for API consistency)
39
+ * @returns Empty ClipPlanes array
40
+ */
41
+ export function createDefaultClipPlanes(_multiscales) {
42
+ return [];
43
+ }
44
+ /**
45
+ * Get volume bounds from multiscales metadata.
46
+ *
47
+ * @param multiscales - The OME-Zarr multiscales data
48
+ * @returns Volume bounds in world space
49
+ */
50
+ export function getVolumeBoundsFromMultiscales(multiscales) {
51
+ // Use highest resolution for most accurate bounds
52
+ const image = multiscales.images[0];
53
+ const shape = getVolumeShape(image);
54
+ const minPixel = [0, 0, 0];
55
+ const maxPixel = [shape[0], shape[1], shape[2]];
56
+ const minWorld = pixelToWorld(minPixel, image);
57
+ const maxWorld = pixelToWorld(maxPixel, image);
58
+ return {
59
+ min: [
60
+ Math.min(minWorld[0], maxWorld[0]),
61
+ Math.min(minWorld[1], maxWorld[1]),
62
+ Math.min(minWorld[2], maxWorld[2]),
63
+ ],
64
+ max: [
65
+ Math.max(minWorld[0], maxWorld[0]),
66
+ Math.max(minWorld[1], maxWorld[1]),
67
+ Math.max(minWorld[2], maxWorld[2]),
68
+ ],
69
+ };
70
+ }
71
+ /**
72
+ * Convert a normal vector to azimuth and elevation angles (for NiiVue).
73
+ *
74
+ * NiiVue convention:
75
+ * - Azimuth: 0 = posterior (+Y), 90 = right (+X), 180 = anterior (-Y), 270 = left (-X)
76
+ * - Elevation: 0 = horizontal, 90 = superior (+Z), -90 = inferior (-Z)
77
+ *
78
+ * @param normal - Unit normal vector [x, y, z]
79
+ * @returns Object with azimuth and elevation in degrees
80
+ */
81
+ export function normalToAzimuthElevation(normal) {
82
+ const [x, y, z] = normal;
83
+ // Elevation: angle from XY plane (arcsin of z component)
84
+ // Clamp to [-1, 1] to handle floating point errors
85
+ const elevation = Math.asin(Math.max(-1, Math.min(1, z))) * (180 / Math.PI);
86
+ // Azimuth: angle in XY plane from +Y axis
87
+ // atan2(x, y) gives angle from +Y, which matches NiiVue's azimuth=0 = posterior (+Y)
88
+ let azimuth = Math.atan2(x, y) * (180 / Math.PI);
89
+ // Normalize to [0, 360)
90
+ azimuth = ((azimuth % 360) + 360) % 360;
91
+ return { azimuth, elevation };
92
+ }
93
+ /**
94
+ * Convert azimuth and elevation angles to a unit normal vector.
95
+ *
96
+ * @param azimuth - Azimuth angle in degrees (0 = +Y, 90 = +X)
97
+ * @param elevation - Elevation angle in degrees (0 = horizontal, 90 = +Z)
98
+ * @returns Unit normal vector [x, y, z]
99
+ */
100
+ export function azimuthElevationToNormal(azimuth, elevation) {
101
+ const azRad = (azimuth * Math.PI) / 180;
102
+ const elRad = (elevation * Math.PI) / 180;
103
+ const cosEl = Math.cos(elRad);
104
+ const x = cosEl * Math.sin(azRad);
105
+ const y = cosEl * Math.cos(azRad);
106
+ const z = Math.sin(elRad);
107
+ return [x, y, z];
108
+ }
109
+ /**
110
+ * Calculate the NiiVue depth parameter for a clip plane.
111
+ *
112
+ * NiiVue's clip plane depth is in normalized texture coordinates where
113
+ * the volume center is at 0.5. Depth represents the signed distance from
114
+ * the center (0) to the plane, where -0.5 is at min boundary and +0.5 is
115
+ * at max boundary. Values beyond [-0.5, 0.5] place the plane outside the volume.
116
+ *
117
+ * @param plane - The clip plane
118
+ * @param volumeBounds - Volume bounds in world space
119
+ * @returns Depth value for NiiVue (typically in range [-0.5, 0.5] for planes within volume)
120
+ */
121
+ export function calculateNiivueDepth(plane, volumeBounds) {
122
+ const { min, max } = volumeBounds;
123
+ // Volume center
124
+ const center = [
125
+ (min[0] + max[0]) / 2,
126
+ (min[1] + max[1]) / 2,
127
+ (min[2] + max[2]) / 2,
128
+ ];
129
+ // Volume extent
130
+ const extent = [
131
+ max[0] - min[0],
132
+ max[1] - min[1],
133
+ max[2] - min[2],
134
+ ];
135
+ // Signed distance from center to plane along normal
136
+ const { point, normal } = plane;
137
+ const signedDistance = normal[0] * (point[0] - center[0]) +
138
+ normal[1] * (point[1] - center[1]) +
139
+ normal[2] * (point[2] - center[2]);
140
+ // Full extent along normal direction (using absolute value of each component)
141
+ // This is the "width" of the bounding box when projected onto the normal direction
142
+ const extentAlongNormal = Math.abs(normal[0]) * extent[0] +
143
+ Math.abs(normal[1]) * extent[1] +
144
+ Math.abs(normal[2]) * extent[2];
145
+ // Avoid division by zero
146
+ if (extentAlongNormal === 0) {
147
+ return 0;
148
+ }
149
+ // Normalize to NiiVue's coordinate system where volume spans -0.5 to 0.5 from center
150
+ return signedDistance / extentAlongNormal;
151
+ }
152
+ /**
153
+ * Convert a single clip plane to NiiVue format [depth, azimuth, elevation].
154
+ *
155
+ * NiiVue's shader convention:
156
+ * - The "back" side of the plane (sampleSide > 0) is VISIBLE
157
+ * - The "front" side of the plane (sampleSide < 0) is CLIPPED
158
+ * - sampleSide = dot(shaderNormal, p - 0.5) + depth
159
+ * - NiiVue internally adds 180° to azimuth, which flips the normal direction
160
+ *
161
+ * Our convention:
162
+ * - Normal points toward the VISIBLE region
163
+ *
164
+ * To reconcile these conventions:
165
+ * 1. We negate the normal before computing azimuth/elevation
166
+ * 2. After NiiVue's +180° flip, the shader sees our original normal direction
167
+ * 3. We also negate the depth to match the flipped normal
168
+ *
169
+ * @param plane - The clip plane
170
+ * @param volumeBounds - Volume bounds in world space
171
+ * @returns [depth, azimuth, elevation] for NiiVue
172
+ */
173
+ export function clipPlaneToNiivue(plane, volumeBounds) {
174
+ const depth = calculateNiivueDepth(plane, volumeBounds);
175
+ // Negate the normal for azimuth/elevation calculation.
176
+ // After NiiVue adds 180° to azimuth, the shader will see our original normal.
177
+ const negatedNormal = [
178
+ -plane.normal[0],
179
+ -plane.normal[1],
180
+ -plane.normal[2],
181
+ ];
182
+ const { azimuth, elevation } = normalToAzimuthElevation(negatedNormal);
183
+ // Also negate the depth to be consistent with the flipped normal.
184
+ // The plane equation dot(n, p-center) + d = 0 changes sign when n is negated.
185
+ const negatedDepth = -depth;
186
+ return [negatedDepth, azimuth, elevation];
187
+ }
188
+ /**
189
+ * Convert clip planes to NiiVue format.
190
+ *
191
+ * @param clipPlanes - Array of clip planes
192
+ * @param volumeBounds - Volume bounds in world space
193
+ * @returns Array of [depth, azimuth, elevation] for NiiVue
194
+ */
195
+ export function clipPlanesToNiivue(clipPlanes, volumeBounds) {
196
+ return clipPlanes.map((plane) => clipPlaneToNiivue(plane, volumeBounds));
197
+ }
198
+ /**
199
+ * Calculate the signed distance from a point to a plane.
200
+ *
201
+ * Positive = point is on the visible side (same side as normal)
202
+ * Negative = point is on the clipped side (opposite side from normal)
203
+ *
204
+ * @param testPoint - Point to test [x, y, z]
205
+ * @param plane - The clip plane
206
+ * @returns Signed distance
207
+ */
208
+ export function pointToPlaneDistance(testPoint, plane) {
209
+ const { point, normal } = plane;
210
+ return (normal[0] * (testPoint[0] - point[0]) +
211
+ normal[1] * (testPoint[1] - point[1]) +
212
+ normal[2] * (testPoint[2] - point[2]));
213
+ }
214
+ /**
215
+ * Check if a point is inside all clip planes (on the visible side).
216
+ *
217
+ * @param worldCoord - World coordinate [x, y, z]
218
+ * @param clipPlanes - Array of clip planes
219
+ * @returns True if the point is inside all clip planes (or if there are no planes)
220
+ */
221
+ export function isInsideClipPlanes(worldCoord, clipPlanes) {
222
+ for (const plane of clipPlanes) {
223
+ if (pointToPlaneDistance(worldCoord, plane) < 0) {
224
+ return false;
225
+ }
226
+ }
227
+ return true;
228
+ }
229
+ /**
230
+ * Calculate the axis-aligned bounding box that contains the clipped region.
231
+ *
232
+ * For oblique clip planes, this finds the intersection of the clip planes
233
+ * with the volume bounds and returns the AABB of that intersection.
234
+ *
235
+ * This is used for data fetching (zarr is always axis-aligned).
236
+ *
237
+ * @param clipPlanes - Array of clip planes
238
+ * @param volumeBounds - Full volume bounds in world space
239
+ * @returns Bounding box of the clipped region
240
+ */
241
+ export function clipPlanesToBoundingBox(clipPlanes, volumeBounds) {
242
+ // If no clip planes, return full volume
243
+ if (clipPlanes.length === 0) {
244
+ return {
245
+ min: [...volumeBounds.min],
246
+ max: [...volumeBounds.max],
247
+ };
248
+ }
249
+ // Start with full volume bounds
250
+ let minX = volumeBounds.min[0];
251
+ let maxX = volumeBounds.max[0];
252
+ let minY = volumeBounds.min[1];
253
+ let maxY = volumeBounds.max[1];
254
+ let minZ = volumeBounds.min[2];
255
+ let maxZ = volumeBounds.max[2];
256
+ // For each clip plane, constrain the bounding box
257
+ // This is an approximation: we check the 8 corners and constrain based on
258
+ // which corners are clipped. For axis-aligned planes, this is exact.
259
+ // For oblique planes, it's a conservative approximation.
260
+ for (const plane of clipPlanes) {
261
+ const { point, normal } = plane;
262
+ // For axis-aligned normals, we can compute exact bounds
263
+ const absNx = Math.abs(normal[0]);
264
+ const absNy = Math.abs(normal[1]);
265
+ const absNz = Math.abs(normal[2]);
266
+ // Check if plane is approximately axis-aligned
267
+ const tolerance = 0.001;
268
+ if (absNx > 1 - tolerance && absNy < tolerance && absNz < tolerance) {
269
+ // X-aligned plane
270
+ if (normal[0] > 0) {
271
+ // Normal points +X, clips -X side
272
+ minX = Math.max(minX, point[0]);
273
+ }
274
+ else {
275
+ // Normal points -X, clips +X side
276
+ maxX = Math.min(maxX, point[0]);
277
+ }
278
+ }
279
+ else if (absNy > 1 - tolerance && absNx < tolerance && absNz < tolerance) {
280
+ // Y-aligned plane
281
+ if (normal[1] > 0) {
282
+ minY = Math.max(minY, point[1]);
283
+ }
284
+ else {
285
+ maxY = Math.min(maxY, point[1]);
286
+ }
287
+ }
288
+ else if (absNz > 1 - tolerance && absNx < tolerance && absNy < tolerance) {
289
+ // Z-aligned plane
290
+ if (normal[2] > 0) {
291
+ minZ = Math.max(minZ, point[2]);
292
+ }
293
+ else {
294
+ maxZ = Math.min(maxZ, point[2]);
295
+ }
296
+ }
297
+ else {
298
+ // Oblique plane - use conservative approximation
299
+ // Find the extent of the plane intersection with the volume
300
+ // For simplicity, we use the point on the plane as a bound hint
301
+ // This is conservative (may fetch more data than needed)
302
+ // Project the plane point onto each axis and use as potential bound
303
+ // Only constrain if the plane actually intersects that face
304
+ if (normal[0] > tolerance) {
305
+ minX = Math.max(minX, Math.min(point[0], maxX));
306
+ }
307
+ else if (normal[0] < -tolerance) {
308
+ maxX = Math.min(maxX, Math.max(point[0], minX));
309
+ }
310
+ if (normal[1] > tolerance) {
311
+ minY = Math.max(minY, Math.min(point[1], maxY));
312
+ }
313
+ else if (normal[1] < -tolerance) {
314
+ maxY = Math.min(maxY, Math.max(point[1], minY));
315
+ }
316
+ if (normal[2] > tolerance) {
317
+ minZ = Math.max(minZ, Math.min(point[2], maxZ));
318
+ }
319
+ else if (normal[2] < -tolerance) {
320
+ maxZ = Math.min(maxZ, Math.max(point[2], minZ));
321
+ }
322
+ }
323
+ }
324
+ // Ensure valid bounds (min <= max)
325
+ return {
326
+ min: [
327
+ Math.min(minX, maxX),
328
+ Math.min(minY, maxY),
329
+ Math.min(minZ, maxZ),
330
+ ],
331
+ max: [
332
+ Math.max(minX, maxX),
333
+ Math.max(minY, maxY),
334
+ Math.max(minZ, maxZ),
335
+ ],
336
+ };
337
+ }
338
+ /**
339
+ * Convert clip planes to a pixel region for a specific NgffImage.
340
+ *
341
+ * This calculates the axis-aligned bounding box of the clipped region
342
+ * and converts it to pixel coordinates. When viewportBounds is provided,
343
+ * the result is further constrained to only include the visible viewport
344
+ * area (for viewport-aware resolution selection).
345
+ *
346
+ * @param clipPlanes - Array of clip planes
347
+ * @param volumeBounds - Full volume bounds in world space
348
+ * @param ngffImage - The NgffImage to convert to
349
+ * @param viewportBounds - Optional viewport bounds to intersect with
350
+ * @returns Pixel region [z, y, x] start and end indices
351
+ */
352
+ export function clipPlanesToPixelRegion(clipPlanes, volumeBounds, ngffImage, viewportBounds) {
353
+ let bounds = clipPlanesToBoundingBox(clipPlanes, volumeBounds);
354
+ // If viewport bounds are provided, intersect with them
355
+ if (viewportBounds) {
356
+ bounds = {
357
+ min: [
358
+ Math.max(bounds.min[0], viewportBounds.min[0]),
359
+ Math.max(bounds.min[1], viewportBounds.min[1]),
360
+ Math.max(bounds.min[2], viewportBounds.min[2]),
361
+ ],
362
+ max: [
363
+ Math.min(bounds.max[0], viewportBounds.max[0]),
364
+ Math.min(bounds.max[1], viewportBounds.max[1]),
365
+ Math.min(bounds.max[2], viewportBounds.max[2]),
366
+ ],
367
+ };
368
+ // Ensure valid bounds
369
+ for (let i = 0; i < 3; i++) {
370
+ if (bounds.min[i] > bounds.max[i]) {
371
+ bounds.min[i] = bounds.max[i] = (bounds.min[i] + bounds.max[i]) * 0.5;
372
+ }
373
+ }
374
+ }
375
+ const shape = getVolumeShape(ngffImage);
376
+ // Convert world corners to pixel coordinates
377
+ const minWorld = [
378
+ bounds.min[0],
379
+ bounds.min[1],
380
+ bounds.min[2],
381
+ ];
382
+ const maxWorld = [
383
+ bounds.max[0],
384
+ bounds.max[1],
385
+ bounds.max[2],
386
+ ];
387
+ const minPixel = worldToPixel(minWorld, ngffImage);
388
+ const maxPixel = worldToPixel(maxWorld, ngffImage);
389
+ // Ensure proper ordering and clamp to valid range
390
+ const start = [
391
+ Math.max(0, Math.floor(Math.min(minPixel[0], maxPixel[0]))),
392
+ Math.max(0, Math.floor(Math.min(minPixel[1], maxPixel[1]))),
393
+ Math.max(0, Math.floor(Math.min(minPixel[2], maxPixel[2]))),
394
+ ];
395
+ const end = [
396
+ Math.min(shape[0], Math.ceil(Math.max(minPixel[0], maxPixel[0]))),
397
+ Math.min(shape[1], Math.ceil(Math.max(minPixel[1], maxPixel[1]))),
398
+ Math.min(shape[2], Math.ceil(Math.max(minPixel[2], maxPixel[2]))),
399
+ ];
400
+ return { start, end };
401
+ }
402
+ /**
403
+ * Align a pixel region to chunk boundaries.
404
+ *
405
+ * This expands the region to include complete chunks, which is necessary
406
+ * for efficient zarr fetching.
407
+ *
408
+ * @param region - The pixel region to align
409
+ * @param ngffImage - The NgffImage (for chunk shape)
410
+ * @returns Chunk-aligned region with clipping information
411
+ */
412
+ export function alignToChunks(region, ngffImage) {
413
+ const chunkShape = getChunkShape(ngffImage);
414
+ const volumeShape = getVolumeShape(ngffImage);
415
+ // Align start down to chunk boundary
416
+ const chunkAlignedStart = [
417
+ Math.floor(region.start[0] / chunkShape[0]) * chunkShape[0],
418
+ Math.floor(region.start[1] / chunkShape[1]) * chunkShape[1],
419
+ Math.floor(region.start[2] / chunkShape[2]) * chunkShape[2],
420
+ ];
421
+ // Align end up to chunk boundary (but don't exceed volume size)
422
+ const chunkAlignedEnd = [
423
+ Math.min(Math.ceil(region.end[0] / chunkShape[0]) * chunkShape[0], volumeShape[0]),
424
+ Math.min(Math.ceil(region.end[1] / chunkShape[1]) * chunkShape[1], volumeShape[1]),
425
+ Math.min(Math.ceil(region.end[2] / chunkShape[2]) * chunkShape[2], volumeShape[2]),
426
+ ];
427
+ // Check if alignment changed the region
428
+ const needsClipping = chunkAlignedStart[0] !== region.start[0] ||
429
+ chunkAlignedStart[1] !== region.start[1] ||
430
+ chunkAlignedStart[2] !== region.start[2] ||
431
+ chunkAlignedEnd[0] !== region.end[0] ||
432
+ chunkAlignedEnd[1] !== region.end[1] ||
433
+ chunkAlignedEnd[2] !== region.end[2];
434
+ return {
435
+ start: region.start,
436
+ end: region.end,
437
+ chunkAlignedStart,
438
+ chunkAlignedEnd,
439
+ needsClipping,
440
+ };
441
+ }
442
+ /**
443
+ * Create an axis-aligned clip plane at a specific position.
444
+ *
445
+ * The normal points toward the VISIBLE region (the region to keep).
446
+ * NiiVue's shader convention is that the "back" side of the plane (where
447
+ * dot(n, p-center) + depth > 0) is visible.
448
+ *
449
+ * @param axis - The axis ('x', 'y', or 'z')
450
+ * @param position - Position along the axis in world coordinates
451
+ * @param direction - Which side to keep visible ('positive' or 'negative')
452
+ * @param volumeBounds - Volume bounds for centering the point
453
+ * @returns ClipPlane with point at center of volume projected to the plane
454
+ */
455
+ export function createAxisAlignedClipPlane(axis, position, direction, volumeBounds) {
456
+ const { min, max } = volumeBounds;
457
+ const center = [
458
+ (min[0] + max[0]) / 2,
459
+ (min[1] + max[1]) / 2,
460
+ (min[2] + max[2]) / 2,
461
+ ];
462
+ let point;
463
+ let normal;
464
+ // Normal points toward the visible region
465
+ const sign = direction === "positive" ? 1 : -1;
466
+ switch (axis) {
467
+ case "x":
468
+ point = [position, center[1], center[2]];
469
+ normal = [sign, 0, 0];
470
+ break;
471
+ case "y":
472
+ point = [center[0], position, center[2]];
473
+ normal = [0, sign, 0];
474
+ break;
475
+ case "z":
476
+ point = [center[0], center[1], position];
477
+ normal = [0, 0, sign];
478
+ break;
479
+ }
480
+ return { point, normal };
481
+ }
482
+ /**
483
+ * Validate clip planes array.
484
+ *
485
+ * @param clipPlanes - Array of clip planes to validate
486
+ * @throws Error if validation fails
487
+ */
488
+ export function validateClipPlanes(clipPlanes) {
489
+ if (clipPlanes.length > MAX_CLIP_PLANES) {
490
+ throw new Error(`Too many clip planes: ${clipPlanes.length} exceeds maximum of ${MAX_CLIP_PLANES}`);
491
+ }
492
+ for (let i = 0; i < clipPlanes.length; i++) {
493
+ const plane = clipPlanes[i];
494
+ // Check point is valid
495
+ if (!Array.isArray(plane.point) ||
496
+ plane.point.length !== 3 ||
497
+ plane.point.some((v) => typeof v !== "number" || !isFinite(v))) {
498
+ throw new Error(`Invalid point in clip plane ${i}`);
499
+ }
500
+ // Check normal is valid
501
+ if (!Array.isArray(plane.normal) ||
502
+ plane.normal.length !== 3 ||
503
+ plane.normal.some((v) => typeof v !== "number" || !isFinite(v))) {
504
+ throw new Error(`Invalid normal in clip plane ${i}`);
505
+ }
506
+ // Check normal is not zero
507
+ const length = Math.sqrt(plane.normal[0] ** 2 + plane.normal[1] ** 2 + plane.normal[2] ** 2);
508
+ if (length < 0.0001) {
509
+ throw new Error(`Zero-length normal in clip plane ${i}`);
510
+ }
511
+ }
512
+ }
513
+ //# sourceMappingURL=ClipPlanes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClipPlanes.js","sourceRoot":"","sources":["../src/ClipPlanes.ts"],"names":[],"mappings":"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"}