@jax-data-science/sci-image-visualizer 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/esm2022/index.mjs +10 -1
  2. package/esm2022/lib/contracts/image.contract.mjs +1 -1
  3. package/esm2022/lib/contracts/ports/region-io.port.mjs +1 -1
  4. package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
  5. package/esm2022/lib/contracts/region-store.contract.mjs +1 -1
  6. package/esm2022/lib/contracts/section-view.contract.mjs +2 -0
  7. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  8. package/esm2022/lib/contracts/viz-config.mjs +1 -1
  9. package/esm2022/lib/geometry/loft.mjs +128 -0
  10. package/esm2022/lib/geometry/polylabel.mjs +156 -0
  11. package/esm2022/lib/geometry/reconstruct.mjs +315 -0
  12. package/esm2022/lib/geometry/reslice-image.mjs +57 -0
  13. package/esm2022/lib/geometry/section.mjs +215 -0
  14. package/esm2022/lib/implementations/napari-js/napari-loading-badge.mjs +11 -7
  15. package/esm2022/lib/implementations/napari-js/napari-loading-state.mjs +20 -3
  16. package/esm2022/lib/implementations/napari-js/napari-region-mesh-pointer.mjs +100 -0
  17. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +28 -5
  18. package/esm2022/lib/implementations/napari-js/napari-scene.mjs +1 -1
  19. package/esm2022/lib/implementations/napari-js/napari-spatial-hover.mjs +9 -2
  20. package/esm2022/lib/implementations/napari-js/napari-tool-bridge.mjs +5 -2
  21. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +81 -1
  22. package/esm2022/lib/implementations/napari-js/napari-volume-region-meshes.mjs +129 -0
  23. package/esm2022/lib/implementations/napari-js/napari-volume-scene.mjs +115 -4
  24. package/esm2022/lib/implementations/napari-js/napari-volume-slice-planes.mjs +103 -0
  25. package/esm2022/lib/implementations/napari-js/region-overlay/region-svg-renderer.mjs +15 -3
  26. package/esm2022/lib/implementations/osd/openseadragon-viewer-options.mjs +18 -2
  27. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +9 -1
  28. package/esm2022/lib/implementations/osd/osd-region-overlay-draw.mjs +18 -7
  29. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +38 -8
  30. package/esm2022/lib/implementations/osd/osd-tile-loading.mjs +71 -0
  31. package/esm2022/lib/implementations/osd/osd-viewer-like.mjs +1 -1
  32. package/esm2022/lib/implementations/osd/slice-cache.mjs +35 -6
  33. package/esm2022/lib/implementations/tile-server/tile-protocol.mjs +1 -1
  34. package/esm2022/lib/models/class-preset.mjs +1 -1
  35. package/esm2022/lib/models/polygon-factory.mjs +17 -1
  36. package/esm2022/lib/models/region-geojson.mjs +44 -6
  37. package/esm2022/lib/models/region.mjs +1 -1
  38. package/esm2022/lib/region-editor/class-tree/class-tree.component.mjs +156 -0
  39. package/esm2022/lib/region-editor/class-tree/class-tree.mjs +108 -0
  40. package/esm2022/lib/region-editor/classes-panel/classes-panel.component.mjs +77 -13
  41. package/esm2022/lib/region-editor/region-editor.component.mjs +670 -42
  42. package/esm2022/lib/region-editor/region-metrics.mjs +20 -2
  43. package/esm2022/lib/region-editor/region-persistence.service.mjs +44 -10
  44. package/esm2022/lib/region-editor/region-table/region-table.component.mjs +24 -8
  45. package/esm2022/lib/region-import/label-classes.mjs +149 -0
  46. package/esm2022/lib/region-import/label-volume.mjs +194 -0
  47. package/esm2022/lib/region-import/region-import.mjs +71 -0
  48. package/esm2022/lib/region-overlay/region-geometry.mjs +3 -3
  49. package/esm2022/lib/region-overlay/region-source-layer.mjs +378 -0
  50. package/esm2022/lib/region-overlay/region-style.mjs +97 -0
  51. package/esm2022/lib/region-source/http-region-source.mjs +104 -0
  52. package/esm2022/lib/region-source/region-source-ops.mjs +53 -0
  53. package/esm2022/lib/region-source/region-source-worker.mjs +10 -0
  54. package/esm2022/lib/region-source/region-source.mjs +67 -0
  55. package/esm2022/lib/region-source/region-tiles.mjs +708 -0
  56. package/esm2022/lib/region-source/worker-region-source.mjs +229 -0
  57. package/esm2022/lib/routing-visualizer.service.mjs +510 -22
  58. package/esm2022/lib/section-pane/section-pane.component.mjs +299 -0
  59. package/esm2022/lib/store/class-color.util.mjs +10 -5
  60. package/esm2022/lib/store/class-signatures.mjs +63 -0
  61. package/esm2022/lib/store/region-scope-cache.mjs +5 -1
  62. package/esm2022/lib/store/region-source-scope.mjs +186 -0
  63. package/esm2022/lib/store/region-store.service.mjs +774 -12
  64. package/esm2022/lib/store/visualizer-store.service.mjs +33 -6
  65. package/esm2022/lib/toolbar/region-tools/region-tools.component.mjs +8 -2
  66. package/esm2022/lib/toolbar/stack-controls/stack-controls.component.mjs +55 -15
  67. package/esm2022/lib/toolbar/toolbar.component.mjs +19 -5
  68. package/esm2022/lib/visualizer/region-load.mjs +67 -4
  69. package/esm2022/lib/visualizer/volume-sections.mjs +175 -0
  70. package/esm2022/lib/visualizer.component.mjs +155 -7
  71. package/fesm2022/{jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs → jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-BELcx0jv.mjs} +9851 -2904
  72. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-BELcx0jv.mjs.map +1 -0
  73. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-DEDSm_NL.mjs} +4 -4
  74. package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-Bkujqg7m.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-DEDSm_NL.mjs.map} +1 -1
  75. package/fesm2022/jax-data-science-sci-image-visualizer-region-source-worker-CAqBjM5L.mjs +12 -0
  76. package/fesm2022/jax-data-science-sci-image-visualizer-region-source-worker-CAqBjM5L.mjs.map +1 -0
  77. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +3 -3
  78. package/fesm2022/region-source.worker.js +1067 -0
  79. package/index.d.ts +12 -0
  80. package/lib/contracts/image.contract.d.ts +4 -0
  81. package/lib/contracts/ports/region-io.port.d.ts +24 -0
  82. package/lib/contracts/region-editor-api.contract.d.ts +102 -0
  83. package/lib/contracts/region-store.contract.d.ts +22 -0
  84. package/lib/contracts/section-view.contract.d.ts +44 -0
  85. package/lib/contracts/visualizer.contract.d.ts +10 -0
  86. package/lib/contracts/viz-config.d.ts +8 -0
  87. package/lib/geometry/loft.d.ts +22 -0
  88. package/lib/geometry/polylabel.d.ts +23 -0
  89. package/lib/geometry/reconstruct.d.ts +54 -0
  90. package/lib/geometry/reslice-image.d.ts +33 -0
  91. package/lib/geometry/section.d.ts +52 -0
  92. package/lib/implementations/napari-js/napari-loading-badge.d.ts +4 -2
  93. package/lib/implementations/napari-js/napari-loading-state.d.ts +8 -1
  94. package/lib/implementations/napari-js/napari-region-mesh-pointer.d.ts +32 -0
  95. package/lib/implementations/napari-js/napari-region-overlay.d.ts +6 -1
  96. package/lib/implementations/napari-js/napari-scene.d.ts +33 -0
  97. package/lib/implementations/napari-js/napari-spatial-hover.d.ts +3 -1
  98. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +48 -0
  99. package/lib/implementations/napari-js/napari-volume-region-meshes.d.ts +66 -0
  100. package/lib/implementations/napari-js/napari-volume-scene.d.ts +45 -1
  101. package/lib/implementations/napari-js/napari-volume-slice-planes.d.ts +43 -0
  102. package/lib/implementations/osd/openseadragon-viewer-options.d.ts +2 -0
  103. package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +2 -0
  104. package/lib/implementations/osd/osd-region-overlay-draw.d.ts +4 -3
  105. package/lib/implementations/osd/osd-region-overlay.d.ts +10 -3
  106. package/lib/implementations/osd/osd-tile-loading.d.ts +43 -0
  107. package/lib/implementations/osd/osd-viewer-like.d.ts +14 -0
  108. package/lib/implementations/osd/slice-cache.d.ts +13 -3
  109. package/lib/implementations/tile-server/tile-protocol.d.ts +3 -0
  110. package/lib/models/class-preset.d.ts +7 -0
  111. package/lib/models/polygon-factory.d.ts +5 -0
  112. package/lib/models/region-geojson.d.ts +40 -0
  113. package/lib/models/region.d.ts +19 -0
  114. package/lib/region-editor/class-tree/class-tree.component.d.ts +78 -0
  115. package/lib/region-editor/class-tree/class-tree.d.ts +31 -0
  116. package/lib/region-editor/classes-panel/classes-panel.component.d.ts +36 -6
  117. package/lib/region-editor/region-editor.component.d.ts +159 -17
  118. package/lib/region-editor/region-metrics.d.ts +8 -0
  119. package/lib/region-editor/region-persistence.service.d.ts +17 -5
  120. package/lib/region-editor/region-table/region-table.component.d.ts +10 -4
  121. package/lib/region-import/label-classes.d.ts +39 -0
  122. package/lib/region-import/label-volume.d.ts +31 -0
  123. package/lib/region-import/region-import.d.ts +46 -0
  124. package/lib/region-overlay/region-geometry.d.ts +4 -3
  125. package/lib/region-overlay/region-source-layer.d.ts +96 -0
  126. package/lib/region-overlay/region-style.d.ts +52 -0
  127. package/lib/region-source/http-region-source.d.ts +45 -0
  128. package/lib/region-source/region-source-ops.d.ts +89 -0
  129. package/lib/region-source/region-source-worker.d.ts +7 -0
  130. package/lib/region-source/region-source.d.ts +82 -0
  131. package/lib/region-source/region-tiles.d.ts +289 -0
  132. package/lib/region-source/worker-region-source.d.ts +65 -0
  133. package/lib/routing-visualizer.service.d.ts +168 -1
  134. package/lib/section-pane/section-pane.component.d.ts +47 -0
  135. package/lib/store/class-color.util.d.ts +6 -3
  136. package/lib/store/class-signatures.d.ts +11 -0
  137. package/lib/store/region-scope-cache.d.ts +2 -0
  138. package/lib/store/region-source-scope.d.ts +74 -0
  139. package/lib/store/region-store.service.d.ts +216 -2
  140. package/lib/store/visualizer-store.service.d.ts +10 -0
  141. package/lib/toolbar/region-tools/region-tools.component.d.ts +3 -1
  142. package/lib/toolbar/stack-controls/stack-controls.component.d.ts +20 -9
  143. package/lib/toolbar/toolbar.component.d.ts +9 -2
  144. package/lib/visualizer/region-load.d.ts +23 -4
  145. package/lib/visualizer/volume-sections.d.ts +64 -0
  146. package/lib/visualizer.component.d.ts +40 -0
  147. package/package.json +2 -2
  148. package/src/lib/assets/slice.svg +23 -0
  149. package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-Csxlnq7I.mjs.map +0 -1
