@idetik/core 0.40.0 → 0.40.1

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 (71) hide show
  1. package/dist/assets/worker_kernel-CBlE4MBI.js.map +1 -1
  2. package/dist/index.d.ts +1717 -381
  3. package/dist/index.js +981 -193
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.umd.cjs +32 -32
  6. package/dist/index.umd.cjs.map +1 -1
  7. package/dist/types/src/core/channel.d.ts +7 -1
  8. package/dist/types/src/core/channel.d.ts.map +1 -1
  9. package/dist/types/src/core/image_source_policy.d.ts +4 -4
  10. package/dist/types/src/core/image_source_policy.d.ts.map +1 -1
  11. package/dist/types/src/core/layer.d.ts +136 -21
  12. package/dist/types/src/core/layer.d.ts.map +1 -1
  13. package/dist/types/src/core/renderable_object.d.ts +108 -3
  14. package/dist/types/src/core/renderable_object.d.ts.map +1 -1
  15. package/dist/types/src/core/viewport.d.ts +82 -2
  16. package/dist/types/src/core/viewport.d.ts.map +1 -1
  17. package/dist/types/src/data/chunk.d.ts +37 -2
  18. package/dist/types/src/data/chunk.d.ts.map +1 -1
  19. package/dist/types/src/data/chunk_manager.d.ts +5 -0
  20. package/dist/types/src/data/chunk_manager.d.ts.map +1 -1
  21. package/dist/types/src/data/ome_zarr/image_source.d.ts +56 -13
  22. package/dist/types/src/data/ome_zarr/image_source.d.ts.map +1 -1
  23. package/dist/types/src/data/ome_zarr/metadata_loaders.d.ts +4 -4
  24. package/dist/types/src/data/ome_zarr/metadata_loaders.d.ts.map +1 -1
  25. package/dist/types/src/idetik.d.ts +129 -59
  26. package/dist/types/src/idetik.d.ts.map +1 -1
  27. package/dist/types/src/index.d.ts +29 -8
  28. package/dist/types/src/index.d.ts.map +1 -1
  29. package/dist/types/src/layers/axes_layer.d.ts +30 -5
  30. package/dist/types/src/layers/axes_layer.d.ts.map +1 -1
  31. package/dist/types/src/layers/image_layer.d.ts +106 -12
  32. package/dist/types/src/layers/image_layer.d.ts.map +1 -1
  33. package/dist/types/src/layers/label_layer.d.ts +106 -5
  34. package/dist/types/src/layers/label_layer.d.ts.map +1 -1
  35. package/dist/types/src/layers/point_picking.d.ts +7 -2
  36. package/dist/types/src/layers/point_picking.d.ts.map +1 -1
  37. package/dist/types/src/layers/volume_layer.d.ts +87 -5
  38. package/dist/types/src/layers/volume_layer.d.ts.map +1 -1
  39. package/dist/types/src/math/axes.d.ts +3 -0
  40. package/dist/types/src/math/axes.d.ts.map +1 -1
  41. package/dist/types/src/math/box2.d.ts +34 -4
  42. package/dist/types/src/math/box2.d.ts.map +1 -1
  43. package/dist/types/src/math/box3.d.ts +38 -4
  44. package/dist/types/src/math/box3.d.ts.map +1 -1
  45. package/dist/types/src/math/color.d.ts +49 -2
  46. package/dist/types/src/math/color.d.ts.map +1 -1
  47. package/dist/types/src/math/frustum.d.ts +28 -0
  48. package/dist/types/src/math/frustum.d.ts.map +1 -1
  49. package/dist/types/src/math/transforms.d.ts +57 -0
  50. package/dist/types/src/math/transforms.d.ts.map +1 -1
  51. package/dist/types/src/objects/cameras/camera.d.ts +55 -0
  52. package/dist/types/src/objects/cameras/camera.d.ts.map +1 -1
  53. package/dist/types/src/objects/cameras/controls.d.ts +88 -1
  54. package/dist/types/src/objects/cameras/controls.d.ts.map +1 -1
  55. package/dist/types/src/objects/cameras/orbit_controls.d.ts +56 -1
  56. package/dist/types/src/objects/cameras/orbit_controls.d.ts.map +1 -1
  57. package/dist/types/src/objects/cameras/orthographic_camera.d.ts +90 -13
  58. package/dist/types/src/objects/cameras/orthographic_camera.d.ts.map +1 -1
  59. package/dist/types/src/objects/cameras/perspective_camera.d.ts +65 -3
  60. package/dist/types/src/objects/cameras/perspective_camera.d.ts.map +1 -1
  61. package/dist/types/src/objects/renderable/image_renderable.d.ts +48 -3
  62. package/dist/types/src/objects/renderable/image_renderable.d.ts.map +1 -1
  63. package/dist/types/src/objects/renderable/label_image_renderable.d.ts +66 -4
  64. package/dist/types/src/objects/renderable/label_image_renderable.d.ts.map +1 -1
  65. package/dist/types/src/objects/renderable/points_renderable.d.ts +47 -2
  66. package/dist/types/src/objects/renderable/points_renderable.d.ts.map +1 -1
  67. package/dist/types/src/objects/renderable/projected_line_renderable.d.ts +27 -3
  68. package/dist/types/src/objects/renderable/projected_line_renderable.d.ts.map +1 -1
  69. package/dist/types/src/objects/renderable/volume_renderable.d.ts +63 -3
  70. package/dist/types/src/objects/renderable/volume_renderable.d.ts.map +1 -1
  71. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1079,30 +1079,53 @@ function TI(g, A) {
1079
1079
  };
1080
1080
  })();
