@jax-data-science/sci-image-visualizer 0.17.0 → 0.18.1
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/display-types.mjs +3 -2
- package/esm2022/lib/contracts/image.contract.mjs +1 -1
- package/esm2022/lib/contracts/ports/spatial-data.port.mjs +1 -1
- package/esm2022/lib/contracts/region-editor-api.contract.mjs +1 -1
- package/esm2022/lib/contracts/region-store.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/geometry/cell-meshes.mjs +37 -0
- package/esm2022/lib/geometry/place.mjs +47 -0
- package/esm2022/lib/implementations/napari-js/napari-camera-depth.mjs +93 -0
- package/esm2022/lib/implementations/napari-js/napari-loading-state.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-region-overlay.mjs +2 -2
- package/esm2022/lib/implementations/napari-js/napari-scene.mjs +1 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-2d-scene.mjs +81 -15
- package/esm2022/lib/implementations/napari-js/napari-spatial-3d-cells.mjs +746 -0
- package/esm2022/lib/implementations/napari-js/napari-spatial-3d-scene.mjs +325 -18
- package/esm2022/lib/implementations/napari-js/napari-spatial-3d-transcripts.mjs +182 -24
- package/esm2022/lib/implementations/napari-js/napari-spatial-hover.mjs +95 -5
- package/esm2022/lib/implementations/napari-js/napari-spatial-scene.mjs +15 -1
- package/esm2022/lib/implementations/napari-js/napari-spatial-tiles.mjs +30 -2
- package/esm2022/lib/implementations/napari-js/napari-visualizer.service.mjs +40 -1
- package/esm2022/lib/implementations/napari-js/napari-volume-region-meshes.mjs +111 -28
- package/esm2022/lib/implementations/napari-js/napari-volume-scene.mjs +14 -2
- package/esm2022/lib/implementations/napari-js/spatial-tiles/cell-layers.mjs +180 -48
- package/esm2022/lib/implementations/napari-js/spatial-tiles/transcript-layers.mjs +28 -8
- package/esm2022/lib/implementations/osd/osd-region-overlay.mjs +2 -2
- package/esm2022/lib/implementations/spatial-data-http/spatial-data-http.service.mjs +10 -1
- package/esm2022/lib/region-editor/class-tree/class-tree.component.mjs +30 -3
- package/esm2022/lib/region-editor/classes-panel/classes-panel.component.mjs +15 -3
- package/esm2022/lib/region-editor/region-editor.component.mjs +24 -4
- package/esm2022/lib/region-source/region-source-ops.mjs +7 -1
- package/esm2022/lib/region-source/region-tiles.mjs +1 -1
- package/esm2022/lib/region-source/worker-region-source.mjs +24 -1
- package/esm2022/lib/routing-visualizer.service.mjs +404 -7
- package/esm2022/lib/spatial/observations-in-selection.mjs +83 -0
- package/esm2022/lib/spatial/plane-cells.mjs +119 -0
- package/esm2022/lib/spatial/spatial-controls.facade.mjs +298 -5
- package/esm2022/lib/spatial/spatial-encoding.mjs +35 -3
- package/esm2022/lib/spatial/spatial-hover.mjs +34 -1
- package/esm2022/lib/spatial/spatial-planes.mjs +49 -0
- package/esm2022/lib/spatial/spatial-selection.mjs +2 -2
- package/esm2022/lib/spatial/spatial-tile-merge.mjs +38 -1
- package/esm2022/lib/spatial/transcripts-in-selection.mjs +208 -0
- package/esm2022/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.mjs +42 -65
- package/esm2022/lib/spatial-controls/spatial-color-by/spatial-color-by.component.mjs +187 -0
- package/esm2022/lib/spatial-controls/spatial-controls.component.mjs +47 -12
- package/esm2022/lib/spatial-controls/spatial-gene-tree/spatial-gene-tree.component.mjs +3 -3
- package/esm2022/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.mjs +38 -10
- 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 +79 -57
- package/esm2022/lib/spatial-controls/spatial-selection-observations/spatial-selection-observations.component.mjs +101 -0
- package/esm2022/lib/spatial-controls/spatial-selection-transcripts/spatial-selection-transcripts.component.mjs +94 -0
- package/esm2022/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.mjs +9 -3
- package/esm2022/lib/store/region-source-scope.mjs +10 -1
- package/esm2022/lib/store/region-store.service.mjs +33 -2
- package/esm2022/lib/toolbar/toolbar.component.mjs +10 -3
- package/esm2022/lib/visualizer/spatial-region-set.mjs +95 -0
- package/esm2022/lib/visualizer.component.mjs +40 -4
- package/fesm2022/{jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DHrHO2ay.mjs → jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-YrNqv8wH.mjs} +4521 -759
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-YrNqv8wH.mjs.map +1 -0
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-CKEUwNta.mjs → jax-data-science-sci-image-visualizer-onnx-sam-session-Dph35S7m.mjs} +2 -2
- package/fesm2022/{jax-data-science-sci-image-visualizer-onnx-sam-session-CKEUwNta.mjs.map → jax-data-science-sci-image-visualizer-onnx-sam-session-Dph35S7m.mjs.map} +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer.mjs +1 -1
- package/fesm2022/region-source.worker.js +84 -2
- package/lib/contracts/display-types.d.ts +90 -4
- package/lib/contracts/image.contract.d.ts +6 -0
- package/lib/contracts/ports/spatial-data.port.d.ts +7 -0
- package/lib/contracts/region-editor-api.contract.d.ts +9 -0
- package/lib/contracts/region-store.contract.d.ts +6 -0
- package/lib/contracts/spatial-dataset.contract.d.ts +7 -1
- package/lib/contracts/visualizer.contract.d.ts +43 -1
- package/lib/geometry/cell-meshes.d.ts +27 -0
- package/lib/geometry/place.d.ts +45 -0
- package/lib/implementations/napari-js/napari-camera-depth.d.ts +23 -0
- package/lib/implementations/napari-js/napari-loading-state.d.ts +1 -1
- package/lib/implementations/napari-js/napari-scene.d.ts +12 -0
- package/lib/implementations/napari-js/napari-spatial-2d-scene.d.ts +19 -3
- package/lib/implementations/napari-js/napari-spatial-3d-cells.d.ts +141 -0
- package/lib/implementations/napari-js/napari-spatial-3d-scene.d.ts +87 -0
- package/lib/implementations/napari-js/napari-spatial-3d-transcripts.d.ts +49 -8
- package/lib/implementations/napari-js/napari-spatial-hover.d.ts +22 -0
- package/lib/implementations/napari-js/napari-spatial-scene.d.ts +12 -0
- package/lib/implementations/napari-js/napari-spatial-tiles.d.ts +15 -0
- package/lib/implementations/napari-js/napari-visualizer.service.d.ts +27 -1
- package/lib/implementations/napari-js/napari-volume-region-meshes.d.ts +38 -19
- package/lib/implementations/napari-js/napari-volume-scene.d.ts +2 -0
- package/lib/implementations/napari-js/spatial-tiles/cell-layers.d.ts +53 -0
- package/lib/implementations/napari-js/spatial-tiles/transcript-layers.d.ts +11 -2
- package/lib/implementations/spatial-data-http/spatial-data-http.service.d.ts +7 -0
- package/lib/region-editor/class-tree/class-tree.component.d.ts +16 -1
- package/lib/region-editor/classes-panel/classes-panel.component.d.ts +10 -1
- package/lib/region-editor/region-editor.component.d.ts +4 -0
- package/lib/region-source/region-source-ops.d.ts +12 -0
- package/lib/region-source/region-tiles.d.ts +8 -0
- package/lib/region-source/worker-region-source.d.ts +18 -0
- package/lib/routing-visualizer.service.d.ts +140 -1
- package/lib/spatial/observations-in-selection.d.ts +43 -0
- package/lib/spatial/plane-cells.d.ts +37 -0
- package/lib/spatial/spatial-controls.facade.d.ts +59 -0
- package/lib/spatial/spatial-encoding.d.ts +14 -1
- package/lib/spatial/spatial-hover.d.ts +23 -0
- package/lib/spatial/spatial-planes.d.ts +22 -0
- package/lib/spatial/spatial-selection.d.ts +2 -1
- package/lib/spatial/spatial-tile-merge.d.ts +9 -0
- package/lib/spatial/transcripts-in-selection.d.ts +95 -0
- package/lib/spatial-controls/spatial-cells-panel/spatial-cells-panel.component.d.ts +19 -28
- package/lib/spatial-controls/spatial-color-by/spatial-color-by.component.d.ts +64 -0
- package/lib/spatial-controls/spatial-controls.component.d.ts +17 -2
- package/lib/spatial-controls/spatial-groups-panel/spatial-groups-panel.component.d.ts +16 -6
- package/lib/spatial-controls/spatial-observations-panel/spatial-observations-panel.component.d.ts +26 -23
- package/lib/spatial-controls/spatial-selection-observations/spatial-selection-observations.component.d.ts +48 -0
- package/lib/spatial-controls/spatial-selection-transcripts/spatial-selection-transcripts.component.d.ts +43 -0
- package/lib/spatial-controls/spatial-transcripts-panel/spatial-transcripts-panel.component.d.ts +3 -1
- package/lib/store/region-source-scope.d.ts +4 -0
- package/lib/store/region-store.service.d.ts +21 -0
- package/lib/toolbar/toolbar.component.d.ts +6 -1
- package/lib/visualizer/spatial-region-set.d.ts +55 -0
- package/lib/visualizer.component.d.ts +14 -0
- package/package.json +1 -1
- package/fesm2022/jax-data-science-sci-image-visualizer-jax-data-science-sci-image-visualizer-DHrHO2ay.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-YrNqv8wH.mjs';
|
|
4
4
|
import '@angular/core';
|
|
5
5
|
import '@angular/common/http';
|
|
6
6
|
import 'rxjs';
|
|
@@ -238,4 +238,4 @@ class OnnxSamSession {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
export { OnnxSamSession };
|
|
241
|
-
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-
|
|
241
|
+
//# sourceMappingURL=jax-data-science-sci-image-visualizer-onnx-sam-session-Dph35S7m.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jax-data-science-sci-image-visualizer-onnx-sam-session-CKEUwNta.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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA;;;;;AAKG;MACU,YAAY,CAAA;AAAzB,IAAA,WAAA,GAAA;QACU,IAAA,CAAA,GAAG,GAAG,CAAC;AACE,QAAA,IAAA,CAAA,OAAO,GAAG,IAAI,GAAG,EAAmB;IAmCvD;;AAhCE,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI;IAC1B;;AAGA,IAAA,IAAI,CAAC,UAAuC,EAAA;AAC1C,QAAA,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG;QACrB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,KAAI;AAC3D,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AACvD,QAAA,CAAC,CAAC;AACF,QAAA,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE;IACxB;;AAGA,IAAA,MAAM,CAAC,KAAkB,EAAA;AACvB,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AACpC,QAAA,IAAI,CAAC,CAAC;YAAE;AACR,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE;YAC7B,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnC;QACF;QACA,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAC7B,QAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;AAAE,YAAA,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,mBAAmB,CAAC,CAAC;;AAC9E,YAAA,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACvB;;AAGA,IAAA,SAAS,CAAC,GAAU,EAAA;AAClB,QAAA,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;AAC/C,QAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;QACpB,KAAK,MAAM,CAAC,IAAI,KAAK;AAAE,YAAA,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACtC;AACD;;ACpDD;;;;;;;;;;;;;;;AAeG;MACU,cAAc,CAAA;AAA3B,IAAA,WAAA,GAAA;QACU,IAAA,CAAA,IAAI,GAA+B,IAAI;QACvC,IAAA,CAAA,MAAM,GAAG,KAAK;QACd,IAAA,CAAA,SAAS,GAAG,IAAI;;QAGhB,IAAA,CAAA,MAAM,GAAkB,IAAI;AACnB,QAAA,IAAA,CAAA,KAAK,GAAG,IAAI,YAAY,EAAE;;QAGnC,IAAA,CAAA,OAAO,GAAgC,IAAI;QAC3C,IAAA,CAAA,OAAO,GAAgC,IAAI;QAC3C,IAAA,CAAA,SAAS,GAAG,CAAC;;AAEJ,QAAA,IAAA,CAAA,UAAU,GAAG,IAAI,QAAQ,CAAwB,CAAC,CAAC;IAiItE;AA/HE,IAAA,MAAM,SAAS,CAAC,KAAkB,EAAE,UAAuC,EAAA;QACzE,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;YAC1C,MAAM,IAAI,KAAK,CAAC,CAAA,WAAA,EAAc,KAAK,CAAC,EAAE,CAAA,8BAAA,CAAgC,CAAC;QACzE;AACA,QAAA,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS;QAChC,MAAM,MAAM,GAAG,OAAO,SAAS,KAAK,WAAW,IAAI,KAAK,IAAI,SAAS;QACrE,MAAM,GAAG,GAAa,KAAK,CAAC,gBAAgB,KAAK,MAAM,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxF,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;;;AAGzC,QAAA,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,IAAI,KAAK,CAAC,SAAS,GAAG,QAAQ,GAAG,QAAQ;AAEhE,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;YAC1B,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,GAAG,cAAc,EAAE;AACzC,YAAA,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC;AAC7E,YAAA,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC;AAC5E,YAAA,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,kBAAkB,EAAE,GAAG,EAAE,CAAC;YACrF,IAAI,CAAC,OAAO,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,kBAAkB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5F;aAAO;YACL,MAAM,IAAI,CAAC,IAAI,CACb;AACE,gBAAA,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,SAAS,EAAE,cAAc,EAAE;gBAC3B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;AACzC,aAAA,EACD,EAAE,EACF,UAAU,CACX;QACH;AACA,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;IACpB;IAEA,QAAQ,GAAA;QACN,OAAO,IAAI,CAAC,MAAM;IACpB;IAEA,MAAM,KAAK,CAAC,KAAiE,EAAA;AAC3E,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;YAC1B,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,OAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;AAChG,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE;YAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;YAC7B,OAAO;AACL,gBAAA,IAAI,EAAE,IAAI,YAAY,CAAC,CAAC,CAAC;gBACzB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,KAAK;aACN;QACH;;AAEA,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM;AAChC,QAAA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;QAC1G,OAAO;AACL,YAAA,IAAI,EAAE,IAAI,YAAY,CAAC,CAAC,CAAC;AACzB,YAAA,IAAI,EAAE,GAAG,CAAC,MAAM,CAAa;AAC7B,YAAA,KAAK,EAAE,GAAG,CAAC,OAAO,CAAW;AAC7B,YAAA,UAAU,EAAE,GAAG,CAAC,YAAY,CAAW;AACvC,YAAA,WAAW,EAAE,GAAG,CAAC,aAAa,CAAW;AACzC,YAAA,KAAK,EAAE,GAAG,CAAC,OAAO,CAAW;SAC9B;IACH;AAEA,IAAA,MAAM,MAAM,CAAC,SAAuB,EAAE,MAAiB,EAAA;AACrD,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC1B,YAAA,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,KAAe,CAAC;AACxD,YAAA,IAAI,CAAC,CAAC;AAAE,gBAAA,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC;YACtE,OAAO,UAAU,CAAC,IAAI,CAAC,OAAQ,EAAE,CAAC,EAAE,MAAM,CAAC;QAC7C;QACA,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAC/E,OAAO;YACL,IAAI,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAgB,CAAC;AAClD,YAAA,KAAK,EAAE,GAAG,CAAC,OAAO,CAAW;AAC7B,YAAA,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAW;AAC/B,YAAA,GAAG,EAAE,GAAG,CAAC,KAAK,CAAW;SAC1B;IACH;AAEA;AAC4E;IAC5E,OAAO,GAAA;QACL,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;AAClD,QAAA,IAAI,CAAC,OAAO,EAAE,OAAO,IAAI;AACzB,QAAA,IAAI,CAAC,OAAO,EAAE,OAAO,IAAI;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI;AAClC,QAAA,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE;AACvB,QAAA,IAAI,CAAC,MAAM,GAAG,KAAK;AACnB,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI;IAClB;;IAGQ,YAAY,GAAA;QAClB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM;QACnC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5F,QAAA,MAAM,CAAC,SAAS,GAAG,CAAC,EAA6B,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;AAChF,QAAA,MAAM,CAAC,OAAO,GAAG,CAAC,EAAc,KAAI;;;AAGlC,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,OAAO,IAAI,qBAAqB,CAAC,CAAC;AAC/D,YAAA,IAAI,CAAC,MAAM,GAAG,KAAK;AACnB,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AAClB,QAAA,CAAC;AACD,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,OAAO,MAAM;IACf;;AAGQ,IAAA,UAAU,CAAC,GAAU,EAAA;AAC3B,QAAA,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE;AACxB,QAAA,IAAI,CAAC,MAAM,GAAG,IAAI;AAClB,QAAA,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC;IAC3B;AAEQ,IAAA,IAAI,CACV,GAA4B,EAC5B,QAAA,GAA2B,EAAE,EAC7B,UAAgC,EAAA;AAEhC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE;AAClC,QAAA,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;AACnD,QAAA,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC;AAC5C,QAAA,OAAO,OAAO;IAChB;AACD;;;;"}
|
|
1
|
+
{"version":3,"file":"jax-data-science-sci-image-visualizer-onnx-sam-session-Dph35S7m.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, 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-
|
|
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-YrNqv8wH.mjs';
|
|
2
2
|
import '@angular/core';
|
|
3
3
|
import '@angular/common/http';
|
|
4
4
|
import 'napari-js';
|
|
@@ -278,6 +278,75 @@ function ringBox(r) {
|
|
|
278
278
|
return [x0, y0, x1, y1];
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
// src/lib/geometry/cell-meshes.ts
|
|
282
|
+
function reconstructCells(cells, opts = {}) {
|
|
283
|
+
const parts = [];
|
|
284
|
+
let nv = 0;
|
|
285
|
+
let nf = 0;
|
|
286
|
+
for (const c of cells) {
|
|
287
|
+
if (!c.slices.length)
|
|
288
|
+
continue;
|
|
289
|
+
const mesh = reconstruct(c.slices, { smooth: 1, ...opts });
|
|
290
|
+
if (!mesh.faces.length)
|
|
291
|
+
continue;
|
|
292
|
+
parts.push({ cell: c.cell, vertices: mesh.vertices, faces: mesh.faces });
|
|
293
|
+
nv += mesh.vertices.length / 3;
|
|
294
|
+
nf += mesh.faces.length;
|
|
295
|
+
}
|
|
296
|
+
const vertices = new Float32Array(nv * 3);
|
|
297
|
+
const faces = new Uint32Array(nf);
|
|
298
|
+
const vertexCell = new Uint32Array(nv);
|
|
299
|
+
let v = 0;
|
|
300
|
+
let f = 0;
|
|
301
|
+
for (const p of parts) {
|
|
302
|
+
vertices.set(p.vertices, v * 3);
|
|
303
|
+
for (let i = 0; i < p.faces.length; i++)
|
|
304
|
+
faces[f + i] = p.faces[i] + v;
|
|
305
|
+
const n = p.vertices.length / 3;
|
|
306
|
+
vertexCell.fill(p.cell, v, v + n);
|
|
307
|
+
v += n;
|
|
308
|
+
f += p.faces.length;
|
|
309
|
+
}
|
|
310
|
+
return { vertices, faces, vertexCell, cells: Uint32Array.from(parts.map((p) => p.cell)) };
|
|
311
|
+
}
|
|
312
|
+
function cellsMeshBuffers(mesh) {
|
|
313
|
+
return [mesh.vertices.buffer, mesh.faces.buffer, mesh.vertexCell.buffer, mesh.cells.buffer];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/lib/geometry/place.ts
|
|
317
|
+
function toWorld(vertices, frame) {
|
|
318
|
+
if (frame.planes)
|
|
319
|
+
return planesToWorld(vertices, frame.planes);
|
|
320
|
+
const [w, h, d] = frame.size;
|
|
321
|
+
const [ww, wh, wd] = frame.world;
|
|
322
|
+
const out = new Float32Array(vertices.length);
|
|
323
|
+
for (let i = 0; i < vertices.length; i += 3) {
|
|
324
|
+
out[i] = (vertices[i] / w - 0.5) * ww;
|
|
325
|
+
out[i + 1] = (vertices[i + 1] / h - 0.5) * wh;
|
|
326
|
+
out[i + 2] = ((vertices[i + 2] + 0.5) / d - 0.5) * wd;
|
|
327
|
+
}
|
|
328
|
+
return out;
|
|
329
|
+
}
|
|
330
|
+
function planesToWorld(vertices, planes) {
|
|
331
|
+
const { z, scale, offset } = planes;
|
|
332
|
+
const out = new Float32Array(vertices.length);
|
|
333
|
+
for (let i = 0; i < vertices.length; i += 3) {
|
|
334
|
+
out[i] = vertices[i] * scale[0] + offset[0];
|
|
335
|
+
out[i + 1] = vertices[i + 1] * scale[1] + offset[1];
|
|
336
|
+
out[i + 2] = sliceZ(z, vertices[i + 2]) + offset[2];
|
|
337
|
+
}
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
function sliceZ(z, k) {
|
|
341
|
+
const n = z.length;
|
|
342
|
+
if (!n)
|
|
343
|
+
return k;
|
|
344
|
+
if (n === 1)
|
|
345
|
+
return z[0] + k;
|
|
346
|
+
const i = Math.min(n - 2, Math.max(0, Math.floor(k)));
|
|
347
|
+
return z[i] + (k - i) * (z[i + 1] - z[i]);
|
|
348
|
+
}
|
|
349
|
+
|
|
281
350
|
// src/lib/contracts/color.ts
|
|
282
351
|
function rgbToHex(rgb) {
|
|
283
352
|
const h = (n) => clampByte(n).toString(16).padStart(2, "0");
|
|
@@ -1038,6 +1107,10 @@ var RegionSourceTables = class {
|
|
|
1038
1107
|
return this.table(r.handle).classOutlines(r.classes, r.level, r.exclude);
|
|
1039
1108
|
case "mesh":
|
|
1040
1109
|
return reconstruct(r.slices, r.options);
|
|
1110
|
+
case "cells":
|
|
1111
|
+
return reconstructCells(r.cells, r.options);
|
|
1112
|
+
case "place":
|
|
1113
|
+
return toWorld(r.vertices, r.frame);
|
|
1041
1114
|
case "export":
|
|
1042
1115
|
return this.table(r.handle).exportGeoJson(r.exclude, r.extra);
|
|
1043
1116
|
case "close":
|
|
@@ -1058,10 +1131,19 @@ var tables = new RegionSourceTables();
|
|
|
1058
1131
|
self.onmessage = (event) => {
|
|
1059
1132
|
const { id } = event.data;
|
|
1060
1133
|
let reply;
|
|
1134
|
+
let transfer = [];
|
|
1061
1135
|
try {
|
|
1062
|
-
|
|
1136
|
+
const result = tables.run(event.data);
|
|
1137
|
+
if (event.data.op === "cells")
|
|
1138
|
+
transfer = cellsMeshBuffers(result);
|
|
1139
|
+
else if (event.data.op === "mesh") {
|
|
1140
|
+
const mesh = result;
|
|
1141
|
+
transfer = [mesh.vertices.buffer, mesh.faces.buffer];
|
|
1142
|
+
} else if (event.data.op === "place")
|
|
1143
|
+
transfer = [result.buffer];
|
|
1144
|
+
reply = { id, ok: true, result };
|
|
1063
1145
|
} catch (err) {
|
|
1064
1146
|
reply = { id, ok: false, error: err?.message ?? String(err) };
|
|
1065
1147
|
}
|
|
1066
|
-
self.postMessage(reply);
|
|
1148
|
+
self.postMessage(reply, [...new Set(transfer)]);
|
|
1067
1149
|
};
|
|
@@ -75,6 +75,83 @@ export interface SpatialColorBy {
|
|
|
75
75
|
/** Display state for the spatial-omics plot mode. Lives in `VisualizerStore`
|
|
76
76
|
* alongside the colormap/channel state so the renderer reacts to edits the
|
|
77
77
|
* same way it does for an image. */
|
|
78
|
+
/**
|
|
79
|
+
* How the spatial 3D view's individual cells stand (a 3D segmentation's cells, each its own
|
|
80
|
+
* surface): how many are shown, whether some are left out (the cap, or not built yet), whether
|
|
81
|
+
* they are being built.
|
|
82
|
+
*/
|
|
83
|
+
export interface Cells3dStatus {
|
|
84
|
+
shown: number;
|
|
85
|
+
capped: boolean;
|
|
86
|
+
building: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The transcripts of what is selected, by gene (the spatial dialog's "Transcripts in selection"):
|
|
90
|
+
* in spatial 3D those of the selected cells, or of the cells of the picked cell types, through the
|
|
91
|
+
* whole volume; in the 2D view of a sliced dataset only those on the plane shown, of the selected
|
|
92
|
+
* cells or inside the selected regions.
|
|
93
|
+
*/
|
|
94
|
+
export interface TranscriptsInSelection {
|
|
95
|
+
/** `empty`: nothing selected (or nothing it applies to); `loading`; `ready`; `error`. */
|
|
96
|
+
state: 'empty' | 'loading' | 'ready' | 'error';
|
|
97
|
+
/** What the counts are of: selected cells, the cells of picked types, or drawn regions. */
|
|
98
|
+
scope?: 'cells' | 'types' | 'regions';
|
|
99
|
+
/** The selection, for the heading ("Cell 1234", "Cell type: Goblet", "2 regions"). */
|
|
100
|
+
label?: string;
|
|
101
|
+
/** The plane counted (2D, a sliced dataset): its index and how many there are. */
|
|
102
|
+
plane?: {
|
|
103
|
+
index: number;
|
|
104
|
+
count: number;
|
|
105
|
+
} | null;
|
|
106
|
+
total?: number;
|
|
107
|
+
/** Distinct cells the counted transcripts lie in. */
|
|
108
|
+
cells?: number;
|
|
109
|
+
/** Genes by count, most first. */
|
|
110
|
+
genes?: {
|
|
111
|
+
name: string;
|
|
112
|
+
count: number;
|
|
113
|
+
}[];
|
|
114
|
+
/** The selection spans more of the section than is read: the counts are a lower bound. */
|
|
115
|
+
partial?: boolean;
|
|
116
|
+
/** Why the list is empty, or what failed. */
|
|
117
|
+
message?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The observations of what is selected (the spatial dialog's "Observations in selection", in the
|
|
121
|
+
* Observations section): how many, by the grouping they are coloured by (else the cell types), with
|
|
122
|
+
* a few measurements' means — or, for one cell, its id, groupings and values. In spatial 3D every
|
|
123
|
+
* observation of the selection; in the 2D view of a sliced dataset those on the plane shown. The
|
|
124
|
+
* selection is the one {@link TranscriptsInSelection} follows.
|
|
125
|
+
*/
|
|
126
|
+
export interface ObservationsInSelection {
|
|
127
|
+
/** `empty`: nothing selected (or nothing it applies to); `loading`; `ready`; `error`. */
|
|
128
|
+
state: 'empty' | 'loading' | 'ready' | 'error';
|
|
129
|
+
scope?: 'cells' | 'types' | 'regions';
|
|
130
|
+
label?: string;
|
|
131
|
+
plane?: {
|
|
132
|
+
index: number;
|
|
133
|
+
count: number;
|
|
134
|
+
} | null;
|
|
135
|
+
total?: number;
|
|
136
|
+
/** The grouping counted by, or null. */
|
|
137
|
+
groupColumn?: string | null;
|
|
138
|
+
/** Groups by count, most first, in their legend colours. */
|
|
139
|
+
groups?: {
|
|
140
|
+
name: string;
|
|
141
|
+
count: number;
|
|
142
|
+
color: string;
|
|
143
|
+
}[];
|
|
144
|
+
/** Means over the selection (`mean`), or one cell's own values (text for a grouping). */
|
|
145
|
+
measures?: {
|
|
146
|
+
name: string;
|
|
147
|
+
value: string | number;
|
|
148
|
+
unit?: string;
|
|
149
|
+
mean: boolean;
|
|
150
|
+
}[];
|
|
151
|
+
/** The one cell's id, when one cell is selected. */
|
|
152
|
+
cellId?: string;
|
|
153
|
+
message?: string;
|
|
154
|
+
}
|
|
78
155
|
export interface SpatialViewState {
|
|
79
156
|
/** Colour source; `null` renders every observation in one neutral colour
|
|
80
157
|
* (useful on its own — it shows where the tissue actually is). */
|
|
@@ -208,15 +285,24 @@ export interface SpatialViewState {
|
|
|
208
285
|
* user's explicit choice.
|
|
209
286
|
*/
|
|
210
287
|
showCells: boolean | null;
|
|
288
|
+
/**
|
|
289
|
+
* How the spatial 3D view draws a 3D segmentation's cells (the dataset's region set): one
|
|
290
|
+
* merged surface per cell type (`types`), or every cell its own surface in the Cell Color
|
|
291
|
+
* (`cells`). A class can be set the other way in the Region Editor.
|
|
292
|
+
*/
|
|
293
|
+
cells3d: 'types' | 'cells';
|
|
211
294
|
/** Which boundary set (`cell`, `nucleus`, or `both` — cells as set by
|
|
212
295
|
* {@link cellDraw}, nuclei outlined over them); null takes the dataset's default. */
|
|
213
296
|
cellSet: string | null;
|
|
214
297
|
/**
|
|
215
298
|
* What colours the cells — Xenium Explorer's "Cell Color": the active group
|
|
216
|
-
* ({@link cellTypeColumn}), one gene's expression ({@link cellColorGene}), the
|
|
217
|
-
*
|
|
299
|
+
* ({@link cellTypeColumn}), one gene's expression ({@link cellColorGene}), the numeric column
|
|
300
|
+
* the observations are coloured by (`column`: {@link colorBy}, a measurement), the cell's
|
|
301
|
+
* transcript density, one flat colour ({@link cellSingleColor}, the markers' too while
|
|
302
|
+
* {@link colorBy} is null), or how the cell was segmented. The panel's "Colour by" sets it
|
|
303
|
+
* with {@link colorBy}, so the markers and the cells are coloured alike.
|
|
218
304
|
*/
|
|
219
|
-
cellColorMode: 'group' | 'gene' | 'transcriptDensity' | 'single' | 'segmentation';
|
|
305
|
+
cellColorMode: 'group' | 'gene' | 'column' | 'transcriptDensity' | 'single' | 'segmentation';
|
|
220
306
|
cellColorGene: string | null;
|
|
221
307
|
/** `#rrggbb` for {@link cellColorMode} `single`. */
|
|
222
308
|
cellSingleColor: string;
|
|
@@ -247,7 +333,7 @@ export interface SpatialViewState {
|
|
|
247
333
|
observationGeneGlyphs: Record<string, TranscriptGlyphName>;
|
|
248
334
|
/** Draw the tissue image (the Images section's switch). */
|
|
249
335
|
showImage: boolean;
|
|
250
|
-
/** Draw the
|
|
336
|
+
/** Draw the regions (the Regions section's switch; off by default). Selecting from them
|
|
251
337
|
* still works while they are hidden. */
|
|
252
338
|
showAnnotations: boolean;
|
|
253
339
|
/** Fill the cells with their colour, outline them, or both. */
|
|
@@ -62,6 +62,12 @@ export interface IImageInfo {
|
|
|
62
62
|
showStack: boolean;
|
|
63
63
|
scaleRatio: boolean;
|
|
64
64
|
fileName: string;
|
|
65
|
+
/**
|
|
66
|
+
* The host's id of the image, as a spatial dataset's `SpatialImageRef.imageId` names it (jit-ui:
|
|
67
|
+
* its path in the bucket). Optional: given, the image is a dataset's own only when the two are
|
|
68
|
+
* equal; without it, only its file name can be compared (see `isDatasetImage`).
|
|
69
|
+
*/
|
|
70
|
+
imageId?: string;
|
|
65
71
|
roiJsonStr?: string;
|
|
66
72
|
/** Per-slice ROI GeoJSON, aligned 1:1 with `urls`, for a folder stack where
|
|
67
73
|
* each slice is a separate file with its own sibling `<stem>.geojson`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InjectionToken } from '@angular/core';
|
|
2
2
|
import { Observable } from 'rxjs';
|
|
3
|
+
import type { RegionSource } from '../../region-source/region-source';
|
|
3
4
|
import { SpatialColumn, SpatialDataset, SpatialDensityRaster, SpatialEmbedding, SpatialPolygonTile, CategoricalColumnMeta, SpatialMarkerGenes, SpatialPolygons, SpatialTranscriptCounts, SpatialTranscriptSummary, SpatialTranscriptTile } from '../spatial-dataset.contract';
|
|
4
5
|
/** Options for {@link SpatialDataPort.getTranscriptTile}. */
|
|
5
6
|
export interface TranscriptTileQuery {
|
|
@@ -145,6 +146,12 @@ export interface SpatialDataPort {
|
|
|
145
146
|
* mode should never pay for it.
|
|
146
147
|
*/
|
|
147
148
|
getVolume?(): Promise<Uint8Array>;
|
|
149
|
+
/**
|
|
150
|
+
* Open the region set a boundary set names (`SpatialPolygonTilesMeta.sets[i].regionSource`): a
|
|
151
|
+
* 3D segmentation's outlines, every plane of them, as a tiled region source. Optional: without
|
|
152
|
+
* it the set is drawn as boundaries only.
|
|
153
|
+
*/
|
|
154
|
+
openRegionSet?(id: string): Promise<RegionSource>;
|
|
148
155
|
}
|
|
149
156
|
/**
|
|
150
157
|
* DI token for the host's {@link SpatialDataPort}. Optional: a host that serves no
|
|
@@ -175,6 +175,15 @@ export interface IRegionEditorApi {
|
|
|
175
175
|
* drawn or picked, and their surfaces in the volume view are hidden.
|
|
176
176
|
*/
|
|
177
177
|
setClassesHidden?(classes: readonly string[], hidden: boolean): void;
|
|
178
|
+
/**
|
|
179
|
+
* The classes the spatial 3D view draws as individual cells (each cell its own surface) rather
|
|
180
|
+
* than as one surface per class, and every change of it. Optional.
|
|
181
|
+
*/
|
|
182
|
+
getIndividualClasses3d$?(): Observable<ReadonlySet<string>>;
|
|
183
|
+
/** The classes that can be drawn as individual cells now (a 3D segmentation's, in spatial 3D). */
|
|
184
|
+
getCellableClasses3d$?(): Observable<ReadonlySet<string>>;
|
|
185
|
+
/** Draw these classes as individual cells in spatial 3D (or merged again). Optional. */
|
|
186
|
+
setClassesIndividual3d?(classes: readonly string[], individual: boolean): void;
|
|
178
187
|
/** Select every region of these classes on the slice, the source's included (taken). */
|
|
179
188
|
selectRegionClasses?(classes: readonly string[], additive?: boolean): Promise<void>;
|
|
180
189
|
/** The classes the selection was picked as by {@link selectRegionClasses}; [] once anything
|
|
@@ -84,6 +84,12 @@ export interface IRegionEditApi {
|
|
|
84
84
|
export interface IRegionSourceApi {
|
|
85
85
|
/** The current slice's source as a layer draws it, and every change of it; null without one. */
|
|
86
86
|
getRegionSourceView$(): Observable<RegionSourceView | null>;
|
|
87
|
+
/**
|
|
88
|
+
* The source view the overlays draw: null while another layer draws the same outlines (see
|
|
89
|
+
* `RegionStore.setRegionSourceDrawn`). Optional: without it, the overlays draw
|
|
90
|
+
* {@link getRegionSourceView$}.
|
|
91
|
+
*/
|
|
92
|
+
getDrawnRegionSourceView$?(): Observable<RegionSourceView | null>;
|
|
87
93
|
/** Take source features into the working set as regions and select them (added to the
|
|
88
94
|
* selection with `additive`). Resolves the region ids. */
|
|
89
95
|
selectSourceFeatures(ids: readonly string[], additive?: boolean): Promise<number[]>;
|
|
@@ -262,10 +262,16 @@ export type SpatialBounds = [number, number, number, number];
|
|
|
262
262
|
/** Tiled cell/nucleus boundaries. Level 0 is the finest (every vertex). */
|
|
263
263
|
export interface SpatialPolygonTilesMeta {
|
|
264
264
|
bounds: SpatialBounds;
|
|
265
|
-
/**
|
|
265
|
+
/**
|
|
266
|
+
* Boundary sets on offer — typically `cell` and `nucleus`. A 3D segmentation's set (its cells
|
|
267
|
+
* outlined on every plane they span) also names its outlines as a region set by `regionSource`,
|
|
268
|
+
* an id the port opens (`SpatialDataPort.openRegionSet`): the visualizer attaches it to the
|
|
269
|
+
* dataset's image in the region editor, which reconstructs the cells in 3D.
|
|
270
|
+
*/
|
|
266
271
|
sets: {
|
|
267
272
|
name: string;
|
|
268
273
|
label: string;
|
|
274
|
+
regionSource?: string;
|
|
269
275
|
}[];
|
|
270
276
|
/** The set to draw when the user has not chosen. */
|
|
271
277
|
defaultSet?: string;
|
|
@@ -7,8 +7,9 @@ import { Polygon, Region } from '../models/region';
|
|
|
7
7
|
import { PlotType, PlotTypeDescriptor } from './plot-type';
|
|
8
8
|
import { ViewerCapabilities } from './capabilities.contract';
|
|
9
9
|
import { IRegionOverlay } from './region-overlay.contract';
|
|
10
|
+
import type { RegionSource } from '../region-source/region-source';
|
|
10
11
|
import { IHistogram } from './channel-histogram-api.contract';
|
|
11
|
-
import { CanvasToolOptions, ColormapNode, IWandOptions, IBrushOptions, SpatialViewState, SpatialColorBy } from './display-types';
|
|
12
|
+
import { CanvasToolOptions, ColormapNode, IWandOptions, IBrushOptions, SpatialViewState, SpatialColorBy, Cells3dStatus, ObservationsInSelection, TranscriptsInSelection } from './display-types';
|
|
12
13
|
import { CategoricalColumnMeta, SpatialDataset, SpatialEmbedding, SpatialMarkerGenes, SpatialSelectionMask, SpatialTranscriptCounts } from './spatial-dataset.contract';
|
|
13
14
|
import type { PlotModeViewport } from './plot-type-contribution.contract';
|
|
14
15
|
/**
|
|
@@ -393,6 +394,26 @@ export interface ISpatialControls {
|
|
|
393
394
|
/** True while the observation gene filter could not be applied (an expression read failed):
|
|
394
395
|
* the observations then still show the last filter that could be. */
|
|
395
396
|
getGeneFilterFailed$?(): Observable<boolean>;
|
|
397
|
+
/** The 3D view's individual cells as they stand (null: none are drawn). Optional. */
|
|
398
|
+
getCells3dStatus$?(): Observable<Cells3dStatus | null>;
|
|
399
|
+
/**
|
|
400
|
+
* Whether the dataset's region set (a 3D segmentation's) is attached and placed among the
|
|
401
|
+
* observations, so the 3D view can show the cells: not until its source opened with its planes,
|
|
402
|
+
* and never when it could not be or was not attached. Optional: without it, the dataset naming a
|
|
403
|
+
* set is taken for it.
|
|
404
|
+
*/
|
|
405
|
+
getCells3dAvailable$?(): Observable<boolean>;
|
|
406
|
+
/**
|
|
407
|
+
* The transcripts of the selection, by gene, kept up to date as the selection, the picked
|
|
408
|
+
* classes, the regions or the plane shown change (see {@link TranscriptsInSelection}). Optional:
|
|
409
|
+
* only a dataset with tiled transcripts has it.
|
|
410
|
+
*/
|
|
411
|
+
getTranscriptsInSelection$?(): Observable<TranscriptsInSelection>;
|
|
412
|
+
/**
|
|
413
|
+
* The observations of the same selection, by group, kept up to date as it changes (see
|
|
414
|
+
* {@link ObservationsInSelection}). Optional.
|
|
415
|
+
*/
|
|
416
|
+
getObservationsInSelection$?(): Observable<ObservationsInSelection>;
|
|
396
417
|
/**
|
|
397
418
|
* The z positions of the sections the dataset was imaged at, ascending — or
|
|
398
419
|
* null when its z is continuous rather than sectioned (so there are no sections
|
|
@@ -517,6 +538,27 @@ export interface IVisualizer extends IDataRenderer, IRegionStore, IToolControlle
|
|
|
517
538
|
/** This backend's region renderer. May be null until a plot is mounted
|
|
518
539
|
* (OpenSeadragon). Drives region draw/select modes uniformly. */
|
|
519
540
|
getRegionOverlay(): IRegionOverlay | null;
|
|
541
|
+
/**
|
|
542
|
+
* Attach a spatial dataset's region set (a 3D segmentation's cells, see
|
|
543
|
+
* `SpatialDataPort.openRegionSet`) to its image, the one plotted, beside the image's own regions;
|
|
544
|
+
* once per image and `key`. Resolves whether it is attached. Optional: the router's.
|
|
545
|
+
*/
|
|
546
|
+
attachDatasetRegionSet?(open: () => Promise<RegionSource>, key: string): Promise<boolean>;
|
|
547
|
+
/**
|
|
548
|
+
* The spatial dataset no longer names the attached region set (it was cleared, or another was
|
|
549
|
+
* selected) while its image is still plotted: take down what the set brought to the views (its
|
|
550
|
+
* planes, its cells and surfaces in 3D) unless `keep` is its key. Its regions stay the image's,
|
|
551
|
+
* with what was taken from or deleted in them, as on another image: the same set attached again
|
|
552
|
+
* is restored as it was. Optional: the router's.
|
|
553
|
+
*/
|
|
554
|
+
releaseDatasetRegionSet?(keep: string | null): void;
|
|
555
|
+
/**
|
|
556
|
+
* The planes of that region set (its `sliceZ`) while they slice the plotted image, an image of
|
|
557
|
+
* one plane; null otherwise. The host's slice slider runs over them: each position shows that
|
|
558
|
+
* plane's regions and, in the 2D spatial view, its observations and transcripts. Optional: the
|
|
559
|
+
* router's.
|
|
560
|
+
*/
|
|
561
|
+
getDatasetPlanes$?(): Observable<readonly number[] | null>;
|
|
520
562
|
/** Split a volume view: the displayed slice, with every region tool, in the div `div` beside
|
|
521
563
|
* the 3D view; null closes it. Resolves whether the slice shows. Optional: the router's. */
|
|
522
564
|
setSliceEditor?(div: string | null): Promise<boolean>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReconstructOptions, SliceOutlines } from './reconstruct';
|
|
2
|
+
/**
|
|
3
|
+
* A 3D segmentation's cells reconstructed one by one: each cell's outlines on the planes it spans
|
|
4
|
+
* (see `SliceOutlines`, slice k at z = k) meshed by themselves ({@link reconstruct}), so two cells
|
|
5
|
+
* of one type that touch stay two surfaces, where a class's union merges them.
|
|
6
|
+
*
|
|
7
|
+
* The meshes are concatenated into one, which a single surface layer draws, with each vertex's
|
|
8
|
+
* cell ({@link CellsMesh.vertexCell}): what colours a cell, hides it, or names it under the pointer.
|
|
9
|
+
*/
|
|
10
|
+
/** One cell's outlines: the cell (an observation index) and its rings on each plane it spans. */
|
|
11
|
+
export interface CellOutlines {
|
|
12
|
+
cell: number;
|
|
13
|
+
slices: SliceOutlines[];
|
|
14
|
+
}
|
|
15
|
+
/** Cells' surfaces in one mesh, in image px (x, y) and slice indices (z), each vertex's cell known. */
|
|
16
|
+
export interface CellsMesh {
|
|
17
|
+
vertices: Float32Array;
|
|
18
|
+
faces: Uint32Array;
|
|
19
|
+
/** The cell (observation index) of each vertex. */
|
|
20
|
+
vertexCell: Uint32Array;
|
|
21
|
+
/** The cells meshed (those with an area on some plane), in order. */
|
|
22
|
+
cells: Uint32Array;
|
|
23
|
+
}
|
|
24
|
+
/** Each cell's surface by itself (one smoothing pass: a cell is a few voxels across). */
|
|
25
|
+
export declare function reconstructCells(cells: readonly CellOutlines[], opts?: ReconstructOptions): CellsMesh;
|
|
26
|
+
/** The buffers of a {@link CellsMesh}, to hand them across a worker boundary without a copy. */
|
|
27
|
+
export declare function cellsMeshBuffers(mesh: CellsMesh): ArrayBuffer[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a stack's reconstructions sit in a 3D view's world (pure: the volume and spatial 3D views
|
|
3
|
+
* place region surfaces with it, on the main thread or in a worker).
|
|
4
|
+
*/
|
|
5
|
+
/** Where the stack sits in the volume's world: its full size, and the centred world box it fills. */
|
|
6
|
+
export interface VolumeFrame {
|
|
7
|
+
/** Image width, height (px) and slice count. */
|
|
8
|
+
size: [number, number, number];
|
|
9
|
+
/** The world box (centred on the origin), Z as currently stretched. */
|
|
10
|
+
world: [number, number, number];
|
|
11
|
+
/**
|
|
12
|
+
* How much of {@link world}'s depth is the view's Z stretch (default 1). The region surfaces keep
|
|
13
|
+
* their geometry in the unstretched box and take the stretch as a GPU scale (`scale3d`), so
|
|
14
|
+
* dragging the Z handle re-uploads no mesh.
|
|
15
|
+
*/
|
|
16
|
+
zScale?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The slices at z of their own rather than spread over the box: a region set's planes (a 3D
|
|
19
|
+
* segmentation's, outlined on an image of one plane) placed where the observations are. Given,
|
|
20
|
+
* it places the surfaces and {@link size} and {@link world} do not.
|
|
21
|
+
*/
|
|
22
|
+
planes?: PlanePlacement;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Image px and slices → world, slice by slice: `x * scale[0] + offset[0]`, `y * scale[1] +
|
|
26
|
+
* offset[1]`, and slice k at `z[k] + offset[2]` (unstretched): between two slices in proportion,
|
|
27
|
+
* past the ends by the nearest spacing (a surface's caps, half a slice out).
|
|
28
|
+
*/
|
|
29
|
+
export interface PlanePlacement {
|
|
30
|
+
/** Each slice's z, by slice index, ascending. */
|
|
31
|
+
z: readonly number[];
|
|
32
|
+
scale: [number, number];
|
|
33
|
+
offset: [number, number, number];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Image px / slice coordinates → the volume's centred world box: pixel x of a `w`-wide image
|
|
37
|
+
* spans the box's width, slice k sits at the middle of its share of the box's depth. With
|
|
38
|
+
* {@link VolumeFrame.planes}, as they place them instead.
|
|
39
|
+
*/
|
|
40
|
+
export declare function toWorld(vertices: Float32Array, frame: VolumeFrame): Float32Array;
|
|
41
|
+
/**
|
|
42
|
+
* The z of slice `k` (fractional): between two slices in proportion, past the ends by the nearest
|
|
43
|
+
* spacing (one slice alone: a unit apart).
|
|
44
|
+
*/
|
|
45
|
+
export declare function sliceZ(z: readonly number[], k: number): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Camera3D } from 'napari-js';
|
|
2
|
+
/**
|
|
3
|
+
* Keep the whole scene within the 3D camera's depth range, however close the camera comes.
|
|
4
|
+
*
|
|
5
|
+
* napari-js (0.20) puts the far plane at `4 · distance + 1` from the eye, so the closer the camera
|
|
6
|
+
* (the more one zooms in), the nearer the far plane: zoomed in on a long, flat specimen seen at an
|
|
7
|
+
* angle (a MERFISH section, 9,000 px long), everything farther than four distances is cut away and
|
|
8
|
+
* the canvas goes black past a straight edge — one cannot zoom close to a small part of it. Here the
|
|
9
|
+
* far plane is pushed out to the far side of the scene (`distance + |target| + radius`, the scene
|
|
10
|
+
* centred on the origin as the 3D views centre it), never nearer than napari-js's own; the near
|
|
11
|
+
* plane, the field of view and the view are napari-js's, as are the matrices (copied: napari-js does
|
|
12
|
+
* not export them). Its picking and projections read the same matrix, so they agree.
|
|
13
|
+
*
|
|
14
|
+
* TODO: napari-js should take the scene's extent itself (a far plane from the layers' bounds); this
|
|
15
|
+
* override is removed once it does.
|
|
16
|
+
*
|
|
17
|
+
* Returns the function that restores napari-js's own projection.
|
|
18
|
+
*/
|
|
19
|
+
export declare function keepSceneInDepth(camera: Camera3D, radius: () => number | null): () => void;
|
|
20
|
+
/** The radius of a centred box `[w, h, d]` (half its diagonal). */
|
|
21
|
+
export declare function boxRadius(w: number, h: number, d: number): number;
|
|
22
|
+
/** WebGPU-style perspective (clip z in [0,1]). */
|
|
23
|
+
export declare function perspective(fovy: number, aspect: number, near: number, far: number): Float32Array;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** What the service itself counts on the badge; the spatial tile layers report their own names. */
|
|
2
|
-
export type LoadingSource = 'Image' | 'Observations' | 'Transcripts';
|
|
2
|
+
export type LoadingSource = 'Image' | 'Observations' | 'Transcripts' | 'Cells';
|
|
3
3
|
/**
|
|
4
4
|
* "x reloading…" at the bottom of the canvas, aggregated over everything that loads into one
|
|
5
5
|
* scene: image tiles (the tile client), observation colourings (the spatial scenes) and the
|
|
@@ -39,6 +39,18 @@ export interface NapariScene {
|
|
|
39
39
|
screenProjection?(obs: SpatialObservations): Float32Array | null;
|
|
40
40
|
/** Show these region reconstructions in the 3D view (volume scenes only). */
|
|
41
41
|
setRegionMeshes?(meshes: readonly RegionMesh3D[]): void;
|
|
42
|
+
/**
|
|
43
|
+
* Where the reconstructions' slices are, in the observations' units, when their region set says
|
|
44
|
+
* (a 3D segmentation's planes); null: they are the plotted stack's slices (spatial 3D only).
|
|
45
|
+
*/
|
|
46
|
+
setRegionPlanes?(z: readonly number[] | null): void;
|
|
47
|
+
/**
|
|
48
|
+
* The plane of those (see {@link setRegionPlanes}) the 2D spatial view shows, over an image of
|
|
49
|
+
* one plane: its observations and transcripts are the ones drawn (spatial 2D only).
|
|
50
|
+
*/
|
|
51
|
+
setRegionPlane?(k: number): void;
|
|
52
|
+
/** Draw these classes of the dataset's region set as individual cells (spatial 3D only). */
|
|
53
|
+
setRegionCells?(classes: readonly string[] | null): void;
|
|
42
54
|
/** Highlight the reconstructions of these keys (none: all as usual). */
|
|
43
55
|
setRegionHighlight?(keys: readonly string[]): void;
|
|
44
56
|
/** Hide the reconstructions of these keys. */
|