@@ -3,9 +3,17 @@ import { IImageInfo } from '../contracts/image.contract';
3
3
  import { Region, Rectangle, Polygon, MultiPolygon } from '../models/region';
4
4
  import { VisualizerStore } from './visualizer-store.service';
5
5
  import { IRegionStore } from '../contracts/visualizer.contract';
6
- import { IRegionEditApi } from '../contracts/region-store.contract';
6
+ import { IRegionEditApi, IRegionSourceApi } from '../contracts/region-store.contract';
7
+ import type { Mesh } from 'napari-js/geometry';
8
+ import { SliceOutlines } from '../geometry/reconstruct';
7
9
  import { StackSaveLayout } from './region-scope-cache';
10
+ import { RegionSourceOpener, RegionSourceSlices } from './region-source-scope';
11
+ import type { RegionSource, RegionSourceView } from '../region-source/region-source';
12
+ import type { RegionIndexPage, RegionIndexQuery } from '../region-source/region-tiles';
13
+ import { RegionStyle } from '../region-overlay/region-style';
8
14
  import * as i0 from "@angular/core";
15
+ /** Selecting classes takes at most this many region-source features into the regions. */
16
+ export declare const SELECT_CLASSES_LIMIT = 5000;
9
17
  /**
10
18
  * Backend-neutral region store — the single source of truth for region state.
11
19
  *
@@ -34,7 +42,7 @@ import * as i0 from "@angular/core";
34
42
  * backends); GeoJSON import/export is `models/region-geojson`. All coordinates
35
43
  * are image pixels.
36
44
  */
