@jax-data-science/sci-image-visualizer 0.3.2 → 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 (93) hide show
  1. package/README.md +215 -11
  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/segmentation/cellpose-segmenter.service.mjs +2 -2
  41. package/esm2022/lib/toolbar/segmentation/sam-model-registry.mjs +9 -9
  42. package/esm2022/lib/toolbar/toolbar.component.mjs +30 -7
  43. package/esm2022/lib/visualization.module.mjs +23 -5
  44. package/esm2022/lib/visualizer.component.mjs +283 -22
  45. 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
  46. 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
  47. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs +12 -0
  48. package/fesm2022/jax-data-science-sci-image-visualizer-tsne-worker-B_nXDGf0.mjs.map +1 -0
  49. package/fesm2022/jax-data-science-sci-image-visualizer.mjs +15282 -7829
  50. package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
  51. package/fesm2022/tsne.worker.js +19091 -0
  52. package/index.d.ts +15 -0
  53. package/lib/contracts/display-types.d.ts +131 -0
  54. package/lib/contracts/image.contract.d.ts +7 -0
  55. package/lib/contracts/plot-type.d.ts +42 -2
  56. package/lib/contracts/ports/spatial-data.port.d.ts +90 -0
  57. package/lib/contracts/spatial-dataset.contract.d.ts +281 -0
  58. package/lib/contracts/visualizer.contract.d.ts +99 -1
  59. package/lib/implementations/napari-js/napari-axes-labels.d.ts +9 -7
  60. package/lib/implementations/napari-js/napari-region-overlay.d.ts +38 -4
  61. package/lib/implementations/napari-js/napari-scale-bar.d.ts +7 -3
  62. package/lib/implementations/napari-js/napari-spatial-tooltip.d.ts +32 -0
  63. package/lib/implementations/napari-js/napari-visualizer.service.d.ts +497 -16
  64. package/lib/implementations/napari-js/napari-zoom.d.ts +15 -0
  65. package/lib/implementations/osd/osd-zoom.d.ts +19 -0
  66. package/lib/implementations/plotly/omics-trace-builders.d.ts +193 -0
  67. package/lib/implementations/plotly/plotly.service.d.ts +9 -0
  68. package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +123 -0
  69. package/lib/implementations/spatial-data-http/spatial-wire.d.ts +115 -0
  70. package/lib/routing-visualizer.service.d.ts +45 -6
  71. package/lib/spatial/embedding-compute.d.ts +73 -0
  72. package/lib/spatial/gene-search.d.ts +48 -0
  73. package/lib/spatial/spatial-density.d.ts +73 -0
  74. package/lib/spatial/spatial-encoding.d.ts +155 -0
  75. package/lib/spatial/spatial-expression.d.ts +144 -0
  76. package/lib/spatial/spatial-framing.d.ts +37 -0
  77. package/lib/spatial/spatial-heatmap.d.ts +111 -0
  78. package/lib/spatial/spatial-hover.d.ts +49 -0
  79. package/lib/spatial/spatial-sections.d.ts +59 -0
  80. package/lib/spatial/spatial-selection.d.ts +113 -0
  81. package/lib/spatial/spatial-volume-image.d.ts +67 -0
  82. package/lib/spatial/tsne-worker.d.ts +7 -0
  83. package/lib/spatial/world-grid.d.ts +58 -0
  84. package/lib/spatial-controls/spatial-charts/spatial-charts.component.d.ts +439 -0
  85. package/lib/spatial-controls/spatial-controls.component.d.ts +257 -0
  86. package/lib/store/spatial-selection.service.d.ts +27 -0
  87. package/lib/store/visualizer-store.service.d.ts +13 -0
  88. package/lib/toolbar/toolbar.component.d.ts +19 -3
  89. package/lib/visualization.module.d.ts +28 -24
  90. package/lib/visualizer.component.d.ts +73 -2
  91. package/package.json +7 -3
  92. package/src/lib/assets/spatial-omics-3d.svg +31 -0
  93. package/src/lib/assets/spatial-omics.svg +27 -0
@@ -7,7 +7,9 @@ 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';
11
13
  /**
12
14
  * Backend-neutral visualization contract. Plotly is one implementation;
13
15
  * OpenSeadragon is another (for the image plot type). The abstraction renders
@@ -270,6 +272,98 @@ export interface IIntensityControls {
270
272
  * backend, or null if no image extent is known yet. */
271
273
  addProfileLine(): Region | null;
272
274
  }