1081
1081
  class SA {
1082
+ /** Minimum corner of the box. */
1082
1083
  min;
1084
+ /** Maximum corner of the box. */
1083
1085
  max;
1084
1086
  /**
1085
- * Initializes as an empty box if no values are provided using the
1086
- * "empty-by-sentinel" pattern: min = +Infinity, max = -Infinity.
1087
- * This allows expansion functions to work without special-casing
1088
- * the first element, and avoids biasing toward (0,0,0).
1087
+ * Creates a box from optional corner points. The corners are cloned.
1088
+ * When a corner is omitted the box starts empty.
1089
+ *
1090
+ * @param min - The minimum corner.
1091
+ * @param max - The maximum corner.
1089
1092
  */
1090
1093
  constructor(A, I) {
1091
1094
  this.min = A ? EA(A) : p(1 / 0, 1 / 0, 1 / 0), this.max = I ? EA(I) : p(-1 / 0, -1 / 0, -1 / 0);
1092
1095
  }
1096
+ /** Returns a deep copy of the box. */
1093
1097
  clone() {
1094
1098
  return new SA(this.min, this.max);
1095
1099
  }
1100
+ /** Returns `true` when the box encloses no volume. */
1096
1101
  isEmpty() {
1097
1102
  return this.max[0] <= this.min[0] || this.max[1] <= this.min[1] || this.max[2] <= this.min[2];
1098
1103
  }
1099
- // Half-open interval intersection: returns true only if boxes overlap.
1104
+ /**
1105
+ * Tests whether two boxes overlap. Boxes are treated as half-open
1106
+ * intervals so touching faces do not count as overlap.
1107
+ *
1108
+ * @param a - The first box.
1109
+ * @param b - The second box.
1110
+ */
1100
1111
  static intersects(A, I) {
1101
1112
  return !(A.max[0] <= I.min[0] || A.min[0] >= I.max[0] || A.max[1] <= I.min[1] || A.min[1] >= I.max[1] || A.max[2] <= I.min[2] || A.min[2] >= I.max[2]);
1102
1113
  }
1114
+ /**
1115
+ * Grows the box in place to contain the given point.
1116
+ *
1117
+ * @param p - The point to include.
1118
+ */
1103
1119
  expandWithPoint(A) {
1104
1120
  A[0] < this.min[0] && (this.min[0] = A[0]), A[1] < this.min[1] && (this.min[1] = A[1]), A[2] < this.min[2] && (this.min[2] = A[2]), A[0] > this.max[0] && (this.max[0] = A[0]), A[1] > this.max[1] && (this.max[1] = A[1]), A[2] > this.max[2] && (this.max[2] = A[2]);
1105
1121
  }
1122
+ /**
1123
+ * Transforms the box in place by the given matrix. The result is the
1124
+ * axis-aligned box of the eight transformed corners so the box can grow
1125
+ * under rotation.
1126
+ *
1127
+ * @param matrix - The transform to apply.
1128
+ */
1106
1129
  applyTransform(A) {
1107
1130
  const { min: I, max: B } = this, C = [
1108
1131
  p(I[0], I[1], I[2]),
@@ -1611,36 +1634,55 @@ class wQ {
1611
1634
  }
1612
1635
  }
1613
1636
  class AA {
1637
+ /** Minimum corner of the box. */
1614
1638
  min;
1639
+ /** Maximum corner of the box. */
1615
1640
  max;
1616
1641
  /**
1617
- * Initializes as an empty box if no values are provided using the
1618
- * "empty-by-sentinel" pattern: min = +Infinity, max = -Infinity.
1619
- * This allows expansion functions to work without special-casing
1620
- * the first element, and avoids biasing toward (0,0).
1642
+ * Creates a box from optional corner points. The corners are cloned.
1643
+ * When a corner is omitted the box starts empty.
1644
+ *
1645
+ * @param min - The minimum corner.
1646
+ * @param max - The maximum corner.
1621
1647
  */
1622
1648
  constructor(A, I) {
1623
1649
  this.min = A ? zg(A) : V(1 / 0, 1 / 0), this.max = I ? zg(I) : V(-1 / 0, -1 / 0);
1624
1650
  }
1651
+ /** Returns a deep copy of the box. */
1625
1652
  clone() {
1626
1653
  return new AA(this.min, this.max);
1627
1654
  }
1655
+ /** Returns `true` when the box encloses no area. */
1628
1656
  isEmpty() {
1629
1657
  return this.max[0] <= this.min[0] || this.max[1] <= this.min[1];
1630
1658
  }
1631
- // Half-open interval intersection: returns true only if boxes overlap.
1659
+ /**
1660
+ * Tests whether two boxes overlap. Boxes are treated as half-open
1661
+ * intervals so touching edges do not count as overlap.
1662
+ *
1663
+ * @param a - The first box.
1664
+ * @param b - The second box.
1665
+ */
1632
1666
  static intersects(A, I) {
1633
1667
  return !(A.max[0] <= I.min[0] || A.min[0] >= I.max[0] || A.max[1] <= I.min[1] || A.min[1] >= I.max[1]);
1634
1668
  }
1669
+ /**
1670
+ * Tests whether two boxes have exactly equal corners.
1671
+ *
1672
+ * @param a - The first box.
1673
+ * @param b - The second box.
1674
+ */
1635
1675
  static equals(A, I) {
1636
1676
  return _g(A.min, I.min) && _g(A.max, I.max);
1637
1677
  }
1678
+ /** Returns a copy with both corners floored componentwise. */
1638
1679
  floor() {
1639
1680
  return new AA(
1640
1681
  V(Math.floor(this.min[0]), Math.floor(this.min[1])),
1641
1682
  V(Math.floor(this.max[0]), Math.floor(this.max[1]))
1642
1683
  );
1643
1684
  }
1685
+ /** Converts the box to an `x, y, width, height` rectangle. */
1644
1686
  toRect() {
1645
1687
  const A = this.min[0], I = this.min[1], B = this.max[0] - this.min[0], C = this.max[1] - this.min[1];
1646
1688
  return { x: A, y: I, width: B, height: C };
@@ -2975,16 +3017,22 @@ class mQ {
2975
3017
  }
2976
3018
  }
2977
3019
  class uQ {
3020
+ /** The viewport's unique identifier. */
2978
3021
  id;
3022
+ /** The host element defining the viewport's area. */
2979
3023
  element;
3024
+ /** The camera the viewport renders with. */
2980
3025
  camera;
3026
+ /** The pointer and wheel event dispatcher for the host element. */
2981
3027
  events;
3028
+ /** Input controls driving the camera. */
2982
3029
  cameraControls;
2983
3030
  // Carried only to relay to `layer.onAttached` / `layer.onDetached`.
2984
3031
  // To be removed when the chunk-infrastructure refactor folds chunk management
2985
3032
  // into the source and the attach lifecycle goes away.
2986
3033
  context_;
2987
3034
  layers_ = [];
3035
+ /** @hidden */
2988
3036
  constructor(A) {
2989
3037
  this.id = A.id, this.element = A.element, this.camera = A.camera, this.context_ = A.context, this.cameraControls = A.cameraControls, this.updateAspectRatio(), this.events = new fQ(this.element), this.events.addEventListener((I) => {
2990
3038
  if (I.event instanceof PointerEvent || I.event instanceof WheelEvent) {
@@ -3000,26 +3048,51 @@ class uQ {
3000
3048
  for (const I of A.layers ?? [])
3001
3049
  this.addLayer(I);
3002
3050
  }
3051
+ /**
3052
+ * The layers rendered by this viewport in order. Layers with `occludes`
3053
+ * set draw before non-occluding layers regardless of stack order.
3054
+ */
3003
3055
  get layers() {
3004
3056
  return this.layers_;
3005
3057
  }
3058
+ /**
3059
+ * Adds a layer to the top of the stack.
3060
+ *
3061
+ * @param layer - The layer to add.
3062
+ */
3006
3063
  addLayer(A) {
3007
3064
  A.onAttached(this.context_), this.layers_.push(A);
3008
3065
  }
3066
+ /**
3067
+ * Removes a previously added layer.
3068
+ *
3069
+ * @param layer - The layer to remove.
3070
+ */
3009
3071
  removeLayer(A) {
3010
3072
  const I = this.layers_.indexOf(A);
3011
3073
  if (I === -1)
3012
3074
  throw new Error(`Layer to remove not found: ${A}`);
3013
3075
  this.layers_.splice(I, 1), A.onDetached(this.context_);
3014
3076
  }
3077
+ /** Removes all layers from the viewport. */
3015
3078
  removeAllLayers() {
3016
3079
  for (const A of this.layers_)
3017
3080
  A.onDetached(this.context_);
3018
3081
  this.layers_ = [];
3019
3082
  }
3083
+ /**
3084
+ * Syncs the camera's aspect ratio to the host element's size. Called
3085
+ * automatically when the host element resizes.
3086
+ */
3020
3087
  updateSize() {
3021
3088
  this.updateAspectRatio();
3022
3089
  }
3090
+ /**
3091
+ * Computes the viewport's box relative to the given canvas in device pixels.
3092
+ *
3093
+ * @param canvas - The canvas to compute the box against.
3094
+ * @returns The viewport's box in the canvas's coordinate space.
3095
+ */
3023
3096
  getBoxRelativeTo(A) {
3024
3097
  const I = this.getBox().toRect(), B = A.getBoundingClientRect(), C = window.devicePixelRatio || 1, Q = B.left * C, E = B.top * C, i = B.height * C, o = I.x - Q, a = I.y - E, s = Math.floor(o), D = Math.floor(i - a - I.height), h = Math.floor(I.width), t = Math.floor(I.height);
3025
3098
  return new AA(
@@ -3027,9 +3100,19 @@ class uQ {
3027
3100
  V(s + h, D + t)
3028
3101
  );
3029
3102
  }
3103
+ /**
3104
+ * The viewport's rectangle in the drawing buffer in device pixels.
3105
+ */
3030
3106
  getBufferRect() {
3031
3107
  return this.getBoxRelativeTo(this.element).toRect();
3032
3108
  }
3109
+ /**
3110
+ * Converts a client-space position to clip space. The `y` axis points
3111
+ * down, matching the renderer's mirrored projection.
3112
+ *
3113
+ * @param position - The client-space position to convert.
3114
+ * @param depth - The clip-space z value.
3115
+ */
3033
3116
  clientToClip(A, I = 0) {
3034
3117
  const [B, C] = A, Q = this.element.getBoundingClientRect();
3035
3118
  return p(
@@ -3038,6 +3121,13 @@ class uQ {
3038
3121
  I
3039
3122
  );
3040
3123
  }
3124
+ /**
3125
+ * Converts a client-space position such as a pointer location to world
3126
+ * space.
3127
+ *
3128
+ * @param position - The client-space position to convert.
3129
+ * @param depth - The clip-space z value.
3130
+ */
3041
3131
  clientToWorld(A, I = 0) {
3042
3132
  const B = this.clientToClip(A, I);
3043
3133
  return this.camera.clipToWorld(B);
@@ -3153,63 +3243,22 @@ class xQ {
3153
3243
  }
3154
3244
  const WQ = 2048;
3155
3245
  class no {
3246
+ /** The canvas element the renderer draws into. */
3247
+ canvas;
3248
+ /** The registered overlays that update once per frame in order. */
3249
+ overlays;
3156
3250
  chunkManager_;
3157
3251
  context_;
3158
3252
  renderer_;
3159
3253
  viewports_;
3160
- canvas;
3161
- overlays;
3162
3254
  stats_;
3163
3255
  sizeObserver_;
3164
3256
  lastAnimationId_;
3165
- // this value will be set after start
3166
3257
  lastTimestamp_ = 0;
3167
3258
  /**
3168
- * Creates a new Idetik visualization runtime instance.
3169
- *
3170
- * @param params - Configuration parameters for the Idetik instance
3171
- * @param params.canvas - HTMLCanvasElement to render to
3172
- * @param params.viewports - Optional array of viewport configurations.
3173
- * Each viewport renders with its own camera, layers, and controls.
3174
- * The `element` property is optional and defaults to the canvas if not provided.
3175
- * Elements must be unique across viewports.
3176
- * The `id` property is optional but useful for referencing specific viewports later.
3177
- * @param params.overlays - Optional array of overlay objects that update each frame (e.g., for HUD elements)
3178
- * @param params.showStats - Optional flag to display performance statistics
3179
- *
3180
- * @example
3181
- * // Single viewport (element defaults to canvas)
3182
- * const camera = new OrthographicCamera({ left: 0, right: 1024, top: 0, bottom: 1024 });
3183
- * const idetik = new Idetik({
3184
- * canvas: document.querySelector('canvas')!,
3185
- * viewports: [{
3186
- * camera: camera,
3187
- * layers: [imageLayer],
3188
- * cameraControls: new PanZoomControls(camera)
3189
- * }]
3190
- * });
3259
+ * Creates an Idetik runtime for the given canvas.
3191
3260
  *
3192
- * @example
3193
- * // Multiple viewports - one defaults to canvas, others use separate elements
3194
- * const idetik = new Idetik({
3195
- * canvas: document.querySelector('canvas')!,
3196
- * viewports: [
3197
- * {
3198
- * id: 'main',
3199
- * // element omitted - defaults to canvas
3200
- * camera: camera1,
3201
- * layers: [layer1]
3202
- * },
3203
- * {
3204
- * id: 'minimap',
3205
- * element: document.querySelector('#minimap')!,
3206
- * camera: camera2,
3207
- * layers: [layer2]
3208
- * }
3209
- * ]
3210
- * });
3211
- *
3212
- * @throws {Error} If viewports have duplicate IDs or shared elements
3261
+ * @param params - Initialization properties.
3213
3262
  */
3214
3263
  constructor(A) {
3215
3264
  this.canvas = A.canvas, this.renderer_ = new RQ(this.canvas);
@@ -3237,9 +3286,11 @@ class no {
3237
3286
  Q.updateSize(), this.renderer_.render(Q);
3238
3287
  });
3239
3288
  }
3289
+ /** Counts of queued and in-flight chunk requests. */
3240
3290
  get chunkQueueStats() {
3241
3291
  return this.chunkManager_.queueStats;
3242
3292
  }
3293
+ /** A snapshot of current CPU/GPU/JS heap memory usage. */
3243
3294
  get memoryStats() {
3244
3295
  const A = performance.memory;
3245
3296
  return {
@@ -3250,28 +3301,52 @@ class no {
3250
3301
  jsHeapLimitBytes: A?.jsHeapSizeLimit
3251
3302
  };
3252
3303
  }
3304
+ /** The number of objects drawn in the last rendered frame. */
3253
3305
  get renderedObjects() {
3254
3306
  return this.renderer_.renderedObjects;
3255
3307
  }
3308
+ /** The width of the rendering surface in pixels. */
3256
3309
  get width() {
3257
3310
  return this.renderer_.width;
3258
3311
  }
3312
+ /** The height of the rendering surface in pixels. */
3259
3313
  get height() {
3260
3314
  return this.renderer_.height;
3261
3315
  }
3316
+ /** The viewports in render order. */
3262
3317
  get viewports() {
3263
3318
  return this.viewports_;
3264
3319
  }
3320
+ /** Whether the render loop is running. */
3265
3321
  get running() {
3266
3322
  return this.lastAnimationId_ !== void 0;
3267
3323
  }
3324
+ /**
3325
+ * Finds a viewport by its id.
3326
+ *
3327
+ * @param id - The id given in the viewport's definition.
3328
+ * @returns The matching viewport or `undefined` if none matches.
3329
+ */
3268
3330
  getViewport(A) {
3269
3331
  return this.viewports_.find((I) => I.id === A);
3270
3332
  }
3333
+ /**
3334
+ * Adds a viewport at runtime.
3335
+ *
3336
+ * @param props - The viewport definition. The `element` defaults to the
3337
+ * canvas and must be unique across viewports.
3338
+ * @returns The created viewport.
3339
+ */
3271
3340
  addViewport(A) {
3272
3341
  const [I] = gB([A], this.canvas, this.context_);
3273
3342
  return $B(I, this.viewports_), this.viewports_.push(I), this.running && (I.events.connect(), I.element !== this.canvas && this.sizeObserver_.observe(I.element)), k.info("Idetik", `Added viewport "${I.id}"`), I;
3274
3343
  }
3344
+ /**
3345
+ * Removes a previously added viewport.
3346
+ *
3347
+ * @param viewport - The viewport to remove.
3348
+ * @returns `true` if the viewport was found and removed.
3349
+ */
3275
3350
  removeViewport(A) {
3276
3351
  const I = this.viewports_.indexOf(A);
3277
3352
  return I === -1 ? (k.warn(
@@ -3279,16 +3354,37 @@ class no {
3279
3354
  `Viewport "${A.id}" not found, nothing to remove`
3280
3355
  ), !1) : (this.running && (A.events.disconnect(), A.element !== this.canvas && this.sizeObserver_.unobserve(A.element)), this.viewports_.splice(I, 1), k.info("Idetik", `Removed viewport "${A.id}"`), !0);
3281
3356
  }
3357
+ /**
3358
+ * Registers an overlay that updates once per frame.
3359
+ *
3360
+ * @param overlay - The overlay to add.
3361
+ */
3282
3362
  addOverlay(A) {
3283
3363
  this.overlays.push(A);
3284
3364
  }
3365
+ /**
3366
+ * Removes a previously added overlay.
3367
+ *
3368
+ * @param overlay - The overlay to remove.
3369
+ * @returns `true` if the overlay was found and removed.
3370
+ */
3285
3371
  removeOverlay(A) {
3286
3372
  const I = this.overlays.indexOf(A);
3287
3373
  return I === -1 ? (k.warn("Idetik", "Overlay not found, nothing to remove"), !1) : (this.overlays.splice(I, 1), !0);
3288
3374
  }
3375
+ /**
3376
+ * Sets the memory budget for chunk data at runtime.
3377
+ *
3378
+ * @param memoryLimitMB - The new budget in megabytes.
3379
+ */
3289
3380
  setMemoryLimitMB(A) {
3290
3381
  this.chunkManager_.memoryLimitBytes = A * 1024 * 1024;
3291
3382
  }
3383
+ /**
3384
+ * Starts the render loop and connects input handlers.
3385
+ *
3386
+ * @returns The instance, for chaining.
3387
+ */
3292
3388
  start() {
3293
3389
  if (k.info("Idetik", "Idetik runtime starting"), this.running)
3294
3390
  k.warn("Idetik", "Idetik runtime already started");
@@ -3314,6 +3410,9 @@ class no {
3314
3410
  (B) => this.animate(B)
3315
3411
  );
3316
3412
  }
3413
+ /**
3414
+ * Stops the render loop and disconnects input handlers.
3415
+ */
3317
3416
  stop() {
3318
3417
  if (k.info("Idetik", "Idetik runtime stopping"), !this.running)
3319
3418
  k.warn("Idetik", "Idetik runtime not started");
@@ -11989,7 +12088,9 @@ ${JSON.stringify(g)}`);
11989
12088
  }
11990
12089
  }
11991
12090
  class ZA {
12091
+ /** The zarr store location the image was opened from. */
11992
12092
  location;
12093
+ /** The OME-Zarr version passed at creation if any. */
11993
12094
  version;
11994
12095
  loader_;
11995
12096
  constructor(A) {
@@ -12017,33 +12118,45 @@ class ZA {
12017
12118
  );
12018
12119
  return new WE({ metadata: i, arrays: a, arrayParams: o });
12019
12120
  }
12121
+ /**
12122
+ * Returns per-axis dimension metadata for the image.
12123
+ *
12124
+ * Each axis entry lists one record per level of detail with its size,
12125
+ * chunk size, scale, and translation. Use these to convert between
12126
+ * array indices and world coordinates, pick slice coordinates, and
12127
+ * frame cameras around the image extent.
12128
+ */
12020
12129
  getDimensions() {
12021
12130
  return this.loader_.getSourceDimensionMap();
12022
12131
  }
12132
+ /**
12133
+ * Returns the number of channels in the image.
12134
+ */
12023
12135
  getChannelCount() {
12024
12136
  return this.getDimensions().c?.lods[0].size ?? 1;
12025
12137
  }
12138
+ /** The chunk loader that streams this image's data. */
12026
12139
  get loader() {
12027
12140
  return this.loader_;
12028
12141
  }
12029
12142
  /**
12030
- * Creates and opens an OmeZarrImageSource from an HTTP(S) URL.
12143
+ * Opens an OME-Zarr image over HTTP(S).
12031
12144
  *
12032
- * @param props.url URL of the Zarr root
12033
- * @param props.version OME-Zarr version
12145
+ * @param props - The store url and optional version.
12034
12146
  */
12035
12147
  static async fromHttp(A) {
12036
12148
  const I = new CA(new zA(A.url)), B = await ZA.openLoader(I, A.version);
12037
12149
  return new ZA({ location: I, version: A.version, loader: B });
12038
12150
  }
12039
12151
  /**
12040
- * Creates and opens an OmeZarrImageSource from a local filesystem directory.
12152
+ * Opens an OME-Zarr image from a local directory.
12153
+ *
12154
+ * Uses the File System Access API so it only works in Chromium-based
12155
+ * browsers. Pass the handle returned by `window.showDirectoryPicker()`.
12156
+ * The optional path lets an application ask once for root permission
12157
+ * and open many images.
12041
12158
  *
12042
- * @param directory return value of `window.showDirectoryPicker()` which gives the browser
12043
- * permission to access a directory (only works in Chrome/Edge)
12044
- * @param version OME-Zarr version
12045
- * @param path path to image, beginning with "/". This argument allows the application to only
12046
- * ask the user once for permission to the root directory
12159
+ * @param props - The directory handle, optional version, and path.
12047
12160
  */
12048
12161
  static async fromFileSystem(A) {
12049
12162
  const I = new CA(
@@ -12054,14 +12167,33 @@ class ZA {
12054
12167
  }
12055
12168
  }
12056
12169
  class qI {
12170
+ /**
12171
+ * How the layer's output blends with previously drawn content. Also
12172
+ * applies to blending between objects within the layer.
12173
+ */
12174
+ blendMode;
12175
+ /**
12176
+ * Whether the layer writes depth and hides content drawn behind it.
12177
+ *
12178
+ * Occluding layers render a depth pass and always draw before
12179
+ * non-occluding layers regardless of their order in the viewport. When
12180
+ * not set explicitly this value is inferred from `blendMode` at
12181
+ * construction only. Reassigning {@link blendMode} later does not update
12182
+ * it.
12183
+ */
12184
+ occludes;
12185
+ /** Set to `true` by subclasses whose shaders read scene depth. */
12186
+ requiresSceneDepth_ = !1;
12057
12187
  coverageGroups_ = /* @__PURE__ */ new Map();
12058
12188
  state_ = "initialized";
12059
12189
  attached_ = !1;
12060
12190
  callbacks_ = [];
12061
12191
  opacity_;
12062
- blendMode;
12063
- occludes;
12064
- requiresSceneDepth_ = !1;
12192
+ /**
12193
+ * Creates a layer with the given presentation state.
12194
+ *
12195
+ * @param props - Initialization properties.
12196
+ */
12065
12197
  constructor({
12066
12198
  opacity: A = 1,
12067
12199
  blendMode: I = "none",
@@ -12069,20 +12201,41 @@ class qI {
12069
12201
  } = {}) {
12070
12202
  this.opacity_ = hA(A, 0, 1), this.blendMode = I, this.occludes = B ?? I === "none";
12071
12203
  }
12204
+ /**
12205
+ * Whether the layer's shaders read scene depth. When `true` the renderer
12206
+ * draws occluding layers to a depth texture the layer's shaders can
12207
+ * sample. A layer cannot both occlude and read scene depth.
12208
+ */
12072
12209
  get requiresSceneDepth() {
12073
12210
  return this.requiresSceneDepth_;
12074
12211
  }
12212
+ /** The layer's opacity in `[0, 1]`. Values outside are clamped. */
12075
12213
  get opacity() {
12076
12214
  return this.opacity_;
12077
12215
  }
12216
+ /** @param value - The new opacity in `[0, 1]`. */
12078
12217
  set opacity(A) {
12079
12218
  (A < 0 || A > 1) && k.warn(
12080
12219
  "Layer",
12081
12220
  `Opacity out of bounds: ${A} — clamping to [0.0, 1.0]`
12082
12221
  ), this.opacity_ = hA(A, 0, 1);
12083
12222
  }
12223
+ /**
12224
+ * Handles a pointer or wheel event from the owning viewport. Called
12225
+ * automatically for each event before the camera controls. The default
12226
+ * implementation does nothing.
12227
+ *
12228
+ * @param _event - The event with clip and world coordinates attached.
12229
+ */
12084
12230
  onEvent(A) {
12085
12231
  }
12232
+ /**
12233
+ * Lifecycle hook that is called automatically when a layer is
12234
+ * is attached to a viewport. A layer can only be attached to one viewport
12235
+ * at a time.
12236
+ *
12237
+ * @param context - The shared runtime context.
12238
+ */
12086
12239
  onAttached(A) {
12087
12240
  if (this.attached_)
12088
12241
  throw new Error(
@@ -12092,42 +12245,79 @@ class qI {
12092
12245
  throw new Error(`${this.type} cannot both occlude and read scene depth.`);
12093
12246
  this.attach(A), this.attached_ = !0;
12094
12247
  }
12248
+ /**
12249
+ * Lifecycle hook that is called automatically when a layer is detached
12250
+ * from a viewport.
12251
+ *
12252
+ * @param context - The shared runtime context.
12253
+ */
12095
12254
  onDetached(A) {
12096
12255
  this.attached_ && (this.detach(A), this.attached_ = !1);
12097
12256
  }
12257
+ /** @hidden */
12098
12258
  attach(A) {
12099
12259
  }
12260
+ /** @hidden */
12100
12261
  detach(A) {
12101
12262
  }
12263
+ /**
12264
+ * The layer's renderable objects grouped by coverage group. Objects in
12265
+ * a group draw each pixel at most once, letting chunks at multiple
12266
+ * levels of detail overlap correctly.
12267
+ */
12102
12268
  get coverageGroups() {
12103
12269
  return this.coverageGroups_;
12104
12270
  }
12271
+ /** The layer's current lifecycle state. */
12105
12272
  get state() {
12106
12273
  return this.state_;
12107
12274
  }
12275
+ /**
12276
+ * Registers a callback invoked after every state change.
12277
+ *
12278
+ * @param callback - Receives the new and previous states.
12279
+ */
12108
12280
  addStateChangeCallback(A) {
12109
12281
  this.callbacks_.push(A);
12110
12282
  }
12283
+ /**
12284
+ * Removes a previously registered state change callback.
12285
+ *
12286
+ * @param callback - The callback to remove.
12287
+ */
12111
12288
  removeStateChangeCallback(A) {
12112
12289
  const I = this.callbacks_.indexOf(A);
12113
12290
  if (I === -1)
12114
12291
  throw new Error(`Callback to remove could not be found: ${A}`);
12115
12292
  this.callbacks_.splice(I, 1);
12116
12293
  }
12294
+ /**
12295
+ * Sets the lifecycle state and notifies state change callbacks.
12296
+ *
12297
+ * @param newState - The state to enter.
12298
+ */
12117
12299
  setState(A) {
12118
12300
  const I = this.state_;
12119
12301
  this.state_ = A, this.callbacks_.forEach((B) => B(A, I));
12120
12302
  }
12303
+ /**
12304
+ * Registers a renderable object for drawing. Objects in the same
12305
+ * coverage group draw each pixel at most once.
12306
+ *
12307
+ * @param object - The object to add.
12308
+ * @param coverageGroup - The group key.
12309
+ */
12121
12310
  addObject(A, I = null) {
12122
12311
  const B = this.coverageGroups_.get(I);
12123
12312
  B ? B.push(A) : this.coverageGroups_.set(I, [A]);
12124
12313
  }
12314
+ /** Removes all registered renderable objects. */
12125
12315
  clearObjects() {
12126
12316
  this.coverageGroups_.clear();
12127
12317
  }
12128
12318
  /**
12129
- * Get uniforms for shader program. Override in derived classes that need custom uniforms.
12130
- * @returns Object containing uniform name-value pairs
12319
+ * Returns uniform name-value pairs applied to every object drawn by
12320
+ * this layer. Override in subclasses that need custom shader uniforms.
12131
12321
  */
12132
12322
  getUniforms() {
12133
12323
  return {};
@@ -12214,41 +12404,82 @@ class eC {
12214
12404
  rotation_ = hI();
12215
12405
  translation_ = d();
12216
12406
  scale_ = p(1, 1, 1);
12407
+ /**
12408
+ * Composes the given rotation onto the current rotation.
12409
+ *
12410
+ * @param q - The rotation to apply.
12411
+ */
12217
12412
  addRotation(A) {
12218
12413
  EQ(this.rotation_, this.rotation_, A), this.dirty_ = !0;
12219
12414
  }
12415
+ /**
12416
+ * Replaces the rotation with the given quaternion.
12417
+ *
12418
+ * @param q - The new rotation.
12419
+ */
12220
12420
  setRotation(A) {
12221
12421
  oQ(this.rotation_, A), this.dirty_ = !0;
12222
12422
  }
12423
+ /** A copy of the rotation quaternion. */
12223
12424
  get rotation() {
12224
12425
  return iQ(this.rotation_);
12225
12426
  }
12427
+ /**
12428
+ * Adds the given offset to the translation.
12429
+ *
12430
+ * @param vec - The offset to add.
12431
+ */
12226
12432
  addTranslation(A) {
12227
12433
  aI(this.translation_, this.translation_, A), this.dirty_ = !0;
12228
12434
  }
12435
+ /**
12436
+ * Replaces the translation with the given vector.
12437
+ *
12438
+ * @param vec - The new translation.
12439
+ */
12229
12440
  setTranslation(A) {
12230
12441
  jI(this.translation_, A), this.dirty_ = !0;
12231
12442
  }
12443
+ /** A copy of the translation vector. */
12232
12444
  get translation() {
12233
12445
  return EA(this.translation_);
12234
12446
  }
12447
+ /**
12448
+ * Multiplies the scale componentwise by the given vector.
12449
+ *
12450
+ * @param vec - The scale factors to apply.
12451
+ */
12235
12452
  addScale(A) {
12236
12453
  zC(this.scale_, this.scale_, A), this.dirty_ = !0;
12237
12454
  }
12455
+ /**
12456
+ * Replaces the scale with the given vector.
12457
+ *
12458
+ * @param vec - The new scale.
12459
+ */
12238
12460
  setScale(A) {
12239
12461
  jI(this.scale_, A), this.dirty_ = !0;
12240
12462
  }
12463
+ /**
12464
+ * Rotates the transform to face the given target point. Uses `+Y` as
12465
+ * world up.
12466
+ *
12467
+ * @param target - The world-space point to face.
12468
+ */
12241
12469
  targetTo(A) {
12242
12470
  Gg(this.translation_, A) && (A = EA(A), A[2] += T);
12243
12471
  const I = XC($(), this.translation_, A, fi), B = xC(VB(), I);
12244
12472
  wg(this.rotation_, B), MI(this.rotation_, this.rotation_), this.dirty_ = !0;
12245
12473
  }
12474
+ /** A copy of the scale vector. */
12246
12475
  get scale() {
12247
12476
  return EA(this.scale_);
12248
12477
  }
12478
+ /** The composed transform matrix. Recomputed when stale. */
12249
12479
  get matrix() {
12250
12480
  return this.dirty_ && (this.computeMatrix(), this.dirty_ = !1), this.matrix_;
12251
12481
  }
12482
+ /** The inverse of the composed transform matrix. */
12252
12483
  get inverse() {
12253
12484
  return OA($(), this.matrix);
12254
12485
  }
@@ -12262,17 +12493,34 @@ class eC {
12262
12493
  }
12263
12494
  }
12264
12495
  class x {
12496
+ /** Opaque red `#ff0000`. */
12265
12497
  static RED = new x(1, 0, 0);
12498
+ /** Opaque green `#00ff00`. */
12266
12499
  static GREEN = new x(0, 1, 0);
12500
+ /** Opaque blue `#0000ff`. */
12267
12501
  static BLUE = new x(0, 0, 1);
12502
+ /** Opaque yellow `#ffff00`. */
12268
12503
  static YELLOW = new x(1, 1, 0);
12504
+ /** Opaque magenta `#ff00ff`. */
12269
12505
  static MAGENTA = new x(1, 0, 1);
12506
+ /** Opaque cyan `#00ffff`. */
12270
12507
  static CYAN = new x(0, 1, 1);
12508
+ /** Opaque black `#000000`. */
12271
12509
  static BLACK = new x(0, 0, 0);
12510
+ /** Opaque white `#ffffff`. */
12272
12511
  static WHITE = new x(1, 1, 1);
12512
+ /** Fully transparent black. */
12273
12513
  static TRANSPARENT = new x(0, 0, 0, 0);
12274
12514
  // RGBA color values in the range [0, 1]
12275
12515
  rgba_;
12516
+ /**
12517
+ * Creates a color from RGBA components in `[0, 1]`.
12518
+ *
12519
+ * @param r - The red component.
12520
+ * @param g - The green component.
12521
+ * @param b - The blue component.
12522
+ * @param a - The alpha component. Defaults to `1`.
12523
+ */
12276
12524
  constructor(A, I, B, C) {
12277
12525
  if (A < 0 || A > 1 || I < 0 || I > 1 || B < 0 || B > 1)
12278
12526
  throw new Error("RGB values must be in the range [0, 1]");
@@ -12280,30 +12528,43 @@ class x {
12280
12528
  throw new Error("Alpha value must be in the range [0, 1]");
12281
12529
  this.rgba_ = [A, I, B, C ?? 1];
12282
12530
  }
12531
+ /** The RGB components as a three-element array. */
12283
12532
  get rgb() {
12284
12533
  return [this.rgba_[0], this.rgba_[1], this.rgba_[2]];
12285
12534
  }
12535
+ /** The RGBA components as a four-element array. */
12286
12536
  get rgba() {
12287
12537
  return this.rgba_;
12288
12538
  }
12539
+ /** The red component. */
12289
12540
  get r() {
12290
12541
  return this.rgba_[0];
12291
12542
  }
12543
+ /** The green component. */
12292
12544
  get g() {
12293
12545
  return this.rgba_[1];
12294
12546
  }
12547
+ /** The blue component. */
12295
12548
  get b() {
12296
12549
  return this.rgba_[2];
12297
12550
  }
12551
+ /** The alpha component. */
12298
12552
  get a() {
12299
12553
  return this.rgba_[3];
12300
12554
  }
12555
+ /** The color as a `#rrggbb` hex string. Alpha is dropped. */
12301
12556
  get rgbHex() {
12302
12557
  return `#${this.toHexComponent(this.r)}${this.toHexComponent(this.g)}${this.toHexComponent(this.b)}`;
12303
12558
  }
12559
+ /** The color packed into a 32-bit integer as RGBA bytes. */
12304
12560
  get packed() {
12305
12561
  return Math.round(this.r * 255) << 24 | Math.round(this.g * 255) << 16 | Math.round(this.b * 255) << 8 | Math.round(this.a * 255);
12306
12562
  }
12563
+ /**
12564
+ * Converts a {@link ColorLike} value to a `Color`.
12565
+ *
12566
+ * @param colorLike - The value to convert.
12567
+ */
12307
12568
  static from(A) {
12308
12569
  if (A instanceof x)
12309
12570
  return A;
@@ -12313,6 +12574,11 @@ class x {
12313
12574
  return x.fromRgbHex(A);
12314
12575
  throw new Error("Unsupported color format");
12315
12576
  }
12577
+ /**
12578
+ * Parses a `#rrggbb` hex string into an opaque color.
12579
+ *
12580
+ * @param hex - The hex string with or without the leading `#`.
12581
+ */
12316
12582
  static fromRgbHex(A) {
12317
12583
  const I = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(A);
12318
12584
  if (!I)
@@ -12330,8 +12596,17 @@ class x {
12330
12596
  }
12331
12597
  }
12332
12598
  class gI extends YI {
12599
+ /**
12600
+ * Draws the geometry's wireframe on top of the normal pass. Layers use
12601
+ * this as a chunk debugging aid. Defaults to `false`.
12602
+ */
12333
12603
  wireframeEnabled = !1;
12604
+ /** The color of the wireframe overlay. Defaults to `Color.WHITE`. */
12334
12605
  wireframeColor = x.WHITE;
12606
+ /**
12607
+ * Whether the object is depth tested when drawn. Objects that opt out
12608
+ * are also left out of the depth prepass. Defaults to `true`.
12609
+ */
12335
12610
  depthTest = !0;
12336
12611
  textures_ = [];
12337
12612
  staleTextures_ = [];
@@ -12341,59 +12616,124 @@ class gI extends YI {
12341
12616
  programName_ = null;
12342
12617
  depthProgramName_ = null;
12343
12618
  cullFaceMode_ = "none";
12619
+ /**
12620
+ * Assigns a texture to the given texture unit. The renderer binds each
12621
+ * entry of {@link textures} to its matching unit before drawing.
12622
+ *
12623
+ * @param index - The texture unit to bind to.
12624
+ * @param texture - The texture to assign.
12625
+ */
12344
12626
  setTexture(A, I) {
12345
12627
  this.textures_[A] = I;
12346
12628
  }
12629
+ /**
12630
+ * Removes all assigned textures.
12631
+ */
12347
12632
  clearTextures() {
12348
12633
  this.textures_.length = 0;
12349
12634
  }
12635
+ /**
12636
+ * Queues a replaced texture for GPU disposal. Subclasses call this when
12637
+ * swapping out a texture they own. The renderer drains the queue
12638
+ * through {@link popStaleTextures} before the next draw. Passing
12639
+ * `undefined` is a no-op.
12640
+ *
12641
+ * @param texture - The texture that is no longer in use.
12642
+ */
12350
12643
  markStaleTexture(A) {
12351
12644
  A !== void 0 && this.staleTextures_.push(A);
12352
12645
  }
12646
+ /**
12647
+ * Drains the queue of textures marked stale. Called automatically by
12648
+ * the renderer, which disposes the GPU resources of each returned
12649
+ * texture.
12650
+ *
12651
+ * @returns The textures queued since the last call.
12652
+ */
12353
12653
  popStaleTextures() {
12354
12654
  const A = this.staleTextures_;
12355
12655
  return this.staleTextures_ = [], A;
12356
12656
  }
12657
+ /**
12658
+ * The geometry drawn for this object.
12659
+ */
12357
12660
  get geometry() {
12358
12661
  return this.geometry_;
12359
12662
  }
12663
+ /**
12664
+ * A line-segment version of {@link geometry} used for the wireframe
12665
+ * overlay. Built lazily on first access and cached until the geometry
12666
+ * changes.
12667
+ */
12360
12668
  get wireframeGeometry() {
12361
12669
  return this.wireframeGeometry_ ??= new li(this.geometry), this.wireframeGeometry_;
12362
12670
  }
12671
+ /** The assigned textures indexed by texture unit. */
12363
12672
  get textures() {
12364
12673
  return this.textures_;
12365
12674
  }
12675
+ /**
12676
+ * The object's world transform as translation, rotation, and scale.
12677
+ * Layers position, orient, and size renderables through it.
12678
+ */
12366
12679
  get transform() {
12367
12680
  return this.transform_;
12368
12681
  }
12682
+ /** @param geometry - The geometry to draw. */
12369
12683
  set geometry(A) {
12370
12684
  this.geometry_ = A, this.wireframeGeometry_ = null;
12371
12685
  }
12686
+ /**
12687
+ * The name of a shader program that draws the object. The
12688
+ * renderer skips objects whose program name is `null`.
12689
+ */
12372
12690
  get programName() {
12373
12691
  return this.programName_;
12374
12692
  }
12693
+ /**
12694
+ * The name of the shader program used in the depth prepass or `null`
12695
+ * to stay out of it. Objects without one never occlude other layers.
12696
+ */
12375
12697
  get depthProgramName() {
12376
12698
  return this.depthProgramName_;
12377
12699
  }
12700
+ /**
12701
+ * The geometry's bounding box transformed to world space. The renderer
12702
+ * culls objects whose box falls outside the view frustum.
12703
+ */
12378
12704
  get boundingBox() {
12379
12705
  const A = this.geometry_.boundingBox.clone();
12380
12706
  return A.applyTransform(this.transform_.matrix), A;
12381
12707
  }
12708
+ /**
12709
+ * @param programName - The shader program name.
12710
+ */
12382
12711
  set programName(A) {
12383
12712
  this.programName_ = A;
12384
12713
  }
12714
+ /**
12715
+ * Selects the shader program for the depth prepass. Subclasses set
12716
+ * this so the object writes depth and occludes content in other
12717
+ * layers.
12718
+ *
12719
+ * @param programName - The shader program name.
12720
+ */
12385
12721
  set depthProgramName(A) {
12386
12722
  this.depthProgramName_ = A;
12387
12723
  }
12724
+ /** Which triangle faces are culled when drawing. Defaults to `"none"`. */
12388
12725
  get cullFaceMode() {
12389
12726
  return this.cullFaceMode_;
12390
12727
  }
12728
+ /** @param mode - The culling mode to apply. */
12391
12729
  set cullFaceMode(A) {
12392
12730
  this.cullFaceMode_ = A;
12393
12731
  }
12394
12732
  /**
12395
- * Get uniforms for shader program. Override in derived classes that need custom uniforms.
12396
- * @returns Object containing uniform name-value pairs
12733
+ * Returns the uniform values to upload before drawing. Override in
12734
+ * subclasses that need custom uniforms. Values are matched to shader
12735
+ * uniforms by name and take precedence over the owning layer's
12736
+ * uniforms.
12397
12737
  */
12398
12738
  getUniforms() {
12399
12739
  return {};
@@ -12402,24 +12742,35 @@ class gI extends YI {
12402
12742
  class pi extends gI {
12403
12743
  color_;
12404
12744
  width_;
12745
+ /**
12746
+ * Creates a projected line renderable for the given path geometry.
12747
+ *
12748
+ * @param props - Initialization properties.
12749
+ */
12405
12750
  constructor({ geometry: A, color: I, width: B }) {
12406
12751
  super(), this.geometry = A, this.color_ = x.from(I), this.width_ = B, this.programName = "projectedLine", this.depthProgramName = "projectedLineDepth";
12407
12752
  }
12753
+ /** Identifies the renderable type as `ProjectedLineRenderable`. */
12408
12754
  get type() {
12409
12755
  return "ProjectedLineRenderable";
12410
12756
  }
12757
+ /** The line color. Assignable from any {@link ColorLike} value. */
12411
12758
  get color() {
12412
12759
  return this.color_;
12413
12760
  }
12761
+ /** @param value - The new line color. */
12414
12762
  set color(A) {
12415
12763
  this.color_ = x.from(A);
12416
12764
  }
12765
+ /** The line width in pixels. */
12417
12766
  get width() {
12418
12767
  return this.width_;
12419
12768
  }
12769
+ /** @param value - The new width in pixels. */
12420
12770
  set width(A) {
12421
12771
  this.width_ = A;
12422
12772
  }
12773
+ /** Returns the color and width uniforms for the line shader. */
12423
12774
  getUniforms() {
12424
12775
  return {
12425
12776
  u_lineColor: this.color.rgb,
@@ -12428,7 +12779,13 @@ class pi extends gI {
12428
12779
  }
12429
12780
  }
12430
12781
  class Ro extends qI {
12782
+ /** Identifies the layer type as `AxesLayer`. */
12431
12783
  type = "AxesLayer";
12784
+ /**
12785
+ * Creates an axes layer with the given dimensions.
12786
+ *
12787
+ * @param props - Initialization properties.
12788
+ */
12432
12789
  constructor(A) {
12433
12790
  super();
12434
12791
  const { length: I, width: B } = A;
@@ -12452,6 +12809,7 @@ class Ro extends qI {
12452
12809
  })
12453
12810
  ), this.setState("ready");
12454
12811
  }
12812
+ /** Performs no per-frame work. The axes are built at construction. */
12455
12813
  update() {
12456
12814
  }
12457
12815
  }
@@ -12620,8 +12978,18 @@ class wC extends mA {
12620
12978
  }
12621
12979
  }
12622
12980
  class Ti extends gI {
12623
- channels_;
12981
+ /**
12982
+ * A matrix mapping world space to the texture's normalized coordinate
12983
+ * space. Layers derive it from the chunk's offset, scale, and shape.
12984
+ */
12624
12985
  worldToTexCoord = $();
12986
+ channels_;
12987
+ /**
12988
+ * Creates an image renderable drawing the given texture. The texture's
12989
+ * data type selects the matching scalar image shader.
12990
+ *
12991
+ * @param props - Initialization properties.
12992
+ */
12625
12993
  constructor({
12626
12994
  width: A,
12627
12995
  height: I,
@@ -12630,12 +12998,27 @@ class Ti extends gI {
12630
12998
  }) {
12631
12999
  super(), this.geometry = new wC(A, I, 1, 1), this.setTexture(0, B), this.channels_ = tg(B, C), this.programName = xi(B), this.depthProgramName = "meshDepth";
12632
13000
  }
13001
+ /** Identifies the renderable type as `ImageRenderable`. */
12633
13002
  get type() {
12634
13003
  return "ImageRenderable";
12635
13004
  }
13005
+ /**
13006
+ * Replaces the channel appearance settings and revalidates them
13007
+ * against the current texture. Only the first entry affects rendering.
13008
+ *
13009
+ * @param channels - The new channel settings.
13010
+ */
12636
13011
  setChannelProps(A) {
12637
13012
  this.channels_ = tg(this.textures[0], A);
12638
13013
  }
13014
+ /**
13015
+ * Updates one property of the channel at the given index and
13016
+ * revalidates the channel against the current texture.
13017
+ *
13018
+ * @param channelIndex - The channel to update.
13019
+ * @param property - The property name to set.
13020
+ * @param value - The new value.
13021
+ */
12639
13022
  setChannelProperty(A, I, B) {
12640
13023
  const C = jA(this.textures[0], {
12641
13024
  ...this.channels_[A],
@@ -12643,6 +13026,10 @@ class Ti extends gI {
12643
13026
  });
12644
13027
  this.channels_[A] = C;
12645
13028
  }
13029
+ /**
13030
+ * Returns the sampler, contrast, color, opacity, and world-to-texture
13031
+ * uniforms for the scalar image shaders.
13032
+ */
12646
13033
  getUniforms() {
12647
13034
  const A = this.textures[0];
12648
13035
  if (!A)
@@ -12737,6 +13124,7 @@ class xg {
12737
13124
  }
12738
13125
  }
12739
13126
  class rC extends qI {
13127
+ /** Identifies the layer type as `ImageLayer`. */
12740
13128
  type = "ImageLayer";
12741
13129
  source_;
12742
13130
  sliceCoords_;
@@ -12764,6 +13152,11 @@ class rC extends qI {
12764
13152
  new x(0.4, 0.4, 0.7),
12765
13153
  new x(0.6, 0.5, 0.3)
12766
13154
  ];
13155
+ /**
13156
+ * Creates an image layer for the given source and slice.
13157
+ *
13158
+ * @param props - Initialization properties.
13159
+ */
12767
13160
  constructor({
12768
13161
  source: A,
12769
13162
  sliceCoords: I,
@@ -12775,6 +13168,7 @@ class rC extends qI {
12775
13168
  }) {
12776
13169
  super({ blendMode: "additive", occludes: !0, ...i }), this.setState("initialized"), this.source_ = A, this.policy_ = B ?? ug(), this.sliceCoords_ = I, this.orientation_ = C ?? "XY", this.axes_ = MA(this.orientation_), this.planeRotation_ = dA(this.axes_), this.channelProps_ = Q, this.initialChannelProps_ = Q, this.onPickValue_ = E;
12777
13170
  }
13171
+ /** @hidden */
12778
13172
  attach(A) {
12779
13173
  this.context_ = A, this.chunkStoreView_ = A.chunkManager.addView(
12780
13174
  this.source_,
@@ -12785,9 +13179,15 @@ class rC extends qI {
12785
13179
  this.chunkStoreView_.channelCount
12786
13180
  );
12787
13181
  }
13182
+ /** @hidden */
12788
13183
  detach(A) {
12789
13184
  this.releaseAndRemoveChunks(this.visibleChunks_.keys()), this.orderedChunks_ = [], this.clearObjects(), this.chunkStoreView_?.dispose(), this.chunkStoreView_ = void 0, this.context_ = void 0;
12790
13185
  }
13186
+ /**
13187
+ * Streams chunks for the current view and refreshes the visible slice.
13188
+ *
13189
+ * @param viewport - The viewport being rendered.
13190
+ */
12791
13191
  update(A) {
12792
13192
  if (!A || !this.chunkStoreView_) return;
12793
13193
  const I = A.camera, B = I.type === "OrthographicCamera" ? I.getWorldViewRect() : this.chunkStoreView_.getWholePlaneRect();
@@ -12798,12 +13198,16 @@ class rC extends qI {
12798
13198
  for (const [C, Q] of this.visibleChunks_)
12799
13199
  this.updateSlicePosition(Q, C);
12800
13200
  }
13201
+ /** The slice plane the layer displays. */
12801
13202
  get orientation() {
12802
13203
  return this.orientation_;
12803
13204
  }
12804
13205
  /**
12805
- * Changes the slice orientation at runtime. Visible renderables are rebuilt
12806
- * for the new plane, chunks already resident in the shared cache are reused.
13206
+ * Changes the slice orientation at runtime. Visible renderables are
13207
+ * rebuilt for the new plane and chunks already resident in the shared
13208
+ * cache are reused.
13209
+ *
13210
+ * @param orientation - The new slice plane.
12807
13211
  */
12808
13212
  setOrientation(A) {
12809
13213
  A !== this.orientation_ && (this.releaseAndRemoveChunks(this.visibleChunks_.keys()), this.orderedChunks_ = [], this.clearObjects(), this.chunkStoreView_?.dispose(), this.lastPresentationTimeStamp_ = void 0, this.lastPresentationTimeCoord_ = void 0, this.orientation_ = A, this.axes_ = MA(A), this.planeRotation_ = dA(this.axes_), this.context_ && (this.chunkStoreView_ = this.context_.chunkManager.addView(
@@ -12839,12 +13243,19 @@ class rC extends qI {
12839
13243
  I === void 0 || this.channelProps_?.[B]?.visible === !1 || this.addObject(I, B);
12840
13244
  }
12841
13245
  }
13246
+ /** The `t` coordinate of the most recently presented slice. */
12842
13247
  get lastPresentationTimeCoord() {
12843
13248
  return this.lastPresentationTimeCoord_;
12844
13249
  }
12845
13250
  isPresentationStale() {
12846
13251
  return this.lastPresentationTimeStamp_ === void 0 ? !1 : performance.now() - this.lastPresentationTimeStamp_ > rC.STALE_PRESENTATION_MS_;
12847
13252
  }
13253
+ /**
13254
+ * Handles click picking for `onPickValue`. Called automatically for
13255
+ * each pointer event.
13256
+ *
13257
+ * @param event - The event with clip and world coordinates attached.
13258
+ */
12848
13259
  onEvent(A) {
12849
13260
  this.pointerDownPos_ = FC(
12850
13261
  A,
@@ -12866,18 +13277,30 @@ class rC extends qI {
12866
13277
  return i === null ? null : { world: E, value: i };
12867
13278
  }
12868
13279
  // exposed for use in chunk info overlay
13280
+ /** The layer's chunk store view for diagnostic overlays. */
12869
13281
  get chunkStoreView() {
12870
13282
  return this.chunkStoreView_;
12871
13283
  }
13284
+ /**
13285
+ * The slice coordinates the layer displays. This is the object passed
13286
+ * at construction and may be mutated to move through the data.
13287
+ */
12872
13288
  get sliceCoords() {
12873
13289
  return this.sliceCoords_;
12874
13290
  }
13291
+ /** The chunked image source the layer streams from. */
12875
13292
  get source() {
12876
13293
  return this.source_;
12877
13294
  }
13295
+ /**
13296
+ * The streaming policy in effect. Assign a new policy to reschedule
13297
+ * loading at runtime, for example when switching between exploration
13298
+ * and playback.
13299
+ */
12878
13300
  get imageSourcePolicy() {
12879
13301
  return this.policy_;
12880
13302
  }
13303
+ /** @param newPolicy - The policy to apply. */
12881
13304
  set imageSourcePolicy(A) {
12882
13305
  this.policy_ !== A && (this.policy_ = A, this.chunkStoreView_ && this.chunkStoreView_.setImageSourcePolicy(
12883
13306
  A,
@@ -12920,6 +13343,14 @@ class rC extends qI {
12920
13343
  const { u: B, v: C } = this.axes_;
12921
13344
  A.transform.setScale([I.scale[B], I.scale[C], 1]), A.transform.setRotation(this.planeRotation_), this.updateSlicePosition(A, I), A.worldToTexCoord = _B(I, this.axes_);
12922
13345
  }
13346
+ /**
13347
+ * Reads the data value at a world position from the resident chunks.
13348
+ * Prefers the current level of detail and falls back to other resident
13349
+ * levels.
13350
+ *
13351
+ * @param world - The world-space position to sample.
13352
+ * @returns The sampled value or `null` if no resident chunk covers it.
13353
+ */
12923
13354
  async getValueAtWorld(A) {
12924
13355
  const I = this.chunkStoreView_?.currentLOD ?? 0;
12925
13356
  for (const B of [!0, !1])
@@ -12941,17 +13372,26 @@ class rC extends qI {
12941
13372
  const s = { x: 0, y: 0, z: 0 };
12942
13373
  return s[Q] = o, s[E] = a, s[i] = this.sliceIndexForChunk(A), await I.textures[0].readTexel?.(s.x, s.y, s.z) ?? null;
12943
13374
  }
13375
+ /** Whether chunk wireframes are drawn colored by level of detail. */
12944
13376
  get debugMode() {
12945
13377
  return this.debugMode_;
12946
13378
  }
13379
+ /** @param debug - Whether to draw chunk wireframes. */
12947
13380
  set debugMode(A) {
12948
13381
  this.debugMode_ = A, this.visibleChunks_.forEach((I, B) => {
12949
13382
  I.wireframeEnabled = this.debugMode_, this.debugMode_ && (I.wireframeColor = this.wireframeColors_[B.lod % this.wireframeColors_.length]);
12950
13383
  });
12951
13384
  }
13385
+ /** The current per-channel appearance settings. */
12952
13386
  get channelProps() {
12953
13387
  return this.channelProps_;
12954
13388
  }
13389
+ /**
13390
+ * Applies new per-channel appearance settings to all visible chunks
13391
+ * and notifies channel change callbacks.
13392
+ *
13393
+ * @param channelProps - One entry per source channel.
13394
+ */
12955
13395
  setChannelProps(A) {
12956
13396
  this.channelProps_ = A, this.visibleChunks_.forEach((I, B) => {
12957
13397
  I.setChannelProps(this.getChannelPropsForChunk(B));
@@ -12959,12 +13399,23 @@ class rC extends qI {
12959
13399
  I();
12960
13400
  });
12961
13401
  }
13402
+ /** Restores the channel settings passed at construction. */
12962
13403
  resetChannelProps() {
12963
13404
  this.initialChannelProps_ !== void 0 && this.setChannelProps(this.initialChannelProps_);
12964
13405
  }
13406
+ /**
13407
+ * Registers a callback invoked after every channel settings change.
13408
+ *
13409
+ * @param callback - The callback to add.
13410
+ */
12965
13411
  addChannelChangeCallback(A) {
12966
13412
  this.channelChangeCallbacks_.push(A);
12967
13413
  }
13414
+ /**
13415
+ * Removes a previously registered channel change callback.
13416
+ *
13417
+ * @param callback - The callback to remove.
13418
+ */
12968
13419
  removeChannelChangeCallback(A) {
12969
13420
  const I = this.channelChangeCallbacks_.indexOf(A);
12970
13421
  if (I === -1)
@@ -13034,16 +13485,35 @@ class Wi extends mA {
13034
13485
  }
13035
13486
  }
13036
13487
  class bi extends gI {
13488
+ /**
13489
+ * World size of a voxel along each axis. Layers set it from the chunk
13490
+ * scale so ray march steps account for anisotropic voxels. Defaults to
13491
+ * `[1, 1, 1]`.
13492
+ */
13037
13493
  voxelScale = p(1, 1, 1);
13038
13494
  channels_;
13039
13495
  loadedChannels_ = /* @__PURE__ */ new Set();
13040
13496
  channelToTextureIndex_ = /* @__PURE__ */ new Map();
13497
+ /**
13498
+ * Creates an empty volume renderable.
13499
+ *
13500
+ * @param props - Initialization properties.
13501
+ */
13041
13502
  constructor({ channelProps: A = [] } = {}) {
13042
13503
  super(), this.geometry = new Wi(1, 1, 1, 1, 1, 1), this.cullFaceMode = "front", this.depthTest = !1, this.channels_ = [], this.setChannelProps(A);
13043
13504
  }
13505
+ /** Identifies the renderable type as `VolumeRenderable`. */
13044
13506
  get type() {
13045
13507
  return "VolumeRenderable";
13046
13508
  }
13509
+ /**
13510
+ * Loads or refreshes the texture for the chunk's channel. The channel
13511
+ * index comes from the chunk and the texture's data type selects the
13512
+ * volume shader, so every channel must share one data type. Chunks
13513
+ * without a texture are ignored.
13514
+ *
13515
+ * @param chunk - The chunk holding the channel texture.
13516
+ */
13047
13517
  updateVolumeWithChunk(A) {
13048
13518
  if (!A.texture) return;
13049
13519
  const I = A.chunkIndex.c, B = this.channelToTextureIndex_.get(I);
@@ -13062,12 +13532,25 @@ class bi extends gI {
13062
13532
  updateChannelTexture(A, I) {
13063
13533
  this.setTexture(A, I);
13064
13534
  }
13535
+ /**
13536
+ * Marks all channels as not loaded so they stop rendering until the
13537
+ * next chunk update. The textures themselves are kept.
13538
+ */
13065
13539
  clearLoadedChannels() {
13066
13540
  this.loadedChannels_ = /* @__PURE__ */ new Set();
13067
13541
  }
13542
+ /**
13543
+ * Clears all textures and channel state so the renderable can be
13544
+ * pooled and reused for another chunk.
13545
+ */
13068
13546
  reset() {
13069
13547
  this.clearTextures(), this.channelToTextureIndex_.clear(), this.clearLoadedChannels();
13070
13548
  }
13549
+ /**
13550
+ * Returns per-channel sampler, color, contrast, opacity, and
13551
+ * visibility uniforms for up to 4 loaded channels plus the voxel
13552
+ * scale.
13553
+ */
13071
13554
  getUniforms() {
13072
13555
  const A = [0, 0, 0, 0], I = [
13073
13556
  1,
@@ -13121,12 +13604,25 @@ class bi extends gI {
13121
13604
  const I = this.channelToTextureIndex_.values().next().value;
13122
13605
  return I !== void 0 ? this.textures[I] : null;
13123
13606
  }
13607
+ /**
13608
+ * Replaces the appearance settings for all channels.
13609
+ *
13610
+ * @param channels - The new channel settings.
13611
+ */
13124
13612
  setChannelProps(A) {
13125
13613
  this.channels_ = tg(
13126
13614
  this.getAvailableChannelTexture(),
13127
13615
  A
13128
13616
  );
13129
13617
  }
13618
+ /**
13619
+ * Updates one property of the channel at the given index and
13620
+ * revalidates it against the channel's texture when available.
13621
+ *
13622
+ * @param channelIndex - The channel to update.
13623
+ * @param property - The property name to set.
13624
+ * @param value - The new value.
13625
+ */
13130
13626
  setChannelProperty(A, I, B) {
13131
13627
  const C = jA(
13132
13628
  this.getAvailableChannelTexture(A),
@@ -13154,7 +13650,17 @@ function Zi(g) {
13154
13650
  }
13155
13651
  const Pi = 2;
13156
13652
  class Uo extends qI {
13653
+ /** Identifies the layer type as `VolumeLayer`. */
13157
13654
  type = "VolumeLayer";
13655
+ /** Highlights rays of zero length for debugging. Defaults to `false`. */
13656
+ debugShowDegenerateRays = !1;
13657
+ /** Ray march step size relative to voxel size. Defaults to `1`. */
13658
+ relativeStepSize = 1;
13659
+ /** Scales sample opacity during compositing. Defaults to `1`. */
13660
+ opacityMultiplier = 1;
13661
+ /** Alpha where a ray stops early. Defaults to `0.99`. */
13662
+ earlyTerminationAlpha = 0.99;
13663
+ /** Volume ray marching reads scene depth to composite with occluding layers. */
13158
13664
  requiresSceneDepth_ = !0;
13159
13665
  source_;
13160
13666
  sliceCoords_;
@@ -13171,13 +13677,11 @@ class Uo extends qI {
13171
13677
  interactiveStepSizeScale_ = 1;
13172
13678
  // TODO: Make a debug config object to manage debug options
13173
13679
  debugShowWireframes_ = !1;
13174
- debugShowDegenerateRays = !1;
13175
- relativeStepSize = 1;
13176
- opacityMultiplier = 1;
13177
- earlyTerminationAlpha = 0.99;
13680
+ /** Whether chunk bounding wireframes are drawn for debugging. */
13178
13681
  get debugShowWireframes() {
13179
13682
  return this.debugShowWireframes_;
13180
13683
  }
13684
+ /** @param value - Whether to draw chunk wireframes. */
13181
13685
  set debugShowWireframes(A) {
13182
13686
  if (this.debugShowWireframes_ !== A) {
13183
13687
  for (const I of this.currentVolumes_.values())
@@ -13185,12 +13689,24 @@ class Uo extends qI {
13185
13689
  this.debugShowWireframes_ = A;
13186
13690
  }
13187
13691
  }
13692
+ /**
13693
+ * Sets the streaming policy at runtime and reschedules loading. The
13694
+ * volume renders the level of detail given by the policy's `lod.min`.
13695
+ *
13696
+ * @param newPolicy - The policy to apply.
13697
+ */
13188
13698
  set imageSourcePolicy(A) {
13189
13699
  this.policy_ !== A && (this.policy_ = A, this.chunkStoreView_ && this.chunkStoreView_.setImageSourcePolicy(
13190
13700
  A,
13191
13701
  dI
13192
13702
  ));
13193
13703
  }
13704
+ /**
13705
+ * Applies new per-channel appearance settings to all visible volumes
13706
+ * and notifies channel change callbacks.
13707
+ *
13708
+ * @param channelProps - One entry per source channel.
13709
+ */
13194
13710
  setChannelProps(A) {
13195
13711
  this.channelProps_ = A;
13196
13712
  for (const I of this.currentVolumes_.values())
@@ -13199,21 +13715,38 @@ class Uo extends qI {
13199
13715
  I();
13200
13716
  });
13201
13717
  }
13718
+ /** The current per-channel appearance settings. */
13202
13719
  get channelProps() {
13203
13720
  return this.channelProps_;
13204
13721
  }
13722
+ /** Restores the channel settings passed at construction. */
13205
13723
  resetChannelProps() {
13206
13724
  this.initialChannelProps_ !== void 0 && this.setChannelProps(this.initialChannelProps_);
13207
13725
  }
13726
+ /**
13727
+ * Registers a callback invoked after every channel settings change.
13728
+ *
13729
+ * @param callback - The callback to add.
13730
+ */
13208
13731
  addChannelChangeCallback(A) {
13209
13732
  this.channelChangeCallbacks_.push(A);
13210
13733
  }
13734
+ /**
13735
+ * Removes a previously registered channel change callback.
13736
+ *
13737
+ * @param callback - The callback to remove.
13738
+ */
13211
13739
  removeChannelChangeCallback(A) {
13212
13740
  const I = this.channelChangeCallbacks_.indexOf(A);
13213
13741
  if (I === -1)
13214
13742
  throw new Error(`Callback to remove could not be found: ${A}`);
13215
13743
  this.channelChangeCallbacks_.splice(I, 1);
13216
13744
  }
13745
+ /**
13746
+ * Creates a volume layer for the given source.
13747
+ *
13748
+ * @param props - Initialization properties.
13749
+ */
13217
13750
  constructor({ source: A, sliceCoords: I, policy: B, channelProps: C }) {
13218
13751
  super({ blendMode: "premultipliedOver" }), this.source_ = A, this.sliceCoords_ = I, this.policy_ = B ?? ug(), this.initialChannelProps_ = C, this.channelProps_ = C, this.setState("initialized");
13219
13752
  }
@@ -13228,6 +13761,7 @@ class Uo extends qI {
13228
13761
  for (const E of I) Q.updateVolumeWithChunk(E);
13229
13762
  return this.updateVolumeTransform(Q, I[0]), Q;
13230
13763
  }
13764
+ /** @hidden */
13231
13765
  attach(A) {
13232
13766
  this.chunkStoreView_ = A.chunkManager.addView(
13233
13767
  this.source_,
@@ -13237,6 +13771,7 @@ class Uo extends qI {
13237
13771
  this.chunkStoreView_.channelCount
13238
13772
  );
13239
13773
  }
13774
+ /** @hidden */
13240
13775
  detach(A) {
13241
13776
  for (const I of this.currentVolumes_.values())
13242
13777
  this.releaseAndRemoveVolume(I);
@@ -13277,6 +13812,12 @@ class Uo extends qI {
13277
13812
  releaseAndRemoveVolume(A) {
13278
13813
  A.reset(), this.pool_.release(this.volumeToPoolKey_.get(A), A), this.volumeToPoolKey_.delete(A);
13279
13814
  }
13815
+ /**
13816
+ * Streams chunks for the current view and rebuilds the volume set
13817
+ * sorted front to back. Called automatically once per frame.
13818
+ *
13819
+ * @param viewport - The viewport being rendered.
13820
+ */
13280
13821
  update(A) {
13281
13822
  if (!A || !this.chunkStoreView_) return;
13282
13823
  this.chunkStoreView_.updateChunksForVolume(
@@ -13292,6 +13833,7 @@ class Uo extends qI {
13292
13833
  for (const B of I)
13293
13834
  this.addObject(B);
13294
13835
  }
13836
+ /** Returns the ray marching uniforms for this layer. */
13295
13837
  getUniforms() {
13296
13838
  return {
13297
13839
  u_debugShowDegenerateRays: Number(this.debugShowDegenerateRays),
@@ -13390,17 +13932,32 @@ function Io(g) {
13390
13932
  return SC.has(g.dataType) ? "intLabelImage" : "labelImage";
13391
13933
  }
13392
13934
  class go extends gI {
13935
+ /**
13936
+ * A matrix mapping world space to the texture's normalized coordinate
13937
+ * space. Layers derive it from the chunk's offset, scale, and shape.
13938
+ */
13939
+ worldToTexCoord = $();
13393
13940
  outlineSelected_;
13394
13941
  selectedValue_;
13395
- worldToTexCoord = $();
13942
+ /**
13943
+ * Creates a label renderable drawing the given label texture. The
13944
+ * texture's data type selects the matching label shader.
13945
+ *
13946
+ * @param props - Initialization properties.
13947
+ */
13396
13948
  constructor(A) {
13397
13949
  super(), this.geometry = new wC(A.width, A.height, 1, 1), this.setTexture(0, Ao(A.imageData));
13398
13950
  const I = JI(A.colorMap);
13399
13951
  this.setTexture(1, this.makeColorCycleTexture(I.cycle)), this.setTexture(2, this.makeColorLookupTableTexture(I.lookupTable)), this.outlineSelected_ = A.outlineSelected ?? !1, this.selectedValue_ = A.selectedValue ?? null, this.programName = Io(A.imageData), this.depthProgramName = "meshDepth";
13400
13952
  }
13953
+ /** Identifies the renderable type as `LabelImageRenderable`. */
13401
13954
  get type() {
13402
13955
  return "LabelImageRenderable";
13403
13956
  }
13957
+ /**
13958
+ * Returns the sampler, color map, and selection uniforms for the label
13959
+ * image shaders.
13960
+ */
13404
13961
  getUniforms() {
13405
13962
  return {
13406
13963
  u_imageSampler: 0,
@@ -13411,10 +13968,23 @@ class go extends gI {
13411
13968
  u_worldToTexCoord: this.worldToTexCoord
13412
13969
  };
13413
13970
  }
13971
+ /**
13972
+ * Replaces the label color map. The previous color map textures are
13973
+ * marked stale for GPU disposal.
13974
+ *
13975
+ * @param colorMap - The new color map.
13976
+ */
13414
13977
  setColorMap(A) {
13415
13978
  const I = JI(A);
13416
13979
  this.markStaleTexture(this.textures[1]), this.markStaleTexture(this.textures[2]), this.setTexture(1, this.makeColorCycleTexture(I.cycle)), this.setTexture(2, this.makeColorLookupTableTexture(I.lookupTable));
13417
13980
  }
13981
+ /**
13982
+ * Sets the label value drawn as selected or `null` to clear the
13983
+ * selection. The selected region is outlined when the renderable was
13984
+ * constructed with `outlineSelected`.
13985
+ *
13986
+ * @param value - The label value to select.
13987
+ */
13418
13988
  setSelectedValue(A) {
13419
13989
  this.selectedValue_ = A;
13420
13990
  }
@@ -13431,6 +14001,7 @@ class go extends gI {
13431
14001
  }
13432
14002
  }
13433
14003
  class RC extends qI {
14004
+ /** Identifies the layer type as `LabelLayer`. */
13434
14005
  type = "LabelLayer";
13435
14006
  source_;
13436
14007
  sliceCoords_;
@@ -13450,6 +14021,11 @@ class RC extends qI {
13450
14021
  static STALE_PRESENTATION_MS_ = 1e3;
13451
14022
  lastPresentationTimeStamp_;
13452
14023
  lastPresentationTimeCoord_;
14024
+ /**
14025
+ * Creates a label layer for the given source and slice.
14026
+ *
14027
+ * @param props - Initialization properties.
14028
+ */
13453
14029
  constructor({
13454
14030
  source: A,
13455
14031
  sliceCoords: I,
@@ -13462,6 +14038,7 @@ class RC extends qI {
13462
14038
  }) {
13463
14039
  super(o), this.setState("initialized"), this.source_ = A, this.policy_ = B ?? ug(), this.sliceCoords_ = I, this.orientation_ = C ?? "XY", this.axes_ = MA(this.orientation_), this.planeRotation_ = dA(this.axes_), this.colorMap_ = JI(Q), this.onPickValue_ = E, this.outlineSelected_ = i;
13464
14040
  }
14041
+ /** @hidden */
13465
14042
  attach(A) {
13466
14043
  if (this.context_ = A, this.chunkStoreView_ = A.chunkManager.addView(
13467
14044
  this.source_,
@@ -13472,9 +14049,16 @@ class RC extends qI {
13472
14049
  `LabelLayer does not support multi-channel sources (found ${this.chunkStoreView_.channelCount} channels). Label data must be single-channel.`
13473
14050
  );
13474
14051
  }
14052
+ /** @hidden */
13475
14053
  detach(A) {
13476
14054
  this.releaseAndRemoveChunks(this.visibleChunks_.keys()), this.clearObjects(), this.chunkStoreView_?.dispose(), this.chunkStoreView_ = void 0, this.context_ = void 0;
13477
14055
  }
14056
+ /**
14057
+ * Streams chunks for the current view and refreshes the visible slice.
14058
+ * Called automatically once per frame.
14059
+ *
14060
+ * @param viewport - The viewport being rendered.
14061
+ */
13478
14062
  update(A) {
13479
14063
  if (!A || !this.chunkStoreView_) return;
13480
14064
  const I = A.camera, B = I.type === "OrthographicCamera" ? I.getWorldViewRect() : this.chunkStoreView_.getWholePlaneRect();
@@ -13485,12 +14069,16 @@ class RC extends qI {
13485
14069
  for (const [C, Q] of this.visibleChunks_)
13486
14070
  this.updateSlicePosition(Q, C);
13487
14071
  }
14072
+ /** The slice plane the layer displays. */
13488
14073
  get orientation() {
13489
14074
  return this.orientation_;
13490
14075
  }
13491
14076
  /**
13492
- * Changes the slice orientation at runtime. Visible renderables are rebuilt
13493
- * for the new plane, chunks already resident in the shared cache are reused.
14077
+ * Changes the slice orientation at runtime. Visible renderables are
14078
+ * rebuilt for the new plane and chunks already resident in the shared
14079
+ * cache are reused.
14080
+ *
14081
+ * @param orientation - The new slice plane.
13494
14082
  */
13495
14083
  setOrientation(A) {
13496
14084
  A !== this.orientation_ && (this.releaseAndRemoveChunks(this.visibleChunks_.keys()), this.clearObjects(), this.chunkStoreView_?.dispose(), this.lastPresentationTimeStamp_ = void 0, this.lastPresentationTimeCoord_ = void 0, this.orientation_ = A, this.axes_ = MA(A), this.planeRotation_ = dA(this.axes_), this.context_ && (this.chunkStoreView_ = this.context_.chunkManager.addView(
@@ -13520,6 +14108,12 @@ class RC extends qI {
13520
14108
  isPresentationStale() {
13521
14109
  return this.lastPresentationTimeStamp_ === void 0 ? !1 : performance.now() - this.lastPresentationTimeStamp_ > RC.STALE_PRESENTATION_MS_;
13522
14110
  }
14111
+ /**
14112
+ * Handles click picking and selection outlining. Called automatically
14113
+ * for each pointer event on the owning viewport.
14114
+ *
14115
+ * @param event - The event with clip and world coordinates attached.
14116
+ */
13523
14117
  onEvent(A) {
13524
14118
  this.pointerDownPos_ = FC(
13525
14119
  A,
@@ -13542,28 +14136,52 @@ class RC extends qI {
13542
14136
  const i = await this.getValueAtWorld(E);
13543
14137
  return i === null ? null : { world: E, value: i };
13544
14138
  }
14139
+ /** The validated color map currently in effect. */
13545
14140
  get colorMap() {
13546
14141
  return this.colorMap_;
13547
14142
  }
14143
+ /**
14144
+ * Replaces the color map and recolors all visible chunks.
14145
+ *
14146
+ * @param colorMap - Colors for label values. Omitted fields fall back
14147
+ * to defaults.
14148
+ */
13548
14149
  setColorMap(A) {
13549
14150
  this.colorMap_ = JI(A), this.visibleChunks_.forEach((I) => {
13550
14151
  I.setColorMap(this.colorMap_);
13551
14152
  });
13552
14153
  }
14154
+ /**
14155
+ * Sets the label value drawn as selected or `null` to clear the
14156
+ * selection.
14157
+ *
14158
+ * @param value - The label value to select.
14159
+ */
13553
14160
  setSelectedValue(A) {
13554
14161
  this.selectedValue_ = A, this.visibleChunks_.forEach((I) => {
13555
14162
  I.setSelectedValue(this.selectedValue_);
13556
14163
  });
13557
14164
  }
14165
+ /**
14166
+ * The slice coordinates the layer displays. This is the object passed
14167
+ * at construction and may be mutated to move through the data.
14168
+ */
13558
14169
  get sliceCoords() {
13559
14170
  return this.sliceCoords_;
13560
14171
  }
14172
+ /** The chunked label source the layer streams from. */
13561
14173
  get source() {
13562
14174
  return this.source_;
13563
14175
  }
14176
+ /**
14177
+ * The streaming policy in effect. Assign a new policy to reschedule
14178
+ * loading at runtime, for example when switching between exploration
14179
+ * and playback.
14180
+ */
13564
14181
  get imageSourcePolicy() {
13565
14182
  return this.policy_;
13566
14183
  }
14184
+ /** @param newPolicy - The policy to apply. */
13567
14185
  set imageSourcePolicy(A) {
13568
14186
  this.policy_ !== A && (this.policy_ = A, this.chunkStoreView_ && this.chunkStoreView_.setImageSourcePolicy(
13569
14187
  A,
@@ -13571,12 +14189,22 @@ class RC extends qI {
13571
14189
  ));
13572
14190
  }
13573
14191
  // exposed for use in chunk info overlay
14192
+ /** The layer's chunk store view for diagnostic overlays. */
13574
14193
  get chunkStoreView() {
13575
14194
  return this.chunkStoreView_;
13576
14195
  }
14196
+ /** The `t` coordinate of the most recently presented slice. */
13577
14197
  get lastPresentationTimeCoord() {
13578
14198
  return this.lastPresentationTimeCoord_;
13579
14199
  }
14200
+ /**
14201
+ * Reads the label value at a world position from the resident chunks.
14202
+ * Prefers the current level of detail and falls back to other resident
14203
+ * levels.
14204
+ *
14205
+ * @param world - The world-space position to sample.
14206
+ * @returns The label value or `null` if no resident chunk covers it.
14207
+ */
13580
14208
  async getValueAtWorld(A) {
13581
14209
  const I = this.chunkStoreView_?.currentLOD ?? 0;
13582
14210
  for (const B of [!0, !1])
@@ -13665,6 +14293,11 @@ const NC = {
13665
14293
  triangle: 2
13666
14294
  };
13667
14295
  class ko extends gI {
14296
+ /**
14297
+ * Creates a renderable drawing one marker per entry.
14298
+ *
14299
+ * @param points - The points to draw.
14300
+ */
13668
14301
  constructor(A) {
13669
14302
  super(), this.programName = "points", this.depthProgramName = "pointsDepth";
13670
14303
  const I = A.flatMap((C) => {
@@ -13699,6 +14332,7 @@ class ko extends gI {
13699
14332
  offset: B.strideBytes
13700
14333
  }), this.geometry = B, this.setTexture(0, Co());
13701
14334
  }
14335
+ /** Identifies the renderable type as `PointsRenderable`. */
13702
14336
  get type() {
13703
14337
  return "PointsRenderable";
13704
14338
  }
@@ -13733,6 +14367,152 @@ function Qo() {
13733
14367
  const o = new Bo(i, 256, 256);
13734
14368
  return o.wrapR = "clamp_to_edge", o.wrapS = "clamp_to_edge", o.wrapT = "clamp_to_edge", o;
13735
14369
  }
14370
+ class Eo {
14371
+ planes_;
14372
+ /**
14373
+ * Creates a frustum from a view-projection matrix.
14374
+ *
14375
+ * @param m - The combined view-projection matrix.
14376
+ */
14377
+ constructor(A) {
14378
+ this.planes_ = [
14379
+ new sA(d(), 0),
14380
+ new sA(d(), 0),
14381
+ new sA(d(), 0),
14382
+ new sA(d(), 0),
14383
+ new sA(d(), 0),
14384
+ new sA(d(), 0)
14385
+ ], this.setWithViewProjection(A);
14386
+ }
14387
+ // Uses the fast plane-extraction algorithm described in
14388
+ // Gribb & Hartmann (1997): https://tinyurl.com/5x5htcwm
14389
+ /**
14390
+ * Re-extracts the six planes from a view-projection matrix.
14391
+ *
14392
+ * @param m - The combined view-projection matrix.
14393
+ */
14394
+ setWithViewProjection(A) {
14395
+ const I = d();
14396
+ this.planes_[0].set(
14397
+ rA(I, A[3] + A[0], A[7] + A[4], A[11] + A[8]),
14398
+ A[15] + A[12]
14399
+ ), this.planes_[1].set(
14400
+ rA(I, A[3] - A[0], A[7] - A[4], A[11] - A[8]),
14401
+ A[15] - A[12]
14402
+ ), this.planes_[2].set(
14403
+ rA(I, A[3] - A[1], A[7] - A[5], A[11] - A[9]),
14404
+ A[15] - A[13]
14405
+ ), this.planes_[3].set(
14406
+ rA(I, A[3] + A[1], A[7] + A[5], A[11] + A[9]),
14407
+ A[15] + A[13]
14408
+ ), this.planes_[4].set(
14409
+ rA(I, A[3] + A[2], A[7] + A[6], A[11] + A[10]),
14410
+ A[15] + A[14]
14411
+ ), this.planes_[5].set(
14412
+ rA(I, A[3] - A[2], A[7] - A[6], A[11] - A[10]),
14413
+ A[15] - A[14]
14414
+ );
14415
+ for (const B of this.planes_) B.normalize();
14416
+ }
14417
+ /**
14418
+ * Tests whether a box is at least partly inside the frustum. The test
14419
+ * is conservative. A box outside the frustum but near a corner can be
14420
+ * reported as intersecting, which only costs a draw of an offscreen
14421
+ * object.
14422
+ *
14423
+ * @param box - The world-space box to test.
14424
+ */
14425
+ intersectsWithBox3(A) {
14426
+ const I = d();
14427
+ for (const B of this.planes_) {
14428
+ const C = B.normal;
14429
+ if (I[0] = C[0] > 0 ? A.max[0] : A.min[0], I[1] = C[1] > 0 ? A.max[1] : A.min[1], I[2] = C[2] > 0 ? A.max[2] : A.min[2], B.signedDistanceToPoint(I) < 0) return !1;
14430
+ }
14431
+ return !0;
14432
+ }
14433
+ }
14434
+ class cC extends YI {
14435
+ transform_ = new eC();
14436
+ /** @hidden */
14437
+ projectionMatrix_ = $();
14438
+ /** @hidden */
14439
+ near_ = 0;
14440
+ /** @hidden */
14441
+ far_ = 0;
14442
+ /** Recomputes the camera's projection matrix. */
14443
+ update() {
14444
+ this.updateProjectionMatrix();
14445
+ }
14446
+ /** The camera's projection matrix. */
14447
+ get projectionMatrix() {
14448
+ return this.projectionMatrix_;
14449
+ }
14450
+ /** The camera's world-space transform. */
14451
+ get transform() {
14452
+ return this.transform_;
14453
+ }
14454
+ /** The view matrix: the inverse of the camera's world transform. */
14455
+ get viewMatrix() {
14456
+ return this.transform.inverse;
14457
+ }
14458
+ /** The camera's local right axis in world space. */
14459
+ get right() {
14460
+ const A = this.transform.matrix;
14461
+ return p(A[0], A[1], A[2]);
14462
+ }
14463
+ /** The camera's local up axis in world space. */
14464
+ get up() {
14465
+ const A = this.transform.matrix;
14466
+ return p(A[4], A[5], A[6]);
14467
+ }
14468
+ /**
14469
+ * Computes the combined view-projection matrix.
14470
+ *
14471
+ * @returns The projection matrix multiplied by the view matrix.
14472
+ */
14473
+ getViewProjection() {
14474
+ return QI($(), this.projectionMatrix, this.viewMatrix);
14475
+ }
14476
+ /** The view frustum derived from the current view-projection. */
14477
+ get frustum() {
14478
+ return new Eo(this.getViewProjection());
14479
+ }
14480
+ /**
14481
+ * Moves the camera by the given world-space offset.
14482
+ *
14483
+ * @param vec - The translation to add to the camera's position.
14484
+ */
14485
+ pan(A) {
14486
+ this.transform.addTranslation(A);
14487
+ }
14488
+ /** The camera's world-space position. */
14489
+ get position() {
14490
+ return this.transform.translation;
14491
+ }
14492
+ /**
14493
+ * Transforms a position from clip space to world space.
14494
+ *
14495
+ * @param position - The clip-space position to transform.
14496
+ * @returns The corresponding world-space position.
14497
+ */
14498
+ clipToWorld(A) {
14499
+ const I = DI(A[0], A[1], A[2], 1), B = OA(
14500
+ $(),
14501
+ this.projectionMatrix_
14502
+ ), C = VA(
14503
+ YA(),
14504
+ I,
14505
+ B
14506
+ );
14507
+ BQ(C, C, 1 / C[3]);
14508
+ const Q = VA(
14509
+ YA(),
14510
+ C,
14511
+ this.transform.matrix
14512
+ );
14513
+ return p(Q[0], Q[1], Q[2]);
14514
+ }
14515
+ }
13736
14516
  class TB {
13737
14517
  radius;
13738
14518
  phi;
@@ -13749,7 +14529,7 @@ class TB {
13749
14529
  );
13750
14530
  }
13751
14531
  }
13752
- const vI = -1, xB = 0, Eo = 1, WB = 9e-3, io = 1e-3, oo = 9e-4, ao = 0.5, so = 60;
14532
+ const vI = -1, xB = 0, io = 1, WB = 9e-3, oo = 1e-3, ao = 9e-4, so = 0.5, Do = 60;
13753
14533
  class Lo {
13754
14534
  camera_;
13755
14535
  orbitVelocity_ = new TB(0, 0, 0);
@@ -13758,32 +14538,49 @@ class Lo {
13758
14538
  currCenter_ = d();
13759
14539
  dampingFactor_;
13760
14540
  currMouseButton_ = vI;
14541
+ /**
14542
+ * Creates orbit controls and moves the camera to the initial pose.
14543
+ *
14544
+ * @param camera - The perspective camera to control.
14545
+ * @param params - Initialization properties.
14546
+ */
13761
14547
  constructor(A, I) {
13762
14548
  this.camera_ = A, this.currPos_ = new TB(
13763
14549
  I?.radius ?? 1,
13764
14550
  I?.yaw ?? 0,
13765
14551
  I?.pitch ?? 0
13766
14552
  ), I?.target && jI(this.currCenter_, I.target), this.dampingFactor_ = hA(
13767
- I?.dampingFactor ?? ao,
14553
+ I?.dampingFactor ?? so,
13768
14554
  0,
13769
14555
  1
13770
14556
  ), this.updateCamera();
13771
14557
  }
14558
+ /** The current distance from the target in world units. */
13772
14559
  get radius() {
13773
14560
  return this.currPos_.radius;
13774
14561
  }
14562
+ /** The current azimuth angle in radians. */
13775
14563
  get yaw() {
13776
14564
  return this.currPos_.phi;
13777
14565
  }
14566
+ /** The current elevation angle in radians. */
13778
14567
  get pitch() {
13779
14568
  return this.currPos_.theta;
13780
14569
  }
14570
+ /** A copy of the point the camera orbits. */
13781
14571
  get target() {
13782
14572
  return EA(this.currCenter_);
13783
14573
  }
14574
+ /** Whether any orbit, pan, or zoom velocity remains. */
13784
14575
  get isMoving() {
13785
14576
  return this.orbitVelocity_.phi !== 0 || this.orbitVelocity_.theta !== 0 || this.orbitVelocity_.radius !== 0 || this.panVelocity_[0] !== 0 || this.panVelocity_[1] !== 0 || this.panVelocity_[2] !== 0;
13786
14577
  }
14578
+ /**
14579
+ * Handles a pointer or wheel event. Called automatically by the owning
14580
+ * viewport unless a layer stops propagation.
14581
+ *
14582
+ * @param event - The event with clip and world coordinates attached.
14583
+ */
13787
14584
  onEvent(A) {
13788
14585
  switch (A.type) {
13789
14586
  case "pointerdown":
@@ -13801,13 +14598,19 @@ class Lo {
13801
14598
  break;
13802
14599
  }
13803
14600
  }
14601
+ /**
14602
+ * Applies pending velocities to the camera and decays them toward zero.
14603
+ * Called automatically by the render loop once per frame.
14604
+ *
14605
+ * @param dt - Time since the last frame in seconds.
14606
+ */
13804
14607
  onUpdate(A) {
13805
14608
  if (this.orbitVelocity_.phi === 0 && this.orbitVelocity_.theta === 0 && this.orbitVelocity_.radius === 0 && Gg(this.panVelocity_, p(0, 0, 0)))
13806
14609
  return;
13807
14610
  this.currPos_.phi += this.orbitVelocity_.phi, this.currPos_.theta += this.orbitVelocity_.theta, this.currPos_.radius += this.orbitVelocity_.radius * this.currPos_.radius, aI(this.currCenter_, this.currCenter_, this.panVelocity_);
13808
14611
  const I = Math.PI / 2 - T;
13809
14612
  this.currPos_.theta = hA(this.currPos_.theta, -I, I), this.currPos_.radius = Math.max(0.01, this.currPos_.radius), this.updateCamera();
13810
- const B = Math.pow(1 - this.dampingFactor_, A * so);
14613
+ const B = Math.pow(1 - this.dampingFactor_, A * Do);
13811
14614
  this.orbitVelocity_.phi *= B, this.orbitVelocity_.theta *= B, this.orbitVelocity_.radius *= B, zI(this.panVelocity_, this.panVelocity_, B), this.cutoffLowVelocity();
13812
14615
  }
13813
14616
  onPointerDown(A) {
@@ -13816,7 +14619,7 @@ class Lo {
13816
14619
  }
13817
14620
  onPointerMove(A) {
13818
14621
  if (this.currMouseButton_ == vI) return;
13819
- const I = A.event, B = I.movementX ?? 0, C = I.movementY ?? 0, Q = this.currMouseButton_ === xB && !I.shiftKey, E = this.currMouseButton_ === xB && I.shiftKey || this.currMouseButton_ === Eo;
14622
+ const I = A.event, B = I.movementX ?? 0, C = I.movementY ?? 0, Q = this.currMouseButton_ === xB && !I.shiftKey, E = this.currMouseButton_ === xB && I.shiftKey || this.currMouseButton_ === io;
13820
14623
  Q && this.orbit(B, C), E && this.pan(B, C);
13821
14624
  }
13822
14625
  onWheel(A) {
@@ -13834,11 +14637,11 @@ class Lo {
13834
14637
  this.orbitVelocity_.phi -= A * WB, this.orbitVelocity_.theta += I * WB;
13835
14638
  }
13836
14639
  pan(A, I) {
13837
- const B = this.currPos_.radius * io, C = d();
14640
+ const B = this.currPos_.radius * oo, C = d();
13838
14641
  _I(C, C, this.camera_.right, A), _I(C, C, this.camera_.up, I), zI(C, C, B), $I(this.panVelocity_, this.panVelocity_, C);
13839
14642
  }
13840
14643
  zoom(A) {
13841
- this.orbitVelocity_.radius += A * oo;
14644
+ this.orbitVelocity_.radius += A * ao;
13842
14645
  }
13843
14646
  updateCamera() {
13844
14647
  const A = aI(d(), this.currCenter_, this.currPos_.toVec3());
@@ -13848,107 +14651,6 @@ class Lo {
13848
14651
  Math.abs(this.orbitVelocity_.phi) < T && (this.orbitVelocity_.phi = 0), Math.abs(this.orbitVelocity_.theta) < T && (this.orbitVelocity_.theta = 0), Math.abs(this.orbitVelocity_.radius) < T && (this.orbitVelocity_.radius = 0), yg(this.panVelocity_) < T && $C(this.panVelocity_);
13849
14652
  }
13850
14653
  }
13851
- class Do {
13852
- planes_;
13853
- constructor(A) {
13854
- this.planes_ = [
13855
- new sA(d(), 0),
13856
- new sA(d(), 0),
13857
- new sA(d(), 0),
13858
- new sA(d(), 0),
13859
- new sA(d(), 0),
13860
- new sA(d(), 0)
13861
- ], this.setWithViewProjection(A);
13862
- }
13863
- // Uses the fast plane-extraction algorithm described in
13864
- // Gribb & Hartmann (1997): https://tinyurl.com/5x5htcwm
13865
- setWithViewProjection(A) {
13866
- const I = d();
13867
- this.planes_[0].set(
13868
- rA(I, A[3] + A[0], A[7] + A[4], A[11] + A[8]),
13869
- A[15] + A[12]
13870
- ), this.planes_[1].set(
13871
- rA(I, A[3] - A[0], A[7] - A[4], A[11] - A[8]),
13872
- A[15] - A[12]
13873
- ), this.planes_[2].set(
13874
- rA(I, A[3] - A[1], A[7] - A[5], A[11] - A[9]),
13875
- A[15] - A[13]
13876
- ), this.planes_[3].set(
13877
- rA(I, A[3] + A[1], A[7] + A[5], A[11] + A[9]),
13878
- A[15] + A[13]
13879
- ), this.planes_[4].set(
13880
- rA(I, A[3] + A[2], A[7] + A[6], A[11] + A[10]),
13881
- A[15] + A[14]
13882
- ), this.planes_[5].set(
13883
- rA(I, A[3] - A[2], A[7] - A[6], A[11] - A[10]),
13884
- A[15] - A[14]
13885
- );
13886
- for (const B of this.planes_) B.normalize();
13887
- }
13888
- intersectsWithBox3(A) {
13889
- const I = d();
13890
- for (const B of this.planes_) {
13891
- const C = B.normal;
13892
- if (I[0] = C[0] > 0 ? A.max[0] : A.min[0], I[1] = C[1] > 0 ? A.max[1] : A.min[1], I[2] = C[2] > 0 ? A.max[2] : A.min[2], B.signedDistanceToPoint(I) < 0) return !1;
13893
- }
13894
- return !0;
13895
- }
13896
- }
13897
- class cC extends YI {
13898
- transform_ = new eC();
13899
- projectionMatrix_ = $();
13900
- near_ = 0;
13901
- far_ = 0;
13902
- update() {
13903
- this.updateProjectionMatrix();
13904
- }
13905
- get projectionMatrix() {
13906
- return this.projectionMatrix_;
13907
- }
13908
- get transform() {
13909
- return this.transform_;
13910
- }
13911
- get viewMatrix() {
13912
- return this.transform.inverse;
13913
- }
13914
- get right() {
13915
- const A = this.transform.matrix;
13916
- return p(A[0], A[1], A[2]);
13917
- }
13918
- get up() {
13919
- const A = this.transform.matrix;
13920
- return p(A[4], A[5], A[6]);
13921
- }
13922
- getViewProjection() {
13923
- return QI($(), this.projectionMatrix, this.viewMatrix);
13924
- }
13925
- get frustum() {
13926
- return new Do(this.getViewProjection());
13927
- }
13928
- pan(A) {
13929
- this.transform.addTranslation(A);
13930
- }
13931
- get position() {
13932
- return this.transform.translation;
13933
- }
13934
- clipToWorld(A) {
13935
- const I = DI(A[0], A[1], A[2], 1), B = OA(
13936
- $(),
13937
- this.projectionMatrix_
13938
- ), C = VA(
13939
- YA(),
13940
- I,
13941
- B
13942
- );
13943
- BQ(C, C, 1 / C[3]);
13944
- const Q = VA(
13945
- YA(),
13946
- C,
13947
- this.transform.matrix
13948
- );
13949
- return p(Q[0], Q[1], Q[2]);
13950
- }
13951
- }
13952
14654
  const UC = 1.77, bB = 128, ZB = 128 / UC, ho = -1e6, to = 1e6;
13953
14655
  class Jo extends cC {
13954
14656
  // width_ and height_ should always be defined by constructor (see setFrame)
@@ -13962,27 +14664,48 @@ class Jo extends cC {
13962
14664
  /**
13963
14665
  * Creates an orthographic camera framing the given world-space rectangle.
13964
14666
  *
13965
- * @param props - The view frame edges in world units, near/far clipping
13966
- * plane distances (default `-1e6` and `1e6`), and the slice orientation
13967
- * the camera faces (default `"XY"`).
14667
+ * @param props - Initialization properties.
13968
14668
  */
13969
14669
  constructor(A) {
13970
14670
  super(), this.near_ = A.near ?? ho, this.far_ = A.far ?? to, this.orientation_ = A.orientation ?? "XY", this.axes_ = MA(this.orientation_), this.rotation_ = dA(this.axes_), this.setFrame(A), this.updateProjectionMatrix();
13971
14671
  }
14672
+ /**
14673
+ * The world-space size of the rendered view as `[width, height]`.
14674
+ *
14675
+ * This is the camera frame padded to match the viewport's aspect ratio, so
14676
+ * it reflects what is actually visible rather than the frame that was set.
14677
+ */
13972
14678
  get viewportSize() {
13973
14679
  return this.viewportSize_;
13974
14680
  }
14681
+ /**
14682
+ * Sets the aspect ratio (width / height) of the viewport the camera renders
14683
+ * into. Called automatically by the owning viewport when it resizes.
14684
+ *
14685
+ * @param aspectRatio - The viewport's width divided by its height.
14686
+ */
13975
14687
  setAspectRatio(A) {
13976
14688
  this.viewportAspectRatio_ = A, this.updateProjectionMatrix();
13977
14689
  }
14690
+ /**
14691
+ * Reframes the camera to the given world-space rectangle, resetting any
14692
+ * accumulated zoom and pan.
14693
+ *
14694
+ * The frame may be padded horizontally or vertically at render time to
14695
+ * match the viewport's aspect ratio (see {@link viewportSize}).
14696
+ *
14697
+ * @param frame - The view frame edges in world units.
14698
+ */
13978
14699
  setFrame({ left: A, right: I, top: B, bottom: C }) {
13979
14700
  this.width_ = Math.abs(I - A), this.height_ = Math.abs(B - C), this.updateProjectionMatrix();
13980
14701
  const Q = d();
13981
14702
  Q[W[this.axes_.u]] = 0.5 * (A + I), Q[W[this.axes_.v]] = 0.5 * (C + B), this.transform.setTranslation(Q), this.transform.setScale([1, 1, 1]), this.transform.setRotation(this.rotation_);
13982
14703
  }
14704
+ /** Identifies the camera type as `OrthographicCamera`. */
13983
14705
  get type() {
13984
14706
  return "OrthographicCamera";
13985
14707
  }
14708
+ /** The slice orientation the camera faces. */
13986
14709
  get orientation() {
13987
14710
  return this.orientation_;
13988
14711
  }
@@ -13990,6 +14713,8 @@ class Jo extends cC {
13990
14713
  * Changes the slice orientation the camera faces. The current frame and
13991
14714
  * zoom carry over numerically to the new plane axes. Call {@link setFrame}
13992
14715
  * to reframe the view for the new plane.
14716
+ *
14717
+ * @param orientation - The slice plane for the camera to face.
13993
14718
  */
13994
14719
  setOrientation(A) {
13995
14720
  if (A === this.orientation_)
@@ -13999,12 +14724,25 @@ class Jo extends cC {
13999
14724
  const C = d();
14000
14725
  C[W[this.axes_.u]] = I, C[W[this.axes_.v]] = B, this.transform.setTranslation(C), this.transform.setRotation(this.rotation_);
14001
14726
  }
14727
+ /**
14728
+ * Zooms the view by the given factor relative to the current zoom level.
14729
+ * Factors greater than `1` zoom in and factors between `0` and `1` zoom
14730
+ * out.
14731
+ *
14732
+ * @param factor - The magnification factor to apply.
14733
+ */
14002
14734
  zoom(A) {
14003
14735
  if (A <= 0)
14004
14736
  throw new Error(`Invalid zoom factor: ${A}`);
14005
14737
  const I = 1 / A;
14006
14738
  this.transform.addScale([I, I, 1]);
14007
14739
  }
14740
+ /**
14741
+ * Computes the world-space rectangle currently visible in the viewport,
14742
+ * accounting for zoom, pan, and aspect-ratio padding.
14743
+ *
14744
+ * @returns The visible rectangle on the camera's slice plane.
14745
+ */
14008
14746
  getWorldViewRect() {
14009
14747
  let A = DI(-1, -1, 0, 1), I = DI(1, 1, 0, 1);
14010
14748
  const B = OA($(), this.getViewProjection());
@@ -14015,6 +14753,7 @@ class Jo extends cC {
14015
14753
  V(I[C], I[Q])
14016
14754
  );
14017
14755
  }
14756
+ /** @hidden */
14018
14757
  updateProjectionMatrix() {
14019
14758
  const A = this.width_, I = this.height_, B = A / I;
14020
14759
  let C = 0.5 * A, Q = 0.5 * I;
@@ -14034,12 +14773,24 @@ class Yo {
14034
14773
  camera_;
14035
14774
  dragActive_ = !1;
14036
14775
  dragStart_ = d();
14776
+ /**
14777
+ * Creates pan and zoom controls for the given camera.
14778
+ *
14779
+ * @param camera - The orthographic camera to control.
14780
+ */
14037
14781
  constructor(A) {
14038
14782
  this.camera_ = A;
14039
14783
  }
14784
+ /** Whether a pan drag is in progress. */
14040
14785
  get isMoving() {
14041
14786
  return this.dragActive_;
14042
14787
  }
14788
+ /**
14789
+ * Handles a pointer or wheel event. Called automatically by the owning
14790
+ * viewport unless a layer stops propagation.
14791
+ *
14792
+ * @param event - The event with clip and world coordinates attached.
14793
+ */
14043
14794
  onEvent(A) {
14044
14795
  switch (A.type) {
14045
14796
  case "wheel":
@@ -14057,6 +14808,11 @@ class Yo {
14057
14808
  break;
14058
14809
  }
14059
14810
  }
14811
+ /**
14812
+ * Does nothing. Pan and zoom apply immediately with no inertia.
14813
+ *
14814
+ * @param _delta - Time since the last frame in seconds. Unused.
14815
+ */
14060
14816
  onUpdate(A) {
14061
14817
  }
14062
14818
  onWheel(A) {
@@ -14086,6 +14842,11 @@ const yo = 60, eo = 1.77, oI = 0.1, XI = 180 - oI;
14086
14842
  class Mo extends cC {
14087
14843
  fov_;
14088
14844
  aspectRatio_;
14845
+ /**
14846
+ * Creates a perspective camera from the given projection settings.
14847
+ *
14848
+ * @param props - Initialization properties.
14849
+ */
14089
14850
  constructor(A = {}) {
14090
14851
  const {
14091
14852
  fov: I = yo,
@@ -14100,20 +14861,40 @@ class Mo extends cC {
14100
14861
  );
14101
14862
  super(), this.fov_ = I, this.aspectRatio_ = B, this.near_ = C, this.far_ = Q, this.transform.setTranslation(E), this.updateProjectionMatrix();
14102
14863
  }
14864
+ /**
14865
+ * Sets the aspect ratio (width / height) of the viewport the camera
14866
+ * renders into. Called automatically by the owning viewport when it
14867
+ * resizes.
14868
+ *
14869
+ * @param aspectRatio - The viewport's width divided by its height.
14870
+ */
14103
14871
  setAspectRatio(A) {
14104
14872
  this.aspectRatio_ = A, this.updateProjectionMatrix();
14105
14873
  }
14874
+ /** Identifies the camera type as `PerspectiveCamera`. */
14106
14875
  get type() {
14107
14876
  return "PerspectiveCamera";
14108
14877
  }
14878
+ /** The vertical field of view in degrees. */
14109
14879
  get fov() {
14110
14880
  return this.fov_;
14111
14881
  }
14882
+ /**
14883
+ * Zooms the view by the given factor relative to the current zoom level.
14884
+ * Factors greater than `1` zoom in and factors between `0` and `1` zoom
14885
+ * out.
14886
+ *
14887
+ * Zooming narrows or widens the field of view rather than moving the
14888
+ * camera, and the result is clamped to valid angles.
14889
+ *
14890
+ * @param factor - The magnification factor to apply.
14891
+ */
14112
14892
  zoom(A) {
14113
14893
  if (A <= 0)
14114
14894
  throw new Error(`Invalid zoom factor: ${A}`);
14115
14895
  this.fov_ = Math.max(oI, Math.min(XI, this.fov_ / A)), this.updateProjectionMatrix();
14116
14896
  }
14897
+ /** @hidden */
14117
14898
  updateProjectionMatrix() {
14118
14899
  OC(
14119
14900
  this.projectionMatrix_,
@@ -14126,7 +14907,11 @@ class Mo extends cC {
14126
14907
  }
14127
14908
  export {
14128
14909
  Ro as AxesLayer,
14910
+ AA as Box2,
14911
+ SA as Box3,
14912
+ cC as Camera,
14129
14913
  x as Color,
14914
+ Eo as Frustum,
14130
14915
  no as Idetik,
14131
14916
  rC as ImageLayer,
14132
14917
  Ti as ImageRenderable,
@@ -14140,6 +14925,9 @@ export {
14140
14925
  Mo as PerspectiveCamera,
14141
14926
  ko as PointsRenderable,
14142
14927
  pi as ProjectedLineRenderable,
14928
+ gI as RenderableObject,
14929
+ eC as TrsTransform,
14930
+ uQ as Viewport,
14143
14931
  Uo as VolumeLayer,
14144
14932
  bi as VolumeRenderable,
14145
14933
  ug as createExplorationPolicy,