@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.
- package/esm2022/index.mjs +10 -1
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/contracts/ports/region-io.port.mjs +1 -1
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
- package/esm2022/lib/contracts/region-store.contract.mjs +1 -1
- package/esm2022/lib/contracts/section-view.contract.mjs +2 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/contracts/viz-config.mjs +1 -1
- package/esm2022/lib/geometry/loft.mjs +128 -0
- package/esm2022/lib/geometry/polylabel.mjs +156 -0
- package/esm2022/lib/geometry/reconstruct.mjs +315 -0
- package/esm2022/lib/geometry/reslice-image.mjs +57 -0
- package/esm2022/lib/geometry/section.mjs +215 -0
- package/esm2022/lib/implementations/napari-js/napari-loading-badge.mjs +11 -7
- package/esm2022/lib/implementations/napari-js/napari-loading-state.mjs +20 -3
- package/esm2022/lib/implementations/napari-js/napari-region-mesh-pointer.mjs +100 -0
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +28 -5
- package/esm2022/lib/implementations/napari-js/napari-scene.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-hover.mjs +9 -2
- package/esm2022/lib/implementations/napari-js/napari-tool-bridge.mjs +5 -2
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +81 -1
- package/esm2022/lib/implementations/napari-js/napari-volume-region-meshes.mjs +129 -0
- package/esm2022/lib/implementations/napari-js/napari-volume-scene.mjs +115 -4
- package/esm2022/lib/implementations/napari-js/napari-volume-slice-planes.mjs +103 -0
- package/esm2022/lib/implementations/napari-js/region-overlay/region-svg-renderer.mjs +15 -3
- package/esm2022/lib/implementations/osd/openseadragon-viewer-options.mjs +18 -2
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +9 -1
- package/esm2022/lib/implementations/osd/osd-region-overlay-draw.mjs +18 -7
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +38 -8
- package/esm2022/lib/implementations/osd/osd-tile-loading.mjs +71 -0
- package/esm2022/lib/implementations/osd/osd-viewer-like.mjs +1 -1
- package/esm2022/lib/implementations/osd/slice-cache.mjs +35 -6
- package/esm2022/lib/implementations/tile-server/tile-protocol.mjs +1 -1
- package/esm2022/lib/models/class-preset.mjs +1 -1
- package/esm2022/lib/models/polygon-factory.mjs +17 -1
- package/esm2022/lib/models/region-geojson.mjs +44 -6
- package/esm2022/lib/models/region.mjs +1 -1
- package/esm2022/lib/region-editor/class-tree/class-tree.component.mjs +156 -0
- package/esm2022/lib/region-editor/class-tree/class-tree.mjs +108 -0
- package/esm2022/lib/region-editor/classes-panel/classes-panel.component.mjs +77 -13
- package/esm2022/lib/region-editor/region-editor.component.mjs +670 -42
- package/esm2022/lib/region-editor/region-metrics.mjs +20 -2
- package/esm2022/lib/region-editor/region-persistence.service.mjs +44 -10
- package/esm2022/lib/region-editor/region-table/region-table.component.mjs +24 -8
- package/esm2022/lib/region-import/label-classes.mjs +149 -0
- package/esm2022/lib/region-import/label-volume.mjs +194 -0
- package/esm2022/lib/region-import/region-import.mjs +71 -0
- package/esm2022/lib/region-overlay/region-geometry.mjs +3 -3
- package/esm2022/lib/region-overlay/region-source-layer.mjs +378 -0
- package/esm2022/lib/region-overlay/region-style.mjs +97 -0
- package/esm2022/lib/region-source/http-region-source.mjs +104 -0
- package/esm2022/lib/region-source/region-source-ops.mjs +53 -0
- package/esm2022/lib/region-source/region-source-worker.mjs +10 -0
- package/esm2022/lib/region-source/region-source.mjs +67 -0
- package/esm2022/lib/region-source/region-tiles.mjs +708 -0
- package/esm2022/lib/region-source/worker-region-source.mjs +229 -0
- package/esm2022/lib/routing-visualizer.service.mjs +510 -22
- package/esm2022/lib/section-pane/section-pane.component.mjs +299 -0
- package/esm2022/lib/store/class-color.util.mjs +10 -5
- package/esm2022/lib/store/class-signatures.mjs +63 -0
- package/esm2022/lib/store/region-scope-cache.mjs +5 -1
- package/esm2022/lib/store/region-source-scope.mjs +186 -0
- package/esm2022/lib/store/region-store.service.mjs +774 -12
- package/esm2022/lib/store/visualizer-store.service.mjs +33 -6
- package/esm2022/lib/toolbar/region-tools/region-tools.component.mjs +8 -2
- package/esm2022/lib/toolbar/stack-controls/stack-controls.component.mjs +55 -15
- package/esm2022/lib/toolbar/toolbar.component.mjs +19 -5
- package/esm2022/lib/visualizer/region-load.mjs +67 -4
- package/esm2022/lib/visualizer/volume-sections.mjs +175 -0
- package/esm2022/lib/visualizer.component.mjs +155 -7
- 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
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-BELcx0jv.mjs.map +1 -0
- 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
- 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
- package/fesm2022/jax-data-science-sci-image-visualizer-region-source-worker-CAqBjM5L.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-region-source-worker-CAqBjM5L.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +3 -3
- package/fesm2022/region-source.worker.js +1067 -0
- package/index.d.ts +12 -0
- package/lib/contracts/image.contract.d.ts +4 -0
- package/lib/contracts/ports/region-io.port.d.ts +24 -0
- package/lib/contracts/region-editor-api.contract.d.ts +102 -0
- package/lib/contracts/region-store.contract.d.ts +22 -0
- package/lib/contracts/section-view.contract.d.ts +44 -0
- package/lib/contracts/visualizer.contract.d.ts +10 -0
- package/lib/contracts/viz-config.d.ts +8 -0
- package/lib/geometry/loft.d.ts +22 -0
- package/lib/geometry/polylabel.d.ts +23 -0
- package/lib/geometry/reconstruct.d.ts +54 -0
- package/lib/geometry/reslice-image.d.ts +33 -0
- package/lib/geometry/section.d.ts +52 -0
- package/lib/implementations/napari-js/napari-loading-badge.d.ts +4 -2
- package/lib/implementations/napari-js/napari-loading-state.d.ts +8 -1
- package/lib/implementations/napari-js/napari-region-mesh-pointer.d.ts +32 -0
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +6 -1
- package/lib/implementations/napari-js/napari-scene.d.ts +33 -0
- package/lib/implementations/napari-js/napari-spatial-hover.d.ts +3 -1
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +48 -0
- package/lib/implementations/napari-js/napari-volume-region-meshes.d.ts +66 -0
- package/lib/implementations/napari-js/napari-volume-scene.d.ts +45 -1
- package/lib/implementations/napari-js/napari-volume-slice-planes.d.ts +43 -0
- package/lib/implementations/osd/openseadragon-viewer-options.d.ts +2 -0
- package/lib/implementations/osd/openseadragon-visualizer.service.d.ts +2 -0
- package/lib/implementations/osd/osd-region-overlay-draw.d.ts +4 -3
- package/lib/implementations/osd/osd-region-overlay.d.ts +10 -3
- package/lib/implementations/osd/osd-tile-loading.d.ts +43 -0
- package/lib/implementations/osd/osd-viewer-like.d.ts +14 -0
- package/lib/implementations/osd/slice-cache.d.ts +13 -3
- package/lib/implementations/tile-server/tile-protocol.d.ts +3 -0
- package/lib/models/class-preset.d.ts +7 -0
- package/lib/models/polygon-factory.d.ts +5 -0
- package/lib/models/region-geojson.d.ts +40 -0
- package/lib/models/region.d.ts +19 -0
- package/lib/region-editor/class-tree/class-tree.component.d.ts +78 -0
- package/lib/region-editor/class-tree/class-tree.d.ts +31 -0
- package/lib/region-editor/classes-panel/classes-panel.component.d.ts +36 -6
- package/lib/region-editor/region-editor.component.d.ts +159 -17
- package/lib/region-editor/region-metrics.d.ts +8 -0
- package/lib/region-editor/region-persistence.service.d.ts +17 -5
- package/lib/region-editor/region-table/region-table.component.d.ts +10 -4
- package/lib/region-import/label-classes.d.ts +39 -0
- package/lib/region-import/label-volume.d.ts +31 -0
- package/lib/region-import/region-import.d.ts +46 -0
- package/lib/region-overlay/region-geometry.d.ts +4 -3
- package/lib/region-overlay/region-source-layer.d.ts +96 -0
- package/lib/region-overlay/region-style.d.ts +52 -0
- package/lib/region-source/http-region-source.d.ts +45 -0
- package/lib/region-source/region-source-ops.d.ts +89 -0
- package/lib/region-source/region-source-worker.d.ts +7 -0
- package/lib/region-source/region-source.d.ts +82 -0
- package/lib/region-source/region-tiles.d.ts +289 -0
- package/lib/region-source/worker-region-source.d.ts +65 -0
- package/lib/routing-visualizer.service.d.ts +168 -1
- package/lib/section-pane/section-pane.component.d.ts +47 -0
- package/lib/store/class-color.util.d.ts +6 -3
- package/lib/store/class-signatures.d.ts +11 -0
- package/lib/store/region-scope-cache.d.ts +2 -0
- package/lib/store/region-source-scope.d.ts +74 -0
- package/lib/store/region-store.service.d.ts +216 -2
- package/lib/store/visualizer-store.service.d.ts +10 -0
- package/lib/toolbar/region-tools/region-tools.component.d.ts +3 -1
- package/lib/toolbar/stack-controls/stack-controls.component.d.ts +20 -9
- package/lib/toolbar/toolbar.component.d.ts +9 -2
- package/lib/visualizer/region-load.d.ts +23 -4
- package/lib/visualizer/volume-sections.d.ts +64 -0
- package/lib/visualizer.component.d.ts +40 -0
- package/package.json +2 -2
- package/src/lib/assets/slice.svg +23 -0
- 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:
|
|
10
|
-
* slices in place
|
|
11
|
-
*
|
|
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
|
-
/**
|
|
32
|
+
/** The heatmap's slice bar moved to a slice (the host shows its number). */
|
|
33
33
|
zIndexInput: EventEmitter<number>;
|
|
34
|
-
/**
|
|
34
|
+
/** The heatmap's slice bar settled: re-plot at that slice. */
|
|
35
35
|
reloadAndPlot: EventEmitter<void>;
|
|
36
|
-
/**
|
|
37
|
-
|
|
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.
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
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
|
+
|