275
+ /** A categorical column resolved for charting: labels, codes and map colours. */
276
+ export interface SpatialCategoricalView {
277
+ name: string;
278
+ categories: string[];
279
+ /** `#rrggbb` per category, index-aligned with {@link categories}. */
280
+ colors: string[];
281
+ /** Per-observation category index, or `NO_CATEGORY`. */
282
+ codes: Uint16Array;
283
+ }
284
+ /**
285
+ * Controls for the SPATIAL_OMICS plot type: what the observation markers are
286
+ * coloured by, and how they are drawn. Capability-gated like
287
+ * {@link IIsosurfaceControls} — `getSpatialControls()` returns null unless a
288
+ * host has bound `SPATIAL_DATA_PORT`.
289
+ *
290
+ * The view state is backend-neutral (it lives in the shared store, like the
291
+ * colormap), so these controls work whichever backend is on screen.
292
+ */
293
+ export interface ISpatialControls {
294
+ /** The dataset being visualized, or null. Drives the column/gene pickers and
295
+ * the legend. */
296
+ getDataset$(): Observable<SpatialDataset | null>;
297
+ /** Current display state (colour source, point scale, opacity, scaling). */
298
+ getViewState$(): Observable<SpatialViewState>;
299
+ /** Synchronous read, for seeding a control's initial value. */
300
+ viewState(): SpatialViewState;
301
+ /** Patch the display state; the markers rebuild without remounting the scene. */
302
+ setViewState(partial: Partial<SpatialViewState>): void;
303
+ /** Colour by an annotation column. Rejects for an unknown column. */
304
+ colorByColumn(name: string): void;
305
+ /** Colour by a gene; its vector is fetched on demand. */
306
+ colorByFeature(name: string): void;
307
+ /** Clear the colour source — every observation renders in one neutral colour. */
308
+ clearColorBy(): void;
309
+ /** Typeahead over feature names, for datasets too wide to inline the list. */
310
+ searchFeatures(query: string, limit?: number): Promise<string[]>;
311
+ /** Per-category display colours for a categorical column, index-aligned with
312
+ * its `categories` — the legend's swatches, resolved the same way the
313
+ * renderer resolves them so the two cannot disagree. */
314
+ categoryColors(name: string): Promise<string[]>;
315
+ /**
316
+ * The values behind a colour source, index-aligned with the observations.
317
+ * Rejects for a categorical column — there is nothing continuous to chart.
318
+ */
319
+ continuousValues(source: SpatialColorBy): Promise<Float32Array>;
320
+ /**
321
+ * A categorical column's categories, per-observation codes and display
322
+ * colours — everything a grouped violin/box needs, with the colours resolved
323
+ * the same way the map resolves them.
324
+ */
325
+ categoricalView(column: string): Promise<SpatialCategoricalView>;
326
+ /**
327
+ * One embedding's coordinates — a UMAP, t-SNE or PCA plane over the same observations.
328
+ *
329
+ * Optional, and absent when the data source serves none: `SpatialDataPort.getEmbedding`
330
+ * is itself optional, so a panel must be able to ask whether this exists rather than
331
+ * calling it and catching.
332
+ */
333
+ getEmbedding?(name: string): Promise<SpatialEmbedding>;
334
+ /** Names of the categorical columns available to group by. */
335
+ categoricalColumns(): string[];
336
+ /**
337
+ * The z positions of the sections the dataset was imaged at, ascending — or
338
+ * null when its z is continuous rather than sectioned (so there are no sections
339
+ * to offer) or when there is no dataset.
340
+ *
341
+ * Lets a panel offer "one section at a time" for the 3D cloud only where that
342
+ * means something, and label the chosen one. Scanned once per dataset.
343
+ */
344
+ sampledSections(): Float32Array | null;
345
+ /** The current selection. Empty means "nothing selected", in which case the
346
+ * whole tissue renders normally rather than everything being muted. */
347
+ getSelection$(): Observable<SpatialSelectionMask>;
348
+ /**
349
+ * Select every observation inside the currently-drawn regions (their union).
350
+ * Reuses the existing ROI tools — rectangle, polygon, freehand, wand, brush —
351
+ * so there is no separate marquee to learn. Returns how many were selected.
352
+ */
353
+ selectFromRegions(): number;
354
+ /** Select every observation in one category of a categorical column — the
355
+ * legend click. Rejects for an unknown or continuous column. */
356
+ selectCategory(column: string, categoryIndex: number): Promise<number>;
357
+ /**
358
+ * Select an explicit set of observations, replacing any current selection.
359
+ *
360
+ * For a linked plot: lassoing points in an embedding is a selection OF observations,
361
+ * and every other view — the map, the distributions — reads the same mask, so brushing
362
+ * one lights up the rest. Returns how many were selected.
363
+ */
364
+ selectIndices(indices: Iterable<number>): number;
365
+ clearSelection(): void;
366
+ }
273
367
  /** Display options (colormap/LUT, reverse scale, image metadata). */
274
368
  export interface IDisplayOptions {
275
369
  getColormap(): Observable<ColormapNode | null>;
@@ -321,6 +415,10 @@ export interface IVisualizer extends IDataRenderer, IRegionStore, IToolControlle
321
415
  * the capability-gated replacement for the deprecated top-level
322
416
  * `setSurfaceDragMode`/`resetSurfaceCamera`. */
323
417
  getSurface3dControls(): ISurface3dControls | null;
418
+ /** Spatial-omics controls when a `SPATIAL_DATA_PORT` is bound, else null.
419
+ * Optional: only the routing service implements it, since the state is shared
420
+ * rather than owned by any one backend. */
421
+ getSpatialControls?(): ISpatialControls | null;
324
422
  /** Binned intensity histogram for a channel from the currently-displayed
325
423
  * pixels, or null when none are available. Feeds the Channels & Histogram
326
424
  * pane. */
@@ -20,14 +20,16 @@ export interface AxesLabelCamera3D {
20
20
  };
21
21
  }
22
22
  /**
23
- * Project a world point through a column-major 4×4 MVP to CSS-pixel screen coordinates.
24
- * Returns `visible: false` when the point is at/behind the camera (w ≤ 0). Pure — unit-tested.
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
- export declare function projectPoint(mvp: ArrayLike<number>, p: [number, number, number], vw: number, vh: number): {
27
- x: number;
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
- /** The slice of the napari Viewer the overlay needs (coord transforms + control gating). */
4
- interface OverlayViewer {
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 → rounded image 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
- /** The slice of the napari Camera the scale bar reads (zoom = CSS px per world/image pixel). */
12
- interface ScaleBarCamera {
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
+ }