@jax-data-science/sci-image-visualizer 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/channel-histogram/channel-histogram.component.mjs +39 -3
- package/esm2022/lib/channel-histogram/channel-tree.mjs +65 -0
- package/esm2022/lib/contracts/channel-histogram-api.contract.mjs +19 -1
- package/esm2022/lib/contracts/display-types.mjs +7 -1
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/contracts/spatial-dataset.contract.mjs +1 -1
- package/esm2022/lib/contracts/visualizer.contract.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-image-2d-scene.mjs +17 -2
- package/esm2022/lib/implementations/napari-js/napari-spatial-2d-scene.mjs +88 -10
- package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +62 -13
- package/esm2022/lib/implementations/napari-js/napari-spatial-encoding.mjs +21 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-scene.mjs +5 -1
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +4 -1
- package/esm2022/lib/implementations/napari-js/spatial-tiles/categorical-lookup.mjs +26 -3
- package/esm2022/lib/implementations/napari-js/spatial-tiles/cell-layers.mjs +7 -9
- package/esm2022/lib/implementations/napari-js/spatial-tiles/density-layer.mjs +20 -4
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-hover.mjs +4 -2
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-jobs.mjs +4 -2
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-layers.mjs +42 -15
- package/esm2022/lib/implementations/osd/openseadragon-visualizer.service.mjs +3 -1
- package/esm2022/lib/spatial/lod.mjs +24 -3
- package/esm2022/lib/spatial/spatial-controls.facade.mjs +8 -3
- package/esm2022/lib/spatial/spatial-gene-filter.mjs +138 -0
- package/esm2022/lib/spatial/spatial-tile-merge.mjs +42 -1
- package/esm2022/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.mjs +47 -10
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +4 -3
- package/esm2022/lib/spatial-controls/spatial-gene-style/spatial-gene-style.component.mjs +66 -0
- package/esm2022/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.mjs +106 -26
- package/esm2022/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.mjs +3 -3
- package/esm2022/lib/spatial-controls/spatial-key/spatial-key.component.mjs +3 -3
- package/esm2022/lib/spatial-controls/spatial-marker-genes-form/spatial-marker-genes-form.component.mjs +3 -3
- package/esm2022/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.mjs +110 -8
- package/esm2022/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.mjs +41 -9
- package/esm2022/lib/store/visualizer-store.service.mjs +67 -1
- package/fesm2022/{jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DURPqs5a.mjs → jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-qUeMuBb0.mjs} +1250 -335
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-qUeMuBb0.mjs.map +1 -0
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-D0IQ6PjZ.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-U1ODcfYD.mjs} +3 -2
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-D0IQ6PjZ.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-U1ODcfYD.mjs.map} +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +2 -1
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs.map +1 -1
- package/lib/channel-histogram/channel-histogram.component.d.ts +14 -0
- package/lib/channel-histogram/channel-tree.d.ts +30 -0
- package/lib/contracts/channel-histogram-api.contract.d.ts +16 -0
- package/lib/contracts/display-types.d.ts +27 -0
- package/lib/contracts/image.contract.d.ts +8 -0
- package/lib/contracts/spatial-dataset.contract.d.ts +7 -0
- package/lib/contracts/visualizer.contract.d.ts +3 -0
- package/lib/implementations/napari-js/napari-image-2d-scene.d.ts +7 -0
- package/lib/implementations/napari-js/napari-spatial-2d-scene.d.ts +13 -2
- package/lib/implementations/napari-js/napari-spatial-encoding.d.ts +5 -0
- package/lib/implementations/napari-js/napari-spatial-scene.d.ts +4 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +2 -0
- package/lib/implementations/napari-js/spatial-tiles/categorical-lookup.d.ts +6 -1
- package/lib/implementations/napari-js/spatial-tiles/transcript-hover.d.ts +4 -0
- package/lib/implementations/napari-js/spatial-tiles/transcript-layers.d.ts +3 -1
- package/lib/spatial/lod.d.ts +14 -2
- package/lib/spatial/spatial-controls.facade.d.ts +3 -0
- package/lib/spatial/spatial-gene-filter.d.ts +44 -0
- package/lib/spatial/spatial-tile-merge.d.ts +17 -0
- package/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.d.ts +18 -1
- package/lib/spatial-controls/spatial-gene-style/spatial-gene-style.component.d.ts +30 -0
- package/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.d.ts +39 -7
- package/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.d.ts +33 -1
- package/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.d.ts +13 -1
- package/lib/store/visualizer-store.service.d.ts +19 -0
- package/package.json +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DURPqs5a.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LruCache } from 'napari-js';
|
|
2
2
|
import * as ort from 'onnxruntime-web';
|
|
3
|
-
import { g as getOrtWasmBase, f as fetchModel, r as runEncoder, a as runDecoder } from './jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-
|
|
3
|
+
import { g as getOrtWasmBase, f as fetchModel, r as runEncoder, a as runDecoder } from './jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-qUeMuBb0.mjs';
|
|
4
4
|
import '@angular/core';
|
|
5
5
|
import '@angular/common/http';
|
|
6
6
|
import 'rxjs';
|
|
@@ -38,6 +38,7 @@ import 'primeng/selectbutton';
|
|
|
38
38
|
import 'primeng/treeselect';
|
|
39
39
|
import 'plotly.js-dist-min';
|
|
40
40
|
import 'primeng/multiselect';
|
|
41
|
+
import 'primeng/inputswitch';
|
|
41
42
|
import 'image-js';
|
|
42
43
|
import 'buffer';
|
|
43
44
|
import 'openseadragon';
|
|
@@ -237,4 +238,4 @@ class OnnxSamSession {
|
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
export { OnnxSamSession };
|
|
240
|
-
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-
|
|
241
|
+
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-U1ODcfYD.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jax-data-science-sci-image-visualizer-onnx-sam-session-D0IQ6PjZ.mjs","sources":["../../src/lib/toolbar/segmentation/pending-calls.ts","../../src/lib/toolbar/segmentation/onnx-sam-session.ts"],"sourcesContent":["/** A reply from the SAM worker: progress for, or the result of, call `id`. */\nexport interface WorkerReply {\n id: number;\n type: string;\n /** Present on `type: 'progress'` replies. */\n fraction?: number;\n /** Present on `type: 'error'` replies. */\n error?: string;\n [key: string]: unknown;\n}\n\ninterface Pending {\n resolve: (reply: WorkerReply) => void;\n reject: (err: Error) => void;\n onProgress?: (fraction: number) => void;\n}\n\n/**\n * Request/response bookkeeping for a worker RPC: each call gets an id and a\n * promise that its reply settles. Every exit path (dispose, a worker crash)\n * must {@link rejectAll}, or awaiting callers never reach their `finally` and a\n * tool's busy flag sticks forever (RT-8).\n */\nexport class PendingCalls {\n private seq = 0;\n private readonly pending = new Map<number, Pending>();\n\n /** Calls still awaiting a reply. */\n get size(): number {\n return this.pending.size;\n }\n\n /** Register a new call; post the message with the returned id. */\n open(onProgress?: (fraction: number) => void): { id: number; promise: Promise<WorkerReply> } {\n const id = ++this.seq;\n const promise = new Promise<WorkerReply>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, onProgress });\n });\n return { id, promise };\n }\n\n /** Route a worker reply to its call: progress, error or result. */\n handle(reply: WorkerReply): void {\n const p = this.pending.get(reply.id);\n if (!p) return;\n if (reply.type === 'progress') {\n p.onProgress?.(reply.fraction ?? 0);\n return;\n }\n this.pending.delete(reply.id);\n if (reply.type === 'error') p.reject(new Error(reply.error ?? 'SAM worker error.'));\n else p.resolve(reply);\n }\n\n /** Reject every pending call with `err` and forget them. */\n rejectAll(err: Error): void {\n const calls = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of calls) p.reject(err);\n }\n}\n","import { LruCache } from 'napari-js';\nimport * as ort from 'onnxruntime-web';\n\nimport { ISamSession, SamEmbedding, SamMaskResult, SamModelDef, SamPrompt } from '../../contracts/sam.contract';\nimport { fetchModel, runEncoder, runDecoder, type CoreEmbedding } from './sam-onnx-core';\nimport { getOrtWasmBase } from './ort-runtime-config';\nimport { PendingCalls, WorkerReply } from './pending-calls';\n\n/**\n * onnxruntime-web implementation of {@link ISamSession} (jit-ui#90), with two\n * execution modes chosen per model:\n *\n * - **in-process** (main thread) — for small WASM models flagged `inProcess`\n * (e.g. micro-sam ViT-T). Fast load + inference with no Web Worker spawn /\n * second ORT runtime / message round-trips; the brief encode runs on the main\n * thread (fine for a tiny model).\n * - **worker** — for WebGPU models (e.g. ViT-B, patho-sam fp16) whose heavy\n * encode would otherwise freeze the tab. Runs in {@link ./onnx-sam.worker} so\n * the UI stays responsive (the spinner animates).\n *\n * Both modes share their inference math via {@link ./sam-onnx-core}. Lazy-imported\n * by the SAM tools so onnxruntime-web / the worker never load in unit tests or\n * the initial bundle.\n */\nexport class OnnxSamSession implements ISamSession {\n private mode: 'inproc' | 'worker' | null = null;\n private loaded = false;\n private inputSize = 1024;\n\n // ── worker mode ──\n private worker: Worker | null = null;\n private readonly calls = new PendingCalls();\n\n // ── in-process mode ──\n private encoder: ort.InferenceSession | null = null;\n private decoder: ort.InferenceSession | null = null;\n private nextToken = 1;\n /** The few most recently used embeddings, by token (one per recently viewed image). */\n private readonly embeddings = new LruCache<CoreEmbedding, number>(3);\n\n async loadModel(model: SamModelDef, onProgress?: (fraction: number) => void): Promise<void> {\n if (!model.encoderUrl || !model.decoderUrl) {\n throw new Error(`SAM model \"${model.id}\" has no ONNX URLs configured.`);\n }\n this.inputSize = model.inputSize;\n const hasGpu = typeof navigator !== 'undefined' && 'gpu' in navigator;\n const eps: string[] = model.encoderProviders ?? (hasGpu ? ['webgpu', 'wasm'] : ['wasm']);\n const usesWebGpu = eps.includes('webgpu');\n // WebGPU must run in the worker (else it freezes the main thread); small\n // WASM models opt into the faster in-process path via `inProcess`.\n this.mode = !usesWebGpu && model.inProcess ? 'inproc' : 'worker';\n\n if (this.mode === 'inproc') {\n ort.env.wasm.wasmPaths = getOrtWasmBase();\n const encBuf = await fetchModel(model.encoderUrl, onProgress, model.revision);\n const decBuf = await fetchModel(model.decoderUrl, undefined, model.revision);\n this.encoder = await ort.InferenceSession.create(encBuf, { executionProviders: eps });\n this.decoder = await ort.InferenceSession.create(decBuf, { executionProviders: ['wasm'] });\n } else {\n await this.call(\n {\n type: 'load',\n encoderUrl: model.encoderUrl,\n decoderUrl: model.decoderUrl,\n revision: model.revision,\n wasmPaths: getOrtWasmBase(),\n inputSize: model.inputSize,\n encoderProviders: model.encoderProviders,\n },\n [],\n onProgress,\n );\n }\n this.loaded = true;\n }\n\n isLoaded(): boolean {\n return this.loaded;\n }\n\n async embed(image: { data: Uint8ClampedArray; width: number; height: number }): Promise<SamEmbedding> {\n if (this.mode === 'inproc') {\n const e = await runEncoder(this.encoder!, image.data, image.width, image.height, this.inputSize);\n const token = this.nextToken++;\n this.embeddings.set(token, e);\n return {\n data: new Float32Array(0),\n dims: e.dims,\n scale: e.scale,\n imageWidth: e.imageWidth,\n imageHeight: e.imageHeight,\n token,\n };\n }\n // worker: transfer the RGBA buffer (callers pass a fresh frame buffer).\n const buffer = image.data.buffer;\n const res = await this.call({ type: 'embed', width: image.width, height: image.height, buffer }, [buffer]);\n return {\n data: new Float32Array(0),\n dims: res['dims'] as number[],\n scale: res['scale'] as number,\n imageWidth: res['imageWidth'] as number,\n imageHeight: res['imageHeight'] as number,\n token: res['token'] as number,\n };\n }\n\n async decode(embedding: SamEmbedding, prompt: SamPrompt): Promise<SamMaskResult> {\n if (this.mode === 'inproc') {\n const e = this.embeddings.get(embedding.token as number);\n if (!e) throw new Error('SAM embedding expired; re-encode the image.');\n return runDecoder(this.decoder!, e, prompt);\n }\n const res = await this.call({ type: 'decode', token: embedding.token, prompt });\n return {\n mask: new Uint8Array(res['buffer'] as ArrayBuffer),\n width: res['width'] as number,\n height: res['height'] as number,\n iou: res['iou'] as number,\n };\n }\n\n /** Release the worker / ORT sessions. Calls still in flight reject (RT-8), so\n * their callers' `finally` blocks run (e.g. a tool's busy flag clears). */\n dispose(): void {\n this.dropWorker(new Error('SAM session disposed'));\n this.encoder?.release?.();\n this.decoder?.release?.();\n this.encoder = this.decoder = null;\n this.embeddings.clear();\n this.loaded = false;\n this.mode = null;\n }\n\n // ── worker plumbing ──────────────────────────────────────────────────────\n private ensureWorker(): Worker {\n if (this.worker) return this.worker;\n const worker = new Worker(new URL('./onnx-sam.worker', import.meta.url), { type: 'module' });\n worker.onmessage = (ev: MessageEvent<WorkerReply>) => this.calls.handle(ev.data);\n worker.onerror = (ev: ErrorEvent) => {\n // A crashed worker can't serve the loaded model any more: drop it, so the\n // next run reloads instead of posting to a dead worker.\n this.dropWorker(new Error(ev.message || 'SAM worker crashed.'));\n this.loaded = false;\n this.mode = null;\n };\n this.worker = worker;\n return worker;\n }\n\n /** Terminate the worker (if any) and reject every call still waiting on it. */\n private dropWorker(err: Error): void {\n this.worker?.terminate();\n this.worker = null;\n this.calls.rejectAll(err);\n }\n\n private call(\n msg: Record<string, unknown>,\n transfer: Transferable[] = [],\n onProgress?: (f: number) => void,\n ): Promise<WorkerReply> {\n const worker = this.ensureWorker();\n const { id, promise } = this.calls.open(onProgress);\n worker.postMessage({ ...msg, id }, transfer);\n return promise;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jax-data-science-sci-image-visualizer-onnx-sam-session-U1ODcfYD.mjs","sources":["../../src/lib/toolbar/segmentation/pending-calls.ts","../../src/lib/toolbar/segmentation/onnx-sam-session.ts"],"sourcesContent":["/** A reply from the SAM worker: progress for, or the result of, call `id`. */\nexport interface WorkerReply {\n id: number;\n type: string;\n /** Present on `type: 'progress'` replies. */\n fraction?: number;\n /** Present on `type: 'error'` replies. */\n error?: string;\n [key: string]: unknown;\n}\n\ninterface Pending {\n resolve: (reply: WorkerReply) => void;\n reject: (err: Error) => void;\n onProgress?: (fraction: number) => void;\n}\n\n/**\n * Request/response bookkeeping for a worker RPC: each call gets an id and a\n * promise that its reply settles. Every exit path (dispose, a worker crash)\n * must {@link rejectAll}, or awaiting callers never reach their `finally` and a\n * tool's busy flag sticks forever (RT-8).\n */\nexport class PendingCalls {\n private seq = 0;\n private readonly pending = new Map<number, Pending>();\n\n /** Calls still awaiting a reply. */\n get size(): number {\n return this.pending.size;\n }\n\n /** Register a new call; post the message with the returned id. */\n open(onProgress?: (fraction: number) => void): { id: number; promise: Promise<WorkerReply> } {\n const id = ++this.seq;\n const promise = new Promise<WorkerReply>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, onProgress });\n });\n return { id, promise };\n }\n\n /** Route a worker reply to its call: progress, error or result. */\n handle(reply: WorkerReply): void {\n const p = this.pending.get(reply.id);\n if (!p) return;\n if (reply.type === 'progress') {\n p.onProgress?.(reply.fraction ?? 0);\n return;\n }\n this.pending.delete(reply.id);\n if (reply.type === 'error') p.reject(new Error(reply.error ?? 'SAM worker error.'));\n else p.resolve(reply);\n }\n\n /** Reject every pending call with `err` and forget them. */\n rejectAll(err: Error): void {\n const calls = Array.from(this.pending.values());\n this.pending.clear();\n for (const p of calls) p.reject(err);\n }\n}\n","import { LruCache } from 'napari-js';\nimport * as ort from 'onnxruntime-web';\n\nimport { ISamSession, SamEmbedding, SamMaskResult, SamModelDef, SamPrompt } from '../../contracts/sam.contract';\nimport { fetchModel, runEncoder, runDecoder, type CoreEmbedding } from './sam-onnx-core';\nimport { getOrtWasmBase } from './ort-runtime-config';\nimport { PendingCalls, WorkerReply } from './pending-calls';\n\n/**\n * onnxruntime-web implementation of {@link ISamSession} (jit-ui#90), with two\n * execution modes chosen per model:\n *\n * - **in-process** (main thread) — for small WASM models flagged `inProcess`\n * (e.g. micro-sam ViT-T). Fast load + inference with no Web Worker spawn /\n * second ORT runtime / message round-trips; the brief encode runs on the main\n * thread (fine for a tiny model).\n * - **worker** — for WebGPU models (e.g. ViT-B, patho-sam fp16) whose heavy\n * encode would otherwise freeze the tab. Runs in {@link ./onnx-sam.worker} so\n * the UI stays responsive (the spinner animates).\n *\n * Both modes share their inference math via {@link ./sam-onnx-core}. Lazy-imported\n * by the SAM tools so onnxruntime-web / the worker never load in unit tests or\n * the initial bundle.\n */\nexport class OnnxSamSession implements ISamSession {\n private mode: 'inproc' | 'worker' | null = null;\n private loaded = false;\n private inputSize = 1024;\n\n // ── worker mode ──\n private worker: Worker | null = null;\n private readonly calls = new PendingCalls();\n\n // ── in-process mode ──\n private encoder: ort.InferenceSession | null = null;\n private decoder: ort.InferenceSession | null = null;\n private nextToken = 1;\n /** The few most recently used embeddings, by token (one per recently viewed image). */\n private readonly embeddings = new LruCache<CoreEmbedding, number>(3);\n\n async loadModel(model: SamModelDef, onProgress?: (fraction: number) => void): Promise<void> {\n if (!model.encoderUrl || !model.decoderUrl) {\n throw new Error(`SAM model \"${model.id}\" has no ONNX URLs configured.`);\n }\n this.inputSize = model.inputSize;\n const hasGpu = typeof navigator !== 'undefined' && 'gpu' in navigator;\n const eps: string[] = model.encoderProviders ?? (hasGpu ? ['webgpu', 'wasm'] : ['wasm']);\n const usesWebGpu = eps.includes('webgpu');\n // WebGPU must run in the worker (else it freezes the main thread); small\n // WASM models opt into the faster in-process path via `inProcess`.\n this.mode = !usesWebGpu && model.inProcess ? 'inproc' : 'worker';\n\n if (this.mode === 'inproc') {\n ort.env.wasm.wasmPaths = getOrtWasmBase();\n const encBuf = await fetchModel(model.encoderUrl, onProgress, model.revision);\n const decBuf = await fetchModel(model.decoderUrl, undefined, model.revision);\n this.encoder = await ort.InferenceSession.create(encBuf, { executionProviders: eps });\n this.decoder = await ort.InferenceSession.create(decBuf, { executionProviders: ['wasm'] });\n } else {\n await this.call(\n {\n type: 'load',\n encoderUrl: model.encoderUrl,\n decoderUrl: model.decoderUrl,\n revision: model.revision,\n wasmPaths: getOrtWasmBase(),\n inputSize: model.inputSize,\n encoderProviders: model.encoderProviders,\n },\n [],\n onProgress,\n );\n }\n this.loaded = true;\n }\n\n isLoaded(): boolean {\n return this.loaded;\n }\n\n async embed(image: { data: Uint8ClampedArray; width: number; height: number }): Promise<SamEmbedding> {\n if (this.mode === 'inproc') {\n const e = await runEncoder(this.encoder!, image.data, image.width, image.height, this.inputSize);\n const token = this.nextToken++;\n this.embeddings.set(token, e);\n return {\n data: new Float32Array(0),\n dims: e.dims,\n scale: e.scale,\n imageWidth: e.imageWidth,\n imageHeight: e.imageHeight,\n token,\n };\n }\n // worker: transfer the RGBA buffer (callers pass a fresh frame buffer).\n const buffer = image.data.buffer;\n const res = await this.call({ type: 'embed', width: image.width, height: image.height, buffer }, [buffer]);\n return {\n data: new Float32Array(0),\n dims: res['dims'] as number[],\n scale: res['scale'] as number,\n imageWidth: res['imageWidth'] as number,\n imageHeight: res['imageHeight'] as number,\n token: res['token'] as number,\n };\n }\n\n async decode(embedding: SamEmbedding, prompt: SamPrompt): Promise<SamMaskResult> {\n if (this.mode === 'inproc') {\n const e = this.embeddings.get(embedding.token as number);\n if (!e) throw new Error('SAM embedding expired; re-encode the image.');\n return runDecoder(this.decoder!, e, prompt);\n }\n const res = await this.call({ type: 'decode', token: embedding.token, prompt });\n return {\n mask: new Uint8Array(res['buffer'] as ArrayBuffer),\n width: res['width'] as number,\n height: res['height'] as number,\n iou: res['iou'] as number,\n };\n }\n\n /** Release the worker / ORT sessions. Calls still in flight reject (RT-8), so\n * their callers' `finally` blocks run (e.g. a tool's busy flag clears). */\n dispose(): void {\n this.dropWorker(new Error('SAM session disposed'));\n this.encoder?.release?.();\n this.decoder?.release?.();\n this.encoder = this.decoder = null;\n this.embeddings.clear();\n this.loaded = false;\n this.mode = null;\n }\n\n // ── worker plumbing ──────────────────────────────────────────────────────\n private ensureWorker(): Worker {\n if (this.worker) return this.worker;\n const worker = new Worker(new URL('./onnx-sam.worker', import.meta.url), { type: 'module' });\n worker.onmessage = (ev: MessageEvent<WorkerReply>) => this.calls.handle(ev.data);\n worker.onerror = (ev: ErrorEvent) => {\n // A crashed worker can't serve the loaded model any more: drop it, so the\n // next run reloads instead of posting to a dead worker.\n this.dropWorker(new Error(ev.message || 'SAM worker crashed.'));\n this.loaded = false;\n this.mode = null;\n };\n this.worker = worker;\n return worker;\n }\n\n /** Terminate the worker (if any) and reject every call still waiting on it. */\n private dropWorker(err: Error): void {\n this.worker?.terminate();\n this.worker = null;\n this.calls.rejectAll(err);\n }\n\n private call(\n msg: Record<string, unknown>,\n transfer: Transferable[] = [],\n onProgress?: (f: number) => void,\n ): Promise<WorkerReply> {\n const worker = this.ensureWorker();\n const { id, promise } = this.calls.open(onProgress);\n worker.postMessage({ ...msg, id }, transfer);\n return promise;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ALL_GENES, b as ALL_TOOLBAR_TOOLS, c as ANNOTATION_IMAGE_MAX_DIM, C as CANVAS_TOOL_IDS, d as CELL_SEGMENTER, e as CHANNEL_HISTOGRAM_API, h as CellposeSegmenterService, i as ChannelHistogramComponent, D as DEFAULT_CATEGORICAL_PALETTE, j as DEFAULT_FALLBACK_PALETTE, k as DEFAULT_MUTED_OPACITY, l as DEFAULT_SAM_MODEL_ID, m as DEFAULT_SPATIAL_VIEW, H as HexColorPickerComponent, I as IMAGE_STATE_PORT,
|
|
1
|
+
export { A as ALL_GENES, b as ALL_TOOLBAR_TOOLS, c as ANNOTATION_IMAGE_MAX_DIM, C as CANVAS_TOOL_IDS, d as CELL_SEGMENTER, e as CHANNEL_HISTOGRAM_API, h as CellposeSegmenterService, i as ChannelHistogramComponent, D as DEFAULT_CATEGORICAL_PALETTE, j as DEFAULT_FALLBACK_PALETTE, k as DEFAULT_MUTED_OPACITY, l as DEFAULT_SAM_MODEL_ID, m as DEFAULT_SPATIAL_VIEW, n as DEFAULT_VISIBLE_CHANNELS, H as HexColorPickerComponent, I as IMAGE_STATE_PORT, o as INSTANCE_SEGMENTER, p as ImageConverterService, L as LUT_COLORS, M as MISSING_COLOR, q as MultiPolygon, N as NAPARI_DECIMATE_OPTIONS, s as NAPARI_DEFAULT_DECIMATE, t as NO_CATEGORY, u as NO_OBSERVATION, P as PLOT_MODE_CONTEXT, v as PLOT_MODE_SESSION, w as PLOT_TYPE_CONTRIBUTIONS, x as PLOT_TYPE_DESCRIPTORS, y as PREFERENCES_PORT, z as PlotType, B as Polygon, E as ProcessingImage, R as REGION_EDITOR_API, F as REGION_IO_PORT, G as REGION_SOURCE_MIN_CHARS, J as Rectangle, K as Region, O as RegionEditorComponent, Q as RoutingVisualizerService, S as SAM_MODELS, T as SEMANTIC_SEGMENTER, U as SPATIAL_DATA_PORT, V as SPATIAL_WIRE_VERSION, W as ShapeSelection, X as SpatialChartsComponent, Y as SpatialControlsComponent, Z as SpatialDataHttpService, _ as SpatialSelectionStore, $ as SupersededError, a0 as TILE_ACCESS_PORT, a1 as TOOLBAR_TOOLS, a2 as VISUALIZER, a3 as VIZ_ALERT_TOAST_KEY, a4 as VIZ_CONFIG, a5 as VIZ_TOAST_KEY, a6 as ViewerFeature, a7 as VisualizationModule, a8 as VisualizerComponent, a9 as assertManifestVersion, aa as benefitsFromGrouping, ab as buildOmicsTraces, ac as capabilitiesOf, ad as clearSamModelCache, ae as contrastWindow, af as contributedPlotTypeOption, ag as countMask, ah as cropImage, ai as datasetFromManifest, aj as decodeColumn, ak as decodeCoords, al as decodeFeatureVector, am as decodePolygons, an as decodeRadius, ao as defaultChannelVisible, ap as defaultPresetSet, aq as dialogToolContributions, ar as emptySelection, as as encodeCategorical, at as encodeContinuous, au as expandZips, av as findColumnMeta, aw as getDefaultSamModelId, g as getOrtWasmBase, ax as getPlotTypeDescriptor, ay as getSamModel, az as hasZarrMetadata, aA as hydrateBounds, aB as importFile, aC as isBuiltinPlotType, aD as isCanvasToolId, aE as isCategoricalColumn, aF as isContinuousColumn, aG as isDialogToolContribution, aH as isEmptyPresetSet, aI as isLabelVolumeName, aJ as isLittleEndian, aK as isNapari3d, aL as isNapariIsosurface, aM as isNapariScatter, aN as isNapariScatter3d, aO as isNapariSurface, aP as isNapariVolume, aQ as isSamModelReady, aR as isSpatialOmics, aS as isSpatialOmics3d, aT as isThreeDimensional, aU as isZipName, aV as labelClasses, aW as labelSliceGeoJson, aX as lutFor, aY as markerDiameters, aZ as maskToIndices, a_ as mutedFromSelection, a$ as omicsLayout, b0 as openAnnotations, b1 as openGeoJsonRegionSource, b2 as openHttpRegionSource, b3 as parsePresetSet, b4 as planRegionImport, b5 as pointInRing, b6 as provideVisualization, b7 as readAnnotationSlices, b8 as readLabelVolume, b9 as readOmeZarrLabels, ba as readZip, bb as rendererOwnsWheel, bc as resolveCategoryColors, bd as selectByCategory, be as selectInRegions, bf as setDefaultSamModel, bg as setOrtWasmBase, bh as setSamModelUrls, bi as sortToolContributions, bj as visibleToolContributions } from './jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-qUeMuBb0.mjs';
|
|
2
2
|
import '@angular/core';
|
|
3
3
|
import '@angular/common/http';
|
|
4
4
|
import 'napari-js';
|
|
@@ -38,6 +38,7 @@ import 'primeng/selectbutton';
|
|
|
38
38
|
import 'primeng/treeselect';
|
|
39
39
|
import 'plotly.js-dist-min';
|
|
40
40
|
import 'primeng/multiselect';
|
|
41
|
+
import 'primeng/inputswitch';
|
|
41
42
|
import 'image-js';
|
|
42
43
|
import 'buffer';
|
|
43
44
|
import 'openseadragon';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jax-data-science-sci-image-visualizer.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jax-data-science-sci-image-visualizer.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter, OnDestroy, OnInit } from '@angular/core';
|
|
2
2
|
import { TreeNode } from 'primeng/api';
|
|
3
|
+
import { ChannelGroupRow, ChannelTreeRow } from './channel-tree';
|
|
3
4
|
import { IChannelHistogramApi, IChannelState, IHistogram } from '../contracts/channel-histogram-api.contract';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
5
6
|
/**
|
|
@@ -27,7 +28,11 @@ export declare class ChannelHistogramComponent implements OnInit, OnDestroy {
|
|
|
27
28
|
color: string;
|
|
28
29
|
}[];
|
|
29
30
|
protected channels: IChannelState[];
|
|
31
|
+
/** The channels as the table lists them: in their groups when the image groups them, else flat. */
|
|
32
|
+
protected rows: ChannelTreeRow[];
|
|
30
33
|
protected selected: IChannelState | null;
|
|
34
|
+
/** The groups folded shut, by key; kept across emissions and images with the same groups. */
|
|
35
|
+
private readonly collapsed;
|
|
31
36
|
protected invert: boolean;
|
|
32
37
|
protected logScale: boolean;
|
|
33
38
|
/** Bounded retries while the (async) histogram sampling resolves. */
|
|
@@ -80,6 +85,15 @@ export declare class ChannelHistogramComponent implements OnInit, OnDestroy {
|
|
|
80
85
|
* dragging, since each change resets the timer). */
|
|
81
86
|
private markBusy;
|
|
82
87
|
protected onVisibleToggle(ch: IChannelState, value: boolean): void;
|
|
88
|
+
/** The table row of the selected channel, for its highlight. */
|
|
89
|
+
protected get selectedRow(): ChannelTreeRow | null;
|
|
90
|
+
/** A row was selected: a channel's selects the channel (a group's row cannot be). */
|
|
91
|
+
protected onRowSelect(row: ChannelTreeRow): void;
|
|
92
|
+
/** Fold a group shut, or open it. */
|
|
93
|
+
protected toggleGroup(row: ChannelGroupRow): void;
|
|
94
|
+
/** Show or hide every channel of a group. */
|
|
95
|
+
protected onGroupVisibleToggle(row: ChannelGroupRow, value: boolean): void;
|
|
96
|
+
protected trackRow(_: number, row: ChannelTreeRow): string;
|
|
83
97
|
protected onColorChange(ch: IChannelState, color: string): void;
|
|
84
98
|
protected onColormap(node: TreeNode): void;
|
|
85
99
|
protected onInvert(value: boolean): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IChannelState } from '../contracts/channel-histogram-api.contract';
|
|
2
|
+
/** A group of channels in the Channels pane: its label, how many channels it holds, how many show. */
|
|
3
|
+
export interface ChannelGroupRow {
|
|
4
|
+
kind: 'group';
|
|
5
|
+
/** The group's path, its labels joined: stable across emissions, so it keys the row and its folding. */
|
|
6
|
+
key: string;
|
|
7
|
+
label: string;
|
|
8
|
+
depth: number;
|
|
9
|
+
/** The channels below it, at any depth, by index. */
|
|
10
|
+
channels: number[];
|
|
11
|
+
/** How many of them are visible. */
|
|
12
|
+
shown: number;
|
|
13
|
+
expanded: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** A channel in the Channels pane, below its groups. */
|
|
16
|
+
export interface ChannelRow {
|
|
17
|
+
kind: 'channel';
|
|
18
|
+
key: string;
|
|
19
|
+
depth: number;
|
|
20
|
+
channel: IChannelState;
|
|
21
|
+
}
|
|
22
|
+
export type ChannelTreeRow = ChannelGroupRow | ChannelRow;
|
|
23
|
+
/** Whether any channel says which group it is in. */
|
|
24
|
+
export declare function hasChannelGroups(channels: readonly IChannelState[]): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The Channels pane's rows: each group (as the image's metadata groups its channels, outermost
|
|
27
|
+
* first) followed by what is in it, in channel order — a group where its first channel is — and
|
|
28
|
+
* the channels in no group where they are. A group in `collapsed` (by key) hides what is in it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function channelTreeRows(channels: readonly IChannelState[], collapsed?: ReadonlySet<string>): ChannelTreeRow[];
|
|
@@ -17,7 +17,23 @@ export interface IChannelState {
|
|
|
17
17
|
max: number;
|
|
18
18
|
gamma: number;
|
|
19
19
|
visible: boolean;
|
|
20
|
+
/** The groups the channel is in, outermost first (`IChannelInfo.group`); absent when it is in none. */
|
|
21
|
+
group?: string[];
|
|
20
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* How many channels of a multichannel image start visible. Each visible channel is drawn
|
|
25
|
+
* from tiles of its own, so a 22-channel plate showed every channel at once and asked the
|
|
26
|
+
* server for 22 tiles per position on every pan and zoom. Channels past these start hidden
|
|
27
|
+
* (one click shows them), and a hidden channel fetches no tiles.
|
|
28
|
+
*/
|
|
29
|
+
export declare const DEFAULT_VISIBLE_CHANNELS = 4;
|
|
30
|
+
/**
|
|
31
|
+
* Whether channel `index` of `count` starts visible: as the image says when its
|
|
32
|
+
* `channelInfo` does, else every channel of an image with at most
|
|
33
|
+
* {@link DEFAULT_VISIBLE_CHANNELS}, and the first {@link DEFAULT_VISIBLE_CHANNELS} of one with more.
|
|
34
|
+
* Only a boolean says: a descriptor's `null` (a JSON field left empty) leaves the default.
|
|
35
|
+
*/
|
|
36
|
+
export declare function defaultChannelVisible(index: number, count: number, specified?: boolean | null): boolean;
|
|
21
37
|
/** The standard LUT pseudo-colours (mirrors Fiji's Merge Channels palette) for
|
|
22
38
|
* quick per-channel assignment. */
|
|
23
39
|
export declare const LUT_COLORS: ReadonlyArray<{
|
|
@@ -225,6 +225,26 @@ export interface SpatialViewState {
|
|
|
225
225
|
* not drawn, nor the transcripts inside them. Cleared when the group column changes.
|
|
226
226
|
*/
|
|
227
227
|
hiddenGroups: string[];
|
|
228
|
+
/**
|
|
229
|
+
* Genes whose observations are drawn: with any set, only the observations that express
|
|
230
|
+
* them (a value above 0) are drawn — the markers, the cell outlines and the transcripts
|
|
231
|
+
* inside them, in 2D and in the 3D cloud — and the rest are hidden. Empty draws every
|
|
232
|
+
* observation. Cleared when the dataset changes.
|
|
233
|
+
*/
|
|
234
|
+
observationGenes: string[];
|
|
235
|
+
/** Whether an observation must express any of {@link observationGenes}, or all of them. */
|
|
236
|
+
observationGenesMatch: 'any' | 'all';
|
|
237
|
+
/**
|
|
238
|
+
* Colour (and mark) the filtered observations by their gene: each drawn observation in the
|
|
239
|
+
* colour and icon of the picked gene it expresses most (relative to that gene's highest
|
|
240
|
+
* value), in place of {@link colorBy}. Off, {@link colorBy} colours them as before.
|
|
241
|
+
*/
|
|
242
|
+
observationGenesColorBy: boolean;
|
|
243
|
+
/** `#rrggbb` per gene of {@link observationGenes}; a gene left out takes the palette's colour
|
|
244
|
+
* for its place in the list. */
|
|
245
|
+
observationGeneColors: Record<string, string>;
|
|
246
|
+
/** The icon per gene of {@link observationGenes} (2D); a gene left out is drawn as a circle. */
|
|
247
|
+
observationGeneGlyphs: Record<string, TranscriptGlyphName>;
|
|
228
248
|
/** Draw the tissue image (the Images section's switch). */
|
|
229
249
|
showImage: boolean;
|
|
230
250
|
/** Draw the annotation regions (the Annotations section's switch). Selecting from them
|
|
@@ -258,6 +278,13 @@ export interface SpatialViewState {
|
|
|
258
278
|
* marker per transcript once those fit {@link transcriptBudget}.
|
|
259
279
|
*/
|
|
260
280
|
transcriptAllGenes: boolean;
|
|
281
|
+
/**
|
|
282
|
+
* Genes of "All genes" switched off with their eye toggle — its own list, so hiding a gene
|
|
283
|
+
* there leaves the selection's {@link transcriptHiddenGenes} alone, and the other way round.
|
|
284
|
+
* Led by `'*'` (`ALL_GENES`), it lists the genes still shown instead: `['*']` hides every
|
|
285
|
+
* gene, `['*', 'KRT5']` shows KRT5 alone, without naming the thousands of others.
|
|
286
|
+
*/
|
|
287
|
+
transcriptAllGenesHidden: string[];
|
|
261
288
|
/**
|
|
262
289
|
* Most transcript markers drawn at once. The level of detail is chosen to stay under it,
|
|
263
290
|
* which is what keeps pan and zoom responsive however dense the data is.
|
|
@@ -21,6 +21,14 @@ export interface IChannelInfo {
|
|
|
21
21
|
minAllowed?: number;
|
|
22
22
|
/** Pixel-type maximum (e.g. 65535 for unsigned 16-bit). Native-unit ceiling. */
|
|
23
23
|
maxAllowed?: number;
|
|
24
|
+
/** Whether the channel starts visible; else the first `DEFAULT_VISIBLE_CHANNELS` do. */
|
|
25
|
+
visible?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The groups the channel is in, outermost first, as the image's metadata describes them (an
|
|
28
|
+
* instrument panel's kind of target, then the panel: `['Protein IF', 'Cell Signaling']`); the
|
|
29
|
+
* Channels pane lists the channels as a tree of them. Absent or empty for a channel in none.
|
|
30
|
+
*/
|
|
31
|
+
group?: string[] | null;
|
|
24
32
|
}
|
|
25
33
|
/** Per-series image metadata (physical size + dimensions). */
|
|
26
34
|
export interface IImageMetadata {
|
|
@@ -278,6 +278,13 @@ export interface SpatialTranscriptTilesMeta {
|
|
|
278
278
|
count?: number;
|
|
279
279
|
/** Real genes (control probes excluded). Names come from the feature search. */
|
|
280
280
|
geneCount: number;
|
|
281
|
+
/**
|
|
282
|
+
* The genes an all-gene tile's `gene` codes index, in code order (the port's
|
|
283
|
+
* `TranscriptTileQuery.genes` with `[ALL_GENES]`); an empty name is a code those tiles never
|
|
284
|
+
* carry (a control probe). Optional: without it "All genes" still draws, but cannot hide one
|
|
285
|
+
* of its genes.
|
|
286
|
+
*/
|
|
287
|
+
genes?: string[];
|
|
281
288
|
hasZ: boolean;
|
|
282
289
|
levels: (SpatialTileLevel & {
|
|
283
290
|
/** True when an entry stands for several transcripts (see {@link SpatialTranscriptTile.weight}). */
|
|
@@ -390,6 +390,9 @@ export interface ISpatialControls {
|
|
|
390
390
|
hi: number;
|
|
391
391
|
max: number;
|
|
392
392
|
} | null>;
|
|
393
|
+
/** True while the observation gene filter could not be applied (an expression read failed):
|
|
394
|
+
* the observations then still show the last filter that could be. */
|
|
395
|
+
getGeneFilterFailed$?(): Observable<boolean>;
|
|
393
396
|
/**
|
|
394
397
|
* The z positions of the sections the dataset was imaged at, ascending — or
|
|
395
398
|
* null when its z is continuous rather than sectioned (so there are no sections
|
|
@@ -101,6 +101,13 @@ export declare class Image2dScene implements NapariScene {
|
|
|
101
101
|
/** Channel states + grayscale colormap → live-apply to the rendered layers (no re-fetch; only z
|
|
102
102
|
* changes re-fetch), and recolour the navigator thumbnail. */
|
|
103
103
|
private followDisplayState;
|
|
104
|
+
/**
|
|
105
|
+
* Put the current channel states back on the rendered layers: each channel's visibility, window,
|
|
106
|
+
* gamma and tint, and which of an RGB image's layers draw it. For a caller that changed the
|
|
107
|
+
* layers' visibility wholesale (the spatial view showing its tissue again), so the channels
|
|
108
|
+
* the user or the defaults switched off stay off. Nothing to do before the image is drawn.
|
|
109
|
+
*/
|
|
110
|
+
reapplyDisplayState(): void;
|
|
104
111
|
/** Apply the current channel states / colormap to the live layers (no re-fetch), delegating the
|
|
105
112
|
* per-channel layer mutations to the {@link MultiChannelImageView}. */
|
|
106
113
|
private applyDisplayState;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ImageLayer, Viewer } from 'napari-js';
|
|
2
2
|
import { IHistogram } from '../../contracts/channel-histogram-api.contract';
|
|
3
3
|
import { SpatialDataset } from '../../contracts/spatial-dataset.contract';
|
|
4
|
-
import { SpatialViewState } from '../../contracts/display-types';
|
|
4
|
+
import { SpatialViewState, TranscriptGlyphName } from '../../contracts/display-types';
|
|
5
5
|
import { SpatialSelectionMask } from '../../spatial/spatial-selection';
|
|
6
6
|
import { NapariSpatialTileLayers } from './napari-spatial-tiles';
|
|
7
7
|
import { Image2dScene } from './napari-image-2d-scene';
|
|
@@ -157,7 +157,18 @@ export declare class Spatial2dScene extends SpatialSceneBase implements NapariSc
|
|
|
157
157
|
* selected to colour by. Categorical columns use the column's own palette; continuous columns
|
|
158
158
|
* and gene vectors go through the active colormap with a percentile-clipped window.
|
|
159
159
|
*/
|
|
160
|
-
|
|
160
|
+
/** The observations the view's gene filter keeps (1 = drawn), or null when it has none.
|
|
161
|
+
* Rejects when an expression read fails. */
|
|
162
|
+
private filterMask;
|
|
163
|
+
/** The markers' colours (and icons, by gene) before the gene filter hides any of them. */
|
|
164
|
+
private markerStyle;
|
|
165
|
+
/** {@link faceColors} before the gene filter: what the colour source and the selection make of them. */
|
|
166
|
+
private sourceColors;
|
|
161
167
|
/** Continuous values → RGBA through the active colormap and a clipped window. */
|
|
162
168
|
encodeContinuous(values: Float32Array, view: SpatialViewState, muted?: Uint8Array | null): Float32Array;
|
|
163
169
|
}
|
|
170
|
+
/** Per-observation napari-js symbol codes: each observation's gene's icon (`codes` index
|
|
171
|
+
* `glyphs`), a disc where it has none. */
|
|
172
|
+
export declare function glyphSymbols(codes: Uint16Array, glyphs: readonly TranscriptGlyphName[]): Uint8Array;
|
|
173
|
+
/** The symbols of the drawn subset (all of them when `indices` is undefined). */
|
|
174
|
+
export declare function gatherSymbols(symbols: Uint8Array | null, indices?: Uint32Array): Uint8Array | null;
|
|
@@ -94,6 +94,11 @@ export declare function encodeSpatialContinuous(values: Float32Array, view: Spat
|
|
|
94
94
|
export type PointColors = Float32Array | RGBA;
|
|
95
95
|
/** Whether a colour input is one RGBA per point rather than one broadcast RGBA. */
|
|
96
96
|
export declare function isPerPoint(colors: PointColors): colors is Float32Array;
|
|
97
|
+
/**
|
|
98
|
+
* `colors` with every observation `shown` leaves at 0 made transparent: the observations a gene
|
|
99
|
+
* filter leaves out. A broadcast colour becomes per-point, since the points now differ.
|
|
100
|
+
*/
|
|
101
|
+
export declare function hideUnshown(colors: PointColors, shown: Uint8Array): Float32Array;
|
|
97
102
|
/** Per-point colours for the drawn subset. A broadcast tuple stays broadcast —
|
|
98
103
|
* it is one colour for every point either way. */
|
|
99
104
|
export declare function gatherColors(colors: PointColors, indices?: Uint32Array): PointColors;
|
|
@@ -26,6 +26,8 @@ export interface SpatialTileSinks {
|
|
|
26
26
|
max: number;
|
|
27
27
|
}): void;
|
|
28
28
|
loadingChanged(layers: string[]): void;
|
|
29
|
+
/** Whether the observation gene filter could be applied; see {@link SpatialSession.reportGeneFilter}. */
|
|
30
|
+
geneFilterFailed?(failed: boolean): void;
|
|
29
31
|
}
|
|
30
32
|
/** The 2D scene the tile layers currently draw into. */
|
|
31
33
|
export interface SpatialTileOwner {
|
|
@@ -64,6 +66,8 @@ export declare class SpatialSession {
|
|
|
64
66
|
private tileLayers;
|
|
65
67
|
private tileOwner;
|
|
66
68
|
constructor(port: SpatialDataPort | null, selection: SpatialSelectionStore | null, sinks: SpatialTileSinks);
|
|
69
|
+
/** Tell the panel whether the observation gene filter could be applied this rebuild. */
|
|
70
|
+
reportGeneFilter(failed: boolean): void;
|
|
67
71
|
/** The tile layers, if they were ever built. */
|
|
68
72
|
get tiles(): NapariSpatialTileLayers | null;
|
|
69
73
|
/**
|
|
@@ -80,6 +80,8 @@ export declare class NapariVisualizerService extends BaseStoreVisualizer impleme
|
|
|
80
80
|
hi: number;
|
|
81
81
|
max: number;
|
|
82
82
|
} | null>;
|
|
83
|
+
/** The observation gene filter could not be applied (an expression read failed). */
|
|
84
|
+
readonly geneFilterFailed$: BehaviorSubject<boolean>;
|
|
83
85
|
/** "x reloading…" at the bottom of the canvas: the tile layers' loads and the observations'. */
|
|
84
86
|
private readonly badge;
|
|
85
87
|
/** The store's display state (colormap, reverse, invert) and the colormaps derived from it. */
|
|
@@ -21,6 +21,11 @@ export declare class CategoricalLookup {
|
|
|
21
21
|
constructor(port: SpatialDataPort);
|
|
22
22
|
/** Codes of the column `name`, or null when it is not categorical. */
|
|
23
23
|
codes(name: string): Promise<CategoricalCodes | null>;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The observations not drawn: those of the groups switched off, and those the gene filter
|
|
26
|
+
* leaves out. Null when nothing is hidden.
|
|
27
|
+
*/
|
|
25
28
|
hiddenCodes(dataset: SpatialDataset, view: SpatialViewState): Promise<HiddenCodes | null>;
|
|
29
|
+
/** Codes of the group column that are switched off, or null when none are. */
|
|
30
|
+
private hiddenGroupCodes;
|
|
26
31
|
}
|
|
@@ -62,6 +62,10 @@ export interface DrawnTranscripts {
|
|
|
62
62
|
merged: SpatialTranscriptTile;
|
|
63
63
|
/** The layer the entries are drawn in, one marker per entry in the same order. */
|
|
64
64
|
layer: PointsLayer;
|
|
65
|
+
/**
|
|
66
|
+
* The genes the entries' `gene` codes index: the selection's, or for every gene's individual
|
|
67
|
+
* transcripts the dataset's gene list (`transcriptTiles.genes`; empty without one).
|
|
68
|
+
*/
|
|
65
69
|
genes: string[];
|
|
66
70
|
/** For a grouped gene selection: each entry's group, and the groups' names. */
|
|
67
71
|
entryGroup?: Int32Array;
|
|
@@ -6,7 +6,7 @@ import { TranscriptGlyph } from '../../../spatial/glyphs';
|
|
|
6
6
|
import type { CategoricalLookup } from './categorical-lookup';
|
|
7
7
|
import type { OrderedLayerGroups, TileGroup } from './layer-groups';
|
|
8
8
|
import type { PlanContext } from './plan-context';
|
|
9
|
-
import type { TranscriptHover } from './transcript-hover';
|
|
9
|
+
import type { TranscriptHover, TranscriptKind } from './transcript-hover';
|
|
10
10
|
import { type TranscriptJobPlanner } from './transcript-jobs';
|
|
11
11
|
/**
|
|
12
12
|
* The napari-js point symbol each transcript icon is drawn as. Every icon has a counterpart of
|
|
@@ -14,6 +14,8 @@ import { type TranscriptJobPlanner } from './transcript-jobs';
|
|
|
14
14
|
* differs.
|
|
15
15
|
*/
|
|
16
16
|
export declare const GLYPH_SYMBOLS: Readonly<Record<TranscriptGlyph, PointSymbol>>;
|
|
17
|
+
/** The view the markers are coloured by, for what was loaded. */
|
|
18
|
+
export declare function colorViewFor(view: SpatialViewState, kind: TranscriptKind, clustered: boolean, hasCellTypes: boolean): SpatialViewState;
|
|
17
19
|
/**
|
|
18
20
|
* The transcript markers of the 2D spatial view: runs the planner's job, drops hidden groups
|
|
19
21
|
* and genes, groups a selection under the marker budget, colours the entries and draws them as
|
package/lib/spatial/lod.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `zoom · scale`. For a Xenium bundle the data unit is the micron and the world unit the
|
|
13
13
|
* morphology pixel.
|
|
14
14
|
*/
|
|
15
|
-
import type { SpatialBounds, SpatialDataset, SpatialImageRef, SpatialTileLevel } from '../contracts/spatial-dataset.contract';
|
|
15
|
+
import type { SpatialBounds, SpatialDataset, SpatialImageRef, SpatialPolygonTilesMeta, SpatialTileLevel } from '../contracts/spatial-dataset.contract';
|
|
16
16
|
import type { SpatialViewState } from '../contracts/display-types';
|
|
17
17
|
/** A rectangle in observation coordinates. */
|
|
18
18
|
export interface DataRect {
|
|
@@ -29,13 +29,25 @@ export interface TileKey {
|
|
|
29
29
|
}
|
|
30
30
|
/** Whether cell outlines are drawn: the user's choice, else on when the dataset has any. */
|
|
31
31
|
export declare function cellsShown(dataset: Pick<SpatialDataset, 'polygonTiles' | 'polygons'> | null, view: Pick<SpatialViewState, 'showCells'>): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The boundary sets drawn for the view's `cellSet`: that set when the dataset has it, else the
|
|
34
|
+
* dataset's default, else its first; for "both", that one with the other set beside it. Null when
|
|
35
|
+
* the dataset has no set. The renderer and the Cells panel's switches both read it, so a set kept
|
|
36
|
+
* from another dataset shows as what is actually drawn.
|
|
37
|
+
*/
|
|
38
|
+
export declare function drawnCellSets(tiled: Pick<SpatialPolygonTilesMeta, 'sets' | 'defaultSet'> | null | undefined, cellSet: string | null | undefined): {
|
|
39
|
+
set: string;
|
|
40
|
+
nucleusSet?: string;
|
|
41
|
+
} | null;
|
|
32
42
|
/** A column holding a hand-curated cell-type annotation, by naming convention. */
|
|
33
43
|
export declare function isCuratedColumn(name: string): boolean;
|
|
34
44
|
/**
|
|
35
45
|
* The categorical column that names each cell's type: the view's choice when it is a
|
|
36
46
|
* column of this dataset, else the first categorical column that is not a curated
|
|
37
47
|
* annotation (a pipeline's clustering is the default; curation is opted into), else
|
|
38
|
-
* any categorical column.
|
|
48
|
+
* any categorical column. A column of one category, such as the `region` a SpatialData
|
|
49
|
+
* table links its cells to their shapes with, names no type and is only used when chosen;
|
|
50
|
+
* a dataset with nothing else has no cell types (null).
|
|
39
51
|
*/
|
|
40
52
|
export declare function cellTypeColumnFor(dataset: SpatialDataset, view: Pick<SpatialViewState, 'cellTypeColumn'>): string | null;
|
|
41
53
|
/** A stable string id for a tile key, `level/gx/gy` — usable as a Map key. */
|
|
@@ -24,6 +24,9 @@ export interface SpatialRendererFeeds {
|
|
|
24
24
|
max: number;
|
|
25
25
|
} | null>;
|
|
26
26
|
};
|
|
27
|
+
readonly geneFilterFailed$?: {
|
|
28
|
+
asObservable(): Observable<boolean>;
|
|
29
|
+
};
|
|
27
30
|
/** Observations projected to canvas pixels by the 3D camera, or null when the cloud is not mounted. */
|
|
28
31
|
getSpatialScreenProjection(obs: SpatialObservations): Float32Array | null;
|
|
29
32
|
/** The ids of the regions drawn over the 3D cloud in screen space (its lassos). */
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { SpatialViewState, TranscriptGlyphName } from '../contracts/display-types';
|
|
2
|
+
/** One gene's value for every observation: the port's `getFeatureVector`. */
|
|
3
|
+
export type FeatureVectorSource = (gene: string) => Promise<Float32Array>;
|
|
4
|
+
type GeneFilterView = Pick<SpatialViewState, 'observationGenes' | 'observationGenesMatch'>;
|
|
5
|
+
/** Whether the view draws only the observations expressing some genes. */
|
|
6
|
+
export declare function hasGeneFilter(view: Partial<GeneFilterView>): boolean;
|
|
7
|
+
/** What decides the gene filter, for the layers' cache keys: '' when there is none. */
|
|
8
|
+
export declare function geneFilterKey(view: Partial<GeneFilterView>): string;
|
|
9
|
+
/**
|
|
10
|
+
* 1 for each of `count` observations that expresses the genes — a value above 0 in any of
|
|
11
|
+
* `vectors`, or in all of them — else 0. A missing value (NaN) is not expressed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function expressingMask(vectors: readonly Float32Array[], count: number, match: 'any' | 'all'): Uint8Array;
|
|
14
|
+
/**
|
|
15
|
+
* The view's gene filter as a per-observation mask (1 = drawn), or null when it has none. Every
|
|
16
|
+
* layer asks on every rebuild, so the mask of the same vectors is built once.
|
|
17
|
+
*/
|
|
18
|
+
export declare function observationGeneMask(view: GeneFilterView, count: number, read: FeatureVectorSource): Promise<Uint8Array | null>;
|
|
19
|
+
/** The colour a gene of the observation filter is drawn in: the view's own, else the palette's
|
|
20
|
+
* for its place in the list. */
|
|
21
|
+
export declare function observationGeneColor(view: Pick<SpatialViewState, 'observationGenes' | 'observationGeneColors'>, gene: string): string;
|
|
22
|
+
/** The icon a gene of the observation filter is drawn as (2D): the view's own, else a circle. */
|
|
23
|
+
export declare function observationGeneGlyph(view: Pick<SpatialViewState, 'observationGeneGlyphs'>, gene: string): TranscriptGlyphName;
|
|
24
|
+
/** Whether the filtered observations are coloured by their gene (rather than by `colorBy`). */
|
|
25
|
+
export declare function colorsByObservationGene(view: Pick<SpatialViewState, 'observationGenes' | 'observationGenesColorBy'>): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Each observation's gene among `vectors`: the index of the one it expresses most relative to
|
|
28
|
+
* that gene's own highest value — so a gene expressed at low counts everywhere is not always
|
|
29
|
+
* outvoted by a highly expressed one — or {@link NO_CATEGORY} where it expresses none.
|
|
30
|
+
*/
|
|
31
|
+
export declare function dominantGenes(vectors: readonly Float32Array[], count: number): Uint16Array;
|
|
32
|
+
/**
|
|
33
|
+
* How the filtered observations are drawn, by gene: each observation's gene (an index into the
|
|
34
|
+
* view's genes, see {@link dominantGenes}) with the genes' colours and icons. Null when the view
|
|
35
|
+
* does not colour by them.
|
|
36
|
+
*/
|
|
37
|
+
export declare function observationGeneStyle(view: GeneFilterView & Pick<SpatialViewState, 'observationGenesColorBy' | 'observationGeneColors' | 'observationGeneGlyphs'>, count: number, read: FeatureVectorSource): Promise<{
|
|
38
|
+
codes: Uint16Array;
|
|
39
|
+
colors: string[];
|
|
40
|
+
glyphs: TranscriptGlyphName[];
|
|
41
|
+
} | null>;
|
|
42
|
+
/** What decides how the filtered observations are drawn by gene, for cache keys. */
|
|
43
|
+
export declare function observationGeneStyleKey(view: Pick<SpatialViewState, 'observationGenes' | 'observationGenesColorBy' | 'observationGeneColors' | 'observationGeneGlyphs'>): string;
|
|
44
|
+
export {};
|
|
@@ -15,6 +15,23 @@ export declare function filterRings(rings: SpatialPolygonTile, hidden: {
|
|
|
15
15
|
} | null): SpatialPolygonTile;
|
|
16
16
|
/** Slots (indices into the selected genes) switched off with their eye toggle. */
|
|
17
17
|
export declare function hiddenGeneSlots(view: SpatialViewState): Uint8Array | null;
|
|
18
|
+
/**
|
|
19
|
+
* Whether "All genes" is switched off as a whole with the gene tree's root eye: its hidden
|
|
20
|
+
* list is `[ALL_GENES]` (every gene hidden, none excepted). Hiding every gene by name would
|
|
21
|
+
* need the gene list, which a dataset need not have.
|
|
22
|
+
*/
|
|
23
|
+
export declare function allGenesHidden(view: Pick<SpatialViewState, 'transcriptAllGenes' | 'transcriptAllGenesHidden'>): boolean;
|
|
24
|
+
/** Whether an "All genes" hidden list hides every gene, none excepted. */
|
|
25
|
+
export declare function everyGeneOff(off: readonly string[]): boolean;
|
|
26
|
+
/** Whether a gene of "All genes" is drawn, by its hidden list (see `transcriptAllGenesHidden`). */
|
|
27
|
+
export declare function allGeneShownBy(off: readonly string[]): (gene: string) => boolean;
|
|
28
|
+
/** "All genes"'s hidden list with `gene` shown (`on`) or hidden, in the list's own form. */
|
|
29
|
+
export declare function toggleAllGeneHidden(off: readonly string[], gene: string, on: boolean): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Codes (indices into `genes`, the dataset's all-gene code order) switched off with their eye
|
|
32
|
+
* toggle while every gene is drawn; null when none is, or without the gene list.
|
|
33
|
+
*/
|
|
34
|
+
export declare function hiddenAllGeneCodes(view: Pick<SpatialViewState, 'transcriptAllGenesHidden'>, genes: readonly string[] | undefined): Uint8Array | null;
|
|
18
35
|
/** Entries in a hidden group's cell, or of a hidden gene, removed (with their sizes). */
|
|
19
36
|
export declare function filterTranscripts(t: SpatialTranscriptTile, px: Float32Array, hidden: {
|
|
20
37
|
codes: Uint16Array;
|
|
@@ -39,6 +39,15 @@ export declare class SpatialCellsPanelComponent implements OnChanges {
|
|
|
39
39
|
* its buttons whenever the array identity changes — which made them impossible to click.
|
|
40
40
|
*/
|
|
41
41
|
protected cellSetOptions: PanelOption<string>[];
|
|
42
|
+
/**
|
|
43
|
+
* The boundary sets, the cells' first, each with a switch when there are at most two (as
|
|
44
|
+
* CytoCanvas's "Cell Boundaries" and "Nuclear Boundaries"): either, both or neither drawn.
|
|
45
|
+
* More sets keep the one-of choice.
|
|
46
|
+
*/
|
|
47
|
+
protected boundarySets: {
|
|
48
|
+
name: string;
|
|
49
|
+
label: string;
|
|
50
|
+
}[];
|
|
42
51
|
/** Cell colour modes on offer (Xenium Explorer's "Cell Color"). */
|
|
43
52
|
protected cellColorOptions: PanelOption<SpatialViewState['cellColorMode']>[];
|
|
44
53
|
/** The density colour bar, rebuilt only when its colormap changes (a 256-entry LUT). */
|
|
@@ -47,12 +56,20 @@ export declare class SpatialCellsPanelComponent implements OnChanges {
|
|
|
47
56
|
ngOnChanges(changes: SimpleChanges): void;
|
|
48
57
|
/** Whether outlines are on — the explicit choice, or automatically for data that has them. */
|
|
49
58
|
protected get cellsOn(): boolean;
|
|
50
|
-
/** The boundary set drawn
|
|
59
|
+
/** The boundary set drawn, or "both", as the renderer resolves the view's choice: a set this
|
|
60
|
+
* dataset has, else its default, else its first. */
|
|
51
61
|
protected get activeCellSet(): string | null;
|
|
52
62
|
/** Fill opacity as a 0–100 number, for the box beside the slider. */
|
|
53
63
|
protected get cellOpacityPercent(): number;
|
|
54
64
|
protected onShowCells(on: boolean): void;
|
|
55
65
|
protected onCellSet(set: string): void;
|
|
66
|
+
/** Whether boundary set `name` is drawn: outlines on, and that set or both. */
|
|
67
|
+
protected boundaryOn(name: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* A set's switch: on draws it with the other when that one is drawn ("both"), else alone;
|
|
70
|
+
* off leaves the other drawn, or no outlines at all.
|
|
71
|
+
*/
|
|
72
|
+
protected onBoundary(name: string, on: boolean): void;
|
|
56
73
|
protected onCellDraw(draw: SpatialViewState['cellDraw']): void;
|
|
57
74
|
protected onCellOpacity(value: number | undefined): void;
|
|
58
75
|
protected onCellOpacityPercent(v: number | null): void;
|