@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.
Files changed (91) hide show
  1. package/README.md +208 -4
  2. package/esm2022/index.mjs +20 -1
  3. package/esm2022/lib/contracts/display-types.mjs +20 -2
  4. package/esm2022/lib/contracts/image.contract.mjs +1 -1
  5. package/esm2022/lib/contracts/plot-type.mjs +36 -1
  6. package/esm2022/lib/contracts/ports/spatial-data.port.mjs +3 -0
  7. package/esm2022/lib/contracts/spatial-dataset.contract.mjs +45 -0
  8. package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
  9. package/esm2022/lib/implementations/napari-js/napari-axes-labels.mjs +10 -19
  10. package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +71 -7
  11. package/esm2022/lib/implementations/napari-js/napari-scale-bar.mjs +1 -1
  12. package/esm2022/lib/implementations/napari-js/napari-spatial-tooltip.mjs +96 -0
  13. package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +1934 -55
  14. package/esm2022/lib/implementations/napari-js/napari-zoom.mjs +18 -0
  15. package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +5 -3
  16. package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +3 -4
  17. package/esm2022/lib/implementations/osd/osd-zoom.mjs +20 -0
  18. package/esm2022/lib/implementations/plotly/omics-trace-builders.mjs +555 -0
  19. package/esm2022/lib/implementations/plotly/plotly.service.mjs +13 -7
  20. package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +304 -0
  21. package/esm2022/lib/implementations/spatial-data-http/spatial-wire.mjs +179 -0
  22. package/esm2022/lib/routing-visualizer.service.mjs +223 -12
  23. package/esm2022/lib/spatial/embedding-compute.mjs +142 -0
  24. package/esm2022/lib/spatial/gene-search.mjs +77 -0
  25. package/esm2022/lib/spatial/spatial-density.mjs +224 -0
  26. package/esm2022/lib/spatial/spatial-encoding.mjs +232 -0
  27. package/esm2022/lib/spatial/spatial-expression.mjs +257 -0
  28. package/esm2022/lib/spatial/spatial-framing.mjs +60 -0
  29. package/esm2022/lib/spatial/spatial-heatmap.mjs +185 -0
  30. package/esm2022/lib/spatial/spatial-hover.mjs +79 -0
  31. package/esm2022/lib/spatial/spatial-sections.mjs +96 -0
  32. package/esm2022/lib/spatial/spatial-selection.mjs +264 -0
  33. package/esm2022/lib/spatial/spatial-volume-image.mjs +163 -0
  34. package/esm2022/lib/spatial/tsne-worker.mjs +10 -0
  35. package/esm2022/lib/spatial/world-grid.mjs +77 -0
  36. package/esm2022/lib/spatial-controls/spatial-charts/spatial-charts.component.mjs +1278 -0
  37. package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +621 -0
  38. package/esm2022/lib/store/spatial-selection.service.mjs +47 -0
  39. package/esm2022/lib/store/visualizer-store.service.mjs +23 -1
  40. package/esm2022/lib/toolbar/toolbar.component.mjs +30 -7
  41. package/esm2022/lib/visualization.module.mjs +23 -5
  42. package/esm2022/lib/visualizer.component.mjs +283 -22
  43. 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
  44. 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
  45. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
  46. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
  47. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15769 -8316
  48. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  49. package/fesm2022/tsne.worker.js +19091 -0
  50. package/index.d.ts +15 -0
  51. package/lib/contracts/display-types.d.ts +131 -0
  52. package/lib/contracts/image.contract.d.ts +7 -0
  53. package/lib/contracts/plot-type.d.ts +42 -2
  54. package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
  55. package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
  56. package/lib/contracts/visualizer.contract.d.ts +99 -1
  57. package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
  58. package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
  59. package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
  60. package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
  61. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
  62. package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
  63. package/lib/implementations/osd/osd-zoom.d.ts +19 -0
  64. package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
  65. package/lib/implementations/plotly/plotly.service.d.ts +9 -0
  66. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
  67. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
  68. package/lib/routing-visualizer.service.d.ts +45 -6
  69. package/lib/spatial/embedding-compute.d.ts +73 -0
  70. package/lib/spatial/gene-search.d.ts +48 -0
  71. package/lib/spatial/spatial-density.d.ts +73 -0
  72. package/lib/spatial/spatial-encoding.d.ts +155 -0
  73. package/lib/spatial/spatial-expression.d.ts +144 -0
  74. package/lib/spatial/spatial-framing.d.ts +37 -0
  75. package/lib/spatial/spatial-heatmap.d.ts +111 -0
  76. package/lib/spatial/spatial-hover.d.ts +49 -0
  77. package/lib/spatial/spatial-sections.d.ts +59 -0
  78. package/lib/spatial/spatial-selection.d.ts +113 -0
  79. package/lib/spatial/spatial-volume-image.d.ts +67 -0
  80. package/lib/spatial/tsne-worker.d.ts +7 -0
  81. package/lib/spatial/world-grid.d.ts +58 -0
  82. package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
  83. package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
  84. package/lib/store/spatial-selection.service.d.ts +27 -0
  85. package/lib/store/visualizer-store.service.d.ts +13 -0
  86. package/lib/toolbar/toolbar.component.d.ts +19 -3
  87. package/lib/visualization.module.d.ts +28 -24
  88. package/lib/visualizer.component.d.ts +73 -2
  89. package/package.json +6 -2
  90. package/src/lib/assets/spatial-omics-3d.svg +31 -0
  91. package/src/lib/assets/spatial-omics.svg +27 -0
