@jax-data-science/sci-image-visualizer 0.3.3 → 0.4.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/README.md +208 -4
- package/esm2022/index.mjs +20 -1
- package/esm2022/lib/contracts/display-types.mjs +20 -2
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/contracts/plot-type.mjs +36 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -0
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +45 -0
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +10 -19
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +71 -7
- package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +96 -0
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +1934 -55
- package/esm2022/lib/implementations/napari-js/napari-zoom.mjs +18 -0
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +5 -3
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +3 -4
- package/esm2022/lib/implementations/osd/osd-zoom.mjs +20 -0
- package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +555 -0
- package/esm2022/lib/implementations/plotly/plotly.service.mjs +13 -7
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +304 -0
- package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +179 -0
- package/esm2022/lib/routing-visualizer.service.mjs +223 -12
- package/esm2022/lib/spatial/embedding-compute.mjs +142 -0
- package/esm2022/lib/spatial/gene-search.mjs +77 -0
- package/esm2022/lib/spatial/spatial-density.mjs +224 -0
- package/esm2022/lib/spatial/spatial-encoding.mjs +232 -0
- package/esm2022/lib/spatial/spatial-expression.mjs +257 -0
- package/esm2022/lib/spatial/spatial-framing.mjs +60 -0
- package/esm2022/lib/spatial/spatial-heatmap.mjs +185 -0
- package/esm2022/lib/spatial/spatial-hover.mjs +79 -0
- package/esm2022/lib/spatial/spatial-sections.mjs +96 -0
- package/esm2022/lib/spatial/spatial-selection.mjs +264 -0
- package/esm2022/lib/spatial/spatial-volume-image.mjs +163 -0
- package/esm2022/lib/spatial/tsne-worker.mjs +10 -0
- package/esm2022/lib/spatial/world-grid.mjs +77 -0
- package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +1278 -0
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +621 -0
- package/esm2022/lib/store/spatial-selection.service.mjs +47 -0
- package/esm2022/lib/store/visualizer-store.service.mjs +23 -1
- package/esm2022/lib/toolbar/toolbar.component.mjs +30 -7
- package/esm2022/lib/visualization.module.mjs +23 -5
- package/esm2022/lib/visualizer.component.mjs +283 -22
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs} +6 -4
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-ci1q0xnJ.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-BwP-A11m.mjs.map} +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
- package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15769 -8316
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/fesm2022/tsne.worker.js +19091 -0
- package/index.d.ts +15 -0
- package/lib/contracts/display-types.d.ts +131 -0
- package/lib/contracts/image.contract.d.ts +7 -0
- package/lib/contracts/plot-type.d.ts +42 -2
- package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
- package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
- package/lib/contracts/visualizer.contract.d.ts +99 -1
- package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
- package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
- package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
- package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
- package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
- package/lib/implementations/osd/osd-zoom.d.ts +19 -0
- package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
- package/lib/implementations/plotly/plotly.service.d.ts +9 -0
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
- package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
- package/lib/routing-visualizer.service.d.ts +45 -6
- package/lib/spatial/embedding-compute.d.ts +73 -0
- package/lib/spatial/gene-search.d.ts +48 -0
- package/lib/spatial/spatial-density.d.ts +73 -0
- package/lib/spatial/spatial-encoding.d.ts +155 -0
- package/lib/spatial/spatial-expression.d.ts +144 -0
- package/lib/spatial/spatial-framing.d.ts +37 -0
- package/lib/spatial/spatial-heatmap.d.ts +111 -0
- package/lib/spatial/spatial-hover.d.ts +49 -0
- package/lib/spatial/spatial-sections.d.ts +59 -0
- package/lib/spatial/spatial-selection.d.ts +113 -0
- package/lib/spatial/spatial-volume-image.d.ts +67 -0
- package/lib/spatial/tsne-worker.d.ts +7 -0
- package/lib/spatial/world-grid.d.ts +58 -0
- package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
- package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
- package/lib/store/spatial-selection.service.d.ts +27 -0
- package/lib/store/visualizer-store.service.d.ts +13 -0
- package/lib/toolbar/toolbar.component.d.ts +19 -3
- package/lib/visualization.module.d.ts +28 -24
- package/lib/visualizer.component.d.ts +73 -2
- package/package.json +6 -2
- package/src/lib/assets/spatial-omics-3d.svg +31 -0
- package/src/lib/assets/spatial-omics.svg +27 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
|
|
2
|
+
import { IVisualizer, ISpatialControls } from '../contracts/visualizer.contract';
|
|
3
|
+
import { SpatialDataset } from '../contracts/spatial-dataset.contract';
|
|
4
|
+
import { ColormapNode, SpatialViewState } from '../contracts/display-types';
|
|
5
|
+
import { SpatialSelectionMask } from '../spatial/spatial-selection';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
/** One legend row for a categorical colouring. */
|
|
8
|
+
export interface SpatialLegendEntry {
|
|
9
|
+
label: string;
|
|
10
|
+
color: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Spatial-omics controls: a non-modal, resizable, draggable dialog for choosing
|
|
14
|
+
* what the observation markers are coloured by, and how they are drawn.
|
|
15
|
+
*
|
|
16
|
+
* Depends only on {@link ISpatialControls}, reached through the `VISUALIZER`
|
|
17
|
+
* contract token — never a concrete backend — mirroring how
|
|
18
|
+
* `ChannelHistogramComponent` depends only on `CHANNEL_HISTOGRAM_API`. When no
|
|
19
|
+
* host has bound `SPATIAL_DATA_PORT`, `getSpatialControls()` returns null and
|
|
20
|
+
* the dialog renders an explanatory empty state rather than dead controls.
|
|
21
|
+
*
|
|
22
|
+
* The legend swatches and the continuous colour bar are built with the SAME
|
|
23
|
+
* functions the renderer uses (`categoryColors` → `resolveCategoryColors`,
|
|
24
|
+
* `spatialContinuousLut`), so the key cannot drift from what is on screen.
|
|
25
|
+
*/
|
|
26
|
+
export declare class SpatialControlsComponent implements OnInit, OnDestroy {
|
|
27
|
+
private readonly viz;
|
|
28
|
+
/** The embedded chart, so a dialog resize can re-fit it. */
|
|
29
|
+
private charts?;
|
|
30
|
+
/**
|
|
31
|
+
* Re-fit the chart after the dialog is resized.
|
|
32
|
+
*
|
|
33
|
+
* Plotly does not follow a container resize on its own — its `responsive`
|
|
34
|
+
* option listens for WINDOW resizes only, so dragging the dialog's handle
|
|
35
|
+
* reaches it through nothing. PrimeNG tells us when the drag ends, which is a
|
|
36
|
+
* far more direct signal than watching boxes, and firing once on release means
|
|
37
|
+
* no relayout churn during the drag.
|
|
38
|
+
*/
|
|
39
|
+
onResizeEnd(): void;
|
|
40
|
+
/** Dialog visibility, two-way bound so a toolbar button can open it. */
|
|
41
|
+
visible: boolean;
|
|
42
|
+
/** Set while the 3D cloud is the active mode. Changes what the ROI selection
|
|
43
|
+
* MEANS — a screen-space lasso cutting through the cloud's full depth rather
|
|
44
|
+
* than a shape in tissue coordinates — so the hint below it says so. */
|
|
45
|
+
is3d: boolean;
|
|
46
|
+
visibleChange: EventEmitter<boolean>;
|
|
47
|
+
readonly clipOptions: {
|
|
48
|
+
label: string;
|
|
49
|
+
value: [number, number];
|
|
50
|
+
}[];
|
|
51
|
+
/** Null when the host bound no `SPATIAL_DATA_PORT`. */
|
|
52
|
+
controls: ISpatialControls | null;
|
|
53
|
+
dataset: SpatialDataset | null;
|
|
54
|
+
view: SpatialViewState;
|
|
55
|
+
/** Colour-by column choices — "None" plus every column the dataset declares. */
|
|
56
|
+
columnOptions: {
|
|
57
|
+
label: string;
|
|
58
|
+
value: string | null;
|
|
59
|
+
}[];
|
|
60
|
+
selectedColumn: string | null;
|
|
61
|
+
/** Virtual-scrolled only past this many options: the scroller earns its
|
|
62
|
+
* complexity for a few thousand names, and for eight it adds only overhead —
|
|
63
|
+
* a virtual viewport that short swallows the clicks it is meant to forward. */
|
|
64
|
+
readonly geneVirtualScrollFrom = 200;
|
|
65
|
+
/**
|
|
66
|
+
* Gene picker: a filterable dropdown rather than a free-text typeahead, so the
|
|
67
|
+
* options are visible before anything is typed and each keystroke narrows a list
|
|
68
|
+
* the user can see.
|
|
69
|
+
*
|
|
70
|
+
* `geneOptions` is the full name list when the dataset inlines it (a targeted
|
|
71
|
+
* panel: 8 for the ABC demo, 300–5,000 for Xenium/CosMx). A whole-transcriptome
|
|
72
|
+
* dataset does not ship its ~31k names, so there the list is what the port's last
|
|
73
|
+
* search returned and filtering is server-side — same control either way.
|
|
74
|
+
*/
|
|
75
|
+
geneOptions: {
|
|
76
|
+
label: string;
|
|
77
|
+
value: string;
|
|
78
|
+
}[];
|
|
79
|
+
/**
|
|
80
|
+
* Every gene the dataset inlined, whether or not it is currently an option.
|
|
81
|
+
*
|
|
82
|
+
* Held as plain strings and searched per keystroke. A whole-transcriptome dataset
|
|
83
|
+
* inlines ~18k names — cheap to keep, ruinous to hand a dropdown all at once — so
|
|
84
|
+
* {@link geneOptions} is only ever the best few hundred of these.
|
|
85
|
+
*/
|
|
86
|
+
private geneNames;
|
|
87
|
+
selectedGene: string | null;
|
|
88
|
+
/** True when the options come from the port per keystroke rather than a resident
|
|
89
|
+
* list, which changes what an empty list means (nothing matched *yet*). */
|
|
90
|
+
genesAreRemote: boolean;
|
|
91
|
+
geneSearchFailed: boolean;
|
|
92
|
+
/** Categorical key, or null when the active colouring is continuous. */
|
|
93
|
+
legend: SpatialLegendEntry[] | null;
|
|
94
|
+
/** CSS gradient for a continuous colouring, or null when categorical. */
|
|
95
|
+
colorBarCss: string | null;
|
|
96
|
+
/** Current selection — drives the count, the Clear button and the muting. */
|
|
97
|
+
selection: SpatialSelectionMask;
|
|
98
|
+
/** The legend row whose category is currently selected, for highlighting. */
|
|
99
|
+
selectedCategory: number | null;
|
|
100
|
+
/** An ROI selection that matched nothing, so the UI can say so rather than
|
|
101
|
+
* looking like the button did nothing. */
|
|
102
|
+
selectionMissed: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Whether the Distribution section is expanded. Collapsed by default: the
|
|
105
|
+
* panel's primary job is the colour controls, and the chart roughly doubles
|
|
106
|
+
* its height.
|
|
107
|
+
*/
|
|
108
|
+
chartsOpen: boolean;
|
|
109
|
+
/** Per-instance id for the collapsible body, so `aria-controls` points at one
|
|
110
|
+
* element and expanding the second panel cannot scroll the first one's chart. */
|
|
111
|
+
readonly chartsBodyId: string;
|
|
112
|
+
/**
|
|
113
|
+
* The dataset's imaged section positions, or null when its z is continuous
|
|
114
|
+
* rather than sectioned. Read once per dataset — the scan walks the z of every
|
|
115
|
+
* observation, so it must not sit in a template getter.
|
|
116
|
+
*/
|
|
117
|
+
sections: Float32Array | null;
|
|
118
|
+
/**
|
|
119
|
+
* Colormap tree for the continuous colour scale, and the node currently picked
|
|
120
|
+
* from it. The library's own `COLORMAP_OPTIONS`, shown with the same swatches
|
|
121
|
+
* the image's colormap picker uses — choosing a gradient is a visual decision,
|
|
122
|
+
* so a list of names would be the wrong control.
|
|
123
|
+
*/
|
|
124
|
+
colormapOptions: ColormapNode[];
|
|
125
|
+
selectedColormapNode: ColormapNode | null;
|
|
126
|
+
private colormap;
|
|
127
|
+
private reverse;
|
|
128
|
+
/** Guards the gene typeahead and the legend/colour-bar rebuild: both are async
|
|
129
|
+
* and both are driven by input the user changes faster than they resolve. */
|
|
130
|
+
private geneSearchToken;
|
|
131
|
+
private keyToken;
|
|
132
|
+
private readonly subs;
|
|
133
|
+
constructor(viz: IVisualizer);
|
|
134
|
+
ngOnInit(): void;
|
|
135
|
+
ngOnDestroy(): void;
|
|
136
|
+
onVisibleChange(value: boolean): void;
|
|
137
|
+
/** Column dropdown. Choosing a column supersedes any gene selection. */
|
|
138
|
+
onColumn(name: string | null): void;
|
|
139
|
+
/**
|
|
140
|
+
* A keystroke in the gene dropdown's filter box.
|
|
141
|
+
*
|
|
142
|
+
* With the names resident the dropdown filters them itself and this only clears a
|
|
143
|
+
* stale failure. Without them there is nothing to filter, so the query goes to the
|
|
144
|
+
* port and its answer BECOMES the option list — the same control, filtering one
|
|
145
|
+
* hop further away.
|
|
146
|
+
*/
|
|
147
|
+
onGeneFilter(query: string): Promise<void>;
|
|
148
|
+
/** A gene was picked; it supersedes any column selection. */
|
|
149
|
+
onGene(name: string | null): void;
|
|
150
|
+
/** Select every observation inside the drawn regions (their union). */
|
|
151
|
+
selectFromRegions(): void;
|
|
152
|
+
/** Legend click: select one category. Clicking the active row clears it, so a
|
|
153
|
+
* second click is an undo rather than a no-op. */
|
|
154
|
+
selectCategory(index: number): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Expand or collapse the distribution section.
|
|
157
|
+
*
|
|
158
|
+
* Expanding SCROLLS it into view, because the panel is taller than the viewport
|
|
159
|
+
* once this section is open and the chart is its last row: expanding it drew a
|
|
160
|
+
* chart ~300 px below the fold, which reads as the section being empty. The
|
|
161
|
+
* scroll is deferred a task so the chart's own deferred first draw has given the
|
|
162
|
+
* body its height.
|
|
163
|
+
*/
|
|
164
|
+
toggleCharts(): void;
|
|
165
|
+
clearSelection(): void;
|
|
166
|
+
/** True while a selection is active — everything else renders muted. */
|
|
167
|
+
get hasSelection(): boolean;
|
|
168
|
+
onPointScale(value: number | undefined): void;
|
|
169
|
+
onOpacity(value: number | undefined): void;
|
|
170
|
+
onLogScale(on: boolean): void;
|
|
171
|
+
/**
|
|
172
|
+
* Set when the active categorical colouring has more categories than the 3D
|
|
173
|
+
* cloud can keep apart, so the points are drawing FLAT.
|
|
174
|
+
*
|
|
175
|
+
* Said in the panel rather than left to a console warning: `subclass` (338) is
|
|
176
|
+
* served precisely because the density volumes and the 2D view can render it, and
|
|
177
|
+
* a user who picks it in the cloud and sees one colour deserves to know both why
|
|
178
|
+
* and what to do instead.
|
|
179
|
+
*/
|
|
180
|
+
get exceedsCloudPalette(): boolean;
|
|
181
|
+
/** The ceiling itself, for the message. */
|
|
182
|
+
readonly cloudPaletteLimit = 95;
|
|
183
|
+
onGeneMap(on: boolean): void;
|
|
184
|
+
/** 3D: smooth the per-section sheets along z into a continuous volume. */
|
|
185
|
+
onGeneMapVolume(on: boolean): void;
|
|
186
|
+
/** 3D: restrict the sheets to one imaged section. */
|
|
187
|
+
onGeneMapOneSection(on: boolean): void;
|
|
188
|
+
onGeneMapSection(value: number | undefined): void;
|
|
189
|
+
get geneMapOneSection(): boolean;
|
|
190
|
+
/** "12 of 53" for the gene map's own section, 1-based like the cloud's. */
|
|
191
|
+
get geneMapSectionLabel(): string;
|
|
192
|
+
onGeneMapSmoothing(value: number | undefined): void;
|
|
193
|
+
onGeneMapOpacity(value: number | undefined): void;
|
|
194
|
+
/**
|
|
195
|
+
* What the 3D gene map is currently showing, said plainly — the sheets are a
|
|
196
|
+
* measurement and the volume is an estimate, and the panel has to be the place
|
|
197
|
+
* that says which one is on screen.
|
|
198
|
+
*/
|
|
199
|
+
get geneMapVolumeNote(): string;
|
|
200
|
+
/** True while a gene is the colour source — the only thing a gene map can map. */
|
|
201
|
+
get canMapGene(): boolean;
|
|
202
|
+
onShowVolume(on: boolean): void;
|
|
203
|
+
onVolumeOpacity(value: number | undefined): void;
|
|
204
|
+
onShowPoints(on: boolean): void;
|
|
205
|
+
/** The "one section at a time" switch: null restores the whole stack. */
|
|
206
|
+
onOneSection(on: boolean): void;
|
|
207
|
+
onPointSection(value: number | undefined): void;
|
|
208
|
+
/** True while the cloud is restricted to a single section. */
|
|
209
|
+
get oneSection(): boolean;
|
|
210
|
+
/** Highest section index the slider can reach. */
|
|
211
|
+
get lastSection(): number;
|
|
212
|
+
/** "12 of 53" — 1-based, because the sections are slides, not array slots. */
|
|
213
|
+
get sectionLabel(): string;
|
|
214
|
+
/** Whether this dataset has sections to pick from at all. */
|
|
215
|
+
get isSectioned(): boolean;
|
|
216
|
+
onDensityVolume(on: boolean): void;
|
|
217
|
+
onDensitySmoothing(value: number | undefined): void;
|
|
218
|
+
/** What the density volumes are actually showing, said plainly — an estimate is
|
|
219
|
+
* only honest if the reader knows it is one, and which clusters are in view. */
|
|
220
|
+
get densityNote(): string;
|
|
221
|
+
/** Mirrors the renderer's cap, for the note only. */
|
|
222
|
+
private static readonly DENSITY_MAX_CLUSTERS;
|
|
223
|
+
onClip(value: [number, number]): void;
|
|
224
|
+
reset(): void;
|
|
225
|
+
/**
|
|
226
|
+
* Description of the active column, when it has one. Surfaced because a
|
|
227
|
+
* DERIVED column (k-means clusters, QC totals computed at conversion) must not
|
|
228
|
+
* read as though it came with the data.
|
|
229
|
+
*/
|
|
230
|
+
get activeDescription(): string | null;
|
|
231
|
+
/** Label for the current colouring, for the key's heading. */
|
|
232
|
+
get colorByLabel(): string;
|
|
233
|
+
/** True when the active colouring is a categorical column (drives the key). */
|
|
234
|
+
get isCategorical(): boolean;
|
|
235
|
+
/** Whether log/clip apply — they are continuous-only knobs. */
|
|
236
|
+
get isContinuous(): boolean;
|
|
237
|
+
private columnLabel;
|
|
238
|
+
/** Rebuild the legend or colour bar for the current colouring. */
|
|
239
|
+
private refreshKey;
|
|
240
|
+
/**
|
|
241
|
+
* The continuous colour scale's colormap. Clearing it goes back to following
|
|
242
|
+
* the image's, which is the default.
|
|
243
|
+
*/
|
|
244
|
+
onContinuousColormap(node: ColormapNode | null): void;
|
|
245
|
+
/** The tree node holding a colormap value, so the picker shows what is in use. */
|
|
246
|
+
private colormapNodeFor;
|
|
247
|
+
/**
|
|
248
|
+
* `linear-gradient(...)` sampling the active colormap at 16 stops.
|
|
249
|
+
*
|
|
250
|
+
* Resolved exactly the way the renderer resolves it — the same override, the
|
|
251
|
+
* same grey fallback. Built from `lutFor` alone, this bar showed a black-to-white
|
|
252
|
+
* ramp while the canvas drew Viridis, which makes the key worse than no key.
|
|
253
|
+
*/
|
|
254
|
+
private buildColorBar;
|
|
255
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SpatialControlsComponent, never>;
|
|
256
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SpatialControlsComponent, "spatial-controls", never, { "visible": { "alias": "visible"; "required": false; }; "is3d": { "alias": "is3d"; "required": false; }; }, { "visibleChange": "visibleChange"; }, never, never, false, never>;
|
|
257
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { SpatialSelectionMask } from '../spatial/spatial-selection';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* The set of currently-selected spatial observations, shared the way
|
|
6
|
+
* `RegionStore` shares regions: one store, many writers (an ROI selection, a
|
|
7
|
+
* legend click, a future chart brush), one reader per consumer.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately thin — every rule about WHICH observations are selected lives in
|
|
10
|
+
* the pure `spatial-selection.ts`; this only holds the answer and announces it.
|
|
11
|
+
*/
|
|
12
|
+
export declare class SpatialSelectionStore {
|
|
13
|
+
private readonly selection$;
|
|
14
|
+
/** Emits on every change, including a clear. */
|
|
15
|
+
getSelection$(): Observable<SpatialSelectionMask>;
|
|
16
|
+
/** Synchronous read, for a renderer building its first frame. */
|
|
17
|
+
current(): SpatialSelectionMask;
|
|
18
|
+
/** True when nothing is selected — the state in which nothing is muted. */
|
|
19
|
+
isEmpty(): boolean;
|
|
20
|
+
/** Replace the selection. An empty result is published like any other, so a
|
|
21
|
+
* "selected nothing" ROI visibly clears rather than silently keeping the
|
|
22
|
+
* previous selection on screen. */
|
|
23
|
+
set(selection: SpatialSelectionMask): void;
|
|
24
|
+
clear(): void;
|
|
25
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SpatialSelectionStore, never>;
|
|
26
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SpatialSelectionStore>;
|
|
27
|
+
}
|
|
@@ -4,6 +4,7 @@ import { ClassPreset, PresetSet } from '../models/class-preset';
|
|
|
4
4
|
import { PreferencesPort } from '../contracts/ports/preferences.port';
|
|
5
5
|
import { IImageMetadata } from '../contracts/image.contract';
|
|
6
6
|
import { IChannelState } from '../contracts/channel-histogram-api.contract';
|
|
7
|
+
import { SpatialViewState } from '../contracts/display-types';
|
|
7
8
|
import * as i0 from "@angular/core";
|
|
8
9
|
/**
|
|
9
10
|
* Backend-neutral store for the **visualization session** — the state that
|
|
@@ -43,6 +44,10 @@ export declare class VisualizerStore {
|
|
|
43
44
|
private readonly savePresets$;
|
|
44
45
|
private presetsLoaded;
|
|
45
46
|
private readonly activeTool$;
|
|
47
|
+
/** Spatial-omics display state (colour source, point size, alpha, scaling).
|
|
48
|
+
* Held here, next to the colormap/channel state, so the active backend reacts
|
|
49
|
+
* to an edit exactly as it does for an image. */
|
|
50
|
+
private readonly spatialView$;
|
|
46
51
|
constructor(http?: HttpClient | null, prefsPort?: PreferencesPort | null);
|
|
47
52
|
/**
|
|
48
53
|
* Resolve the colormap tree's LUT keys (e.g. "GREYS_LUT") to their colour
|
|
@@ -122,6 +127,14 @@ export declare class VisualizerStore {
|
|
|
122
127
|
resetPresets(): void;
|
|
123
128
|
/** Force a (debounced) write-back of the current preset set. */
|
|
124
129
|
persistPresets(): void;
|
|
130
|
+
getSpatialView$(): Observable<SpatialViewState>;
|
|
131
|
+
/** Synchronous read, for a renderer building its first frame. */
|
|
132
|
+
currentSpatialView(): SpatialViewState;
|
|
133
|
+
/** Patch the spatial view state; emits once with the merged value. */
|
|
134
|
+
setSpatialView(partial: Partial<SpatialViewState>): void;
|
|
135
|
+
/** Reset to defaults — e.g. when a different dataset is selected, where the
|
|
136
|
+
* previous colour column almost certainly does not exist. */
|
|
137
|
+
resetSpatialView(): void;
|
|
125
138
|
getActiveTool$(): Observable<string | null>;
|
|
126
139
|
getActiveTool(): string | null;
|
|
127
140
|
setActiveTool(tool: string | null): void;
|
|
@@ -30,6 +30,13 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
30
30
|
zIndex: number;
|
|
31
31
|
/** True for any 2D (non Surface-3D) plot type. */
|
|
32
32
|
isHeatmap: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Region tools apply even though this is a 3D view, because they draw a
|
|
35
|
+
* SCREEN-SPACE lasso (the 3D spatial cloud). Separate from {@link isHeatmap},
|
|
36
|
+
* which means "2D view" and also drives the zoom tools and the 3D camera
|
|
37
|
+
* controls — flipping that would show 2D pan/zoom and hide the orbit controls.
|
|
38
|
+
*/
|
|
39
|
+
is3dRegions: boolean;
|
|
33
40
|
activeDragMode: string | null;
|
|
34
41
|
activeSurface3dMode: string;
|
|
35
42
|
/** Whether the napari 3D axes/scale gizmo is currently shown (drives the toggle's look). */
|
|
@@ -83,6 +90,8 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
83
90
|
isoRangeChange: EventEmitter<number[] | undefined>;
|
|
84
91
|
/** Open the Channels & Histogram dialog (brightness/contrast, colormap, …). */
|
|
85
92
|
openChannelHistogram: EventEmitter<void>;
|
|
93
|
+
/** Open the spatial-omics controls (colour-by, legend, point display). */
|
|
94
|
+
openSpatialControls: EventEmitter<void>;
|
|
86
95
|
openRegionEditor: EventEmitter<void>;
|
|
87
96
|
selectStackOption: EventEmitter<{
|
|
88
97
|
name: string;
|
|
@@ -190,6 +199,11 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
190
199
|
/** The Image plot type renders as a natively pan/zoom-able raster, so the
|
|
191
200
|
* generic zoom/pan tools are hidden. */
|
|
192
201
|
get isImageView(): boolean;
|
|
202
|
+
/** Either spatial-omics mode is active, so its controls are worth offering.
|
|
203
|
+
* The 3D cloud needs the panel MORE than the 2D view does, not less: colouring,
|
|
204
|
+
* the legend and category show/hide are the only way to make sense of a
|
|
205
|
+
* million overlapping points. */
|
|
206
|
+
get isSpatialMode(): boolean;
|
|
193
207
|
/** A plot-type icon is a PrimeNG font glyph (e.g. `pi pi-image`) rather than an
|
|
194
208
|
* SVG asset path — drives which element the selector item template renders. */
|
|
195
209
|
isPiIcon(icon: string | undefined): boolean;
|
|
@@ -197,8 +211,10 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
197
211
|
* (jit-ui#102). Gates the polygon / add-vertex / delete-vertex / Bézier-convert tools. */
|
|
198
212
|
get supportsRegionVertexTools(): boolean;
|
|
199
213
|
/** Plot types that scrub a z-stack live (the renderer swaps the slice in place): the OSD Image
|
|
200
|
-
* view, the napari-js WebGPU image,
|
|
201
|
-
* from the picked slice (jit-ui#102)
|
|
214
|
+
* view, the napari-js WebGPU image, the napari-js surface — which rebuilds the height field
|
|
215
|
+
* from the picked slice (jit-ui#102) — and the 2D spatial-omics view, where a 3D dataset's
|
|
216
|
+
* registered volume is the image and the slider picks the section whose observations are
|
|
217
|
+
* drawn. Drives the per-slice slider. */
|
|
202
218
|
get showsLiveSliceScrubber(): boolean;
|
|
203
219
|
/** Any napari-js WebGPU plot type. The single-image/stack toggle + slice-number field are
|
|
204
220
|
* Plotly-only stack controls, so they're hidden for napari (jit-ui#102). */
|
|
@@ -214,5 +230,5 @@ export declare class ToolbarComponent implements OnChanges {
|
|
|
214
230
|
get isIntensityCapable(): boolean;
|
|
215
231
|
showHelp(): void;
|
|
216
232
|
static ɵfac: i0.ɵɵFactoryDeclaration<ToolbarComponent, never>;
|
|
217
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "required": false; }; "isoRange": { "alias": "isoRange"; "required": false; }; "maxIndex": { "alias": "maxIndex"; "required": false; }; "zIndex": { "alias": "zIndex"; "required": false; }; "isHeatmap": { "alias": "isHeatmap"; "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; }; "toolModelIds": { "alias": "toolModelIds"; "required": false; }; }, { "selectPlotType": "selectPlotType"; "addProfileLine": "addProfileLine"; "toggleImageSmoothing": "toggleImageSmoothing"; "isoRangeChange": "isoRangeChange"; "openChannelHistogram": "openChannelHistogram"; "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"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
|
|
233
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ToolbarComponent, "plotting-toolbar", never, { "imageInfo": { "alias": "imageInfo"; "required": false; }; "plotTypeOptions": { "alias": "plotTypeOptions"; "required": false; }; "selectedPlotType": { "alias": "selectedPlotType"; "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; }; "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"; "toolModelChange": "toolModelChange"; "openToolParams": "openToolParams"; "samModelChange": "samModelChange"; "wandSensitivityChange": "wandSensitivityChange"; "brushSizeChange": "brushSizeChange"; "vertexEraserRadiusChange": "vertexEraserRadiusChange"; }, never, ["*"], false, never>;
|
|
218
234
|
}
|
|
@@ -4,29 +4,33 @@ import * as i2 from "./toolbar/toolbar.component";
|
|
|
4
4
|
import * as i3 from "./region-editor/region-editor.component";
|
|
5
5
|
import * as i4 from "./hex-color-picker/hex-color-picker.component";
|
|
6
6
|
import * as i5 from "./channel-histogram/channel-histogram.component";
|
|
7
|
-
import * as i6 from "
|
|
8
|
-
import * as i7 from "
|
|
9
|
-
import * as i8 from "
|
|
10
|
-
import * as i9 from "
|
|
11
|
-
import * as i10 from "primeng/
|
|
12
|
-
import * as i11 from "primeng/
|
|
13
|
-
import * as i12 from "primeng/
|
|
14
|
-
import * as i13 from "primeng/
|
|
15
|
-
import * as i14 from "primeng/
|
|
16
|
-
import * as i15 from "primeng/
|
|
17
|
-
import * as i16 from "primeng/
|
|
18
|
-
import * as i17 from "primeng/
|
|
19
|
-
import * as i18 from "primeng/
|
|
20
|
-
import * as i19 from "primeng/
|
|
21
|
-
import * as i20 from "primeng/
|
|
22
|
-
import * as i21 from "primeng/
|
|
23
|
-
import * as i22 from "primeng/
|
|
24
|
-
import * as i23 from "primeng/
|
|
25
|
-
import * as i24 from "primeng/
|
|
26
|
-
import * as i25 from "primeng/
|
|
27
|
-
import * as i26 from "primeng/
|
|
28
|
-
import * as i27 from "primeng/
|
|
29
|
-
import * as i28 from "primeng/
|
|
7
|
+
import * as i6 from "./spatial-controls/spatial-controls.component";
|
|
8
|
+
import * as i7 from "./spatial-controls/spatial-charts/spatial-charts.component";
|
|
9
|
+
import * as i8 from "@angular/common";
|
|
10
|
+
import * as i9 from "@angular/forms";
|
|
11
|
+
import * as i10 from "primeng/toolbar";
|
|
12
|
+
import * as i11 from "primeng/button";
|
|
13
|
+
import * as i12 from "primeng/menu";
|
|
14
|
+
import * as i13 from "primeng/dropdown";
|
|
15
|
+
import * as i14 from "primeng/slider";
|
|
16
|
+
import * as i15 from "primeng/treeselect";
|
|
17
|
+
import * as i16 from "primeng/inputnumber";
|
|
18
|
+
import * as i17 from "primeng/dialog";
|
|
19
|
+
import * as i18 from "primeng/contextmenu";
|
|
20
|
+
import * as i19 from "primeng/progressbar";
|
|
21
|
+
import * as i20 from "primeng/toast";
|
|
22
|
+
import * as i21 from "primeng/tooltip";
|
|
23
|
+
import * as i22 from "primeng/ripple";
|
|
24
|
+
import * as i23 from "primeng/table";
|
|
25
|
+
import * as i24 from "primeng/paginator";
|
|
26
|
+
import * as i25 from "primeng/overlaypanel";
|
|
27
|
+
import * as i26 from "primeng/confirmdialog";
|
|
28
|
+
import * as i27 from "primeng/inputtext";
|
|
29
|
+
import * as i28 from "primeng/checkbox";
|
|
30
|
+
import * as i29 from "primeng/multiselect";
|
|
31
|
+
import * as i30 from "primeng/radiobutton";
|
|
32
|
+
import * as i31 from "primeng/selectbutton";
|
|
33
|
+
import * as i32 from "primeng/autocomplete";
|
|
30
34
|
/**
|
|
31
35
|
* Self-contained plotting UI: the {@link VisualizerComponent} (plot surface
|
|
32
36
|
* + render orchestration), its {@link ToolbarComponent}, and the
|
|
@@ -40,6 +44,6 @@ import * as i28 from "primeng/selectbutton";
|
|
|
40
44
|
*/
|
|
41
45
|
export declare class VisualizationModule {
|
|
42
46
|
static ɵfac: i0.ɵɵFactoryDeclaration<VisualizationModule, never>;
|
|
43
|
-
static ɵmod: i0.ɵɵNgModuleDeclaration<VisualizationModule, [typeof i1.VisualizerComponent, typeof i2.ToolbarComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent], [typeof
|
|
47
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<VisualizationModule, [typeof i1.VisualizerComponent, typeof i2.ToolbarComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent, typeof i6.SpatialControlsComponent, typeof i7.SpatialChartsComponent], [typeof i8.CommonModule, typeof i9.FormsModule, typeof i10.ToolbarModule, typeof i11.ButtonModule, typeof i12.MenuModule, typeof i13.DropdownModule, typeof i14.SliderModule, typeof i15.TreeSelectModule, typeof i16.InputNumberModule, typeof i17.DialogModule, typeof i18.ContextMenuModule, typeof i19.ProgressBarModule, typeof i20.ToastModule, typeof i21.TooltipModule, typeof i22.RippleModule, typeof i23.TableModule, typeof i24.PaginatorModule, typeof i25.OverlayPanelModule, typeof i26.ConfirmDialogModule, typeof i27.InputTextModule, typeof i28.CheckboxModule, typeof i29.MultiSelectModule, typeof i30.RadioButtonModule, typeof i31.SelectButtonModule, typeof i32.AutoCompleteModule], [typeof i1.VisualizerComponent, typeof i3.RegionEditorComponent, typeof i4.HexColorPickerComponent, typeof i5.ChannelHistogramComponent, typeof i6.SpatialControlsComponent, typeof i7.SpatialChartsComponent]>;
|
|
44
48
|
static ɵinj: i0.ɵɵInjectorDeclaration<VisualizationModule>;
|
|
45
49
|
}
|
|
@@ -15,6 +15,7 @@ import { CellSegmentToolService } from './toolbar/segmentation/cell-segment-tool
|
|
|
15
15
|
import { NumberParamSpec, SelectParamSpec, ToolParamSpec, ToolbarToolContribution } from './contracts/toolbar-tool.contract';
|
|
16
16
|
import { ToolbarToolVisibility } from './contracts/toolbar-config';
|
|
17
17
|
import { VizConfig } from './contracts/viz-config';
|
|
18
|
+
import { SpatialDataPort } from './contracts/ports/spatial-data.port';
|
|
18
19
|
import * as i0 from "@angular/core";
|
|
19
20
|
export declare class VisualizerComponent implements OnInit, OnChanges, AfterViewInit, OnDestroy {
|
|
20
21
|
private state;
|
|
@@ -28,6 +29,7 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
28
29
|
private samPointTool;
|
|
29
30
|
private regionOps;
|
|
30
31
|
private vizConfig?;
|
|
32
|
+
private spatialData?;
|
|
31
33
|
/** Per-instance toast key. MessageService is a global singleton, so two live
|
|
32
34
|
* `<visualizer>` instances (main viewer + pipeline-dialog preview) sharing
|
|
33
35
|
* one key would each render the same message — a duplicate toast. A unique
|
|
@@ -177,6 +179,8 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
177
179
|
selectedColormap: any;
|
|
178
180
|
/** Channels & Histogram dialog visibility (opened from the toolbar). */
|
|
179
181
|
showChannelHistogram: boolean;
|
|
182
|
+
/** Spatial-omics controls dialog visibility (toolbar button). */
|
|
183
|
+
showSpatialControls: boolean;
|
|
180
184
|
/** Region Editor dialog visibility (opened from the toolbar). */
|
|
181
185
|
showRegionEditor: boolean;
|
|
182
186
|
/** Region Editor dialog width. On open it is set to the configured host
|
|
@@ -230,6 +234,10 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
230
234
|
private profileDragMoveListener?;
|
|
231
235
|
private profileDragUpListener?;
|
|
232
236
|
private profileResizeListener?;
|
|
237
|
+
/** True while the 3D spatial cloud is the active mode. The controls drop the ROI
|
|
238
|
+
* selection there: region tools are screen-space, and against an orbiting
|
|
239
|
+
* camera a drawn rectangle has no fixed meaning in the data. */
|
|
240
|
+
get isSpatial3dMode(): boolean;
|
|
233
241
|
/** LINE plot type shows the image + draggable line ROI + intensity inset. */
|
|
234
242
|
get isProfileMode(): boolean;
|
|
235
243
|
/** True for the Image plot type, which renders as a natively pan/zoom-able
|
|
@@ -239,6 +247,24 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
239
247
|
/** Isosurface band as a 0–255 slider position, mapped onto the volume's real
|
|
240
248
|
* intensity range by the renderer. Defaults to the full range. */
|
|
241
249
|
isoRange: number[];
|
|
250
|
+
/** Whether a spatial-omics dataset is currently published on
|
|
251
|
+
* `SPATIAL_DATA_PORT` — gates the spatial plot types in the selector. */
|
|
252
|
+
private hasSpatialDataset;
|
|
253
|
+
/** Whether that dataset's observations carry a z, gating the 3D spatial mode. */
|
|
254
|
+
private hasSpatial3dDataset;
|
|
255
|
+
/** Whether it carries a registered volume. Change detection only: a volume
|
|
256
|
+
* appearing is what publishes it as the image, and the plot-type gates then
|
|
257
|
+
* see an ordinary grayscale stack. */
|
|
258
|
+
private hasSpatialVolume;
|
|
259
|
+
/** Dataset identity + capability shape the last emission was handled at, so a
|
|
260
|
+
* switch between two datasets of the same shape is not mistaken for a repeat. */
|
|
261
|
+
private spatialDatasetKey;
|
|
262
|
+
/** Dataset + geometry the currently published volume image was built from, so a
|
|
263
|
+
* re-emitted dataset doesn't re-fetch megabytes or reset the user's scrub. */
|
|
264
|
+
private volumeImageKey;
|
|
265
|
+
/** Blob URLs backing that image — ours to revoke. */
|
|
266
|
+
private volumeImageUrls;
|
|
267
|
+
private spatialDatasetSubscription?;
|
|
242
268
|
plotWidthSubscription?: Subscription;
|
|
243
269
|
imageLoadingSubscription?: Subscription;
|
|
244
270
|
imgLoadingMessageSubscription?: Subscription;
|
|
@@ -250,7 +276,7 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
250
276
|
private wheelListener?;
|
|
251
277
|
/** Debounced z-slice scrubbing (see SliceScrubber — refactoring plan Step 7). */
|
|
252
278
|
private readonly scrubber;
|
|
253
|
-
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 ToolbarToolContribution[]);
|
|
279
|
+
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 ToolbarToolContribution[], spatialData?: SpatialDataPort | undefined);
|
|
254
280
|
/**
|
|
255
281
|
* Plot types offered in the selector for the current image:
|
|
256
282
|
* - Outside **test mode**, only the curated default set (descriptors with a
|
|
@@ -263,12 +289,55 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
263
289
|
* - scalar-intensity types (contour, surface, isosurface) hidden for RGB
|
|
264
290
|
* images — they map a single intensity per pixel. Image and Heatmap render
|
|
265
291
|
* any image.
|
|
292
|
+
* - spatial-omics types hidden until a `SpatialDataset` is published on
|
|
293
|
+
* `SPATIAL_DATA_PORT` — the mode has nothing to draw without observations,
|
|
294
|
+
* exactly as a volume has nothing to draw without a stack.
|
|
266
295
|
*/
|
|
296
|
+
/**
|
|
297
|
+
* Track whether a spatial-omics dataset is available. A dataset appearing (or
|
|
298
|
+
* being cleared) changes which plot types make sense, so it drives the same
|
|
299
|
+
* recompute + reconcile that the image stream and the test-mode toggle do.
|
|
300
|
+
*
|
|
301
|
+
* No-op when the host provides no `SPATIAL_DATA_PORT` — the spatial types then
|
|
302
|
+
* stay hidden for the life of the component.
|
|
303
|
+
*/
|
|
304
|
+
private watchSpatialDataset;
|
|
305
|
+
/**
|
|
306
|
+
* Publish a dataset's reference volume AS the image, and open the 2D Image view
|
|
307
|
+
* on it.
|
|
308
|
+
*
|
|
309
|
+
* The volume is a 3D image delivered in one file, so for a dataset that has no
|
|
310
|
+
* section image it is the honest thing to put on screen: the slice bar scrubs z,
|
|
311
|
+
* and the contrast window, colormaps and region tools all work because the
|
|
312
|
+
* volume genuinely is the image now. The 3D cloud stays one menu pick away.
|
|
313
|
+
*
|
|
314
|
+
* Keyed by dataset + geometry: the dataset stream re-emits on things like a
|
|
315
|
+
* colour-column change, and rebuilding then would re-fetch the voxels and throw
|
|
316
|
+
* the user back to the middle slice.
|
|
317
|
+
*/
|
|
318
|
+
private showVolumeAsImage;
|
|
319
|
+
/** Forget the published volume image so re-selecting the dataset rebuilds it.
|
|
320
|
+
* The URLs deliberately stay alive: the host may still be displaying that image
|
|
321
|
+
* at this instant, and revoking under it would break every later tile read. They
|
|
322
|
+
* are freed when the next volume replaces them, or on destroy. */
|
|
323
|
+
private dropVolumeImage;
|
|
324
|
+
private revokeVolumeImageUrls;
|
|
267
325
|
private computePlotTypeOptions;
|
|
268
326
|
ngOnChanges(changes: SimpleChanges): void;
|
|
269
327
|
/** If the active plot type is no longer in the offered options (e.g. test mode
|
|
270
328
|
* turned off while a test-only type was active, or a scalar type carried onto
|
|
271
329
|
* an RGB image), fall back to the default 2D Image view. */
|
|
330
|
+
/** Whether the live spatial dataset brings pixels of its own — a tissue image it
|
|
331
|
+
* registers onto, or a volume that is published as a z-stack image. */
|
|
332
|
+
private spatialDatasetHasPixels;
|
|
333
|
+
/**
|
|
334
|
+
* The host published NO image — a spatial dataset that brings none does this.
|
|
335
|
+
*
|
|
336
|
+
* Without handling the empty emission, `imageInfo` keeps the LAST image's metadata: the
|
|
337
|
+
* selector goes on offering pixel modes for pixels that are gone, and every
|
|
338
|
+
* `isStack`/`isGrayscale` decision is made against a file that is no longer loaded.
|
|
339
|
+
*/
|
|
340
|
+
private onImageCleared;
|
|
272
341
|
private reconcileSelectedPlotType;
|
|
273
342
|
ngOnInit(): void;
|
|
274
343
|
ngAfterViewInit(): void;
|
|
@@ -302,6 +371,8 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
302
371
|
selectColormap(colormapNode: TreeNode): void;
|
|
303
372
|
/** Open the Channels & Histogram dialog (toolbar button). */
|
|
304
373
|
openChannelHistogram(): void;
|
|
374
|
+
/** Toolbar → open the spatial-omics controls. */
|
|
375
|
+
openSpatialControls(): void;
|
|
305
376
|
/** Open the Region Editor dialog (toolbar button). The editor stays linked to
|
|
306
377
|
* this plotting instance through the root RegionStore singleton, so it works
|
|
307
378
|
* the same as the former right-panel Regions tab. */
|
|
@@ -476,6 +547,6 @@ export declare class VisualizerComponent implements OnInit, OnChanges, AfterView
|
|
|
476
547
|
onSelectPlotType(type: PlotType): void;
|
|
477
548
|
/** Deactivate whatever tool is armed and clear every tool mode. */
|
|
478
549
|
private deactivateActiveTool;
|
|
479
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerComponent, [null, null, null, null, null, null, null, null, null, null, { optional: true; }, { optional: true; }]>;
|
|
550
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<VisualizerComponent, [null, null, null, null, null, null, null, null, null, null, { optional: true; }, { optional: true; }, { optional: true; }]>;
|
|
480
551
|
static ɵcmp: i0.ɵɵComponentDeclaration<VisualizerComponent, "visualizer", never, { "toolbarTools": { "alias": "toolbarTools"; "required": false; }; "testMode": { "alias": "testMode"; "required": false; }; }, { "isStackEvent": "isStackEvent"; "isGrayscaleEvent": "isGrayscaleEvent"; }, never, ["*"], false, never>;
|
|
481
552
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jax-data-science/sci-image-visualizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=20.9.0"
|
|
6
|
+
},
|
|
4
7
|
"description": "Browser-based scientific image visualization for Angular — gigapixel whole-slide (OpenSeadragon), 2D/3D (Plotly) and WebGPU (napari-js) rendering, with region/annotation editing and in-browser SAM segmentation.",
|
|
5
8
|
"keywords": [
|
|
6
9
|
"angular",
|
|
@@ -34,9 +37,10 @@
|
|
|
34
37
|
"access": "public"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
40
|
+
"@jax-js/jax": "^0.1.24",
|
|
37
41
|
"cellpose-js": "^0.6.0",
|
|
38
42
|
"fast-png": "^6.4.0",
|
|
39
|
-
"napari-js": "^0.
|
|
43
|
+
"napari-js": "^0.14.0",
|
|
40
44
|
"openseadragon": "^6.0.2",
|
|
41
45
|
"plotly.js-dist-min": "^3.0.1",
|
|
42
46
|
"tslib": "^2.3.0"
|