@jax-data-science/sci-image-visualizer 0.3.3 → 0.5.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 +268 -4
- package/esm2022/index.mjs +21 -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-contribution.contract.mjs +66 -0
- package/esm2022/lib/contracts/plot-type.mjs +41 -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 +73 -8
- package/esm2022/lib/implementations/osd/osd-coordinate-transform.mjs +8 -1
- 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/plot-mode/plot-mode-controller.mjs +329 -0
- package/esm2022/lib/routing-visualizer.service.mjs +228 -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 +61 -21
- package/esm2022/lib/visualization.module.mjs +23 -5
- package/esm2022/lib/visualizer.component.mjs +527 -44
- 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 +15971 -7808
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/fesm2022/tsne.worker.js +19091 -0
- package/index.d.ts +16 -0
- package/lib/contracts/display-types.d.ts +131 -0
- package/lib/contracts/image.contract.d.ts +7 -0
- package/lib/contracts/plot-type-contribution.contract.d.ts +172 -0
- package/lib/contracts/plot-type.d.ts +52 -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 +104 -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/openseadragon-visualizer.service.d.ts +21 -0
- package/lib/implementations/osd/osd-coordinate-transform.d.ts +6 -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/plot-mode/plot-mode-controller.d.ts +130 -0
- package/lib/routing-visualizer.service.d.ts +49 -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 +36 -8
- package/lib/visualization.module.d.ts +28 -24
- package/lib/visualizer.component.d.ts +139 -7
- 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,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend-neutral data model for a **spatial-omics dataset**: N observations
|
|
3
|
+
* (Visium spots, segmented cells, nuclei, …) positioned in the pixel space of a
|
|
4
|
+
* tissue image, each carrying categorical and continuous annotations, plus an
|
|
5
|
+
* optional feature (gene) matrix and optional per-observation boundaries.
|
|
6
|
+
*
|
|
7
|
+
* DESIGN
|
|
8
|
+
* ------
|
|
9
|
+
* 1. **Struct-of-arrays, not object-per-cell.** Real datasets run 10^3 (Visium)
|
|
10
|
+
* to 10^6 (Xenium/CosMx) observations. `{x, y}[]` at 500k cells is ~500k
|
|
11
|
+
* objects the GC has to walk; two `Float32Array`s are two allocations and
|
|
12
|
+
* upload to the GPU without a copy.
|
|
13
|
+
*
|
|
14
|
+
* 2. **Metadata is eager, values are lazy.** A dataset advertises *which*
|
|
15
|
+
* columns and features exist ({@link SpatialColumnMeta},
|
|
16
|
+
* {@link SpatialFeatureMeta}); the vectors themselves are fetched on demand
|
|
17
|
+
* through {@link SpatialDataPort}. A Visium table is ~30k genes wide — the
|
|
18
|
+
* dense matrix is ~800 MB — so "load the dataset" can never mean "load the
|
|
19
|
+
* matrix". Only the handful of columns actually being displayed is resident.
|
|
20
|
+
*
|
|
21
|
+
* 3. **Data stays data.** No I/O, no RxJS, no Angular in this file — the same
|
|
22
|
+
* rule `plotly-trace-builders.ts` follows. Fetching lives on the port.
|
|
23
|
+
*
|
|
24
|
+
* The shape mirrors how the field already stores this (SpatialData/AnnData:
|
|
25
|
+
* `obs` annotations, `var` features, `obsm/spatial` coordinates, shapes with a
|
|
26
|
+
* coordinate transform) so an adapter is a projection, not a translation.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Observation positions. Every array is length {@link count} and index-aligned:
|
|
30
|
+
* observation `i` is at `(x[i], y[i])`, and column/feature vectors index the
|
|
31
|
+
* same way. This shared index is the join key for the whole model.
|
|
32
|
+
*/
|
|
33
|
+
export interface SpatialObservations {
|
|
34
|
+
/** N — number of observations. */
|
|
35
|
+
readonly count: number;
|
|
36
|
+
/** X in the reference image's pixel space (see {@link SpatialImageRef}). */
|
|
37
|
+
x: Float32Array;
|
|
38
|
+
/** Y in the reference image's pixel space. */
|
|
39
|
+
y: Float32Array;
|
|
40
|
+
/** Z, for serial sections or genuinely 3D assays. Absent for a single plane. */
|
|
41
|
+
z?: Float32Array;
|
|
42
|
+
/** Stable per-observation ids (barcodes, cell ids) for tooltips and export.
|
|
43
|
+
* Optional — omit for large datasets where the strings cost more than they
|
|
44
|
+
* are worth. */
|
|
45
|
+
ids?: string[];
|
|
46
|
+
/** Marker RADIUS in image pixels: one value shared by all observations (a
|
|
47
|
+
* Visium spot is a fixed 55 µm ⇒ `27.5 / mppX` px), or one per observation
|
|
48
|
+
* (segmented cells). Absent ⇒ the renderer picks a display default. */
|
|
49
|
+
radius?: Float32Array | number;
|
|
50
|
+
}
|
|
51
|
+
/** Fields common to every column descriptor. */
|
|
52
|
+
interface SpatialColumnMetaBase {
|
|
53
|
+
/** Column name as the user sees it (`leiden`, `total_counts`). */
|
|
54
|
+
name: string;
|
|
55
|
+
/** Longer human-readable description for tooltips/menus. */
|
|
56
|
+
description?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A discrete annotation — cluster, cell type, region, sample. Rendered as a
|
|
60
|
+
* palette + legend; a legend click selects the category's observations.
|
|
61
|
+
*/
|
|
62
|
+
export interface CategoricalColumnMeta extends SpatialColumnMetaBase {
|
|
63
|
+
kind: 'categorical';
|
|
64
|
+
/** Category labels; a value's label is `categories[codes[i]]`. */
|
|
65
|
+
categories: string[];
|
|
66
|
+
/** Authored display colours as `#rrggbb`, index-aligned with
|
|
67
|
+
* {@link categories}. Supply these to keep the viewer's palette identical to
|
|
68
|
+
* the figures the same analysis produced in R/Python; omit to let the viewer
|
|
69
|
+
* derive one. */
|
|
70
|
+
colors?: string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A continuous measurement — expression, QC metric, area, density. Rendered
|
|
74
|
+
* through the shared colormap/LUT + contrast window.
|
|
75
|
+
*/
|
|
76
|
+
export interface ContinuousColumnMeta extends SpatialColumnMetaBase {
|
|
77
|
+
kind: 'continuous';
|
|
78
|
+
/** Unit for axis/tooltip labels (`counts`, `µm²`). */
|
|
79
|
+
unit?: string;
|
|
80
|
+
/** True when the column reads best log-scaled — count data almost always
|
|
81
|
+
* does, and a linear scale collapses it against a few bright outliers. */
|
|
82
|
+
logScaleHint?: boolean;
|
|
83
|
+
/** Observed extremes, when the producer knows them. Lets the UI seed a
|
|
84
|
+
* contrast window and axis range without first scanning the vector. */
|
|
85
|
+
min?: number;
|
|
86
|
+
max?: number;
|
|
87
|
+
}
|
|
88
|
+
export type SpatialColumnMeta = CategoricalColumnMeta | ContinuousColumnMeta;
|
|
89
|
+
/**
|
|
90
|
+
* Code meaning "this observation has no category" (unassigned / filtered out /
|
|
91
|
+
* not in tissue). Renderers should draw it in the muted background style rather
|
|
92
|
+
* than as a real category, and it must never index {@link
|
|
93
|
+
* CategoricalColumnMeta.categories}.
|
|
94
|
+
*/
|
|
95
|
+
export declare const NO_CATEGORY = 65535;
|
|
96
|
+
/** A loaded categorical column: per-observation indices into `meta.categories`,
|
|
97
|
+
* or {@link NO_CATEGORY}. */
|
|
98
|
+
export interface CategoricalColumn {
|
|
99
|
+
meta: CategoricalColumnMeta;
|
|
100
|
+
/** Length = {@link SpatialObservations.count}. */
|
|
101
|
+
codes: Uint16Array;
|
|
102
|
+
}
|
|
103
|
+
/** A loaded continuous column. `NaN` marks a missing value. */
|
|
104
|
+
export interface ContinuousColumn {
|
|
105
|
+
meta: ContinuousColumnMeta;
|
|
106
|
+
/** Length = {@link SpatialObservations.count}. */
|
|
107
|
+
values: Float32Array;
|
|
108
|
+
}
|
|
109
|
+
export type SpatialColumn = CategoricalColumn | ContinuousColumn;
|
|
110
|
+
export declare function isCategoricalColumn(c: SpatialColumn): c is CategoricalColumn;
|
|
111
|
+
export declare function isContinuousColumn(c: SpatialColumn): c is ContinuousColumn;
|
|
112
|
+
/**
|
|
113
|
+
* The feature (gene) matrix, described but never delivered whole. Vectors come
|
|
114
|
+
* one at a time from {@link SpatialDataPort.getFeatureVector}.
|
|
115
|
+
*/
|
|
116
|
+
export interface SpatialFeatureMeta {
|
|
117
|
+
/** Number of features available. */
|
|
118
|
+
count: number;
|
|
119
|
+
/**
|
|
120
|
+
* All feature names, when the producer chose to inline them. Present for
|
|
121
|
+
* targeted panels (Xenium/CosMx ship 300–5,000 genes ⇒ a few dozen KB) and
|
|
122
|
+
* absent for whole-transcriptome data (Visium ships ~31k ⇒ ~350 KB), where
|
|
123
|
+
* the UI should use {@link SpatialDataPort.searchFeatures} for typeahead
|
|
124
|
+
* instead of downloading the list. Consumers must handle both.
|
|
125
|
+
*/
|
|
126
|
+
names?: string[];
|
|
127
|
+
/** What a feature value means (`log1p normalized`, `raw counts`). */
|
|
128
|
+
unit?: string;
|
|
129
|
+
/** True when feature vectors read best log-scaled (raw counts). */
|
|
130
|
+
logScaleHint?: boolean;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Per-observation boundaries (cell/nucleus segmentation, spot outlines) as flat
|
|
134
|
+
* rings — deliberately NOT GeoJSON objects, which cost one object + one array
|
|
135
|
+
* per cell and would dominate memory at 10^5 polygons.
|
|
136
|
+
*
|
|
137
|
+
* Ring `i` occupies `coords[2*offsets[i] .. 2*offsets[i+1])` as `x0,y0,x1,y1,…`
|
|
138
|
+
* and is implicitly closed. `offsets` has length `count + 1`.
|
|
139
|
+
*/
|
|
140
|
+
export interface SpatialPolygons {
|
|
141
|
+
coords: Float32Array;
|
|
142
|
+
offsets: Uint32Array;
|
|
143
|
+
/** Number of rings — `offsets.length - 1`. */
|
|
144
|
+
readonly count: number;
|
|
145
|
+
}
|
|
146
|
+
/** Presence/size of the boundary geometry, without loading it. */
|
|
147
|
+
export interface SpatialPolygonsMeta {
|
|
148
|
+
count: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* How observation coordinates relate to the tissue image they are drawn over.
|
|
152
|
+
* Applied as `world = coord * scale + translate`, matching the affine that
|
|
153
|
+
* SpatialData records per coordinate system — and the full-resolution world
|
|
154
|
+
* convention the napari-js backend already uses so pre-saved regions align on
|
|
155
|
+
* pyramidal slides.
|
|
156
|
+
*/
|
|
157
|
+
export interface SpatialImageRef {
|
|
158
|
+
/** Host-defined id of the image these coordinates live in, so the host can
|
|
159
|
+
* resolve it to an `IImageInfo`. */
|
|
160
|
+
imageId?: string;
|
|
161
|
+
/** Data→world scale. Defaults to `[1, 1]` (coordinates already in
|
|
162
|
+
* full-resolution image pixels). */
|
|
163
|
+
scale?: [number, number];
|
|
164
|
+
/** Data→world translation, in the same units as {@link scale}'s output. */
|
|
165
|
+
translate?: [number, number];
|
|
166
|
+
/** Physical pixel size in µm — drives the scale bar and physical marker
|
|
167
|
+
* sizing (a 55 µm Visium spot). */
|
|
168
|
+
mppX?: number;
|
|
169
|
+
mppY?: number;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A spatial-omics dataset: everything cheap enough to hold resident. Column and
|
|
173
|
+
* feature *values*, and polygon *geometry*, are fetched through
|
|
174
|
+
* {@link SpatialDataPort} as they are displayed.
|
|
175
|
+
*/
|
|
176
|
+
/**
|
|
177
|
+
* A precomputed low-dimensional embedding over the SAME observations — UMAP, t-SNE, PCA.
|
|
178
|
+
*
|
|
179
|
+
* Announced in the dataset and fetched on demand, exactly like a column or a gene: the
|
|
180
|
+
* coordinates are another per-observation vector, and a dataset may carry several.
|
|
181
|
+
*/
|
|
182
|
+
export interface SpatialEmbeddingMeta {
|
|
183
|
+
/** Key as the source names it, e.g. `X_umap`. */
|
|
184
|
+
name: string;
|
|
185
|
+
/** Short label for menus; falls back to {@link name}. */
|
|
186
|
+
label?: string;
|
|
187
|
+
/** 2 for a plane, 3 for a cloud. */
|
|
188
|
+
dims: 2 | 3;
|
|
189
|
+
/**
|
|
190
|
+
* True when the coordinates were COMPUTED for this bundle rather than published with the
|
|
191
|
+
* dataset. Worth surfacing: a UMAP recomputed with different parameters is a different picture,
|
|
192
|
+
* and a reader comparing against a paper's figure needs to know which they are looking at.
|
|
193
|
+
*/
|
|
194
|
+
derived?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Fraction of total variance each axis accounts for, index-aligned with the dimensions.
|
|
197
|
+
*
|
|
198
|
+
* Only meaningful for a LINEAR embedding — PCA. Its axes are ordered, and each one explains a
|
|
199
|
+
* measurable share of the variance, which is the whole reason to look at a PCA rather than a
|
|
200
|
+
* UMAP: "PC1 (23%)" says something, where "UMAP 1" says nothing at all. A UMAP's coordinates
|
|
201
|
+
* are an arbitrary output of an optimisation with no variance to report, so this stays absent
|
|
202
|
+
* there rather than being faked.
|
|
203
|
+
*/
|
|
204
|
+
varianceRatio?: number[];
|
|
205
|
+
/**
|
|
206
|
+
* How a derived embedding was computed, in a few words — e.g. "PCA(50) then perplexity 30,
|
|
207
|
+
* seed 0".
|
|
208
|
+
*
|
|
209
|
+
* Not decoration. A t-SNE or UMAP is stochastic, and its picture changes materially with
|
|
210
|
+
* perplexity or `n_neighbors`; two runs at different settings are different pictures of the same
|
|
211
|
+
* cells, and neither is more correct. Saying "computed here" without saying HOW leaves a reader
|
|
212
|
+
* unable to reproduce or compare it. Absent for a published embedding, whose parameters belong
|
|
213
|
+
* to whoever published it.
|
|
214
|
+
*/
|
|
215
|
+
params?: string;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* One embedding's coordinates.
|
|
219
|
+
*
|
|
220
|
+
* Deliberately shaped like {@link SpatialObservations}' `x`/`y`/`z` rather than an (N, D) matrix:
|
|
221
|
+
* the point of an embedding here is to be swapped in as the scatter's coordinate source, and
|
|
222
|
+
* matching that shape means the renderer, the hover hit-test and the selection code need no
|
|
223
|
+
* variant for it.
|
|
224
|
+
*/
|
|
225
|
+
export interface SpatialEmbedding {
|
|
226
|
+
meta: SpatialEmbeddingMeta;
|
|
227
|
+
x: Float32Array;
|
|
228
|
+
y: Float32Array;
|
|
229
|
+
z?: Float32Array;
|
|
230
|
+
}
|
|
231
|
+
export interface SpatialDataset {
|
|
232
|
+
/** Stable id — the key the port's lazy accessors are scoped to. */
|
|
233
|
+
id: string;
|
|
234
|
+
/** Human-readable name for menus. */
|
|
235
|
+
name: string;
|
|
236
|
+
observations: SpatialObservations;
|
|
237
|
+
/** Every column available, whether or not its values are loaded. */
|
|
238
|
+
columns: SpatialColumnMeta[];
|
|
239
|
+
features?: SpatialFeatureMeta;
|
|
240
|
+
polygons?: SpatialPolygonsMeta;
|
|
241
|
+
imageRef?: SpatialImageRef;
|
|
242
|
+
/** Presence + geometry of a reference volume the observations sit inside. */
|
|
243
|
+
volume?: SpatialVolumeMeta;
|
|
244
|
+
/** Embeddings available for these observations, whose coordinates are fetched on demand. */
|
|
245
|
+
embeddings?: SpatialEmbeddingMeta[];
|
|
246
|
+
/**
|
|
247
|
+
* Microns per observation coordinate unit — what makes a scale bar possible.
|
|
248
|
+
*
|
|
249
|
+
* The 2D path gets this from `imageRef.mppX`, because there the coordinates are
|
|
250
|
+
* image pixels. A 3D cloud has no image, so it has to say for itself: `1` means
|
|
251
|
+
* the coordinates are already microns.
|
|
252
|
+
*
|
|
253
|
+
* Absent means the unit is UNKNOWN, and a renderer must then draw no scale bar
|
|
254
|
+
* rather than assume one. A bar labelled in microns over pixel-space
|
|
255
|
+
* coordinates is worse than no bar: it looks like a measurement.
|
|
256
|
+
*/
|
|
257
|
+
micronsPerUnit?: number;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* A 3D scalar reference volume registered to the observation coordinates — the
|
|
261
|
+
* anatomical backdrop for a point cloud (an atlas template, or an image z-stack).
|
|
262
|
+
*
|
|
263
|
+
* Geometry only; the voxels are fetched through
|
|
264
|
+
* {@link SpatialDataPort.getVolume} when something is going to draw them. A cloud
|
|
265
|
+
* without one still renders, just in empty space.
|
|
266
|
+
*
|
|
267
|
+
* The observations' coordinate frame must be the volume's own: voxel `(i, j, k)`
|
|
268
|
+
* covers `[i * voxelSize[0], (i + 1) * voxelSize[0])` on x, and so on. That means
|
|
269
|
+
* the volume's near corner sits at the coordinate origin, so a renderer can place
|
|
270
|
+
* the two together knowing nothing else.
|
|
271
|
+
*/
|
|
272
|
+
export interface SpatialVolumeMeta {
|
|
273
|
+
width: number;
|
|
274
|
+
height: number;
|
|
275
|
+
depth: number;
|
|
276
|
+
/** World size of one voxel per axis, in the observations' units. */
|
|
277
|
+
voxelSize: [number, number, number];
|
|
278
|
+
}
|
|
279
|
+
/** Look a column's descriptor up by name. */
|
|
280
|
+
export declare function findColumnMeta(dataset: SpatialDataset, name: string): SpatialColumnMeta | undefined;
|
|
281
|
+
export {};
|
|
@@ -7,7 +7,10 @@ import { PlotType, PlotTypeDescriptor } from './plot-type';
|
|
|
7
7
|
import { ViewerCapabilities } from './capabilities.contract';
|
|
8
8
|
import { IRegionOverlay } from './region-overlay.contract';
|
|
9
9
|
import { IHistogram } from './channel-histogram-api.contract';
|
|
10
|
-
import { ColormapNode, IWandOptions, IBrushOptions } from './display-types';
|
|
10
|
+
import { ColormapNode, IWandOptions, IBrushOptions, SpatialViewState, SpatialColorBy } from './display-types';
|
|
11
|
+
import { SpatialDataset, SpatialEmbedding } from './spatial-dataset.contract';
|
|
12
|
+
import { SpatialSelectionMask } from '../spatial/spatial-selection';
|
|
13
|
+
import type { PlotModeViewport } from './plot-type-contribution.contract';
|
|
11
14
|
/**
|
|
12
15
|
* Backend-neutral visualization contract. Plotly is one implementation;
|
|
13
16
|
* OpenSeadragon is another (for the image plot type). The abstraction renders
|
|
@@ -270,6 +273,98 @@ export interface IIntensityControls {
|
|
|
270
273
|
* backend, or null if no image extent is known yet. */
|
|
271
274
|
addProfileLine(): Region | null;
|
|
272
275
|
}
|
|
276
|
+
/** A categorical column resolved for charting: labels, codes and map colours. */
|
|
277
|
+
export interface SpatialCategoricalView {
|
|
278
|
+
name: string;
|
|
279
|
+
categories: string[];
|
|
280
|
+
/** `#rrggbb` per category, index-aligned with {@link categories}. */
|
|
281
|
+
colors: string[];
|
|
282
|
+
/** Per-observation category index, or `NO_CATEGORY`. */
|
|
283
|
+
codes: Uint16Array;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Controls for the SPATIAL_OMICS plot type: what the observation markers are
|
|
287
|
+
* coloured by, and how they are drawn. Capability-gated like
|
|
288
|
+
* {@link IIsosurfaceControls} — `getSpatialControls()` returns null unless a
|
|
289
|
+
* host has bound `SPATIAL_DATA_PORT`.
|
|
290
|
+
*
|
|
291
|
+
* The view state is backend-neutral (it lives in the shared store, like the
|
|
292
|
+
* colormap), so these controls work whichever backend is on screen.
|
|
293
|
+
*/
|
|
294
|
+
export interface ISpatialControls {
|
|
295
|
+
/** The dataset being visualized, or null. Drives the column/gene pickers and
|
|
296
|
+
* the legend. */
|
|
297
|
+
getDataset$(): Observable<SpatialDataset | null>;
|
|
298
|
+
/** Current display state (colour source, point scale, opacity, scaling). */
|
|
299
|
+
getViewState$(): Observable<SpatialViewState>;
|
|
300
|
+
/** Synchronous read, for seeding a control's initial value. */
|
|
301
|
+
viewState(): SpatialViewState;
|
|
302
|
+
/** Patch the display state; the markers rebuild without remounting the scene. */
|
|
303
|
+
setViewState(partial: Partial<SpatialViewState>): void;
|
|
304
|
+
/** Colour by an annotation column. Rejects for an unknown column. */
|
|
305
|
+
colorByColumn(name: string): void;
|
|
306
|
+
/** Colour by a gene; its vector is fetched on demand. */
|
|
307
|
+
colorByFeature(name: string): void;
|
|
308
|
+
/** Clear the colour source — every observation renders in one neutral colour. */
|
|
309
|
+
clearColorBy(): void;
|
|
310
|
+
/** Typeahead over feature names, for datasets too wide to inline the list. */
|
|
311
|
+
searchFeatures(query: string, limit?: number): Promise<string[]>;
|
|
312
|
+
/** Per-category display colours for a categorical column, index-aligned with
|
|
313
|
+
* its `categories` — the legend's swatches, resolved the same way the
|
|
314
|
+
* renderer resolves them so the two cannot disagree. */
|
|
315
|
+
categoryColors(name: string): Promise<string[]>;
|
|
316
|
+
/**
|
|
317
|
+
* The values behind a colour source, index-aligned with the observations.
|
|
318
|
+
* Rejects for a categorical column — there is nothing continuous to chart.
|
|
319
|
+
*/
|
|
320
|
+
continuousValues(source: SpatialColorBy): Promise<Float32Array>;
|
|
321
|
+
/**
|
|
322
|
+
* A categorical column's categories, per-observation codes and display
|
|
323
|
+
* colours — everything a grouped violin/box needs, with the colours resolved
|
|
324
|
+
* the same way the map resolves them.
|
|
325
|
+
*/
|
|
326
|
+
categoricalView(column: string): Promise<SpatialCategoricalView>;
|
|
327
|
+
/**
|
|
328
|
+
* One embedding's coordinates — a UMAP, t-SNE or PCA plane over the same observations.
|
|
329
|
+
*
|
|
330
|
+
* Optional, and absent when the data source serves none: `SpatialDataPort.getEmbedding`
|
|
331
|
+
* is itself optional, so a panel must be able to ask whether this exists rather than
|
|
332
|
+
* calling it and catching.
|
|
333
|
+
*/
|
|
334
|
+
getEmbedding?(name: string): Promise<SpatialEmbedding>;
|
|
335
|
+
/** Names of the categorical columns available to group by. */
|
|
336
|
+
categoricalColumns(): string[];
|
|
337
|
+
/**
|
|
338
|
+
* The z positions of the sections the dataset was imaged at, ascending — or
|
|
339
|
+
* null when its z is continuous rather than sectioned (so there are no sections
|
|
340
|
+
* to offer) or when there is no dataset.
|
|
341
|
+
*
|
|
342
|
+
* Lets a panel offer "one section at a time" for the 3D cloud only where that
|
|
343
|
+
* means something, and label the chosen one. Scanned once per dataset.
|
|
344
|
+
*/
|
|
345
|
+
sampledSections(): Float32Array | null;
|
|
346
|
+
/** The current selection. Empty means "nothing selected", in which case the
|
|
347
|
+
* whole tissue renders normally rather than everything being muted. */
|
|
348
|
+
getSelection$(): Observable<SpatialSelectionMask>;
|
|
349
|
+
/**
|
|
350
|
+
* Select every observation inside the currently-drawn regions (their union).
|
|
351
|
+
* Reuses the existing ROI tools — rectangle, polygon, freehand, wand, brush —
|
|
352
|
+
* so there is no separate marquee to learn. Returns how many were selected.
|
|
353
|
+
*/
|
|
354
|
+
selectFromRegions(): number;
|
|
355
|
+
/** Select every observation in one category of a categorical column — the
|
|
356
|
+
* legend click. Rejects for an unknown or continuous column. */
|
|
357
|
+
selectCategory(column: string, categoryIndex: number): Promise<number>;
|
|
358
|
+
/**
|
|
359
|
+
* Select an explicit set of observations, replacing any current selection.
|
|
360
|
+
*
|
|
361
|
+
* For a linked plot: lassoing points in an embedding is a selection OF observations,
|
|
362
|
+
* and every other view — the map, the distributions — reads the same mask, so brushing
|
|
363
|
+
* one lights up the rest. Returns how many were selected.
|
|
364
|
+
*/
|
|
365
|
+
selectIndices(indices: Iterable<number>): number;
|
|
366
|
+
clearSelection(): void;
|
|
367
|
+
}
|
|
273
368
|
/** Display options (colormap/LUT, reverse scale, image metadata). */
|
|
274
369
|
export interface IDisplayOptions {
|
|
275
370
|
getColormap(): Observable<ColormapNode | null>;
|
|
@@ -321,6 +416,14 @@ export interface IVisualizer extends IDataRenderer, IRegionStore, IToolControlle
|
|
|
321
416
|
* the capability-gated replacement for the deprecated top-level
|
|
322
417
|
* `setSurfaceDragMode`/`resetSurfaceCamera`. */
|
|
323
418
|
getSurface3dControls(): ISurface3dControls | null;
|
|
419
|
+
/** Spatial-omics controls when a `SPATIAL_DATA_PORT` is bound, else null.
|
|
420
|
+
* Optional: only the routing service implements it, since the state is shared
|
|
421
|
+
* rather than owned by any one backend. */
|
|
422
|
+
getSpatialControls?(): ISpatialControls | null;
|
|
423
|
+
/** The viewport a contributed plot mode draws over, when the backend on
|
|
424
|
+
* screen can provide one (OpenSeadragon), else null. Optional: backends that
|
|
425
|
+
* no contributed mode rides on need not implement it. */
|
|
426
|
+
getPlotModeViewport?(): PlotModeViewport | null;
|
|
324
427
|
/** Binned intensity histogram for a channel from the currently-displayed
|
|
325
428
|
* pixels, or null when none are available. Feeds the Channels & Histogram
|
|
326
429
|
* pane. */
|
|
@@ -20,14 +20,16 @@ export interface AxesLabelCamera3D {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* Re-exported from napari-js, which owns the projection now.
|
|
24
|
+
*
|
|
25
|
+
* This used to be a local copy: the same column-major multiply, perspective divide and y-flip
|
|
26
|
+
* that {@link NapariVisualizerService.getSpatialScreenProjection} ALSO carried, written twice
|
|
27
|
+
* with two different behind-the-eye conventions. The renderer owns the camera and the clip
|
|
28
|
+
* convention, so it owns the projection; kept as a named export here only so the overlays that
|
|
29
|
+
* import it from this module do not all have to change.
|
|
25
30
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
y: number;
|
|
29
|
-
visible: boolean;
|
|
30
|
-
};
|
|
31
|
+
import { projectPoint } from 'napari-js';
|
|
32
|
+
export { projectPoint };
|
|
31
33
|
export declare class NapariAxesLabels {
|
|
32
34
|
private readonly host;
|
|
33
35
|
private readonly camera3d;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { IRegionOverlay, RegionToolMode } from '../../contracts/region-overlay.contract';
|
|
2
2
|
import { RegionStore } from '../../store/region-store.service';
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* The slice of the napari Viewer the overlay needs (coord transforms + control gating).
|
|
5
|
+
*
|
|
6
|
+
* Exported because the 3D spatial mode supplies a SCREEN-SPACE implementation of it: there the
|
|
7
|
+
* drawn shape is a lasso in canvas pixels, not a rectangle in image space, so "world" is the
|
|
8
|
+
* canvas itself and both transforms collapse to identity. That lets the 3D view reuse this whole
|
|
9
|
+
* overlay — every tool, the handles, the store round-trip — with no 3D-specific drawing code.
|
|
10
|
+
*/
|
|
11
|
+
export interface OverlayViewer {
|
|
5
12
|
canvasToWorld(clientX: number, clientY: number): [number, number];
|
|
6
13
|
worldToCanvas(worldX: number, worldY: number): [number, number];
|
|
7
14
|
setControlsEnabled(enabled: boolean): void;
|
|
@@ -45,11 +52,39 @@ export declare class NapariRegionOverlay implements IRegionOverlay {
|
|
|
45
52
|
* rectangle corner. `anchor` is the fixed opposite corner for a rectangle resize. */
|
|
46
53
|
private edit;
|
|
47
54
|
constructor(host: HTMLElement, viewer: OverlayViewer, store: RegionStore);
|
|
55
|
+
/**
|
|
56
|
+
* Whether a region tool currently owns the pointer.
|
|
57
|
+
*
|
|
58
|
+
* Exposed so the spatial views can tell a bare click on the cloud (select the
|
|
59
|
+
* class under the cursor) from a click that belongs to a drawing gesture —
|
|
60
|
+
* placing a polygon vertex is also a click that does not move, and it must not
|
|
61
|
+
* change the selection behind the shape being drawn.
|
|
62
|
+
*/
|
|
63
|
+
get toolActive(): boolean;
|
|
48
64
|
setMode(mode: RegionToolMode): void;
|
|
49
65
|
setSelectedBezier(bezier: boolean): void;
|
|
66
|
+
/**
|
|
67
|
+
* How finely a drawn vertex may be placed, in world units.
|
|
68
|
+
*
|
|
69
|
+
* One by default, because for an image the world IS pixels and a region should align to
|
|
70
|
+
* them. A dataset whose coordinates are not pixels sets it finer — seqFISH's whole
|
|
71
|
+
* sample spans about 5 x 7 units, where whole-unit vertices leave roughly six by eight
|
|
72
|
+
* placeable positions and no ROI can be drawn at all.
|
|
73
|
+
*/
|
|
74
|
+
private worldQuantum;
|
|
75
|
+
setWorldQuantum(quantum: number): void;
|
|
50
76
|
destroy(): void;
|
|
51
|
-
/** Pointer client coords →
|
|
77
|
+
/** Pointer client coords → world coords, snapped to {@link worldQuantum}. */
|
|
52
78
|
private toImage;
|
|
79
|
+
/**
|
|
80
|
+
* World units spanned by one screen pixel, measured through the viewer's own transform.
|
|
81
|
+
*
|
|
82
|
+
* Read from the transform rather than from the camera, so it holds for both the 2D and
|
|
83
|
+
* the 3D screen-space adapters without either having to expose a zoom. Falls back to 1
|
|
84
|
+
* if the transform gives nothing usable, which keeps the established pixel behaviour
|
|
85
|
+
* rather than collapsing the threshold to zero and recording a point per event.
|
|
86
|
+
*/
|
|
87
|
+
private worldPerCanvasPixel;
|
|
53
88
|
/** Image coords → SVG-local px (the svg overlays the canvas at the same client rect). */
|
|
54
89
|
private toLocal;
|
|
55
90
|
private readonly onPointerDown;
|
|
@@ -112,4 +147,3 @@ export declare class NapariRegionOverlay implements IRegionOverlay {
|
|
|
112
147
|
private drawDraft;
|
|
113
148
|
private styleDraft;
|
|
114
149
|
}
|
|
115
|
-
export {};
|
|
@@ -8,8 +8,13 @@
|
|
|
8
8
|
/** Format a length given in micrometres, picking the unit that suits its magnitude (jit-service
|
|
9
9
|
* normalises whatever unit Bio-Formats reports to µm). */
|
|
10
10
|
export declare function formatUm(um: number): string;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The slice of the napari Camera the scale bar reads (zoom = CSS px per world/image pixel).
|
|
13
|
+
*
|
|
14
|
+
* Exported so the 3D spatial view can satisfy it with a shim: an orbit camera has no `zoom`, but
|
|
15
|
+
* px-per-world-unit AT THE PIVOT DEPTH is well defined, and that is what a 3D scale bar means.
|
|
16
|
+
*/
|
|
17
|
+
export interface ScaleBarCamera {
|
|
13
18
|
readonly zoom: number;
|
|
14
19
|
readonly changed: {
|
|
15
20
|
connect(listener: () => void): () => void;
|
|
@@ -28,4 +33,3 @@ export declare class NapariScaleBar {
|
|
|
28
33
|
private update;
|
|
29
34
|
destroy(): void;
|
|
30
35
|
}
|
|
31
|
-
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor tooltip naming the observation under the pointer, for the spatial-omics
|
|
3
|
+
* views — the same overlay-in-the-host pattern as {@link NapariScaleBar}.
|
|
4
|
+
*
|
|
5
|
+
* A 34-entry legend cannot be read back from a dot: several classes get similar
|
|
6
|
+
* colours, and matching one to a swatch by eye is the job a tooltip removes. It
|
|
7
|
+
* shows what the cloud is CURRENTLY coloured by, so it always agrees with the
|
|
8
|
+
* legend beside it.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately not PrimeNG's `pTooltip`: that binds to an element, and these
|
|
11
|
+
* points are pixels in a WebGPU canvas with no DOM of their own.
|
|
12
|
+
*/
|
|
13
|
+
export declare class NapariSpatialTooltip {
|
|
14
|
+
private readonly host;
|
|
15
|
+
private readonly el;
|
|
16
|
+
private readonly primary;
|
|
17
|
+
private readonly secondary;
|
|
18
|
+
/** Gap between the cursor and the box, so the box never sits under the arrow. */
|
|
19
|
+
private static readonly OFFSET;
|
|
20
|
+
constructor(host: HTMLElement);
|
|
21
|
+
/**
|
|
22
|
+
* Show `lines` at a point given in HOST-relative pixels.
|
|
23
|
+
*
|
|
24
|
+
* Flips to the other side of the cursor near an edge rather than being clipped
|
|
25
|
+
* by the host — a tooltip half outside the plot is worse than one on the left.
|
|
26
|
+
*/
|
|
27
|
+
show(lines: readonly string[], hostX: number, hostY: number): void;
|
|
28
|
+
hide(): void;
|
|
29
|
+
/** True while the box is on screen — for tests, and to skip redundant hides. */
|
|
30
|
+
get visible(): boolean;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
}
|