@@ -0,0 +1,439 @@
1
+ import { AfterViewInit, OnDestroy, OnInit } from '@angular/core';
2
+ import { IVisualizer, ISpatialControls } from '../../contracts/visualizer.contract';
3
+ import { SpatialEmbeddingMeta } from '../../contracts/spatial-dataset.contract';
4
+ import { SpatialColorBy } from '../../contracts/display-types';
5
+ import { OmicsChartKind } from '../../implementations/plotly/omics-trace-builders';
6
+ import * as i0 from "@angular/core";
7
+ /**
8
+ * Distribution charts over the spatial-omics values — histogram, violin, box —
9
+ * **linked to the map**: they chart whatever the map is coloured by, and narrow
10
+ * to the current selection.
11
+ *
12
+ * Embedded INSIDE `<spatial-controls>` rather than owning a dialog of its own:
13
+ * the two are one workflow (change the colour source, watch the distribution
14
+ * move), and splitting them across two floating windows made the link harder to
15
+ * see, not easier. It stays a separate component so the pure trace builders and
16
+ * its own tests keep their boundary.
17
+ *
18
+ * Charting the active colour source rather than offering its own value picker is
19
+ * deliberate: it keeps one source of truth, so what you see on the map and what
20
+ * you see in the chart cannot disagree, and it makes the link legible without
21
+ * explaining it.
22
+ *
23
+ * Depends only on {@link ISpatialControls} through the `VISUALIZER` contract
24
+ * token, like `SpatialControlsComponent`. Trace building lives in the pure
25
+ * `omics-trace-builders`; this component only moves data and owns the div.
26
+ */
27
+ export declare class SpatialChartsComponent implements OnInit, AfterViewInit, OnDestroy {
28
+ private readonly viz;
29
+ /**
30
+ * Whether the host panel is on screen. The chart only draws when it is: the
31
+ * enclosing dialog creates and destroys its content, so without this the
32
+ * component would mount with a div present but no state change to trigger a
33
+ * first draw.
34
+ */
35
+ set active(on: boolean);
36
+ get active(): boolean;
37
+ private isActive;
38
+ /** Per-instance, for the same reason the visualizer's plot div is
39
+ * (`visualizer.component.ts`): two mounted charts sharing one DOM id means
40
+ * `getElementById` hands both of them the first element, so one instance draws
41
+ * into — or purges — the other's canvas. */
42
+ /** One increment per INSTANCE — a static initializer would run once per class and
43
+ * hand every instance the same id, which is the bug this id exists to avoid. Declared
44
+ * before the ids because field initializers run in order. */
45
+ private readonly seq;
46
+ readonly chartDiv: string;
47
+ /**
48
+ * The div inside the detached window.
49
+ *
50
+ * A separate id rather than moving the existing div: Angular destroys and recreates a
51
+ * div across an `*ngIf`, so Plotly would be left holding a detached node. Two divs and
52
+ * a redraw is both simpler and correct.
53
+ *
54
+ * ONE is enough for every kind, because only the active kind is ever plotted. Detaching
55
+ * is remembered per kind, but at most one window is open at a time.
56
+ */
57
+ readonly detachedDiv: string;
58
+ /**
59
+ * Whether the chart is shown in its own window rather than inline.
60
+ *
61
+ * Detached, it sits ALONGSIDE the spatial-omics dialog instead of inside it — which is
62
+ * the point: these are read against the map, not instead of it. The dialog is only so
63
+ * wide.
64
+ *
65
+ * One flag for the PANEL, not one per kind. Where the window is, is a property of the
66
+ * workspace someone has arranged, not of the tab they happen to be on: having put the
67
+ * charts beside the map, switching from the heatmap to the counts should swap what the
68
+ * window shows, not yank it back into the dialog and make them detach it again.
69
+ */
70
+ detached: boolean;
71
+ private static readonly CONTINUOUS_KINDS;
72
+ private static readonly CATEGORICAL_KINDS;
73
+ /** Available whatever the map is coloured by: the heatmap's subject is a GENE
74
+ * LIST crossed with a grouping, not the active colour source. */
75
+ private static readonly ALWAYS_KINDS;
76
+ /**
77
+ * A t-SNE the dataset does not publish, offered so it can be computed here.
78
+ *
79
+ * A dropped-in `.h5ad` typically carries one UMAP and a PCA, and no t-SNE — the two
80
+ * views answer different questions, so having only one is a real gap. Computing it
81
+ * needs no expression data: t-SNE runs on the PCA scores, which is why this can happen
82
+ * in the browser at all while PCA cannot.
83
+ */
84
+ private static readonly COMPUTABLE;
85
+ /** The menu suffix that says a click will start work; not part of the name. */
86
+ private static readonly COMPUTE_SUFFIX;
87
+ /**
88
+ * Largest dataset this will offer to embed in the browser.
89
+ *
90
+ * t-SNE is O(N²) per iteration — vectorised on the GPU, but not approximated — so the
91
+ * cost is quadratic in observations. Measured end to end in Firefox on WebGPU: 2,688
92
+ * points took 95 s. That scales to roughly five and a half minutes at this threshold,
93
+ * and to about 76 minutes at seqFISH's 19,416, which was also measured offline.
94
+ *
95
+ * Past this the option is WITHDRAWN rather than offered with a warning. An hour-long
96
+ * job started from a button is not a choice a reader can meaningfully consent to in a
97
+ * dialog, and a t-SNE that large belongs in the offline pipeline, where it can run
98
+ * once and be served to everyone.
99
+ */
100
+ static readonly BROWSER_TSNE_MAX_OBSERVATIONS = 5000;
101
+ /** Measured anchor for the estimate: seconds for 2,688 points on WebGPU. */
102
+ private static readonly TSNE_SECONDS_AT;
103
+ /** Coordinates computed in this browser, by name. Not persisted: a reload recomputes. */
104
+ private readonly computed;
105
+ /** The live run, when one is going. */
106
+ private computeRun;
107
+ /**
108
+ * Bumped by every dataset change and by teardown, so a computation in progress can tell
109
+ * that what it is computing is no longer what is on screen.
110
+ *
111
+ * A run is several awaits long — the PCA scores arrive over HTTP, then the worker takes
112
+ * a minute or more — and the dataset can change at any of them, including BEFORE
113
+ * `computeRun` exists to be terminated. Without a token, the scores fetched for one
114
+ * dataset get embedded and drawn over another's observations: a plot of two datasets at
115
+ * once, which reads as a strange-looking t-SNE rather than as a bug.
116
+ */
117
+ private computeToken;
118
+ /** 0..1 while running, or null before the first report. Drives the progress bar. */
119
+ computeFraction: number | null;
120
+ /** Which backend the worker got — 'webgpu', 'wasm' or 'cpu'. Shown while running. */
121
+ computeBackend: string | null;
122
+ computeMessage: string | null;
123
+ computeError: string | null;
124
+ /** True when a t-SNE is missing but the dataset is too big to embed here. */
125
+ tsneTooLarge: boolean;
126
+ /** Observations in the live dataset, kept for the cost estimate. */
127
+ private observationCount;
128
+ /** Offered only when the dataset publishes an embedding to draw. */
129
+ private static readonly EMBEDDING_KINDS;
130
+ /** The kinds the ACTIVE subject can be drawn as. A category code is a label,
131
+ * not a magnitude, so a histogram of it would be meaningless — what a
132
+ * categorical column has is a frequency distribution. */
133
+ get kindOptions(): {
134
+ label: string;
135
+ value: OmicsChartKind;
136
+ }[];
137
+ controls: ISpatialControls | null;
138
+ kind: OmicsChartKind;
139
+ /** Embeddings the dataset publishes, and which of them is drawn. */
140
+ embeddings: SpatialEmbeddingMeta[];
141
+ embedding: SpatialEmbeddingMeta | null;
142
+ private embeddingCoords;
143
+ /** The categorical colouring behind the embedding's colours, kept so a redraw for a
144
+ * selection change does not refetch the column. */
145
+ private embeddingCodes;
146
+ /**
147
+ * Genes the heatmap's rows are, and the vectors behind them.
148
+ *
149
+ * Local rather than in `SpatialViewState`, matching how the chart KIND is
150
+ * held: this is what the panel is charting, not what the map is drawing, and
151
+ * the renderer has no use for it.
152
+ */
153
+ heatmapGenes: string[];
154
+ /** Z-score each gene across the groups. On by default: without it one loud
155
+ * gene saturates the scale and the rest of the panel reads as blank. */
156
+ heatmapZScore: boolean;
157
+ /** Past this many selected cells the per-cell view is a texture, not a
158
+ * readable panel, so the columns go back to being classes. */
159
+ private static readonly HEATMAP_CELL_COLUMNS;
160
+ /**
161
+ * Column cap for the grouped heatmap.
162
+ *
163
+ * Any categorical column can be the x axis, and they are not the same size:
164
+ * `neurotransmitter` has 9 categories, `class` 34, `subclass` 338. At 338 the
165
+ * panel is a texture — columns a couple of pixels wide, no readable label —
166
+ * so past this many the strongest columns are kept and the note says how many
167
+ * were dropped. See `maxCols` in `spatial-heatmap.ts`.
168
+ */
169
+ private static readonly HEATMAP_MAX_COLUMNS;
170
+ /** Columns the cap dropped from the last render, for the note. */
171
+ private heatmapHidden;
172
+ geneOptions: {
173
+ label: string;
174
+ value: string;
175
+ }[];
176
+ /** Every gene the dataset inlined. `geneOptions` is only ever the best few hundred of
177
+ * these — see `gene-search.ts` for why a whole-transcriptome list cannot be handed to
178
+ * the control whole. */
179
+ private geneNames;
180
+ /** What is typed in the picker's filter box, so the options can be rebuilt when the
181
+ * selection changes without losing the query. */
182
+ private geneQuery;
183
+ /** Fetched vectors by gene name, so adding a fourth gene does not refetch the
184
+ * first three — each is a full per-observation Float32Array. */
185
+ private readonly geneCache;
186
+ /** Categorical column the violin/box splits by; null = one trace for all. */
187
+ groupBy: string | null;
188
+ groupOptions: {
189
+ label: string;
190
+ value: string | null;
191
+ }[];
192
+ /** What the map is coloured by — the chart's subject. */
193
+ colorBy: SpatialColorBy | null;
194
+ selectionCount: number;
195
+ /** Set when the active colour source cannot be charted, for an inline hint. */
196
+ notice: string | null;
197
+ busy: boolean;
198
+ private view;
199
+ private selection;
200
+ private values;
201
+ /** Set instead of `values` when the colour source is a categorical column: its
202
+ * distribution is counts per category, not a histogram of its codes. */
203
+ private categorical;
204
+ private grouping;
205
+ /** Guards the async value fetch: a fast colour-source change can resolve out
206
+ * of order, and a stale vector would be charted against the new label. */
207
+ private token;
208
+ /** The same guard for the grouping fetch, which the user can change as fast. */
209
+ private groupToken;
210
+ /** Whether the first view emission has been handled. */
211
+ private primed;
212
+ private readonly subs;
213
+ constructor(viz: IVisualizer);
214
+ ngOnInit(): void;
215
+ /**
216
+ * The height the drawn layout asked for, or null where it autosizes.
217
+ *
218
+ * {@link resize} needs this: the counts and heatmap layouts size their height
219
+ * to their content — one band per bar, per gene row — while the distribution
220
+ * kinds want to fill whatever height they are given.
221
+ */
222
+ private drawnHeight;
223
+ /**
224
+ * Re-fit the plot to the panel's current width.
225
+ *
226
+ * Called by the host when the dialog finishes resizing. Plotly does not notice
227
+ * a container resize on its own — its `responsive` option listens for WINDOW
228
+ * resizes only, so dragging a dialog edge reaches it through nothing at all.
229
+ */
230
+ resize(): void;
231
+ /**
232
+ * Re-fit whatever is plotted in `div` to its container.
233
+ *
234
+ * Shared by the panel and the detached window because the rule is the same and getting
235
+ * it wrong is invisible until it is not: `autosize` takes BOTH dimensions from the
236
+ * container, which is right only where the layout did not fix its own height. Measured:
237
+ * autosizing a layout with an explicit height of 500 replaced it with the container's
238
+ * 400. The counts and heatmap layouts size themselves to their row count, so the
239
+ * window resize has to set the width ALONE for them and let the height stand.
240
+ */
241
+ private refit;
242
+ ngOnDestroy(): void;
243
+ ngAfterViewInit(): void;
244
+ onKind(kind: OmicsChartKind): void;
245
+ /**
246
+ * The grouping the heatmap's columns come from, defaulting rather than
247
+ * demanding one: a heatmap with no columns is not a chart, and "No grouping"
248
+ * is a sensible answer for a violin but not for this.
249
+ */
250
+ private ensureHeatmapGrouping;
251
+ onHeatmapZScore(on: boolean): void;
252
+ /** What the heatmap is currently showing, said plainly. */
253
+ get heatmapNote(): string;
254
+ /** Gene rows for the heatmap. */
255
+ onHeatmapGenes(names: string[]): Promise<void>;
256
+ /**
257
+ * Fetch any gene the heatmap needs and does not already hold.
258
+ *
259
+ * Sequenced on the same token the colour-source load uses: each vector is a
260
+ * separate request, and a slower one must not paint rows for a gene list the
261
+ * user has already moved on from.
262
+ */
263
+ private loadHeatmapGenes;
264
+ onGroupBy(name: string | null): Promise<void>;
265
+ /** True when the current kind would read better with a grouping chosen. */
266
+ get suggestsGrouping(): boolean;
267
+ get subject(): string;
268
+ /** Fetch the active colour source's vector, then draw. */
269
+ private reload;
270
+ /**
271
+ * The embedding scatter — a UMAP over the same observations.
272
+ *
273
+ * Answers what the map cannot: the map says WHERE a population sits, this says which
274
+ * populations there are and how close they are in expression.
275
+ *
276
+ * Linked to the map without asking for anything: it reuses `this.categorical`, the same
277
+ * loaded colouring the counts chart draws from, so the two cannot disagree about what a
278
+ * colour means and no second fetch happens. A selection dims the rest rather than
279
+ * dropping it — the embedding's shape is the context that makes a selection legible.
280
+ */
281
+ private renderEmbedding;
282
+ /**
283
+ * Turn a lasso in the embedding into a selection of observations.
284
+ *
285
+ * This is what makes two views of one dataset worth having side by side: draw round a
286
+ * cluster here and those cells light up on the tissue, because every view reads the
287
+ * same mask.
288
+ *
289
+ * Bound after each draw, and the previous listener removed first: `Plotly.react`
290
+ * preserves handlers, so re-binding without removing would fire the selection once per
291
+ * redraw the plot had ever had.
292
+ */
293
+ private bindEmbeddingSelection;
294
+ /**
295
+ * Drop the embedding's selection handlers from a div another kind is about to use.
296
+ *
297
+ * The detached window outlives any one kind — switching tabs swaps its content and
298
+ * keeps the div — so without this the lasso handlers bound for a UMAP stay live under
299
+ * the counts plot that replaces it. `plotly_deselect` fires on a plain click in ANY
300
+ * Plotly chart, so the first click on a bar would silently clear the map's selection.
301
+ */
302
+ private unbindEmbeddingSelection;
303
+ /**
304
+ * The view the user has set up on the live plot, to carry across a redraw.
305
+ *
306
+ * `Plotly.react` resets a 3D scene camera and any zoomed 2D range unless the layout
307
+ * carries them, so selecting a category or recolouring would throw away an orientation
308
+ * that took work to find. Rotating a cloud to see a structure and losing it on the next
309
+ * click makes the plot useless for what it is for.
310
+ */
311
+ private liveView;
312
+ /**
313
+ * Whether the plot on screen fixed its own height.
314
+ *
315
+ * The counts and heatmap layouts size themselves to their row count, so in the detached
316
+ * window they can be TALLER than the window — which has to scroll rather than clip. An
317
+ * embedding fixes no height and fills the window instead. The template needs to know
318
+ * which, because the two want opposite `flex` and `overflow`.
319
+ */
320
+ get hasFixedHeight(): boolean;
321
+ /** Where the active kind draws: its own window, or the shared chart div. */
322
+ get plotTarget(): string;
323
+ /**
324
+ * Header for the detached window — what it is showing, named as its tab is.
325
+ *
326
+ * The embedding names the embedding rather than the tab, because "Embedding" beside a
327
+ * window drawing a t-SNE says less than "t-SNE" does.
328
+ */
329
+ get detachedTitle(): string;
330
+ /**
331
+ * Move the active kind between its own window and the panel.
332
+ *
333
+ * Purges the div it is LEAVING first. Plotly keeps per-div state, and a graph left
334
+ * behind in a div that Angular then removes leaks its WebGL context — and if the div
335
+ * comes back, react would resize a plot whose data belongs to the other place.
336
+ */
337
+ toggleDetached(): void;
338
+ /** The detached window is up and its div exists — now the plot can be drawn into it. */
339
+ onDetachedWindowShown(): void;
340
+ /** Re-fit the detached window's plot after it is resized. */
341
+ onDetachedResizeEnd(): void;
342
+ /**
343
+ * Rebuild the visible options: the best matches for what is typed, plus what is chosen.
344
+ *
345
+ * A capped multi-select that drops its own selections cannot resolve their labels, and
346
+ * the model loses them on the next change — the chips vanish for no reason the user
347
+ * can see.
348
+ */
349
+ private refreshGeneOptions;
350
+ /** The gene picker's filter box changed: search the resident names, show the best. */
351
+ onGeneFilter(query: string): void;
352
+ /**
353
+ * Roughly how long a run will take, in seconds, from the measured anchor.
354
+ *
355
+ * Shown before the click. A progress bar tells you a job is going; only an estimate
356
+ * tells you whether to start it.
357
+ */
358
+ get computeEstimateSeconds(): number;
359
+ /** That estimate as something to put on a button. */
360
+ get computeEstimateLabel(): string;
361
+ /** Why the option is absent, when a dataset is past the threshold. */
362
+ get tsneTooLargeNote(): string;
363
+ /** Whether the selected embedding is one this browser would have to compute. */
364
+ get isComputable(): boolean;
365
+ /** Whether it has already been computed in this session. */
366
+ get isComputed(): boolean;
367
+ get isComputing(): boolean;
368
+ /**
369
+ * Compute the selected embedding here, in a worker.
370
+ *
371
+ * The PCA scores come from the port as an ordinary embedding — the server derives them,
372
+ * because PCA needs the whole expression matrix (185 MB for a Visium dataset) while
373
+ * t-SNE needs only the scores (0.51 MB). Sending the matrix to the browser to save a
374
+ * few seconds of arithmetic would be a far slower answer.
375
+ */
376
+ computeEmbedding(): Promise<void>;
377
+ /** Stop a run. It settles shortly after, leaving no embedding. */
378
+ cancelCompute(): void;
379
+ /**
380
+ * Drop any computation in progress, because what it is computing no longer applies.
381
+ *
382
+ * Distinct from {@link cancelCompute}, which is the user's Cancel and asks the worker to
383
+ * stop politely. This one does not wait: the run is settled and the worker killed, so a
384
+ * dataset switch or a teardown cannot leave a t-SNE grinding on in the background over
385
+ * observations nothing is showing any more.
386
+ */
387
+ private abandonCompute;
388
+ /**
389
+ * The dataset's PCA, as row-major scores.
390
+ *
391
+ * Assembled from whichever PCA the source offers, widest first — a 3-D one gives t-SNE
392
+ * three components instead of two for the same request.
393
+ */
394
+ private loadPcaScores;
395
+ /** Which embedding to draw, when the dataset publishes more than one. */
396
+ onEmbedding(name: string): void;
397
+ /**
398
+ * What the chart on screen actually shows, and what it cannot be read for.
399
+ *
400
+ * Shown on hover from a `?` beside the tabs. Each of these plots answers a different
401
+ * question and two of them are routinely over-read — a UMAP's distances and a heatmap's
402
+ * unscaled colours — so the caveat is part of the explanation rather than a footnote.
403
+ *
404
+ * HTML, because the tooltip renders with `[escape]="false"`: a paragraph and a caveat
405
+ * read as two thoughts, and a single run-on line is skipped rather than read.
406
+ */
407
+ get kindHelp(): string;
408
+ /**
409
+ * What the embedding on screen is, and how far its geometry can be trusted.
410
+ *
411
+ * Per METHOD, because that is the part people get wrong: a PCA's axes are ordered and
412
+ * measurable while a UMAP's are neither, and the same picture read the two ways supports
413
+ * opposite conclusions.
414
+ */
415
+ private get embeddingHelp();
416
+ /** What the embedding view is showing, said plainly. */
417
+ get embeddingNote(): string;
418
+ private purgePlot;
419
+ /**
420
+ * Draw, remembering whether this layout fixed its own height.
421
+ *
422
+ * The layout builders return `unknown` on purpose — they keep Plotly's layout
423
+ * types out of the pure module — so the height is read back by narrowing
424
+ * rather than declared. `height` is Plotly's own key, not ours.
425
+ */
426
+ private draw;
427
+ private render;
428
+ /**
429
+ * Genes × groups mean expression.
430
+ *
431
+ * Columns are the grouping column's categories — or, inside a SMALL
432
+ * selection, the selected cells themselves. A two-column class matrix is a
433
+ * poor answer for an ROI holding forty cells; "what is in front of me" is the
434
+ * question there, and each cell earns a column.
435
+ */
436
+ private renderHeatmap;
437
+ static ɵfac: i0.ɵɵFactoryDeclaration<SpatialChartsComponent, never>;
438
+ static ɵcmp: i0.ɵɵComponentDeclaration<SpatialChartsComponent, "spatial-charts", never, { "active": { "alias": "active"; "required": false; }; }, {}, never, never, false, never>;
439
+ }