37
- export declare class RegionStore implements IRegionStore, IRegionEditApi {
45
+ export declare class RegionStore implements IRegionStore, IRegionEditApi, IRegionSourceApi {
38
46
  private store;
39
47
  private static readonly UNDO_LIMIT;
40
48
  private static readonly UNDO_COALESCE_MS;
@@ -78,6 +86,35 @@ export declare class RegionStore implements IRegionStore, IRegionEditApi {
78
86
  /** Emit coalescing for live drags (see IRegionEditApi.beginBatch). */
79
87
  private batchDepth;
80
88
  private pendingEmit;
89
+ /**
90
+ * Large region sets held as tiled region sources rather than as regions (see
91
+ * {@link attachRegionSource}): per image and slice, with the ids taken out of each.
92
+ */
93
+ private readonly sources;
94
+ private readonly sourceView$;
95
+ /** The region look the user chose (overrides the source's hint), field by field. */
96
+ private userStyle;
97
+ /** The current source's style hint. */
98
+ private hintStyle;
99
+ private readonly regionStyle$;
100
+ /** Bumped on every change of the slice's source, so a late open is dropped. */
101
+ private sourceGeneration;
102
+ /** The source last published as the view: a source shown again does not turn the labels back on. */
103
+ private shownSource;
104
+ /** Bumped when a region source is attached or dropped: what is read from the sources is stale. */
105
+ private sourcesVersion;
106
+ /**
107
+ * The region sources' part of {@link classVolumes}, by class and what the sources hold (see
108
+ * {@link sourceStateKey}): an edit of the regions leaves it, so the volumes of a slice being drawn
109
+ * on cost no source reads.
110
+ */
111
+ private readonly sourceVolumeCache;
112
+ /** The region sources' outlines of the class sets reconstructed last (see {@link sourceOutlines}). */
113
+ private readonly sourceOutlineCache;
114
+ /** The classes hidden from view (names as given); every class is shown at first. */
115
+ private readonly hiddenClasses$;
116
+ /** {@link hiddenClasses$} by {@link presetKey}, as a region's label is matched. */
117
+ private hiddenKeys;
81
118
  constructor(store: VisualizerStore);
82
119
  /**
83
120
  * Replace (or, with `append`, add to) the current image's regions. Mints ids
@@ -236,6 +273,10 @@ export declare class RegionStore implements IRegionStore, IRegionEditApi {
236
273
  * region it edits) rather than changing them, so it never changes after this.
237
274
  */
238
275
  private recordUndoSnapshot;
276
+ /** The state an undo step restores. */
277
+ private snapshot;
278
+ /** Parse GeoJSON into regions, remembering the class colours the file carries so they are
279
+ * kept for classes with no preset (see VisualizerStore.rememberImportedClassColors). */
239
280
  importRegions(geoJsonStr: string): Region[];
240
281
  /** Download `regions` as GeoJSON, named after `fileName` (extension
241
282
  * replaced by `.geojson`) or `rois.geojson` without one. */
@@ -286,6 +327,179 @@ export declare class RegionStore implements IRegionStore, IRegionEditApi {
286
327
  beginGesture(): void;
287
328
  /** End the gesture opened by {@link beginGesture}; the next commit starts a new step. */
288
329
  endGesture(): void;
330
+ /**
331
+ * Hold a large region set as a tiled region source instead of as regions: the backends that
332
+ * draw sources (OpenSeadragon, napari) draw its features at the detail the zoom shows, from
333
+ * the tiles in view, and a feature joins the regions — exactly, as an ordinary editable
334
+ * {@link Region} with its {@link Region.sourceId} — only when it is selected or edited
335
+ * ({@link takeSourceFeatures}). Until then {@link getRegions} does not include it.
336
+ *
337
+ * `source` may be an opener, called the first time its slice is shown (and again if the
338
+ * source was closed to save memory). `slices` says which slice of the current image it
339
+ * serves: one (default: the slice shown; plane 0 of the source), or `'planes'`, every slice z
340
+ * from plane z of a z-indexed source. Replaces the source those slices had.
341
+ *
342
+ * `imported` marks a source brought in over the image (an imported volume, folder or served set)
343
+ * rather than the image's own regions: it is on no file yet, so a save writes its slices though
344
+ * nothing was taken from it — `true` for a source of one slice, the slices its planes cover for a
345
+ * `'planes'` source. The image's own sources are not marked: loading them changes nothing to save.
346
+ */
347
+ attachRegionSource(source: RegionSource | RegionSourceOpener, slices?: RegionSourceSlices, imported?: boolean | readonly number[]): void;
348
+ /** Drop the current image's region sources (closing them) and what was taken from them. */
349
+ detachRegionSources(): void;
350
+ /**
351
+ * Drop the slice's region source, for regions imported in place of the slice's (a small file
352
+ * replacing a large one): a source of the slice's own is detached; a z-indexed source over every
353
+ * slice has this slice's features taken out (as deleted). The other slices keep theirs.
354
+ */
355
+ dropSliceRegionSource(): Promise<void>;
356
+ /** Whether the slice shown has a region source. */
357
+ hasRegionSource(): boolean;
358
+ /** The slice's region source as a source layer draws it; null without one (or until it opens). */
359
+ getRegionSourceView(): RegionSourceView | null;
360
+ /** {@link getRegionSourceView} and every change of it (slice, taken ids, class colours). */
361
+ getRegionSourceView$(): Observable<RegionSourceView | null>;
362
+ /**
363
+ * Take source features into the working set for editing: their exact geometry becomes
364
+ * ordinary regions (minted, coloured and slice-tagged as any region entering the store) and
365
+ * the source stops drawing them. Not an undo step of its own — undoing past it puts the
366
+ * features back in the source. Resolves the region ids, in the order asked (a feature already
367
+ * taken resolves to its region); [] when the slice changed meanwhile.
368
+ */
369
+ takeSourceFeatures(ids: readonly string[]): Promise<number[]>;
370
+ /** {@link takeSourceFeatures}, then select them (added to the selection with `additive`). */
371
+ selectSourceFeatures(ids: readonly string[], additive?: boolean): Promise<number[]>;
372
+ /**
373
+ * Features per class still in the slice's region source (not taken); {} without one. With
374
+ * `stack`, a z-stack's over every slice: its source's planes, or each slice's source added up.
375
+ */
376
+ getRegionSourceClassCounts(stack?: boolean): Promise<Record<string, number>>;
377
+ /**
378
+ * Select every region of these classes on the slice — the regions', and the region source's,
379
+ * which are taken into the regions (at most {@link SELECT_CLASSES_LIMIT} of them). Added to the
380
+ * selection with `additive`. Resolves the selected region ids.
381
+ */
382
+ selectClasses(classes: readonly string[], additive?: boolean): Promise<number[]>;
383
+ /**
384
+ * The 3D reconstruction of each group of classes over every slice of the stack (an atlas
385
+ * structure with its substructures, say): the outlines of the regions of those classes on every
386
+ * slice — the region sources' and those drawn or taken — stacked and meshed in a worker. Meshes
387
+ * are in image px (x, y) and slice indices (z). `pixelStep` is the in-plane voxel size (image
388
+ * px); the outlines are read at the matching level of detail. A group with no outline gets an
389
+ * empty mesh.
390
+ */
391
+ reconstructClasses(groups: ReadonlyArray<{
392
+ key: string;
393
+ classes: readonly string[];
394
+ }>, pixelStep?: number): Promise<Array<{
395
+ key: string;
396
+ mesh: Mesh;
397
+ }>>;
398
+ /**
399
+ * Each class's volume over the whole stack, in px² × slices: its outlines' area on every slice
400
+ * (its features' union, see `outlinesArea`), one slice thick (Cavalieri's estimator), from the
401
+ * exact outlines. Where a feature is, a region source's or a drawn or taken region, does not
402
+ * change it: taking one leaves the volume as it was. The region sources' part is cached until
403
+ * what they hold changes (a feature taken or deleted, another source); only the slices with
404
+ * drawn or taken regions of the class are measured again.
405
+ */
406
+ classVolumes(classes: readonly string[]): Promise<Map<string, number>>;
407
+ /** The region sources' part of a class's volume, cached by what they hold (see classVolumes). */
408
+ private sourceVolume;
409
+ /**
410
+ * Every slice's outlines of these classes, the region sources' then the regions': closed shapes'
411
+ * rings, and open polylines as `lines`.
412
+ */
413
+ private classOutlines;
414
+ /**
415
+ * The region sources' outlines of these classes on every slice, without the features taken or
416
+ * deleted. The last few class sets are kept until what the sources hold changes: rebuilding a
417
+ * surface after an edit reads nothing again.
418
+ */
419
+ private sourceOutlines;
420
+ private readSourceOutlines;
421
+ /**
422
+ * Every class's outlines on every slice, by class: the region sources' (one read for all, kept
423
+ * like {@link sourceOutlines}) and the drawn and taken regions'. What a section through the
424
+ * stack cuts (see `geometry/section`).
425
+ */
426
+ outlinesByClass(level: number): Promise<Map<string, SliceOutlines[]>>;
427
+ /** The drawn and taken regions' outlines of these classes, by slice. */
428
+ private regionOutlines;
429
+ /** Each drawn or taken region's outlines (of the `wanted` classes; null: every class) with its class. */
430
+ private labelledRegionOutlines;
431
+ /** What the region sources of the image hold (see {@link sourceStateKey}): it changes when a
432
+ * feature is taken or deleted, or that is undone. */
433
+ getSourceStateKey(): string;
434
+ /**
435
+ * What the region sources of the image hold: the image, its sources (one attached or dropped
436
+ * bumps the version) and the features taken from them. A key for what is read from them.
437
+ */
438
+ private sourceStateKey;
439
+ /** Delete source features without taking them (one undo step). */
440
+ deleteSourceFeatures(ids: readonly string[]): void;
441
+ /**
442
+ * A page of the slice source's features not taken from it (file order), e.g. for the Region
443
+ * Editor's list; an empty page without a source.
444
+ */
445
+ getRegionSourceIndex(query: Omit<RegionIndexQuery, 'exclude'>): Promise<RegionIndexPage>;
446
+ /**
447
+ * `regions` as a GeoJSON FeatureCollection string merged with the region sources behind them:
448
+ * the sources' features that were not taken (or deleted), then `regions`. Without a source,
449
+ * {@link getGeoJsonString}.
450
+ *
451
+ * `slice` names one slice-file of a folder stack (a per-slice-file save): its source's features,
452
+ * on the file's own plane — of a z-indexed source over every slice, only that slice's plane.
453
+ * Without it, the export is the image's: on a stack saved as one file, every slice's source
454
+ * (each feature on its slice's plane, `geometry.plane.z`) and a z-indexed source's every plane;
455
+ * otherwise the source of the slice shown.
456
+ */
457
+ getGeoJsonWithSource(regions: Region[], slice?: number): Promise<string>;
458
+ /**
459
+ * Whether a region of class `label` goes unlabelled because a subclass of it (by the region
460
+ * source's class hierarchy) is among the regions too — an atlas's "Isocortex" under its areas.
461
+ * False without a hierarchy.
462
+ */
463
+ isLabelCovered(label: string | undefined): boolean;
464
+ private coveredFor;
465
+ /** The classes hidden from view (see {@link setClassesHidden}). */
466
+ getHiddenClasses(): ReadonlySet<string>;
467
+ /** {@link getHiddenClasses} and every change of it. */
468
+ getHiddenClasses$(): Observable<ReadonlySet<string>>;
469
+ /**
470
+ * Hide (or show again) these classes' regions: the overlays and the region source's layer stop
471
+ * drawing them, and a hidden region can be neither picked nor kept selected. The regions stay in
472
+ * the set (the table lists them; a save keeps them).
473
+ */
474
+ setClassesHidden(classes: readonly string[], hidden: boolean): void;
475
+ /** Whether regions labelled `label` are hidden (an unlabelled region never is). */
476
+ isClassHidden(label: string | null | undefined): boolean;
477
+ /** The region look: outlines, or filled (an atlas's look); see `region-style`. */
478
+ getRegionStyle(): RegionStyle;
479
+ /** {@link getRegionStyle} and every change of it. */
480
+ getRegionStyle$(): Observable<RegionStyle>;
481
+ /**
482
+ * Choose the region look, field by field; it overrides a region source's hint for the session.
483
+ * The overlays redraw.
484
+ */
485
+ setRegionStyle(patch: Partial<RegionStyle>): void;
486
+ /** The look now: the user's choice, else the source's hint, else outlines. */
487
+ private effectiveStyle;
488
+ /** Publish the look when it changed: the overlays redraw the regions, the source layer its features. */
489
+ private restyle;
490
+ /** Re-emit the source view with the current look and label setting. */
491
+ private republishView;
492
+ /** The slice a region source is keyed by: the display slice in stack mode, else 0. */
493
+ private get sourceSlice();
494
+ private currentTaken;
495
+ /** Replace the slice's taken ids and tell the source layers. */
496
+ private setTaken;
497
+ /**
498
+ * Look up (opening it if needed) the slice's source and publish it as the source view. The view
499
+ * is withdrawn at once: until the incoming source opens, nothing reads, takes or deletes from the
500
+ * outgoing one into the slice now shown.
501
+ */
502
+ private refreshSourceView;
289
503
  /**
290
504
  * Switch the active image: snapshot the outgoing regions, restore the
291
505
  * incoming image's (or [] if none), clear selection, and notify. Idempotent
@@ -36,6 +36,8 @@ export declare class VisualizerStore {
36
36
  private readonly grayscale$;
37
37
  private readonly invert$;
38
38
  private presetSet;
39
+ /** Session-only class colours from imported files (see PresetSet.importedColors). */
40
+ private importedColors;
39
41
  private readonly presetSet$;
40
42
  private readonly savePresets$;
41
43
  private presetsLoaded;
@@ -46,6 +48,8 @@ export declare class VisualizerStore {
46
48
  private readonly spatialView$;
47
49
  constructor(prefsPort?: PreferencesPort | null);
48
50
  private initPresetPersistence;
51
+ /** Publish the preset set with this session's imported class colours. */
52
+ private emitPresetSet;
49
53
  private normalizePresetSet;
50
54
  /** Queue a debounced save. No-ops until the initial load resolves (and when no port is bound). */
51
55
  private queuePresetSave;
@@ -114,6 +118,12 @@ export declare class VisualizerStore {
114
118
  /** Upsert a single class colour (used by the editor's per-class colour picker). */
115
119
  setClassificationColor(label: string, color: string): void;
116
120
  getPresetSet(): PresetSet;
121
+ /**
122
+ * Remember the class colours an imported file carried (label → colour), so its regions keep
123
+ * them: a class with a preset keeps the preset's colour, and the latest import wins for the
124
+ * rest. Not persisted.
125
+ */
126
+ rememberImportedClassColors(colors: ReadonlyMap<string, string>): void;
117
127
  getPresetSet$(): Observable<PresetSet>;
118
128
  setPresetSet(set: PresetSet): void;
119
129
  /** Add or update a class (keyed by exact stored `name`). */
@@ -12,6 +12,8 @@ export declare class RegionToolsComponent {
12
12
  activeDragMode: string | null;
13
13
  /** The 3D cloud's screen-space lasso: no brush or polyline. */
14
14
  is3dRegions: boolean;
15
+ /** Only the selection tool (a volume view: it picks the regions' surfaces in 3D). */
16
+ selectOnly: boolean;
15
17
  /** The backend has a vertex-editing overlay (OSD + napari-js image). */
16
18
  vertexTools: boolean;
17
19
  /** Magic-wand sensitivity (the wand's slider value). */
@@ -56,5 +58,5 @@ export declare class RegionToolsComponent {
56
58
  /** A single slider's value (a range slider never feeds these). */
57
59
  protected asNumber(v: number | number[] | undefined): number | undefined;
58
60
  static ɵfac: i0.ɵɵFactoryDeclaration<RegionToolsComponent, never>;
59
- static ɵcmp: i0.ɵɵComponentDeclaration<RegionToolsComponent, "toolbar-region-tools", never, { "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "vertexTools": { "alias": "vertexTools"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; }, { "openRegionEditor": "openRegionEditor"; "toggleDragMode": "toggleDragMode"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "deleteRegion": "deleteRegion"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, never, true, never>;
61
+ static ɵcmp: i0.ɵɵComponentDeclaration<RegionToolsComponent, "toolbar-region-tools", never, { "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "selectOnly": { "alias": "selectOnly"; "required": false; }; "vertexTools": { "alias": "vertexTools"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; }, { "openRegionEditor": "openRegionEditor"; "toggleDragMode": "toggleDragMode"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "deleteRegion": "deleteRegion"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, never, true, never>;
60
62
  }
@@ -1,4 +1,4 @@
1
- import { EventEmitter } from '@angular/core';
1
+ import { EventEmitter, OnDestroy } from '@angular/core';
2
2
  import * as i0 from "@angular/core";
3
3
  /** A stack-mode choice (`val` 'false' = single image, 'true' = stack). */
4
4
  export interface StackOption {
@@ -6,11 +6,11 @@ export interface StackOption {
6
6
  val: string;
7
7
  }
8
8
  /**
9
- * The toolbar's z-stack group: a live slice scrubber for the views that swap
10
- * slices in place, or (Plotly heatmap) the single-image / stack toggle with a
11
- * slice-number field. Presentational: the host owns `zIndex`.
9
+ * The toolbar's z-stack group: the slice bar, and (Plotly heatmap) the single-image / stack
10
+ * toggle. The views that swap slices in place follow the bar as it moves; the heatmap's single
11
+ * image is re-plotted at the slice it settles on. Presentational: the host owns `zIndex`.
12
12
  */
13
- export declare class StackControlsComponent {
13
+ export declare class StackControlsComponent implements OnDestroy {
14
14
  /** Show the live slice scrubber. */
15
15
  liveScrubber: boolean;
16
16
  /** Show the single-image / stack toggle (Plotly heatmap). */
@@ -29,12 +29,23 @@ export declare class StackControlsComponent {
29
29
  zSlide: EventEmitter<number | undefined>;
30
30
  /** A stack-mode choice was picked. */
31
31
  selectStackOption: EventEmitter<StackOption>;
32
- /** A slice number was typed (blank reports the current slice). */
32
+ /** The heatmap's slice bar moved to a slice (the host shows its number). */
33
33
  zIndexInput: EventEmitter<number>;
34
- /** Enter in the slice field / the reload button: re-plot at the typed slice. */
34
+ /** The heatmap's slice bar settled: re-plot at that slice. */
35
35
  reloadAndPlot: EventEmitter<void>;
36
- /** The slice field reports what was typed (blank keeps the current slice). */
37
- protected onSliceInput(value: string | number | null): void;
36
+ /** How long key steps on the heatmap's bar pause before the slice is re-plotted. */
37
+ static readonly REPLOT_DELAY_MS = 400;
38
+ /** A drag of the bar is under way (its end re-plots the heatmap, not each step). */
39
+ protected dragging: boolean;
40
+ private replot;
41
+ /** The heatmap in single-image mode: its bar re-plots the slice it settles on. */
42
+ protected get singleImage(): boolean;
43
+ /** The bar moved: a live view follows it; the heatmap shows the number, and re-plots after key steps pause. */
44
+ protected onSliceChange(z: number | undefined): void;
45
+ /** The bar was let go (a drag's end, or a click on its track). */
46
+ protected onSliceEnd(z: number | undefined): void;
47
+ private cancelReplot;
48
+ ngOnDestroy(): void;
38
49
  static ɵfac: i0.ɵɵFactoryDeclaration<StackControlsComponent, never>;
39
50
  static ɵcmp: i0.ɵɵComponentDeclaration<StackControlsComponent, "toolbar-stack-controls", never, { "liveScrubber": { "alias": "liveScrubber"; "required": false; }; "stackToggle": { "alias": "stackToggle"; "required": false; }; "showStack": { "alias": "showStack"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; }, { "zScrub": "zScrub"; "zSlide": "zSlide"; "selectStackOption": "selectStackOption"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; }, never, never, true, never>;
40
51
  }
@@ -52,6 +52,10 @@ export declare class ToolbarComponent {
52
52
  * controls — flipping that would show 2D pan/zoom and hide the orbit controls.
53
53
  */
54
54
  is3dRegions: boolean;
55
+ /** A napari volume / isosurface view: the Region Editor (regions in 3D) without drawing tools. */
56
+ isVolumeView: boolean;
57
+ /** Whether the volume view is split with the slice editor (the slice, with every region tool). */
58
+ sliceEditor: boolean;
55
59
  /** The armed tool's mode (zoom, pan, a drawing tool, `samPoint`, …), or null. */
56
60
  activeDragMode: string | null;
57
61
  /** The 3D camera mode (`turntable` / `orbit` / …). */
@@ -118,6 +122,8 @@ export declare class ToolbarComponent {
118
122
  openSpatialControls: EventEmitter<void>;
119
123
  /** Open the Region Editor dialog. */
120
124
  openRegionEditor: EventEmitter<void>;
125
+ /** Split the volume view with the slice editor, or close it. */
126
+ toggleSliceEditor: EventEmitter<void>;
121
127
  /** A single-image / stack choice was picked. */
122
128
  selectStackOption: EventEmitter<{
123
129
  name: string;
@@ -202,7 +208,8 @@ export declare class ToolbarComponent {
202
208
  * view, the napari-js WebGPU image, the napari-js surface — which rebuilds the height field
203
209
  * from the picked slice (jit-ui#102) — and the 2D spatial-omics view, where a 3D dataset's
204
210
  * registered volume is the image and the slider picks the section whose observations are
205
- * drawn. Drives the per-slice slider. */
211
+ * drawn. Also a split volume view, whose slice editor shows the slice picked. Drives the
212
+ * per-slice slider. */
206
213
  protected get showsLiveSliceScrubber(): boolean;
207
214
  /** Any napari-js WebGPU plot type. The single-image/stack toggle + slice-number field are
208
215
  * Plotly-only stack controls, so they're hidden for napari (jit-ui#102). */
@@ -218,5 +225,5 @@ export declare class ToolbarComponent {
218
225
  protected get isIntensityCapable(): boolean;
219
226
  protected showHelp(): void;
220
227
  static ɵfac: i0.ɵɵFactoryDeclaration<ToolbarComponent, never>;
221
- static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "hasSpatialDataset": { "alias": "hasSpatialDataset"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "basePlotType": { "alias": "basePlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "contributedTools": { "alias": "contributedTools"; "required": false; }; "dialogTools": { "alias": "dialogTools"; "required": false; }; "openDialogToolId": { "alias": "openDialogToolId"; "required": false; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openSpatialControls": "openSpatialControls"; "openRegionEditor": "openRegionEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "runTool": "runTool"; "toggleDialogTool": "toggleDialogTool"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], true, never>;
228
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "hasSpatialDataset": { "alias": "hasSpatialDataset"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "basePlotType": { "alias": "basePlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "required": false; }; "is3dRegions": { "alias": "is3dRegions"; "required": false; }; "isVolumeView": { "alias": "isVolumeView"; "required": false; }; "sliceEditor": { "alias": "sliceEditor"; "required": false; }; "activeDragMode": { "alias": "activeDragMode"; "required": false; }; "activeSurface3dMode": { "alias": "activeSurface3dMode"; "required": false; }; "axesVisible": { "alias": "axesVisible"; "required": false; }; "wireframeActive": { "alias": "wireframeActive"; "required": false; }; "resolutionScale": { "alias": "resolutionScale"; "required": false; }; "wandSensitivity": { "alias": "wandSensitivity"; "required": false; }; "brushSize": { "alias": "brushSize"; "required": false; }; "vertexEraserRadius": { "alias": "vertexEraserRadius"; "required": false; }; "canUndo": { "alias": "canUndo"; "required": false; }; "canRedo": { "alias": "canRedo"; "required": false; }; "stackOptions": { "alias": "stackOptions"; "required": false; }; "tools": { "alias": "tools"; "required": false; }; "imageSmoothingEnabled": { "alias": "imageSmoothingEnabled"; "required": false; }; "samModels": { "alias": "samModels"; "required": false; }; "samModelId": { "alias": "samModelId"; "required": false; }; "contributedTools": { "alias": "contributedTools"; "required": false; }; "dialogTools": { "alias": "dialogTools"; "required": false; }; "openDialogToolId": { "alias": "openDialogToolId"; "required": false; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "openSpatialControls": "openSpatialControls"; "openRegionEditor": "openRegionEditor"; "toggleSliceEditor": "toggleSliceEditor"; "selectStackOption": "selectStackOption"; "zScrub": "zScrub"; "zSlide": "zSlide"; "zIndexInput": "zIndexInput"; "reloadAndPlot": "reloadAndPlot"; "downloadImage": "downloadImage"; "autoscaleImage": "autoscaleImage"; "toggleDragMode": "toggleDragMode"; "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "toggleSurface3dMode": "toggleSurface3dMode"; "resetSurfaceCamera": "resetSurfaceCamera"; "toggleAxes": "toggleAxes"; "toggleWireframe": "toggleWireframe"; "selectResolution": "selectResolution"; "deleteRegion": "deleteRegion"; "undoRegion": "undoRegion"; "redoRegion": "redoRegion"; "segmentRegions": "segmentRegions"; "segmentCellpose": "segmentCellpose"; "runTool": "runTool"; "toggleDialogTool": "toggleDialogTool"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], true, never>;
222
229
  }
@@ -1,7 +1,18 @@
1
+ import type { HttpClient } from '@angular/common/http';
1
2
  import { IImageInfo } from '../contracts/image.contract';
2
3
  import { IRegionStore } from '../contracts/visualizer.contract';
3
- /** The region-store members {@link applyImageRois} writes through. */
4
- export type RegionLoadTarget = Pick<IRegionStore, 'importRegions' | 'enterStackMode' | 'setRegions' | 'resetUndoHistory'>;
4
+ import { RegionSource } from '../region-source/region-source';
5
+ import type { RegionSourceOpener, RegionSourceSlices } from '../store/region-source-scope';
6
+ /** The region-store members {@link applyImageRois} writes through. A target without
7
+ * `attachRegionSource` loads every ROI file as regions. */
8
+ export type RegionLoadTarget = Pick<IRegionStore, 'importRegions' | 'enterStackMode' | 'setRegions' | 'resetUndoHistory'> & {
9
+ attachRegionSource?(source: RegionSource | RegionSourceOpener, slices?: RegionSourceSlices): void;
10
+ /** The slice shown now (what a stack's source, laid out once it opens, makes live). */
11
+ getDisplaySlice?(): number;
12
+ /** Record the slice shown (outside stack mode it only records it): see {@link applyImageRois}. */
13
+ setDisplaySlice?(z: number): void;
14
+ isStackMode?(): boolean;
15
+ };
5
16
  /**
6
17
  * Apply an image's saved ROIs once its render has landed, choosing the region
7
18
  * layout from the image's shape:
@@ -22,6 +33,14 @@ export type RegionLoadTarget = Pick<IRegionStore, 'importRegions' | 'enterStackM
22
33
  * Loading saved ROIs is not a user edit, so the undo history starts fresh and the
23
34
  * first undo cannot wipe them (jit-ui#85).
24
35
  *
25
- * `z` is the slice shown now; entering stack mode makes it live and resets undo.
36
+ * Large ROI files (`minSourceChars` and up) and a server-side source (`regionSourceUrl`)
37
+ * become tiled region sources instead of regions; a z-indexed one serves each slice its own
38
+ * plane. On a single-file stack the source is opened first, to tell which: one on the default
39
+ * plane only stays global, as its regions would. A folder stack's sources open lazily, the first
40
+ * time their slice is shown.
41
+ *
42
+ * `z` is the slice shown now; entering stack mode makes it live and resets undo. `http`, the
43
+ * host's `HttpClient`, carries a server-side source's requests, so the host's interceptors (its
44
+ * auth) apply to them as to the image tiles.
26
45
  */
27
- export declare function applyImageRois(info: IImageInfo, target: RegionLoadTarget, z: number): void;
46
+ export declare function applyImageRois(info: IImageInfo, target: RegionLoadTarget, z: number, minSourceChars?: number, http?: HttpClient): void;
@@ -0,0 +1,64 @@
1
+ import { Observable } from 'rxjs';
2
+ import type { IChannelState } from '../contracts/channel-histogram-api.contract';
3
+ import type { Colormap } from 'napari-js';
4
+ import type { SectionAxis, SectionViews } from '../contracts/section-view.contract';
5
+ import type { SliceOutlines } from '../geometry/reconstruct';
6
+ import type { VolumeVoxelsOnScreen } from '../implementations/napari-js/napari-scene';
7
+ import type { SlicePlanes } from '../implementations/napari-js/napari-volume-slice-planes';
8
+ /** What the sections are cut from and shown with. */
9
+ export interface VolumeSectionsDeps {
10
+ /** The volume on screen as assembled, or null (not a volume view, or not assembled yet). */
11
+ volume(): VolumeVoxelsOnScreen | null;
12
+ /** The image the volume is of: another one drops the outlines read and puts the planes back. */
13
+ image(): string | undefined;
14
+ /** Show the planes in the volume (null: hide one). */
15
+ showPlanes(planes: SlicePlanes): void;
16
+ /** Every class's outlines on every slice (see RegionStore.outlinesByClass). */
17
+ outlinesByClass(level: number): Promise<Map<string, SliceOutlines[]>>;
18
+ isClassHidden(label: string): boolean;
19
+ colorOf(label: string): string;
20
+ channelStates(): IChannelState[];
21
+ /** The colormap the 3D view draws a channel with: its tint in a multichannel volume, else the volume's. */
22
+ colormap(channel: IChannelState | undefined, multichannel: boolean): Colormap;
23
+ invert(): boolean;
24
+ /** The slice shown and edited. */
25
+ slice(): number;
26
+ }
27
+ /**
28
+ * The sections beside a split volume view: the planes x = const and y = const (image px), each cut
29
+ * from the assembled volume as an image, displayed like the 2D view displays a slice (the channel
30
+ * windows, tints and invert), with every class's outlines cut by the same plane and the other two
31
+ * planes as a crosshair. Recut when a plane moves, the slice changes, the regions or their
32
+ * visibility change, the display changes or the volume is assembled again — once per frame at most.
33
+ */
34
+ export declare class VolumeSections {
35
+ private readonly deps;
36
+ private readonly views$;
37
+ private open;
38
+ private planes;
39
+ /** The image the planes and outlines are of. */
40
+ private image;
41
+ /** Every class's outlines at a level, read and indexed once until the regions change. */
42
+ private outlines;
43
+ private scheduled;
44
+ /** Bumped per cut, so a cut that waited on the outlines is dropped when a newer one starts. */
45
+ private request;
46
+ constructor(deps: VolumeSectionsDeps);
47
+ /** Both sections as they are cut; nulls while closed. */
48
+ getViews$(): Observable<SectionViews>;
49
+ /** Show the sections (their planes through the middle of the image at first). */
50
+ start(): void;
51
+ stop(): void;
52
+ /** Move a section's plane to `at` (image px; clamped into the image). */
53
+ setPlane(axis: SectionAxis, at: number): void;
54
+ /** The planes as they are (image px), the z plane being the slice. */
55
+ getPlanes(): SlicePlanes;
56
+ /** The regions changed (an edit): their outlines are read again, once the edits pause. */
57
+ regionsChanged(): void;
58
+ /** Something the sections show changed (the slice, the display, the visible classes, the volume). */
59
+ schedule(): void;
60
+ private cut;
61
+ private view;
62
+ /** The volume's channels as displayed now. */
63
+ private voxels;
64
+ }
@@ -26,6 +26,7 @@ import { RegionActions } from './visualizer/region-actions';
26
26
  import { ToolModes } from './visualizer/tool-modes';
27
27
  import { FloatingPos } from './visualizer/floating-drag.directive';
28
28
  import { IntensityInsetComponent } from './intensity-inset/intensity-inset.component';
29
+ import type { SectionAxis, SectionViews } from './contracts/section-view.contract';
29
30
  import { SpatialDatasetBinder } from './visualizer/spatial-dataset-binder';
30
31
  import * as i0 from "@angular/core";
31
32
  /**
@@ -245,6 +246,36 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
245
246
  * selection there: region tools are screen-space, and against an orbiting
246
247
  * camera a drawn rectangle has no fixed meaning in the data. */
247
248
  protected get isSpatial3dMode(): boolean;
249
+ /** True in a napari volume or isosurface view, where the Region Editor shows regions in 3D. */
250
+ protected get isVolumeView(): boolean;
251
+ /** Whether the user split the volume view with the slice editor (off by default; kept while
252
+ * the view shows other plot types, so a volume view opens split again). */
253
+ protected sliceEditor: boolean;
254
+ /** The slice editor's pane: the displayed slice, with every region tool, top right. */
255
+ protected readonly sliceDivName: string;
256
+ /** The left column's and the top row's shares of the slice editor (%). */
257
+ protected splitX: number;
258
+ protected splitY: number;
259
+ /** The sections through x and y shown below (nulls until the volume is cut). */
260
+ protected sections: SectionViews;
261
+ protected get sliceEditorShown(): boolean;
262
+ /**
263
+ * Split the volume view with the slice editor, or close it. The editor opens on the displayed
264
+ * slice and takes the drawing tools; closing it leaves the selection tool armed (it picks the
265
+ * surfaces in 3D) and disarms any other.
266
+ */
267
+ protected toggleSliceEditor(): Promise<void>;
268
+ /** A point picked in a section: the slice and the other section's plane move there. */
269
+ protected onSectionPick(axis: SectionAxis, at: {
270
+ u: number;
271
+ v: number;
272
+ }): void;
273
+ /** Drag a divider of the slice editor: the vertical one sets the columns, the horizontal one the rows. */
274
+ protected startPaneResize(event: PointerEvent, axis: 'x' | 'y'): void;
275
+ /** A divider's keyboard: ← → move the vertical one, ↑ ↓ the horizontal one, by 5%. */
276
+ protected nudgePaneSize(event: KeyboardEvent, axis: 'x' | 'y'): void;
277
+ private setSplit;
278
+ private relayoutFrame;
248
279
  /** True for the Image plot type, which renders as a natively pan/zoom-able
249
280
  * raster — so the backend-agnostic zoom/pan toolbar tools are hidden. The
250
281
  * component drives this off the plot type, not the active backend. */
@@ -259,6 +290,8 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
259
290
  protected get hasSpatialDataset(): boolean;
260
291
  /** Keyboard, wheel and context-menu handling; created once the view exists. */
261
292
  private shortcuts?;
293
+ /** Waits for a viewer created outside the document to be attached (see {@link whenInDocument}). */
294
+ private attachObserver;
262
295
  /** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
263
296
  private readonly scrubber;
264
297
  constructor(state: ImageStatePort, plotService: IVisualizer, messageService: MessageService, ngZone: NgZone, cdr: ChangeDetectorRef, session: VisualizerStore, samTool: SamToolService, cellSegmentTool: CellSegmentToolService, samPointTool: SamPointToolService, regionOps: RegionOpsService, vizConfig?: VizConfig | undefined, toolContributions?: readonly ToolbarContribution[], spatialData?: SpatialDataPort | undefined, plotTypeContributions?: readonly PlotTypeContribution[], injector?: Injector | undefined, hostRef?: ElementRef<HTMLElement> | undefined, destroyRef?: DestroyRef);
@@ -307,6 +340,13 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
307
340
  ngAfterViewInit(): void;
308
341
  /** The plot div exists now: run an image-less draw that arrived before it did. */
309
342
  private onViewReady;
343
+ /**
344
+ * Run `ready` once this viewer is in the document. A host may create it detached — a closed
345
+ * dialog's projected content, such as jit-ui's pipeline preview — and the backends find the
346
+ * plot div by id, so a draw then found no target and alerted "the renderer could not start"
347
+ * over another viewer. Until then its view is not ready, and an image-less draw waits.
348
+ */
349
+ private whenInDocument;
310
350
  /** What the keyboard, wheel and context-menu shortcuts act on. Their listeners live
311
351
  * outside this view, so every action that changes what it shows marks it for check. */
312
352
  private shortcutHost;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jax-data-science/sci-image-visualizer",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "engines": {
5
5
  "node": ">=20.9.0"
6
6
  },
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "cellpose-js": "^0.6.0",
41
41
  "fast-png": "^6.4.0",
42
- "napari-js": "^0.15.0",
42
+ "napari-js": "^0.18.0",
43
43
  "openseadragon": "^6.0.2",
44
44
  "plotly.js-dist-min": "^3.0.1",
45
45
  "tslib": "^2.3.0"
@@ -0,0 +1,23 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
2
+ <!-- A volume (a cube) with a slab sliced off it by a scalpel: the slice editor. -->
3
+ <defs>
4
+ <mask id="slice-cut" maskUnits="userSpaceOnUse" x="-64" y="-64" width="192" height="192">
5
+ <rect x="-64" y="-64" width="192" height="192" fill="#fff"/>
6
+ <!-- The cube's lines stop short of the scalpel, so it reads at toolbar size. -->
7
+ <path d="M27.4 19.4 Q20.5 29.3 24.9 42.6 L34.1 21.4 Z" fill="#000" stroke="#000" stroke-width="5" stroke-linejoin="round"/>
8
+ <path d="M31.8 19.1 L34.2 11.0 M31.4 20.6 L31.8 19.1" stroke="#000" stroke-width="12" stroke-linecap="round"/>
9
+ </mask>
10
+ </defs>
11
+ <g transform="translate(4.89 -8.48) scale(1.1566)">
12
+ <g fill="none" stroke="#000" stroke-width="4.6" stroke-linejoin="round" stroke-linecap="round" mask="url(#slice-cut)">
13
+ <path d="M22.0 59.0 L30.3 54.2 L30.3 35.7 L14.3 26.4 L6.0 31.2 L6.0 49.8 Z M22.0 40.5 L22.0 59.0 M22.0 40.5 L30.3 35.7 M22.0 40.5 L6.0 31.2"/>
14
+ <path d="M35.5 51.2 L40.9 48.1 L40.9 29.6 L24.9 20.3 L19.4 23.5 L19.4 42.0 Z M35.5 32.7 L35.5 51.2 M35.5 32.7 L40.9 29.6 M35.5 32.7 L19.4 23.5"/>
15
+ <!-- The slab's cut face: the section the slice shows. -->
16
+ <path d="M35.5 51.2 L35.5 32.7 L19.4 23.5 L19.4 42.0 Z" fill="#000"/>
17
+ </g>
18
+ <path d="M27.4 19.4 Q20.5 29.3 24.9 42.6 L34.1 21.4 Z" fill="#000"/>
19
+ <path d="M31.8 19.1 L34.2 11.0" stroke="#000" stroke-width="7.5" stroke-linecap="round"/>
20
+ <path d="M31.4 20.6 L31.8 19.1" stroke="#000" stroke-width="3.5" stroke-linecap="round"/>
21
+ </g>
22
+ </svg>
23
+