@idetik/core 0.45.4 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1011,181 +1011,6 @@ declare class EventDispatcher {
1011
1011
  private readonly handleEvent;
1012
1012
  }
1013
1013
 
1014
- /**
1015
- * The loading lifecycle state of a layer.
1016
- */
1017
- type LayerState = "initialized" | "loading" | "ready";
1018
- /**
1019
- * How a layer's output blends with previously drawn content.
1020
- */
1021
- type BlendMode = "none" | "normal" | "additive" | "subtractive" | "multiply" | "premultipliedOver";
1022
- /**
1023
- * A callback invoked after a layer's state changes.
1024
- */
1025
- type StateChangeCallback = (newState: LayerState, prevState?: LayerState) => void;
1026
- /**
1027
- * Initialization properties for constructing a layer.
1028
- */
1029
- type LayerProps = {
1030
- /** Layer opacity in `[0, 1]`. Defaults to `1`. */
1031
- opacity?: number;
1032
- /** How the layer blends. Defaults to `"none"`. */
1033
- blendMode?: BlendMode;
1034
- /** Hides content behind. Inferred from `blendMode`. */
1035
- occludes?: boolean;
1036
- };
1037
- /**
1038
- * Abstract base class for any layer that can be added to a viewport.
1039
- *
1040
- * A layer owns a set of renderable objects and contributes them to the
1041
- * scene each frame. Subclasses such as {@link ImageLayer},
1042
- * {@link VolumeLayer}, and {@link LabelLayer} implement {@link update} to
1043
- * build or refresh those objects for the current view. Custom layers
1044
- * register objects with {@link addObject} and report readiness through
1045
- * {@link setState}.
1046
- *
1047
- * Layers carry shared presentation state in {@link opacity},
1048
- * {@link blendMode}, and {@link occludes}, and expose a lifecycle
1049
- * {@link LayerState} that observers can subscribe to. A layer instance may
1050
- * be attached to only one viewport at a time.
1051
- *
1052
- * ```ts
1053
- * class Particles extends Layer {
1054
- * public readonly type = "Particles";
1055
- *
1056
- * constructor(points: PointProps[]) {
1057
- * super();
1058
- * this.addObject(new PointsRenderable(points));
1059
- * this.setState("ready");
1060
- * }
1061
- *
1062
- * public update() {}
1063
- * }
1064
- *
1065
- * viewport.addLayer(new Particles(points));
1066
- * ```
1067
- *
1068
- * @group Layers
1069
- */
1070
- declare abstract class Layer {
1071
- /** A string identifying the concrete layer type. */
1072
- abstract readonly type: string;
1073
- /**
1074
- * How the layer's output blends with previously drawn content. Also
1075
- * applies to blending between objects within the layer.
1076
- */
1077
- blendMode: BlendMode;
1078
- /**
1079
- * Whether the layer writes depth and hides content drawn behind it.
1080
- *
1081
- * Occluding layers render a depth pass and always draw before
1082
- * non-occluding layers regardless of their order in the viewport. When
1083
- * not set explicitly this value is inferred from `blendMode` at
1084
- * construction only. Reassigning {@link blendMode} later does not update
1085
- * it.
1086
- */
1087
- occludes: boolean;
1088
- /** Set to `true` by subclasses whose shaders read scene depth. */
1089
- protected requiresSceneDepth_: boolean;
1090
- private readonly coverageGroups_;
1091
- private state_;
1092
- private attached_;
1093
- private readonly callbacks_;
1094
- private opacity_;
1095
- /**
1096
- * Creates a layer with the given presentation state.
1097
- *
1098
- * @param props - Initialization properties.
1099
- */
1100
- constructor({ opacity, blendMode, occludes, }?: LayerProps);
1101
- /**
1102
- * Whether the layer's shaders read scene depth. When `true` the renderer
1103
- * draws occluding layers to a depth texture the layer's shaders can
1104
- * sample. A layer cannot both occlude and read scene depth.
1105
- */
1106
- get requiresSceneDepth(): boolean;
1107
- /** The layer's opacity in `[0, 1]`. Values outside are clamped. */
1108
- get opacity(): number;
1109
- /** @param value - The new opacity in `[0, 1]`. */
1110
- set opacity(value: number);
1111
- /**
1112
- * Builds or refreshes the layer's renderable objects for the current
1113
- * view. Called automatically once per frame for every layer in a
1114
- * viewport.
1115
- *
1116
- * @param viewport - The viewport being rendered.
1117
- */
1118
- abstract update(viewport?: Viewport): void;
1119
- /**
1120
- * Handles a pointer or wheel event from the owning viewport. Called
1121
- * automatically for each event before the camera controls. The default
1122
- * implementation does nothing.
1123
- *
1124
- * @param _event - The event with clip and world coordinates attached.
1125
- */
1126
- onEvent(_event: EventContext): void;
1127
- /**
1128
- * Lifecycle hook that is called automatically when a layer is
1129
- * is attached to a viewport. A layer can only be attached to one viewport
1130
- * at a time.
1131
- *
1132
- * @param context - The shared runtime context.
1133
- */
1134
- onAttached(context: IdetikContext): void;
1135
- /**
1136
- * Lifecycle hook that is called automatically when a layer is detached
1137
- * from a viewport.
1138
- *
1139
- * @param context - The shared runtime context.
1140
- */
1141
- onDetached(context: IdetikContext): void;
1142
- /** @hidden */
1143
- protected attach(_context: IdetikContext): void;
1144
- /** @hidden */
1145
- protected detach(_context: IdetikContext): void;
1146
- /**
1147
- * The layer's renderable objects grouped by coverage group. Objects in
1148
- * a group draw each pixel at most once, letting chunks at multiple
1149
- * levels of detail overlap correctly.
1150
- */
1151
- get coverageGroups(): ReadonlyMap<number | null, readonly RenderableObject[]>;
1152
- /** The layer's current lifecycle state. */
1153
- get state(): LayerState;
1154
- /**
1155
- * Registers a callback invoked after every state change.
1156
- *
1157
- * @param callback - Receives the new and previous states.
1158
- */
1159
- addStateChangeCallback(callback: StateChangeCallback): void;
1160
- /**
1161
- * Removes a previously registered state change callback.
1162
- *
1163
- * @param callback - The callback to remove.
1164
- */
1165
- removeStateChangeCallback(callback: StateChangeCallback): void;
1166
- /**
1167
- * Sets the lifecycle state and notifies state change callbacks.
1168
- *
1169
- * @param newState - The state to enter.
1170
- */
1171
- protected setState(newState: LayerState): void;
1172
- /**
1173
- * Registers a renderable object for drawing. Objects in the same
1174
- * coverage group draw each pixel at most once.
1175
- *
1176
- * @param object - The object to add.
1177
- * @param coverageGroup - The group key.
1178
- */
1179
- protected addObject(object: RenderableObject, coverageGroup?: number | null): void;
1180
- /** Removes all registered renderable objects. */
1181
- protected clearObjects(): void;
1182
- /**
1183
- * Returns uniform name-value pairs applied to every object drawn by
1184
- * this layer. Override in subclasses that need custom shader uniforms.
1185
- */
1186
- getUniforms(): Record<string, unknown>;
1187
- }
1188
-
1189
1014
  /**
1190
1015
  * A world-space rectangle for the camera to frame.
1191
1016
  */
