@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
@@ -58,7 +58,9 @@ export declare class SpatialHover {
58
58
  * Listen on `el` (the plot host) rather than the canvas, so it keeps working over the region
59
59
  * overlay (an SVG covering the canvas, which would otherwise swallow every move), and throttle
60
60
  * to one hit-test per animation frame: a pointermove can fire far more often than that, and
61
- * the first test after an orbit re-projects the cloud.
61
+ * the first test after an orbit re-projects the cloud. None while a button is held: that is an
62
+ * orbit or a pan moving the camera every frame, and a test per frame re-projected all of a 3.7M
63
+ * cloud each time (≈150 ms a frame); the first move after the drag pays for one.
62
64
  */
63
65
  install(el: HTMLElement): void;
64
66
  /** Hide the tooltip (the navigator is being dragged, say). */
@@ -23,6 +23,9 @@ import { CellSegmentToolService } from '../../toolbar/segmentation/cell-segment-
23
23
  import { BaseStoreVisualizer } from '../base-store-visualizer';
24
24
  import { SimpleSliceAccessService } from '../simple-slice-access.service';
25
25
  import { TranscriptEstimate } from './napari-spatial-tiles';
26
+ import { VolumeVoxelsOnScreen } from './napari-scene';
27
+ import type { PlaneAxis, SlicePlanes } from './napari-volume-slice-planes';
28
+ import type { RegionMesh3D } from './napari-volume-region-meshes';
26
29
  import * as i0 from "@angular/core";
27
30
  /** Opaque handle from {@link NapariVisualizerService.load}, passed back to plot(). */
28
31
  interface NapariLoaded {
@@ -90,6 +93,20 @@ export declare class NapariVisualizerService extends BaseStoreVisualizer impleme
90
93
  private lifetime;
91
94
  /** What {@link plot} mounted, while it is mounted. */
92
95
  private scene;
96
+ /** Region reconstructions to show in a volume view, kept across re-plots of their image. */
97
+ private regionMeshes;
98
+ /** The image (file name) {@link regionMeshes} were built from. */
99
+ private regionMeshesImage;
100
+ /** The keys of the reconstructions highlighted. */
101
+ private regionHighlight;
102
+ /** The keys of the reconstructions hidden. */
103
+ private regionHidden;
104
+ /** The planes of the sections beside a volume view, or none. */
105
+ private slicePlanes;
106
+ /** A section's plane dragged in the volume (see {@link getSlicePlaneMove$}). */
107
+ private readonly slicePlaneMove$;
108
+ /** Clicks on the reconstructions (see {@link getRegionMeshPick$}). */
109
+ private readonly regionMeshPick$;
93
110
  /** Spatial state that outlives a scene (latest data, windows, estimated gene-map fields). */
94
111
  private readonly spatial;
95
112
  /** Viewer settings that outlive a scene. */
@@ -136,12 +153,43 @@ export declare class NapariVisualizerService extends BaseStoreVisualizer impleme
136
153
  private fitCameraSoon;
137
154
  /** @deprecated Plotly-specific; host re-drives plot() from its image stream. */
138
155
  reloadAndPlot(): void;
156
+ /** Show `message` on the canvas's loading badge until the returned function is called. */
157
+ beginTask(message: string): () => void;
158
+ /** Show these region reconstructions of `image` in the volume view (kept for its next volume plot). */
159
+ setRegionMeshes(meshes: readonly RegionMesh3D[], image: string | undefined): void;
160
+ /** Highlight the reconstructions of these keys (none: all as usual). */
161
+ setRegionHighlight(keys: readonly string[]): void;
162
+ /** Hide the reconstructions of these keys (kept for the next volume plot). */
163
+ setRegionHidden(keys: readonly string[]): void;
164
+ /** Draw the reconstructions through the volume rather than hidden behind it (kept across plots). */
165
+ setRegionsThroughVolume(through: boolean): void;
166
+ private regionsThroughVolume;
167
+ /** Show the planes of the sections beside a volume view (null: hide one); kept across plots. */
168
+ setSlicePlanes(planes: SlicePlanes): void;
169
+ /** A section's plane dragged in the volume view, to `value` along its axis (image px, or a slice). */
170
+ getSlicePlaneMove$(): Observable<{
171
+ axis: PlaneAxis;
172
+ value: number;
173
+ }>;
174
+ /** The volume on screen, as assembled (see VolumeScene.volumeVoxels); null outside a volume view. */
175
+ volumeVoxels(): VolumeVoxelsOnScreen | null;
176
+ /** A reconstruction clicked with the selection tool (its key), or the volume beside them (null). */
177
+ getRegionMeshPick$(): Observable<{
178
+ key: string | null;
179
+ additive: boolean;
180
+ }>;
181
+ /** Image px per voxel of the volume on screen, in-plane; null outside a volume view. */
182
+ regionVoxelStep(): number | null;
139
183
  /**
140
184
  * End the mounted scene: abort its lifetime (its descriptor poll, tile counts, frame loading and
141
185
  * awaits) and dispose it — its layers, keys, subscriptions and listeners go with it, so the next
142
186
  * plot can never inherit an "already built" key (NAPARI-SVC-1) — then tear the viewer down.
143
187
  */
144
188
  reset(): void;
189
+ /**
190
+ * The plot area changed size (the slice editor split it, a divider moved): fit the canvas, whose
191
+ * height was fixed at plot time, to it again.
192
+ */
145
193
  relayout(_trueImageSize?: number[]): void;
146
194
  /** @deprecated Plotly-specific axis reset. */
147
195
  resetAxes(): void;
@@ -0,0 +1,66 @@
1
+ import type { Viewer } from 'napari-js';
2
+ /** A region's 3D reconstruction to show in the volume: a mesh in image px (x, y) and slices (z). */
3
+ export interface RegionMesh3D {
4
+ /** Identity (a class name): a mesh with the same key and arrays is kept, not rebuilt. */
5
+ key: string;
6
+ /** `#rrggbb`. */
7
+ color: string;
8
+ /** What hovering it shows ("HPF · Hippocampal formation"); default: the key. */
9
+ label?: string;
10
+ vertices: Float32Array;
11
+ faces: Uint32Array;
12
+ }
13
+ /** Where the stack sits in the volume's world: its full size, and the centred world box it fills. */
14
+ export interface VolumeFrame {
15
+ /** Image width, height (px) and slice count. */
16
+ size: [number, number, number];
17
+ /** The world box (centred on the origin), Z as currently stretched. */
18
+ world: [number, number, number];
19
+ }
20
+ /** Opacity of a region's surface in the volume. */
21
+ export declare const REGION_MESH_OPACITY = 0.55;
22
+ /** While some surfaces are highlighted: theirs (near opaque, so their shape reads) … */
23
+ export declare const REGION_MESH_HIGHLIGHT_OPACITY = 0.9;
24
+ /** … and the others' (ghosted: still there for context, not in the way). */
25
+ export declare const REGION_MESH_DIMMED_OPACITY = 0.12;
26
+ /**
27
+ * The region reconstructions shown in a volume view: one translucent surface per region, in its
28
+ * colour, placed in the volume's world box (pixel x, y and slice z mapped onto it the way the
29
+ * volume maps its voxels) and re-placed when the volume's Z is stretched. A surface is updated in
30
+ * place (`setData`, `color`) rather than added again, and picked by napari-js (`pickSurface`).
31
+ *
32
+ * Highlighting is focus + context: the highlighted surfaces turn near opaque and the others fade
33
+ * to a ghost, so the selection stands out while staying in place among its neighbours.
34
+ */
35
+ export declare class VolumeRegionMeshes {
36
+ private readonly viewer;
37
+ private readonly layers;
38
+ private highlighted;
39
+ private hidden;
40
+ /** The meshes to show (kept while the volume has no frame yet). */
41
+ private wanted;
42
+ private frame;
43
+ constructor(viewer: Viewer);
44
+ /** Show exactly `meshes` (adding, keeping or removing surfaces). */
45
+ set(meshes: readonly RegionMesh3D[]): void;
46
+ /** Follow the volume's frame (it opened, or its Z was stretched): re-place every surface. */
47
+ setFrame(frame: VolumeFrame): void;
48
+ /** Highlight the surfaces of these keys (none: every surface as usual). */
49
+ setHighlight(keys: Iterable<string>): void;
50
+ /** Hide the surfaces of these keys (they are kept, so showing them again is instant). */
51
+ setHidden(keys: Iterable<string>): void;
52
+ /** The front-most surface under screen point (`x`, `y`) (canvas CSS px), or null. */
53
+ pickAt(x: number, y: number): RegionMesh3D | null;
54
+ /** The meshes shown. */
55
+ get shown(): RegionMesh3D[];
56
+ clear(): void;
57
+ private sync;
58
+ private add;
59
+ private opacityOf;
60
+ private remove;
61
+ }
62
+ /**
63
+ * Image px / slice coordinates → the volume's centred world box: pixel x of a `w`-wide image
64
+ * spans the box's width, slice k sits at the middle of its share of the box's depth.
65
+ */
66
+ export declare function toWorld(vertices: Float32Array, frame: VolumeFrame): Float32Array;
@@ -4,7 +4,9 @@ import { IImageInfo } from '../../contracts/image.contract';
4
4
  import { IHistogram } from '../../contracts/channel-histogram-api.contract';
5
5
  import { IIsosurfaceControls, ISurface3dControls } from '../../contracts/visualizer.contract';
6
6
  import { Axes3dGizmo, Box3 } from './napari-axes-gizmo';
7
- import type { NapariScene, SceneContext } from './napari-scene';
7
+ import type { NapariScene, SceneContext, VolumeVoxelsOnScreen } from './napari-scene';
8
+ import { RegionMesh3D } from './napari-volume-region-meshes';
9
+ import { SlicePlanes } from './napari-volume-slice-planes';
8
10
  /**
9
11
  * NAPARI_VOLUME / NAPARI_ISOSURFACE (review Appendix B, cluster F): the stack assembled into a
10
12
  * decimated volume — one additive, tinted `VolumeLayer` per channel for a multichannel image, a
@@ -29,7 +31,49 @@ export declare class VolumeScene implements NapariScene {
29
31
  gizmo: Axes3dGizmo | null;
30
32
  private zHandle;
31
33
  private displaySub;
34
+ /** Regions' 3D reconstructions shown in the volume (see {@link setRegionMeshes}). */
35
+ private readonly regionMeshes;
36
+ /** The planes of the sections shown beside the volume, marked and draggable in it. */
37
+ private readonly slicePlanes;
38
+ /** The image's full size (px, px, slices), which reconstructions are in. */
39
+ private fullSize;
40
+ /** Clicks and hover on the reconstructions (with the selection tool). */
41
+ private meshPointer;
42
+ private toolSub;
43
+ private selecting;
32
44
  constructor(ctx: SceneContext, info: IImageInfo | undefined, rendering: 'iso' | 'mip');
45
+ /** Show these region reconstructions in the volume (replacing the ones shown). */
46
+ setRegionMeshes(meshes: readonly RegionMesh3D[]): void;
47
+ setRegionHighlight(keys: readonly string[]): void;
48
+ setRegionHidden(keys: readonly string[]): void;
49
+ /** Whether the volume lets every region through (see {@link setRegionsThroughVolume}). */
50
+ private regionsThrough;
51
+ /**
52
+ * The volume hides the region reconstructions behind what it shows (its brightest sample along
53
+ * each ray, napari-js's `'translucent'` depth) — the perspective of a region behind the
54
+ * cerebellum — or, `through`, hides none (`'translucent_no_depth'`): surfaces traced inside its
55
+ * bright structures, an OCT's layer boundaries in its bright bands, which that depth speckles
56
+ * or hides. A multichannel volume's channels add up and hide nothing either way.
57
+ */
58
+ setRegionsThroughVolume(through: boolean): void;
59
+ private applyVolumeDepth;
60
+ /** Show the planes of the sections beside the volume where they are (null: hide one). */
61
+ setSlicePlanes(planes: SlicePlanes): void;
62
+ /**
63
+ * The assembled volume's voxels per channel (key 0: the grayscale one), its frame, and whether
64
+ * it is a multichannel composite — what the sections beside it are cut from. Null before it is
65
+ * assembled.
66
+ */
67
+ volumeVoxels(): VolumeVoxelsOnScreen | null;
68
+ /**
69
+ * Picking the reconstructions: with the selection tool armed, a click on one selects its class
70
+ * (null beside them); resting over one names it, whatever the tool.
71
+ */
72
+ private listenToRegionMeshes;
73
+ /** Image px per voxel of the assembled volume (in-plane), or null before it is assembled. */
74
+ voxelStep(): number | null;
75
+ /** Place the region reconstructions in the volume's world box as it is now. */
76
+ private frameRegionMeshes;
33
77
  /**
34
78
  * Assemble and render the volume, then add the axes gizmo + labels and the Z handle and follow
35
79
  * the display state.
@@ -0,0 +1,43 @@
1
+ import type { Viewer } from 'napari-js';
2
+ import type { VolumeFrame } from './napari-volume-region-meshes';
3
+ /** A plane across the volume: x = const, y = const (image px) or z = const (a slice). */
4
+ export type PlaneAxis = 'x' | 'y' | 'z';
5
+ /** Where each plane is (image px for x and y, the slice for z); null: not shown. */
6
+ export type SlicePlanes = Record<PlaneAxis, number | null>;
7
+ /** Each plane's colour: its axis's, as the axes gizmo draws them. */
8
+ export declare const SLICE_PLANE_COLORS: Readonly<Record<PlaneAxis, string>>;
9
+ /** A plane's opacity: enough to read where it is, not to hide the volume behind it … */
10
+ export declare const SLICE_PLANE_OPACITY = 0.22;
11
+ /** … and while it is dragged. */
12
+ export declare const SLICE_PLANE_DRAG_OPACITY = 0.4;
13
+ export interface SlicePlanesOptions {
14
+ /** A plane dragged to `value` along its axis (image px, or a fractional slice). */
15
+ onMove(axis: PlaneAxis, value: number): void;
16
+ }
17
+ /**
18
+ * The planes of the sections shown beside a volume view: napari-js's {@link SlicePlanesWidget}
19
+ * (translucent quads in their axis's colour, each with a grip that drags it along its axis) over
20
+ * the stack's frame. It works in the frame's image px and slices: the widget's box is the image,
21
+ * one voxel per px, so a px position is the widget's shifted by half a pixel (pixel i spans
22
+ * [i, i + 1) here, is centred on i there); a slice is the same in both.
23
+ */
24
+ export declare class VolumeSlicePlanes {
25
+ private readonly viewer;
26
+ private readonly host;
27
+ private readonly opts;
28
+ private widget;
29
+ private frame;
30
+ private planes;
31
+ /** Set while the planes are moved from here, so the widget's echo is not reported as a drag. */
32
+ private applying;
33
+ constructor(viewer: Viewer, host: HTMLElement, opts: SlicePlanesOptions);
34
+ /** Follow the volume's frame (it opened, or its Z was stretched). */
35
+ setFrame(frame: VolumeFrame): void;
36
+ /** Show the planes where they are now (null: hide one). */
37
+ set(planes: SlicePlanes): void;
38
+ clear(): void;
39
+ destroy(): void;
40
+ /** The widget over the current frame, made when there is one and a plane to show. */
41
+ private rebuild;
42
+ private apply;
43
+ }
@@ -23,6 +23,8 @@ export interface ViewerOptionsInput {
23
23
  sliceCount: number;
24
24
  /** How many slices the slice cache keeps resident (see SliceCache.maxSlices). */
25
25
  maxSlices: number;
26
+ /** Tiled images per slice: the channel count when channels are drawn per channel, else 1. */
27
+ channelImages?: number;
26
28
  }
27
29
  /**
28
30
  * The options object handed to the OpenSeadragon factory. Pure: every value
@@ -53,6 +53,8 @@ export declare class OpenSeadragonVisualizerService extends BaseStoreVisualizer
53
53
  private viewer;
54
54
  private overlay;
55
55
  private scaleBar;
56
+ /** "Image reloading…" while the view fetches its tiles. */
57
+ private tileLoading;
56
58
  private descriptor;
57
59
  /** Base64 RawFileInfo + current z-slice, kept so setZIndex can rebuild the
58
60
  * tile source and swap slices live (stack navigation). */
@@ -1,5 +1,6 @@
1
1
  import { Region } from '../../models/region';
2
2
  import { ScreenLayer } from '../../region-overlay/svg-region-renderer';
3
+ import { RegionStyle } from '../../region-overlay/region-style';
3
4
  /**
4
5
  * The OpenSeadragon region overlay's look: region stroke/fill, labels, the
5
6
  * selected region's handles, the marquee and the in-progress drawing. Pure SVG
@@ -12,14 +13,14 @@ type Pt = {
12
13
  };
13
14
  /** `color` with `alpha`; a colour that doesn't parse (a named one) is used as-is. */
14
15
  export declare function rgba(color: string, alpha: number): string;
15
- /** Stroke/fill for a region shape (drawn in image coordinates by the renderer). */
16
- export declare function styleRegionShape(el: SVGElement, region: Region, selected: boolean, shapeColor: string): void;
16
+ /** Stroke/fill for a region shape (drawn in image coordinates by the renderer), in `style`. */
17
+ export declare function styleRegionShape(el: SVGElement, region: Region, selected: boolean, shapeColor: string, style?: RegionStyle): void;
17
18
  /**
18
19
  * The class label (legend) just above the region's top-left — mirrors Plotly's
19
20
  * per-shape label, drawn in the region's colour. region.label is restored from
20
21
  * the shape's legend in getRegion().
21
22
  */
22
- export declare function drawRegionLabel(layer: ScreenLayer, region: Region, shapeColor: string): void;
23
+ export declare function drawRegionLabel(layer: ScreenLayer, region: Region, shapeColor: string, style?: RegionStyle): void;
23
24
  /** A small vertex handle (filled when emphasised, hollow otherwise) at an image point. */
24
25
  export declare function vertexMarker(layer: ScreenLayer, x: number, y: number, emphasised: boolean, color: string): void;
25
26
  /**
@@ -1,5 +1,5 @@
1
1
  import { IRegionStore } from '../../contracts/visualizer.contract';
2
- import { IRegionEditApi } from '../../contracts/region-store.contract';
2
+ import { IRegionEditApi, IRegionSourceApi } from '../../contracts/region-store.contract';
3
3
  import { IRegionOverlay, RegionToolMode } from '../../contracts/region-overlay.contract';
4
4
  import { OsdViewerLike } from './osd-viewer-like';
5
5
  /**
@@ -8,9 +8,10 @@ import { OsdViewerLike } from './osd-viewer-like';
8
8
  * {@link IRegionEditApi} (add/move/resize/vertex edits). The shared RegionStore
9
9
  * satisfies both. The overlay edits through these typed operations on the
10
10
  * neutral Region model — it never touches a backend's own shape representation
11
- * (e.g. Plotly's `x0/y0/x1/y1` / `M…L…Z` dicts).
11
+ * (e.g. Plotly's `x0/y0/x1/y1` / `M…L…Z` dicts). With {@link IRegionSourceApi}
12
+ * (optional) it also draws and picks the image's region source.
12
13
  */
13
- export type RegionEditStore = IRegionStore & IRegionEditApi;
14
+ export type RegionEditStore = IRegionStore & IRegionEditApi & Partial<IRegionSourceApi>;
14
15
  /**
15
16
  * OpenSeadragon implementation of {@link IRegionOverlay}.
16
17
  *
@@ -62,6 +63,8 @@ export declare class OsdRegionOverlay implements IRegionOverlay {
62
63
  * projection, and so the DOM, untouched. */
63
64
  private readonly cameraHandler;
64
65
  private readonly renderer;
66
+ /** The image's region source, drawn under the regions at the zoom's level of detail. */
67
+ private readonly sourceLayer;
65
68
  /** OSD's MouseTracker drops a cancelled pointer (touch interrupted, pointer
66
69
  * capture lost to the browser) without calling releaseHandler, so the
67
70
  * gesture is ended here instead. */
@@ -152,8 +155,12 @@ export declare class OsdRegionOverlay implements IRegionOverlay {
152
155
  */
153
156
  private applyEdit;
154
157
  private selectAt;
158
+ /** Take the region-source features the band meets into the regions, adding them to the selection. */
159
+ private selectSourceIn;
155
160
  /** Index of the topmost region under an image point, or -1. */
156
161
  private regionIndexAt;
162
+ /** Whether a region's class is hidden (not drawn, not picked). */
163
+ private hidden;
157
164
  /** Whether the element-pixel `position` lands on `region` (see `regionContains`). */
158
165
  private containsPoint;
159
166
  /** Abandon whatever pointer gesture is in progress (drawing, edit drag,
@@ -0,0 +1,43 @@
1
+ /** What it needs of the viewer. */
2
+ interface LoadingViewer {
3
+ element: HTMLElement;
4
+ world: {
5
+ getItemCount(): number;
6
+ getItemAt(i: number): unknown;
7
+ addHandler(event: 'add-item' | 'remove-item', handler: (e: {
8
+ item: unknown;
9
+ }) => void): unknown;
10
+ removeHandler(event: 'add-item' | 'remove-item', handler: (e: {
11
+ item: unknown;
12
+ }) => void): unknown;
13
+ };
14
+ }
15
+ /**
16
+ * "Image reloading…" over the OpenSeadragon view while it fetches the tiles of what it shows, as
17
+ * napari's views show it for their image, cells and transcripts (the same badge). Driven by each
18
+ * world item's own `fully-loaded-change` (an image, a stack slice, a channel), so it covers every
19
+ * tile source alike, and shown only after a short delay, so tiles served from cache never flash it.
20
+ *
21
+ * Only items on screen count (opacity above 0): a hidden channel loads nothing by design (it is
22
+ * not preloaded), so it never becomes fully loaded, and a slice cached or loading in the
23
+ * background is not what the view shows. An item is weighed again when its opacity changes.
24
+ */
25
+ export declare class OsdTileLoading {
26
+ private readonly viewer;
27
+ private readonly state;
28
+ /** Each item still loading, with the function that un-counts it. */
29
+ private readonly loading;
30
+ private readonly watched;
31
+ private readonly onAdd;
32
+ private readonly onRemove;
33
+ constructor(viewer: LoadingViewer);
34
+ /** The badge's text (shown, or about to be). */
35
+ get text(): string;
36
+ destroy(): void;
37
+ private watch;
38
+ private unwatch;
39
+ /** Whether the item is on screen and still fetching its tiles. */
40
+ private isLoading;
41
+ private set;
42
+ }
43
+ export {};
@@ -48,6 +48,18 @@ export interface OsdViewerLike {
48
48
  /** OSD's own pan/zoom (drag + wheel); region tools switch it off. */
49
49
  setMouseNavEnabled(enabled: boolean): unknown;
50
50
  isMouseNavEnabled(): boolean;
51
+ /** OSD's tile request queue: the jobs not started yet (see OsdQueuedTileJob). */
52
+ imageLoader?: {
53
+ jobQueue?: unknown[];
54
+ };
55
+ }
56
+ /** A tile request waiting in OSD's queue: `abort` (before it starts) only marks the tile as
57
+ * not loading, so it is requested again when it is needed. */
58
+ export interface OsdQueuedTileJob {
59
+ tile?: {
60
+ tiledImage?: unknown;
61
+ };
62
+ abort?: () => void;
51
63
  }
52
64
  /** A point in any OSD space (viewer-element px, viewport units or image px —
53
65
  * the method it comes from says which). */
@@ -70,6 +82,8 @@ export interface OsdTiledImageLike {
70
82
  imageToViewportRectangle(r: OpenSeadragon.Rect): OpenSeadragon.Rect;
71
83
  viewportToImageRectangle(r: OsdRectLike): OsdRectLike;
72
84
  setOpacity(opacity: number): void;
85
+ /** Load tiles even while hidden (opacity 0); without it a hidden item loads nothing. */
86
+ setPreload?(preload: boolean): void;
73
87
  /** False while any of the image's tiles are still loading. */
74
88
  getFullyLoaded(): boolean;
75
89
  /** Re-run the tile pipeline (restore + re-process) for this image's tiles;
@@ -106,14 +106,22 @@ export declare class SliceCache {
106
106
  /**
107
107
  * Add slice z's channel group: one additively-composited ('lighter' = Fiji
108
108
  * "Composite") TiledImage per channel, hidden (opacity 0) until revealed.
109
- * Each is preloaded so the background loader can pre-fill every slice.
109
+ * The visible channels' are preloaded, so the background loader can pre-fill
110
+ * every slice; a hidden channel's loads nothing until it is shown (a 22-channel
111
+ * plate fetched and decoded every channel's tiles, overflowing the tile cache:
112
+ * tiles evicted mid-conversion failed to decode).
110
113
  * Cached so a revisit is an instant opacity toggle — no re-fetch, no
111
114
  * white-flicker. Mirrors addSlice for the composite path.
112
115
  */
113
116
  addChannelSlice(z: number): void;
114
117
  /** Reveal slice z's channel group (each visible channel → opacity 1) and
115
- * hide every other cached slice's group. Instant; no fetch/decode. */
118
+ * hide every other cached slice's group. Instant; no fetch/decode. Only the
119
+ * visible channels keep loading (preload): a hidden channel's tiles are not
120
+ * fetched or decoded until it is shown. */
116
121
  revealChannelSlice(z: number): void;
122
+ /** Drop the tile requests still waiting in OSD's queue for `items` (channels just hidden), so
123
+ * the visible channels' tiles are not stuck behind them. A request already sent completes. */
124
+ private dropQueuedTiles;
117
125
  /** Show the given composite slice (opacity 1) and hide all other cached slices. */
118
126
  private showOnlySlice;
119
127
  /** Re-tint after a display change: invalidate ONLY the visible slice's
@@ -154,7 +162,9 @@ export declare class SliceCache {
154
162
  private sliceCacheHas;
155
163
  /** The cached TiledImage(s) for slice z (0–1 for composite, N for multichannel). */
156
164
  private sliceGroup;
157
- /** Whether slice z's tiles are still streaming (any cached image not fully loaded). */
165
+ /** Whether slice z's tiles are still streaming (any cached image not fully loaded). A hidden
166
+ * channel loads nothing, so it never becomes fully loaded: only the visible channels count, or
167
+ * the background loader would wait on it forever. */
158
168
  private sliceTilesLoading;
159
169
  /** Remove slice z's cached image(s) from the world and the active cache. */
160
170
  private removeCachedSlice;
@@ -38,6 +38,9 @@ export interface TileDescriptor {
38
38
  /** Physical pixel size in µm (0 when the format doesn't report it). */
39
39
  mppX?: number;
40
40
  mppY?: number;
41
+ /** Spacing between z slices in µm (0 or absent when the format doesn't report it): what
42
+ * lets a z-stack be drawn as a volume to scale. */
43
+ mppZ?: number;
41
44
  }
42
45
  /** Tile coordinates for one `/tile` request. `channel == null` (or omitted)
43
46
  * requests the server-composited tile; an index (including 0) requests that
@@ -38,6 +38,13 @@ export interface PresetSet {
38
38
  autoPromote: boolean;
39
39
  /** Label matching against `Region.label` (default `'exact'`). */
40
40
  matchMode: MatchMode;
41
+ /**
42
+ * Class colours that imported GeoJSON carried for classes with no preset, keyed by the
43
+ * match mode's key (see `presetKey`). They come before the fallback palette, so an imported
44
+ * file keeps its own colours. Session-only: never persisted, and absent from a set a caller
45
+ * saves or exports.
46
+ */
47
+ importedColors?: Readonly<Record<string, string>>;
41
48
  }
42
49
  /**
43
50
  * Fallback palette for classes not present in the preset set — reasonably distinct hues.
@@ -38,3 +38,8 @@ export declare function replaceBounds(existing: Region, bounds: Region['bounds']
38
38
  * are rounded to whole pixels.
39
39
  */
40
40
  export declare function regionPolygons(region: Region): Polygon[];
41
+ /**
42
+ * The open polylines a region traces (a polyline, or a multi-polygon's open parts), as interleaved
43
+ * `x, y` image px; a bézier polyline as its flattened curve. Closed shapes yield nothing.
44
+ */
45
+ export declare function regionPolylines(region: Region): Float64Array[];
@@ -5,13 +5,53 @@ import { Region } from './region';
5
5
  * coordinates.
6
6
  */
7
7
  export declare function regionsFromGeoJson(geoJsonStr: string): Region[];
8
+ /** A feature's `classification`: QuPath's `{name, color}`, or a bare name. */
9
+ type Classification = string | {
10
+ name?: string;
11
+ color?: number[];
12
+ };
13
+ /** The parts of a GeoJSON feature the reader looks at, JIT's bezier extras
14
+ * included. Parsed from a file, so nothing here is guaranteed. */
15
+ export interface GeoJsonFeature {
16
+ id?: string | number;
17
+ geometry: {
18
+ type?: string;
19
+ coordinates?: unknown;
20
+ plane?: {
21
+ z?: number;
22
+ };
23
+ };
24
+ properties?: {
25
+ id?: string | number;
26
+ classification?: Classification;
27
+ isBezier?: boolean;
28
+ bezierAnchors?: number[][];
29
+ bezierHandlesIn?: number[][];
30
+ bezierHandlesOut?: number[][];
31
+ holes?: number[][][];
32
+ holeHandlesIn?: number[][][];
33
+ holeHandlesOut?: number[][][];
34
+ };
35
+ }
36
+ /**
37
+ * One GeoJSON feature as the region `shape<idx>`. Throws on a feature without coordinates.
38
+ * The feature's id becomes {@link Region.sourceId} (or `sourceId`, when given: a region source's
39
+ * id for the feature) and its other properties {@link Region.properties}.
40
+ */
41
+ export declare function regionFromGeoJsonFeature(feature: GeoJsonFeature, idx: number, sourceId?: string): Region;
8
42
  /**
9
43
  * Serialise regions as a GeoJSON FeatureCollection string (QuPath-compatible),
10
44
  * leaving out intensity-profile lines.
11
45
  */
12
46
  export declare function regionsToGeoJson(rois: readonly Region[]): string;
47
+ /**
48
+ * A region as a GeoJSON feature; null for a region without bounds. Its {@link Region.sourceId}
49
+ * is the feature's `id` and its {@link Region.properties} come along beside the classification.
50
+ */
51
+ export declare function featureFromRegion(roi: Region): object | null;
13
52
  /**
14
53
  * Download a GeoJSON string as `<baseName without extension>.geojson`
15
54
  * (`rois.geojson` without a base name).
16
55
  */
17
56
  export declare function downloadGeoJson(jsonString: string, baseName?: string): void;
57
+ export {};
@@ -35,6 +35,25 @@ export declare class Region {
35
35
  * through the store, so the marker has to travel with the data.
36
36
  */
37
37
  source?: string;
38
+ /**
39
+ * The id of the GeoJSON feature this region was read from (the feature's `id`, else
40
+ * `properties.id`; for a tiled region source, else its position in the file). Written back
41
+ * as the feature's `id` on export, so a region keeps its identity through a save. A region
42
+ * taken out of a region source for editing carries it, and the source stops drawing that
43
+ * feature while it does.
44
+ */
45
+ sourceId?: string;
46
+ /**
47
+ * A region source feature's area (image px²) while it stands in for the feature in the Region
48
+ * Editor's table: such a row has no geometry until the feature is taken.
49
+ */
50
+ sourceArea?: number;
51
+ /**
52
+ * The feature's other GeoJSON properties (everything but the ones SIV reads and writes:
53
+ * `classification`, `id` and the bézier fields), carried through untouched so a save keeps
54
+ * them, e.g. an atlas structure's acronym and id.
55
+ */
56
+ properties?: Readonly<Record<string, unknown>>;
38
57
  color?: string;
39
58
  label?: string;
40
59
  /** True when the user set this region's colour explicitly (Region Editor Color
@@ -0,0 +1,78 @@
1
+ import { ElementRef, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
2
+ import type { RegionClassNode } from '../../region-source/region-tiles';
3
+ import { ClassTree, ClassTreeRow } from './class-tree';
4
+ import * as i0 from "@angular/core";
5
+ /** A pick in the tree: the class, and it with every class under it. */
6
+ export interface ClassTreePick {
7
+ name: string;
8
+ classes: string[];
9
+ additive: boolean;
10
+ }
11
+ /** Whether a class is shown: it and its subclasses all, none ('hidden'), or only some ('partial'). */
12
+ export type ClassVisibility = 'shown' | 'hidden' | 'partial';
13
+ /**
14
+ * The Region Editor's classes as a hierarchy (a brain atlas's ontology), as the Allen atlas viewer
15
+ * shows it: collapsible, each class with its colour, name and long name, and the regions it and
16
+ * its subclasses have on this slice; a class with none is greyed. Picking a class selects all its
17
+ * regions and its subclasses' (the editor does the selecting). Search by name or long name. A
18
+ * class's swatch recolours it, as the class list's does; its eye hides it and its subclasses.
19
+ */
20
+ export declare class ClassTreeComponent implements OnChanges {
21
+ private readonly el;
22
+ /** The hierarchy. */
23
+ nodes: readonly RegionClassNode[];
24
+ /** Regions per class on this slice (the regions and the region source's). */
25
+ counts: ReadonlyMap<string, number>;
26
+ /** A class's colour (a new function when the colours change, so the rows repaint). */
27
+ colorOf: (name: string) => string;
28
+ /** The class picked last (highlighted, its path expanded, scrolled into view). */
29
+ picked: string | null;
30
+ /** Classes with a selected region (marked). */
31
+ marked: ReadonlySet<string>;
32
+ /** Whether a volume view is on screen: each class can then be shown in 3D. */
33
+ volume: boolean;
34
+ /** The classes shown in 3D. */
35
+ shown3d: ReadonlySet<string>;
36
+ /** The classes hidden from view. */
37
+ hidden: ReadonlySet<string>;
38
+ pick: EventEmitter<ClassTreePick>;
39
+ /** A class's swatch picked a new colour. */
40
+ recolor: EventEmitter<{
41
+ name: string;
42
+ color: string;
43
+ }>;
44
+ /** A class's 3D toggle: it with every class under it. */
45
+ toggle3d: EventEmitter<{
46
+ name: string;
47
+ classes: string[];
48
+ }>;
49
+ /** A class's eye: hide (or show) it with every class under it. */
50
+ toggleHidden: EventEmitter<{
51
+ classes: string[];
52
+ hidden: boolean;
53
+ }>;
54
+ protected tree: ClassTree;
55
+ protected rows: ClassTreeRow[];
56
+ /** Each row's visibility (a row absent: shown). */
57
+ protected visibility: Map<string, ClassVisibility>;
58
+ protected query: string;
59
+ private expanded;
60
+ constructor(el: ElementRef<HTMLElement>);
61
+ ngOnChanges(changes: SimpleChanges): void;
62
+ protected node(name: string): RegionClassNode | undefined;
63
+ protected total(name: string): number;
64
+ protected toggle(row: ClassTreeRow, event: Event): void;
65
+ protected choose(name: string, event: Event): void;
66
+ /** The eye: a class hidden (wholly) is shown again with its subclasses; otherwise they are hidden. */
67
+ protected flipHidden(name: string, event: Event): void;
68
+ protected flip3d(name: string, event: Event): void;
69
+ protected onQuery(value: string): void;
70
+ /** Enter in the search box picks the first match. */
71
+ protected pickFirstMatch(event: Event): void;
72
+ protected readonly trackRow: (_: number, r: ClassTreeRow) => string;
73
+ private refresh;
74
+ static ɵfac: i0.ɵɵFactoryDeclaration<ClassTreeComponent, never>;
75
+ static ɵcmp: i0.ɵɵComponentDeclaration<ClassTreeComponent, "region-class-tree", never, { "nodes": { "alias": "nodes"; "required": false; }; "counts": { "alias": "counts"; "required": false; }; "colorOf": { "alias": "colorOf"; "required": false; }; "picked": { "alias": "picked"; "required": false; }; "marked": { "alias": "marked"; "required": false; }; "volume": { "alias": "volume"; "required": false; }; "shown3d": { "alias": "shown3d"; "required": false; }; "hidden": { "alias": "hidden"; "required": false; }; }, { "pick": "pick"; "recolor": "recolor"; "toggle3d": "toggle3d"; "toggleHidden": "toggleHidden"; }, never, never, true, never>;
76
+ }
77
+ /** Scroll the row matching `selector` inside `host` into view, if it is out of it. */
78
+ export declare function scrollToPicked(host: HTMLElement, selector: string): void;