@jax-data-science/sci-image-visualizer 0.13.0 → 0.14.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/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 +3 -2
- 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 +6 -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 +3 -3
- 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 +50 -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-DvFp1jNH.mjs} +1064 -318
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DvFp1jNH.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-D7lSUAM1.mjs} +2 -2
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-D0IQ6PjZ.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-D7lSUAM1.mjs.map} +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +1 -1
- package/lib/contracts/channel-histogram-api.contract.d.ts +14 -0
- package/lib/contracts/display-types.d.ts +27 -0
- package/lib/contracts/image.contract.d.ts +2 -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 +3 -1
- 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-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 +13 -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-DvFp1jNH.mjs';
|
|
4
4
|
import '@angular/core';
|
|
5
5
|
import '@angular/common/http';
|
|
6
6
|
import 'rxjs';
|
|
@@ -237,4 +237,4 @@ class OnnxSamSession {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
export { OnnxSamSession };
|
|
240
|
-
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-
|
|
240
|
+
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-D7lSUAM1.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-D7lSUAM1.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-DvFp1jNH.mjs';
|
|
2
2
|
import '@angular/core';
|
|
3
3
|
import '@angular/common/http';
|
|
4
4
|
import 'napari-js';
|
|
@@ -18,6 +18,20 @@ export interface IChannelState {
|
|
|
18
18
|
gamma: number;
|
|
19
19
|
visible: boolean;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* How many channels of a multichannel image start visible. Each visible channel is drawn
|
|
23
|
+
* from tiles of its own, so a 22-channel plate showed every channel at once and asked the
|
|
24
|
+
* server for 22 tiles per position on every pan and zoom. Channels past these start hidden
|
|
25
|
+
* (one click shows them), and a hidden channel fetches no tiles.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_VISIBLE_CHANNELS = 4;
|
|
28
|
+
/**
|
|
29
|
+
* Whether channel `index` of `count` starts visible: as the image says when its
|
|
30
|
+
* `channelInfo` does, else every channel of an image with at most
|
|
31
|
+
* {@link DEFAULT_VISIBLE_CHANNELS}, and the first {@link DEFAULT_VISIBLE_CHANNELS} of one with more.
|
|
32
|
+
* Only a boolean says: a descriptor's `null` (a JSON field left empty) leaves the default.
|
|
33
|
+
*/
|
|
34
|
+
export declare function defaultChannelVisible(index: number, count: number, specified?: boolean | null): boolean;
|
|
21
35
|
/** The standard LUT pseudo-colours (mirrors Fiji's Merge Channels palette) for
|
|
22
36
|
* quick per-channel assignment. */
|
|
23
37
|
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,8 @@ 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;
|
|
24
26
|
}
|
|
25
27
|
/** Per-series image metadata (physical size + dimensions). */
|
|
26
28
|
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
|
@@ -35,7 +35,9 @@ export declare function isCuratedColumn(name: string): boolean;
|
|
|
35
35
|
* The categorical column that names each cell's type: the view's choice when it is a
|
|
36
36
|
* column of this dataset, else the first categorical column that is not a curated
|
|
37
37
|
* annotation (a pipeline's clustering is the default; curation is opted into), else
|
|
38
|
-
* any categorical column.
|
|
38
|
+
* any categorical column. A column of one category, such as the `region` a SpatialData
|
|
39
|
+
* table links its cells to their shapes with, names no type and is only used when chosen;
|
|
40
|
+
* a dataset with nothing else has no cell types (null).
|
|
39
41
|
*/
|
|
40
42
|
export declare function cellTypeColumnFor(dataset: SpatialDataset, view: Pick<SpatialViewState, 'cellTypeColumn'>): string | null;
|
|
41
43
|
/** 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;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { EventEmitter } from '@angular/core';
|
|
2
|
+
import { TranscriptGlyphName } from '../../contracts/display-types';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* One gene's icon and colour: the icon grid, a colour picker with its hex value, presets, and
|
|
6
|
+
* a reset — the content of a popover its host opens on the gene's swatch. Writes nothing itself:
|
|
7
|
+
* it reports the choice, and the host stores it where it belongs.
|
|
8
|
+
*/
|
|
9
|
+
export declare class SpatialGeneStyleComponent {
|
|
10
|
+
/** The gene, for the controls' labels. */
|
|
11
|
+
gene: string;
|
|
12
|
+
/** What the icons and colour are of ("Observation", "Transcript"), for the titles. */
|
|
13
|
+
what: string;
|
|
14
|
+
/** The gene's colour, `#rrggbb`. */
|
|
15
|
+
color: string;
|
|
16
|
+
/** The gene's icon. */
|
|
17
|
+
glyph: TranscriptGlyphName;
|
|
18
|
+
/** A note under the icons, e.g. where they show; none when null. */
|
|
19
|
+
iconHint: string | null;
|
|
20
|
+
readonly colorChange: EventEmitter<string>;
|
|
21
|
+
readonly glyphChange: EventEmitter<TranscriptGlyphName>;
|
|
22
|
+
/** Back to the gene's default icon and colour. */
|
|
23
|
+
readonly resetStyle: EventEmitter<void>;
|
|
24
|
+
protected readonly glyphOptions: import("../../spatial/spatial-panel-model").GlyphOption[];
|
|
25
|
+
protected readonly colorPresets: string[];
|
|
26
|
+
/** A typed hex colour, accepted as `#rrggbb` or `rrggbb`; anything else is ignored. */
|
|
27
|
+
protected onHex(text: string): void;
|
|
28
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SpatialGeneStyleComponent, never>;
|
|
29
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SpatialGeneStyleComponent, "spatial-gene-style", never, { "gene": { "alias": "gene"; "required": false; }; "what": { "alias": "what"; "required": false; }; "color": { "alias": "color"; "required": false; }; "glyph": { "alias": "glyph"; "required": false; }; "iconHint": { "alias": "iconHint"; "required": false; }; }, { "colorChange": "colorChange"; "glyphChange": "glyphChange"; "resetStyle": "resetStyle"; }, never, never, true, never>;
|
|
30
|
+
}
|
|
@@ -13,11 +13,21 @@ interface GeneRowView {
|
|
|
13
13
|
/** Transcripts in view, or null before the renderer reports any. */
|
|
14
14
|
count: number | null;
|
|
15
15
|
}
|
|
16
|
+
/** One row of the all-genes list. */
|
|
17
|
+
interface AllGeneRowView {
|
|
18
|
+
gene: string;
|
|
19
|
+
shown: boolean;
|
|
20
|
+
/** Its colour when coloured by gene, or null when the dataset's code order is unknown. */
|
|
21
|
+
swatch: string | null;
|
|
22
|
+
count: number | null;
|
|
23
|
+
}
|
|
16
24
|
/**
|
|
17
25
|
* The selected transcript genes as Xenium Explorer's tree: the gene groups, then the
|
|
18
26
|
* ungrouped genes, each row with an eye that hides it without deselecting it, its marker
|
|
19
27
|
* in its colour (opening one icon-and-colour picker per gene), and its transcript count in
|
|
20
|
-
* view.
|
|
28
|
+
* view. With "All genes" on, the root eye hides or shows every transcript and the root
|
|
29
|
+
* expands to every gene of the dataset, each with its eye when the dataset lists its genes
|
|
30
|
+
* in code order (`transcriptTiles.genes`), filtered by name past a few hundred.
|
|
21
31
|
*
|
|
22
32
|
* Writes only through `controls.setViewState`.
|
|
23
33
|
*/
|
|
@@ -34,9 +44,7 @@ export declare class SpatialGeneTreeComponent implements OnChanges {
|
|
|
34
44
|
geneCounts: Record<string, number> | null;
|
|
35
45
|
/** Real genes in the panel — the tree's denominator. */
|
|
36
46
|
geneTotal: number;
|
|
37
|
-
protected readonly glyphOptions: import("../../spatial/spatial-panel-model").GlyphOption[];
|
|
38
47
|
/** Swatches in the picker: the categorical palette genes are coloured from by default. */
|
|
39
|
-
protected readonly colorPresets: string[];
|
|
40
48
|
/** The tree, rebuilt when the view changes — never per change-detection pass. */
|
|
41
49
|
protected geneTree: GeneTreeNode[];
|
|
42
50
|
/** Per gene: its eye, swatch, glyph and count — rebuilt with the inputs (and when the
|
|
@@ -47,6 +55,18 @@ export declare class SpatialGeneTreeComponent implements OnChanges {
|
|
|
47
55
|
protected rootShown: boolean;
|
|
48
56
|
protected collapsedGeneGroups: Set<string>;
|
|
49
57
|
protected geneTreeOpen: boolean;
|
|
58
|
+
/** The all-genes list starts closed: it can run to thousands of genes. */
|
|
59
|
+
protected allGenesOpen: boolean;
|
|
60
|
+
/** The genes "All genes" lists, or null when the dataset names none. */
|
|
61
|
+
protected allGeneList: readonly string[] | null;
|
|
62
|
+
/** Whether a gene of "All genes" can be hidden: the dataset lists its genes in code order. */
|
|
63
|
+
protected canHideAllGenes: boolean;
|
|
64
|
+
protected allGenesFilter: string;
|
|
65
|
+
/** The list's rows matching the filter, at most {@link ALL_GENES_ROWS}. */
|
|
66
|
+
protected allRows: AllGeneRowView[];
|
|
67
|
+
/** How many genes match the filter, for the "showing n of m" line. */
|
|
68
|
+
protected allGenesMatched: number;
|
|
69
|
+
protected readonly allGenesRowCap = 200;
|
|
50
70
|
/** The gene the open picker edits. */
|
|
51
71
|
protected styleGene: string | null;
|
|
52
72
|
/** Colours of the cells' groups, by name — what a cluster of the same name is drawn in. */
|
|
@@ -58,13 +78,26 @@ export declare class SpatialGeneTreeComponent implements OnChanges {
|
|
|
58
78
|
constructor(zone: NgZone, cdr: ChangeDetectorRef);
|
|
59
79
|
ngOnChanges(changes: SimpleChanges): void;
|
|
60
80
|
private rebuildRows;
|
|
81
|
+
/**
|
|
82
|
+
* The all-genes list's rows: the genes matching the filter, up to the cap. Built only while
|
|
83
|
+
* the list is open: the counts in view change with every pan, and the list can be thousands.
|
|
84
|
+
*/
|
|
85
|
+
private rebuildAllRows;
|
|
86
|
+
/** Whether "All genes" draws anything: not switched off whole, nor every listed gene hidden. */
|
|
87
|
+
private anyOfAllGenesShown;
|
|
88
|
+
/** A gene of "All genes" in its colour by gene: its chosen one, else the palette's by code. */
|
|
89
|
+
private allGeneColorOf;
|
|
90
|
+
protected toggleAllGenesOpen(): void;
|
|
91
|
+
protected onAllGenesFilter(text: string): void;
|
|
61
92
|
protected toggleGeneGroup(name: string): void;
|
|
62
93
|
protected isGeneShown(gene: string): boolean;
|
|
63
94
|
protected areGenesShown(genes: string[]): boolean;
|
|
64
95
|
/** The eye toggle: hide or show genes without removing them from the selection. */
|
|
65
96
|
protected onGenesShown(genes: string[], on: boolean): void;
|
|
66
|
-
/** The root eye while every gene is
|
|
67
|
-
protected
|
|
97
|
+
/** The root eye while every gene is drawn: hide or show them all (the '±' leaves "All genes"). */
|
|
98
|
+
protected onAllGenesShown(on: boolean): void;
|
|
99
|
+
/** One gene's eye in the all-genes list. Showing one while all are off shows it alone. */
|
|
100
|
+
protected onAllGeneShown(gene: string, on: boolean): void;
|
|
68
101
|
protected onGeneColor(gene: string, hex: string): void;
|
|
69
102
|
protected openGeneStyle(event: Event, gene: string, panel: {
|
|
70
103
|
toggle(e: Event): void;
|
|
@@ -75,8 +108,6 @@ export declare class SpatialGeneTreeComponent implements OnChanges {
|
|
|
75
108
|
protected glyphPoints(glyph: TranscriptGlyphName): string;
|
|
76
109
|
protected glyphOf(gene: string, slot: number): TranscriptGlyphName;
|
|
77
110
|
protected onGlyph(gene: string, glyph: TranscriptGlyphName): void;
|
|
78
|
-
/** A typed hex colour, accepted as `#rrggbb` or `rrggbb`; anything else is ignored. */
|
|
79
|
-
protected onGeneHex(gene: string, text: string): void;
|
|
80
111
|
/** Back to the gene's default icon and colour (by its position in the list). */
|
|
81
112
|
protected resetGeneStyle(gene: string): void;
|
|
82
113
|
/** The colour gene `slot` is drawn in when transcripts are coloured by gene. */
|
|
@@ -89,6 +120,7 @@ export declare class SpatialGeneTreeComponent implements OnChanges {
|
|
|
89
120
|
name: string | null;
|
|
90
121
|
}) => string;
|
|
91
122
|
protected trackByLabelString: (_i: number, s: string) => string;
|
|
123
|
+
protected trackByAllRow: (_i: number, row: AllGeneRowView) => string;
|
|
92
124
|
/**
|
|
93
125
|
* The cluster colours, rebuilt only when what they come from changes. The template asks
|
|
94
126
|
* once per gene row per change-detection pass, and rebuilding per call made that O(G²).
|