@@ -1240,13 +1065,15 @@ type OrthographicCameraProps = {
1240
1065
  * bottom: 1024
1241
1066
  * });
1242
1067
  *
1068
+ * const canvas = document.querySelector<HTMLCanvasElement>('canvas')!;
1243
1069
  * const idetik = new Idetik({
1244
- * canvas: document.querySelector('canvas')!,
1245
- * viewports: [{
1070
+ * canvas,
1071
+ * viewports: [new Viewport({
1072
+ * domElement: canvas,
1246
1073
  * camera,
1247
1074
  * layers: [imageLayer],
1248
1075
  * cameraControls: new PanZoomControls(camera),
1249
- * }],
1076
+ * })],
1250
1077
  * });
1251
1078
  * ```
1252
1079
  * @group Cameras
@@ -1394,11 +1221,12 @@ type PanZoomControlsProps = {
1394
1221
  *
1395
1222
  * const idetik = new Idetik({
1396
1223
  * canvas,
1397
- * viewports: [{
1224
+ * viewports: [new Viewport({
1225
+ * domElement: canvas,
1398
1226
  * camera,
1399
1227
  * layers: [imageLayer],
1400
1228
  * cameraControls: new PanZoomControls(camera),
1401
- * }],
1229
+ * })],
1402
1230
  * });
1403
1231
  * ```
1404
1232
  *
@@ -1443,8 +1271,8 @@ declare class PanZoomControls implements CameraControls {
1443
1271
  type ViewportProps = {
1444
1272
  /** Unique id. Defaults to the element id or a generated id. */
1445
1273
  id?: string;
1446
- /** Host element. Defaults to the Idetik canvas. */
1447
- element?: HTMLElement;
1274
+ /** Host element. */
1275
+ domElement: HTMLElement;
1448
1276
  /** The camera the viewport renders with. */
1449
1277
  camera: Camera;
1450
1278
  /** Layers to render in order. */
@@ -1452,17 +1280,11 @@ type ViewportProps = {
1452
1280
  /** Input controls driving the camera. */
1453
1281
  cameraControls?: CameraControls;
1454
1282
  };
1455
- interface ResolvedViewportProps extends ViewportProps {
1456
- id: string;
1457
- element: HTMLElement;
1458
- context: IdetikContext;
1459
- }
1460
1283
  /**
1461
1284
  * A region of the canvas that renders a stack of layers through a camera.
1462
1285
  *
1463
1286
  * Every viewport draws into the shared canvas through the area of its host
1464
- * element. The element defaults to the canvas itself and must be unique
1465
- * across viewports.
1287
+ * element. The element must be unique across viewports.
1466
1288
  *
1467
1289
  * Viewports also route input. Pointer and wheel events on the host element
1468
1290
  * are enriched with clip and world coordinates and a picking ray, sent to
@@ -1470,12 +1292,13 @@ interface ResolvedViewportProps extends ViewportProps {
1470
1292
  * propagation.
1471
1293
  *
1472
1294
  * ```ts
1473
- * const idetik = new Idetik({
1474
- * canvas,
1475
- * viewports: [{ id: 'main', camera, layers: [imageLayer] }],
1295
+ * const viewport = new Viewport({
1296
+ * domElement: canvas,
1297
+ * camera,
1298
+ * layers: [imageLayer],
1476
1299
  * });
1300
+ * const idetik = new Idetik({ canvas, viewports: [viewport] });
1477
1301
  *
1478
- * const viewport = idetik.getViewport('main')!;
1479
1302
  * viewport.addLayer(labelLayer);
1480
1303
  * ```
1481
1304
  *
@@ -1485,24 +1308,23 @@ declare class Viewport {
1485
1308
  /** The viewport's unique identifier. */
1486
1309
  readonly id: string;
1487
1310
  /** The host element defining the viewport's area. */
1488
- readonly element: HTMLElement;
1311
+ readonly domElement: HTMLElement;
1489
1312
  /** The camera the viewport renders with. */
1490
1313
  readonly camera: Camera;
1491
1314
  /** The pointer and wheel event dispatcher for the host element. */
1492
1315
  readonly events: EventDispatcher;
1493
1316
  /** Input controls driving the camera. */
1494
1317
  cameraControls?: CameraControls;
1495
- private readonly context_;
1496
1318
  private layers_;
1497
- /** @hidden */
1498
- constructor(props: ResolvedViewportProps);
1319
+ constructor(props: ViewportProps);
1499
1320
  /**
1500
1321
  * The layers rendered by this viewport in order. Layers with `occludes`
1501
1322
  * set draw before non-occluding layers regardless of stack order.
1502
1323
  */
1503
1324
  get layers(): readonly Layer[];
1504
1325
  /**
1505
- * Adds a layer to the top of the stack.
1326
+ * Adds a layer to the top of the stack. Throws if the viewport already
1327
+ * contains the layer.
1506
1328
  *
1507
1329
  * @param layer - The layer to add.
1508
1330
  */
@@ -1521,14 +1343,17 @@ declare class Viewport {
1521
1343
  */
1522
1344
  updateSize(): void;
1523
1345
  /**
1524
- * Computes the viewport's box relative to the given canvas in device pixels.
1346
+ * Computes the viewport's box relative to the given element in device
1347
+ * pixels, with `y` flipped to match WebGL's bottom-left origin.
1525
1348
  *
1526
- * @param canvas - The canvas to compute the box against.
1527
- * @returns The viewport's box in the canvas's coordinate space.
1349
+ * @param element - The element to compute the box against, typically the
1350
+ * canvas.
1351
+ * @returns The viewport's box in the element's coordinate space.
1528
1352
  */
1529
- getBoxRelativeTo(canvas: HTMLCanvasElement): Box2;
1353
+ getBoxRelativeTo(element: HTMLElement): Box2;
1530
1354
  /**
1531
- * The viewport's rectangle in the drawing buffer in device pixels.
1355
+ * The viewport's size in device pixels. Measured relative to its own host
1356
+ * element, so `x` and `y` are always `0`.
1532
1357
  */
1533
1358
  getBufferRect(): {
1534
1359
  x: number;
@@ -1556,6 +1381,253 @@ declare class Viewport {
1556
1381
  private updateAspectRatio;
1557
1382
  }
1558
1383
 
1384
+ /**
1385
+ * The loading lifecycle state of a layer.
1386
+ */
1387
+ type LayerState = "initialized" | "loading" | "ready";
1388
+ /**
1389
+ * How a layer's output blends with previously drawn content.
1390
+ */
1391
+ type BlendMode = "none" | "normal" | "additive" | "subtractive" | "multiply" | "premultipliedOver";
1392
+ /**
1393
+ * A callback invoked after a layer's state changes.
1394
+ */
1395
+ type StateChangeCallback = (newState: LayerState, prevState?: LayerState) => void;
1396
+ /**
1397
+ * Initialization properties for constructing a layer.
1398
+ */
1399
+ type LayerProps = {
1400
+ /** Layer opacity in `[0, 1]`. Defaults to `1`. */
1401
+ opacity?: number;
1402
+ /** How the layer blends. Defaults to `"none"`. */
1403
+ blendMode?: BlendMode;
1404
+ /** Hides content behind. Inferred from `blendMode`. */
1405
+ occludes?: boolean;
1406
+ };
1407
+ /**
1408
+ * Abstract base class for any layer that can be added to a viewport.
1409
+ *
1410
+ * A layer owns a set of renderable objects and contributes them to the
1411
+ * scene each frame. Subclasses such as {@link ImageLayer},
1412
+ * {@link VolumeLayer}, and {@link LabelLayer} implement {@link update} to
1413
+ * build or refresh those objects for the current view. Custom layers
1414
+ * register objects with {@link addObject} and report readiness through
1415
+ * {@link setState}.
1416
+ *
1417
+ * Layers carry shared presentation state in {@link opacity},
1418
+ * {@link blendMode}, and {@link occludes}, and expose a lifecycle
1419
+ * {@link LayerState} that observers can subscribe to. A layer instance may
1420
+ * be attached to only one viewport at a time.
1421
+ *
1422
+ * ```ts
1423
+ * class Particles extends Layer {
1424
+ * public readonly type = "Particles";
1425
+ *
1426
+ * constructor(points: PointProps[]) {
1427
+ * super();
1428
+ * this.addObject(new PointsRenderable(points));
1429
+ * this.setState("ready");
1430
+ * }
1431
+ *
1432
+ * public update() {}
1433
+ * }
1434
+ *
1435
+ * viewport.addLayer(new Particles(points));
1436
+ * ```
1437
+ *
1438
+ * @group Layers
1439
+ */
1440
+ declare abstract class Layer {
1441
+ /** A string identifying the concrete layer type. */
1442
+ abstract readonly type: string;
1443
+ /**
1444
+ * How the layer's output blends with previously drawn content. Also
1445
+ * applies to blending between objects within the layer.
1446
+ */
1447
+ blendMode: BlendMode;
1448
+ /**
1449
+ * Whether the layer writes depth and hides content drawn behind it.
1450
+ *
1451
+ * Occluding layers render a depth pass and always draw before
1452
+ * non-occluding layers regardless of their order in the viewport. When
1453
+ * not set explicitly this value is inferred from `blendMode` at
1454
+ * construction only. Reassigning {@link blendMode} later does not update
1455
+ * it.
1456
+ */
1457
+ occludes: boolean;
1458
+ /** Set to `true` by subclasses whose shaders read scene depth. */
1459
+ protected requiresSceneDepth_: boolean;
1460
+ private readonly coverageGroups_;
1461
+ private state_;
1462
+ private attached_;
1463
+ private readonly callbacks_;
1464
+ private opacity_;
1465
+ /**
1466
+ * Creates a layer with the given presentation state.
1467
+ *
1468
+ * @param props - Initialization properties.
1469
+ */
1470
+ constructor({ opacity, blendMode, occludes, }?: LayerProps);
1471
+ /**
1472
+ * Whether the layer's shaders read scene depth. When `true` the renderer
1473
+ * draws occluding layers to a depth texture the layer's shaders can
1474
+ * sample. A layer cannot both occlude and read scene depth.
1475
+ */
1476
+ get requiresSceneDepth(): boolean;
1477
+ /** The layer's opacity in `[0, 1]`. Values outside are clamped. */
1478
+ get opacity(): number;
1479
+ /** @param value - The new opacity in `[0, 1]`. */
1480
+ set opacity(value: number);
1481
+ /**
1482
+ * Builds or refreshes the layer's renderable objects for the current
1483
+ * view. Called automatically once per frame for every layer in a
1484
+ * viewport.
1485
+ *
1486
+ * @param viewport - The viewport being rendered.
1487
+ */
1488
+ abstract update(viewport?: Viewport): void;
1489
+ /**
1490
+ * Handles a pointer or wheel event from the owning viewport. Called
1491
+ * automatically for each event before the camera controls. The default
1492
+ * implementation does nothing.
1493
+ *
1494
+ * @param _event - The event with clip and world coordinates attached.
1495
+ */
1496
+ onEvent(_event: EventContext): void;
1497
+ /** Whether the layer is attached to a viewport. */
1498
+ get attached(): boolean;
1499
+ /**
1500
+ * Lifecycle hook that is called automatically when a layer is
1501
+ * is attached to a viewport. A layer can only be attached to one viewport
1502
+ * at a time.
1503
+ *
1504
+ * @param context - The shared runtime context.
1505
+ */
1506
+ onAttached(context: IdetikContext): void;
1507
+ /**
1508
+ * Lifecycle hook that is called automatically when a layer is detached
1509
+ * from a viewport.
1510
+ *
1511
+ * @param context - The shared runtime context.
1512
+ */
1513
+ onDetached(context: IdetikContext): void;
1514
+ /** @hidden */
1515
+ protected attach(_context: IdetikContext): void;
1516
+ /** @hidden */
1517
+ protected detach(_context: IdetikContext): void;
1518
+ /**
1519
+ * The layer's renderable objects grouped by coverage group. Objects in
1520
+ * a group draw each pixel at most once, letting chunks at multiple
1521
+ * levels of detail overlap correctly.
1522
+ */
1523
+ get coverageGroups(): ReadonlyMap<number | null, readonly RenderableObject[]>;
1524
+ /** The layer's current lifecycle state. */
1525
+ get state(): LayerState;
1526
+ /**
1527
+ * Registers a callback invoked after every state change.
1528
+ *
1529
+ * @param callback - Receives the new and previous states.
1530
+ */
1531
+ addStateChangeCallback(callback: StateChangeCallback): void;
1532
+ /**
1533
+ * Removes a previously registered state change callback.
1534
+ *
1535
+ * @param callback - The callback to remove.
1536
+ */
1537
+ removeStateChangeCallback(callback: StateChangeCallback): void;
1538
+ /**
1539
+ * Sets the lifecycle state and notifies state change callbacks.
1540
+ *
1541
+ * @param newState - The state to enter.
1542
+ */
1543
+ protected setState(newState: LayerState): void;
1544
+ /**
1545
+ * Registers a renderable object for drawing. Objects in the same
1546
+ * coverage group draw each pixel at most once.
1547
+ *
1548
+ * @param object - The object to add.
1549
+ * @param coverageGroup - The group key.
1550
+ */
1551
+ protected addObject(object: RenderableObject, coverageGroup?: number | null): void;
1552
+ /** Removes all registered renderable objects. */
1553
+ protected clearObjects(): void;
1554
+ /**
1555
+ * Returns uniform name-value pairs applied to every object drawn by
1556
+ * this layer. Override in subclasses that need custom shader uniforms.
1557
+ */
1558
+ getUniforms(): Record<string, unknown>;
1559
+ }
1560
+
1561
+ declare abstract class Renderer {
1562
+ private readonly canvas_;
1563
+ private width_;
1564
+ private height_;
1565
+ protected renderedObjects_: number;
1566
+ protected abstract resize(width: number, height: number): void;
1567
+ protected abstract renderObject(layer: Layer, object: RenderableObject, camera: Camera): void;
1568
+ protected abstract clear(): void;
1569
+ constructor(canvas: HTMLCanvasElement);
1570
+ beginFrame(): void;
1571
+ abstract render(viewport: Viewport): void;
1572
+ updateSize(): void;
1573
+ private updateRendererSize;
1574
+ protected get canvas(): HTMLCanvasElement;
1575
+ get width(): number;
1576
+ get height(): number;
1577
+ get renderedObjects(): number;
1578
+ abstract get gpuTextureBytes(): number;
1579
+ abstract get gpuTextureCount(): number;
1580
+ abstract uploadTexture(texture: Texture): void;
1581
+ abstract disposeTexture(texture: Texture): void;
1582
+ }
1583
+
1584
+ /** A snapshot of memory used by chunks, renderer textures, and the JS heap. */
1585
+ type MemoryStats = {
1586
+ /** Bytes of chunk data in CPU memory, shared across all Idetik instances. */
1587
+ cpuChunkBytes: number;
1588
+ /** Number of chunks in CPU memory, shared across all Idetik instances. */
1589
+ cpuChunkCount: number;
1590
+ /** Bytes of texture data resident on this instance's GPU renderer. */
1591
+ gpuTextureBytes: number;
1592
+ /** Number of textures resident on this instance's GPU renderer. */
1593
+ gpuTextureCount: number;
1594
+ /** Used JS heap in bytes, when the browser exposes it. Not instance-specific. */
1595
+ jsHeapUsedBytes?: number;
1596
+ /** JS heap size limit in bytes, when the browser exposes it. */
1597
+ jsHeapLimitBytes?: number;
1598
+ };
1599
+ /**
1600
+ * Statistics available through `idetik.stats`.
1601
+ *
1602
+ * Getters read the current state. Only `chunks` walks chunk states. Reading
1603
+ * memory usage, queue counts, or rendered objects does not scan chunks.
1604
+ *
1605
+ * @group Core
1606
+ */
1607
+ declare class Stats {
1608
+ private readonly renderer_;
1609
+ private readonly chunkManager_;
1610
+ private readonly viewports_;
1611
+ /** @hidden */
1612
+ constructor(renderer: Renderer, chunkManager: ChunkManager, viewports: readonly Viewport[]);
1613
+ /**
1614
+ * CPU chunk memory shared across instances, this renderer's GPU texture
1615
+ * memory, and optional JS heap figures. Heap figures are not specific to
1616
+ * this instance. Sizes are in bytes.
1617
+ */
1618
+ get memory(): MemoryStats;
1619
+ /** Counts of queued and in-flight chunk requests for this instance. */
1620
+ get queue(): QueueStats;
1621
+ /**
1622
+ * Chunk demand and GPU residency per source, by timepoint, and loading
1623
+ * progress per viewport. Each read walks every view's chunk states, so poll
1624
+ * it at the rate you need rather than every frame.
1625
+ */
1626
+ get chunks(): ChunkStats;
1627
+ /** Objects reported by the renderer for the last viewport render. */
1628
+ get renderedObjects(): number;
1629
+ }
1630
+
1559
1631
  /**
1560
1632
  * An object updated once per frame after all viewports have rendered.
1561
1633
  *
@@ -1565,7 +1637,7 @@ declare class Viewport {
1565
1637
  * ```ts
1566
1638
  * const chunkReadout: Overlay = {
1567
1639
  * update(idetik) {
1568
- * div.textContent = `${idetik.memoryStats.cpuChunkCount} chunks`;
1640
+ * div.textContent = `${idetik.stats.memory.cpuChunkCount} chunks`;
1569
1641
  * },
1570
1642
  * };
1571
1643
  *
@@ -1582,8 +1654,8 @@ type Overlay = {
1582
1654
  type IdetikProps = {
1583
1655
  /** The canvas element to render into. */
1584
1656
  canvas: HTMLCanvasElement;
1585
- /** Viewport definitions to create at startup. */
1586
- viewports?: ViewportProps[];
1657
+ /** Viewports to render at startup. */
1658
+ viewports?: Viewport[];
1587
1659
  /** Overlays to run each frame. */
1588
1660
  overlays?: Overlay[];
1589
1661
  /** Shows an FPS meter. Defaults to `false`. */
@@ -1598,23 +1670,6 @@ type IdetikProps = {
1598
1670
  type IdetikContext = {
1599
1671
  chunkManager: ChunkManager;
1600
1672
  };
1601
- /**
1602
- * A snapshot of the runtime's memory usage.
1603
- */
1604
- type MemoryStats = {
1605
- /** Bytes of chunk data held in CPU memory. */
1606
- cpuChunkBytes: number;
1607
- /** Number of chunks held in CPU memory. */
1608
- cpuChunkCount: number;
1609
- /** Bytes of texture data resident on the GPU. */
1610
- gpuTextureBytes: number;
1611
- /** Number of textures resident on the GPU. */
1612
- gpuTextureCount: number;
1613
- /** Used JS heap in bytes. */
1614
- jsHeapUsedBytes?: number;
1615
- /** JS heap size limit in bytes. */
1616
- jsHeapLimitBytes?: number;
1617
- };
1618
1673
  /**
1619
1674
  * The entry point of an Idetik application.
1620
1675
  *
@@ -1639,13 +1694,15 @@ type MemoryStats = {
1639
1694
  * bottom: 1024,
1640
1695
  * });
1641
1696
  *
1697
+ * const canvas = document.querySelector<HTMLCanvasElement>('canvas')!;
1642
1698
  * const idetik = new Idetik({
1643
- * canvas: document.querySelector('canvas')!,
1644
- * viewports: [{
1699
+ * canvas,
1700
+ * viewports: [new Viewport({
1701
+ * domElement: canvas,
1645
1702
  * camera,
1646
1703
  * layers: [layer],
1647
1704
  * cameraControls: new PanZoomControls(camera),
1648
- * }],
1705
+ * })],
1649
1706
  * });
1650
1707
  *
1651
1708
  * idetik.start();
@@ -1660,10 +1717,13 @@ declare class Idetik {
1660
1717
  readonly canvas: HTMLCanvasElement;
1661
1718
  /** The registered overlays that update once per frame in order. */
1662
1719
  readonly overlays: Overlay[];
1720
+ /** Memory, queue, chunk, and rendered-object statistics. */
1721
+ readonly stats: Stats;
1663
1722
  private readonly chunkManager_;
1664
1723
  private readonly context_;
1665
1724
  private readonly renderer_;
1666
1725
  private readonly viewports_;
1726
+ private readonly attachments_;
1667
1727
  private readonly stats_?;
1668
1728
  private readonly sizeObserver_;
1669
1729
  private lastAnimationId_?;
@@ -1674,18 +1734,6 @@ declare class Idetik {
1674
1734
  * @param params - Initialization properties.
1675
1735
  */
1676
1736
  constructor(params: IdetikProps);
1677
- /** Counts of queued and in-flight chunk requests. */
1678
- get chunkQueueStats(): QueueStats;
1679
- /**
1680
- * Chunk demand and GPU residency per source, by timepoint, and loading
1681
- * progress per viewport. Each read walks every view's chunk states and returns a
1682
- * new snapshot, so poll it at the rate you need rather than every frame.
1683
- */
1684
- get chunkStats(): ChunkStats;
1685
- /** A snapshot of current CPU/GPU/JS heap memory usage. */
1686
- get memoryStats(): MemoryStats;
1687
- /** The number of objects drawn in the last rendered frame. */
1688
- get renderedObjects(): number;
1689
1737
  /** The width of the rendering surface in pixels. */
1690
1738
  get width(): number;
1691
1739
  /** The height of the rendering surface in pixels. */
@@ -1704,11 +1752,10 @@ declare class Idetik {
1704
1752
  /**
1705
1753
  * Adds a viewport at runtime.
1706
1754
  *
1707
- * @param props - The viewport definition. The `element` defaults to the
1708
- * canvas and must be unique across viewports.
1709
- * @returns The created viewport.
1755
+ * @param viewport - The viewport to add.
1756
+ * @returns The added viewport.
1710
1757
  */
1711
- addViewport(props: ViewportProps): Viewport;
1758
+ addViewport(viewport: Viewport): Viewport;
1712
1759
  /**
1713
1760
  * Removes a previously added viewport.
1714
1761
  *
@@ -1741,6 +1788,8 @@ declare class Idetik {
1741
1788
  * @returns The instance, for chaining.
1742
1789
  */
1743
1790
  start(): this;
1791
+ private detachRemovedLayers;
1792
+ private renderViewport;
1744
1793
  private animate;
1745
1794
  /**
1746
1795
  * Stops the render loop and disconnects input handlers.
@@ -3392,9 +3441,15 @@ type PerspectiveCameraProps = {
3392
3441
  * target: [0, 0, 0],
3393
3442
  * });
3394
3443
  *
3444
+ * const canvas = document.querySelector<HTMLCanvasElement>('canvas')!;
3395
3445
  * const idetik = new Idetik({
3396
- * canvas: document.querySelector('canvas')!,
3397
- * viewports: [{ camera, layers: [volumeLayer], cameraControls: controls }],
3446
+ * canvas,
3447
+ * viewports: [new Viewport({
3448
+ * domElement: canvas,
3449
+ * camera,
3450
+ * layers: [volumeLayer],
3451
+ * cameraControls: controls
3452
+ * })],
3398
3453
  * });
3399
3454
  * ```
3400
3455
  *
@@ -3468,14 +3523,15 @@ type OrbitControlsProps = {
3468
3523
  *
3469
3524
  * const idetik = new Idetik({
3470
3525
  * canvas,
3471
- * viewports: [{
3526
+ * viewports: [new Viewport({
3527
+ * domElement: canvas,
3472
3528
  * camera,
3473
3529
  * layers: [volumeLayer],
3474
3530
  * cameraControls: new OrbitControls(camera, {
3475
3531
  * radius: 100,
3476
3532
  * target: [40, 40, 10],
3477
3533
  * }),
3478
- * }],
3534
+ * })],
3479
3535
  * });
3480
3536
  * ```
3481
3537
  *
@@ -3532,5 +3588,5 @@ declare class OrbitControls implements CameraControls {
3532
3588
  private cutoffLowVelocity;
3533
3589
  }
3534
3590
 
3535
- export { AxesLayer, Box2, Box3, Camera, Color, Frustum, Idetik, ImageLayer, ImageRenderable, LabelImageRenderable, LabelLayer, Layer, OmeZarrImageSource, OrbitControls, OrthographicCamera, PanZoomControls, PerspectiveCamera, PointsRenderable, ProjectedLineRenderable, RenderableObject, TrsTransform, Viewport, VolumeLayer, VolumeRenderable, createExplorationPolicy, createImageSourcePolicy, createNoPrefetchPolicy, createPlaybackPolicy, loadOmeZarrPlate, loadOmeZarrWell, loadOmeroChannels, loadOmeroDefaults };
3591
+ export { AxesLayer, Box2, Box3, Camera, Color, Frustum, Idetik, ImageLayer, ImageRenderable, LabelImageRenderable, LabelLayer, Layer, OmeZarrImageSource, OrbitControls, OrthographicCamera, PanZoomControls, PerspectiveCamera, PointsRenderable, ProjectedLineRenderable, RenderableObject, Stats, TrsTransform, Viewport, VolumeLayer, VolumeRenderable, createExplorationPolicy, createImageSourcePolicy, createNoPrefetchPolicy, createPlaybackPolicy, loadOmeZarrPlate, loadOmeZarrWell, loadOmeroChannels, loadOmeroDefaults };
3536
3592
  export type { AxesLayerProps, BlendMode, CameraControls, CameraType, ChannelProps, ChunkStats, ColorLike, FileSystemOmeZarrImageSourceProps, HttpOmeZarrImageSourceProps, IdetikProps, ImageLayerProps, ImageRenderableProps, ImageSourcePolicy, ImageSourcePolicyProps, LabelColorMap, LabelColorMapProps, LabelImageRenderableProps, LabelLayerProps, LayerProps, LayerState, MemoryStats, OrbitControlsProps, OrthographicCameraFrame, OrthographicCameraProps, Overlay, PanZoomControlsProps, PerspectiveCameraProps, PointPickingResult, PointProps, PriorityCategory, ProjectedLineRenderableProps, QueueStats, SliceCoordinates, SliceOrientation, SourceChunkStats, SourceDimension, SourceDimensionLod, SourceDimensionMap, StateChangeCallback, ViewportChunkStats, ViewportProps, VolumeLayerProps, VolumeRenderableProps };