@aics/vole-core 3.13.0 → 3.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/README.md +5 -2
- package/es/FusedChannelData.js +80 -7
- package/es/PickVolume.js +244 -0
- package/es/RayMarchedAtlasVolume.js +5 -7
- package/es/ThreeJsPanel.js +31 -1
- package/es/View3d.js +66 -3
- package/es/Volume.js +1 -1
- package/es/VolumeCache.js +10 -3
- package/es/VolumeDrawable.js +85 -3
- package/es/constants/volumeRayMarchPickShader.js +91 -0
- package/es/constants/volumeRayMarchShader.js +1 -1
- package/es/loaders/JsonImageInfoLoader.js +2 -1
- package/es/loaders/OmeZarrLoader.js +30 -31
- package/es/loaders/VolumeLoadError.js +1 -1
- package/es/loaders/zarr_utils/ChunkPrefetchIterator.js +7 -0
- package/es/loaders/zarr_utils/validation.js +18 -7
- package/es/loaders/zarr_utils/wrapArray.js +39 -0
- package/es/types/FusedChannelData.d.ts +2 -0
- package/es/types/PickVolume.d.ts +43 -0
- package/es/types/ThreeJsPanel.d.ts +2 -1
- package/es/types/View3d.d.ts +35 -3
- package/es/types/VolumeCache.d.ts +5 -2
- package/es/types/VolumeDrawable.d.ts +8 -2
- package/es/types/constants/volumeRayMarchPickShader.d.ts +85 -0
- package/es/types/index.d.ts +2 -2
- package/es/types/loaders/zarr_utils/types.d.ts +17 -12
- package/es/types/loaders/zarr_utils/validation.d.ts +14 -2
- package/es/types/loaders/zarr_utils/wrapArray.d.ts +7 -0
- package/es/types/types.d.ts +17 -1
- package/package.json +3 -3
- package/es/loaders/zarr_utils/WrappedStore.js +0 -51
- package/es/types/loaders/zarr_utils/WrappedStore.d.ts +0 -24
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { errorConstructors } from "serialize-error";
|
|
2
|
-
import { NodeNotFoundError, KeyError } from "
|
|
2
|
+
import { NodeNotFoundError, KeyError } from "zarrita";
|
|
3
3
|
// geotiff doesn't export its error types...
|
|
4
4
|
|
|
5
5
|
/** Groups possible load errors into a few broad categories which we can give similar guidance to the user about. */
|
|
@@ -34,6 +34,13 @@ export default class ChunkPrefetchIterator {
|
|
|
34
34
|
updateMinMax(chunk[4], extrema[3]);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// Bail out if we have any non-finite values in the extrema (the iterator will be empty)
|
|
38
|
+
if (extrema.flat().some(val => !Number.isFinite(val))) {
|
|
39
|
+
this.directionStates = [];
|
|
40
|
+
this.priorityDirectionStates = [];
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
// Create `PrefetchDirectionState`s for each direction
|
|
38
45
|
this.directionStates = [];
|
|
39
46
|
this.priorityDirectionStates = [];
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { VolumeLoadError, VolumeLoadErrorType } from "../VolumeLoadError.js";
|
|
2
|
+
/**
|
|
3
|
+
* If `meta` is the top-level metadata of a zarr node formatted according to the OME-Zarr spec version 0.5, returns
|
|
4
|
+
* the object formatted according to v0.4 of the spec. For our purposes this just means flattening out the `ome` key.
|
|
5
|
+
*
|
|
6
|
+
* Return type is `unknown` because this does no actual validation; use `validateOMEZarrMetadata` for that.
|
|
7
|
+
*/
|
|
8
|
+
export const toOMEZarrMetaV4 = meta => meta.ome ?? meta;
|
|
2
9
|
function isObjectWithProp(obj, prop) {
|
|
3
10
|
return typeof obj === "object" && obj !== null && prop in obj;
|
|
4
11
|
}
|
|
@@ -17,18 +24,22 @@ function assertPropIsArray(obj, prop, name = "zarr") {
|
|
|
17
24
|
}
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
/** Intermediate stage of validation, before we've picked a single multiscale to validate */
|
|
28
|
+
|
|
29
|
+
export function assertMetadataHasMultiscales(meta, name = "zarr") {
|
|
30
|
+
// data is an object with a key "multiscales", which is a non-empty array
|
|
31
|
+
assertMetadataHasProp(meta, "multiscales", name);
|
|
32
|
+
assertPropIsArray(meta, "multiscales", name);
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
/**
|
|
21
|
-
* Validates that the `OMEZarrMetadata` record `
|
|
36
|
+
* Validates that the `OMEZarrMetadata` record `meta` has the minimal amount of data required to open a volume. Since
|
|
22
37
|
* we only ever open one multiscale, we only validate the multiscale metadata record at index `multiscaleIdx` here.
|
|
23
38
|
* `name` is used in error messages to identify the source of the metadata.
|
|
24
39
|
*/
|
|
25
|
-
export function validateOMEZarrMetadata(
|
|
26
|
-
// data is an object with a key "multiscales", which is an array
|
|
27
|
-
assertMetadataHasProp(data, "multiscales", name);
|
|
28
|
-
assertPropIsArray(data, "multiscales", name);
|
|
29
|
-
|
|
40
|
+
export function validateOMEZarrMetadata(meta, multiscaleIdx = 0, name = "zarr") {
|
|
30
41
|
// check that a multiscale metadata entry exists at `multiscaleIdx`
|
|
31
|
-
const multiscaleMeta =
|
|
42
|
+
const multiscaleMeta = meta.multiscales[multiscaleIdx];
|
|
32
43
|
if (!multiscaleMeta) {
|
|
33
44
|
throw new VolumeLoadError(`${name} metadata does not have requested multiscale level ${multiscaleIdx}`, {
|
|
34
45
|
type: VolumeLoadErrorType.INVALID_METADATA
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isChunk } from "../../VolumeCache.js";
|
|
2
|
+
export default function wrapArray(array, basePath, cache, queue) {
|
|
3
|
+
const path = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
4
|
+
const keyBase = path + array.path + (array.path.endsWith("/") ? "" : "/");
|
|
5
|
+
const getChunk = async (coords, opts) => {
|
|
6
|
+
if (opts?.subscriber && opts.reportChunk) {
|
|
7
|
+
opts.reportChunk(coords, opts.subscriber);
|
|
8
|
+
}
|
|
9
|
+
const fullKey = keyBase + coords.join(",");
|
|
10
|
+
const cacheResult = cache?.get(fullKey);
|
|
11
|
+
if (cacheResult && isChunk(cacheResult)) {
|
|
12
|
+
return cacheResult;
|
|
13
|
+
}
|
|
14
|
+
let result;
|
|
15
|
+
if (queue && opts?.subscriber) {
|
|
16
|
+
result = await queue.addRequest(fullKey, opts?.subscriber, () => array.getChunk(coords, opts), opts.isPrefetch);
|
|
17
|
+
} else {
|
|
18
|
+
result = await array.getChunk(coords, opts);
|
|
19
|
+
}
|
|
20
|
+
cache?.insert(fullKey, result);
|
|
21
|
+
return result;
|
|
22
|
+
};
|
|
23
|
+
return new Proxy(array, {
|
|
24
|
+
get: (target, prop) => {
|
|
25
|
+
if (prop === "getChunk") {
|
|
26
|
+
return getChunk;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding
|
|
30
|
+
const value = target[prop];
|
|
31
|
+
if (value instanceof Function) {
|
|
32
|
+
return function (...args) {
|
|
33
|
+
return value.apply(target, args);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -11,12 +11,14 @@ export default class FusedChannelData {
|
|
|
11
11
|
private fuseMaterialF;
|
|
12
12
|
private fuseMaterialUI;
|
|
13
13
|
private fuseMaterialI;
|
|
14
|
+
private fuseMaterialColorizeUI;
|
|
14
15
|
private fuseMaterialProps;
|
|
15
16
|
private fuseScene;
|
|
16
17
|
private quadCamera;
|
|
17
18
|
private fuseRenderTarget;
|
|
18
19
|
constructor(atlasX: number, atlasY: number);
|
|
19
20
|
private setupFuseMaterial;
|
|
21
|
+
private setupFuseColorizeMaterial;
|
|
20
22
|
getFusedTexture(): Texture;
|
|
21
23
|
cleanup(): void;
|
|
22
24
|
private getShader;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DepthTexture, Group, OrthographicCamera, PerspectiveCamera, Texture, WebGLRenderer, WebGLRenderTarget } from "three";
|
|
2
|
+
import { Volume } from "./index.js";
|
|
3
|
+
import Channel from "./Channel.js";
|
|
4
|
+
import type { VolumeRenderImpl } from "./VolumeRenderImpl.js";
|
|
5
|
+
import type { FuseChannel } from "./types.js";
|
|
6
|
+
import { VolumeRenderSettings, SettingsFlags } from "./VolumeRenderSettings.js";
|
|
7
|
+
export default class PickVolume implements VolumeRenderImpl {
|
|
8
|
+
private settings;
|
|
9
|
+
volume: Volume;
|
|
10
|
+
private geometry;
|
|
11
|
+
private geometryMesh;
|
|
12
|
+
private geometryTransformNode;
|
|
13
|
+
private scene;
|
|
14
|
+
private uniforms;
|
|
15
|
+
private emptyPositionTex;
|
|
16
|
+
needRedraw: boolean;
|
|
17
|
+
private pickBuffer;
|
|
18
|
+
private channelToPick;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new PickVolume.
|
|
21
|
+
* @param volume The volume that this renderer should render data from.
|
|
22
|
+
* @param settings Optional settings object. If set, updates the renderer with
|
|
23
|
+
* the given settings. Otherwise, uses the default VolumeRenderSettings.
|
|
24
|
+
*/
|
|
25
|
+
constructor(volume: Volume, settings?: VolumeRenderSettings);
|
|
26
|
+
setChannelToPick(channel: number): void;
|
|
27
|
+
getPickBuffer(): WebGLRenderTarget;
|
|
28
|
+
updateVolumeDimensions(): void;
|
|
29
|
+
viewpointMoved(): void;
|
|
30
|
+
updateSettings(newSettings: VolumeRenderSettings, dirtyFlags?: number | SettingsFlags): void;
|
|
31
|
+
/**
|
|
32
|
+
* Creates the geometry mesh and material for rendering the volume.
|
|
33
|
+
* @param uniforms object containing uniforms to pass to the shader material.
|
|
34
|
+
* @returns the new geometry and geometry mesh.
|
|
35
|
+
*/
|
|
36
|
+
private createGeometry;
|
|
37
|
+
cleanup(): void;
|
|
38
|
+
doRender(renderer: WebGLRenderer, camera: PerspectiveCamera | OrthographicCamera, depthTexture?: DepthTexture | Texture | null): void;
|
|
39
|
+
get3dObject(): Group;
|
|
40
|
+
private setUniform;
|
|
41
|
+
updateActiveChannels(_channelcolors: FuseChannel[], _channeldata: Channel[]): void;
|
|
42
|
+
setRenderUpdateListener(_listener?: ((iteration: number) => void) | undefined): void;
|
|
43
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Color, Event, EventListener, OrthographicCamera, PerspectiveCamera,
|
|
1
|
+
import { Color, DepthTexture, Event, EventListener, OrthographicCamera, PerspectiveCamera, Scene, WebGLRenderer, WebGLRenderTarget } from "three";
|
|
2
2
|
import TrackballControls from "./TrackballControls.js";
|
|
3
3
|
import { ViewportCorner } from "./types.js";
|
|
4
4
|
export declare const VOLUME_LAYER = 0;
|
|
@@ -104,4 +104,5 @@ export declare class ThreeJsPanel {
|
|
|
104
104
|
stopRenderLoop(): void;
|
|
105
105
|
removeControlHandlers(): void;
|
|
106
106
|
setControlHandlers(onstart: EventListener<Event, "start", TrackballControls>, onchange: EventListener<Event, "change", TrackballControls>, onend: EventListener<Event, "end", TrackballControls>): void;
|
|
107
|
+
hitTest(offsetX: number, offsetY: number, pickBuffer: WebGLRenderTarget | undefined): number;
|
|
107
108
|
}
|
package/es/types/View3d.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { CameraState } from "./ThreeJsPanel.js";
|
|
|
2
2
|
import VolumeDrawable from "./VolumeDrawable.js";
|
|
3
3
|
import { Light } from "./Light.js";
|
|
4
4
|
import Volume from "./Volume.js";
|
|
5
|
-
import { type VolumeChannelDisplayOptions, type VolumeDisplayOptions, ViewportCorner, RenderMode } from "./types.js";
|
|
5
|
+
import { type ColorizeFeature, type VolumeChannelDisplayOptions, type VolumeDisplayOptions, ViewportCorner, RenderMode } from "./types.js";
|
|
6
6
|
import { PerChannelCallback } from "./loaders/IVolumeLoader.js";
|
|
7
7
|
import VolumeLoaderContext from "./workers/VolumeLoaderContext.js";
|
|
8
8
|
export declare const RENDERMODE_RAYMARCH = RenderMode.RAYMARCH;
|
|
@@ -49,12 +49,16 @@ export declare class View3d {
|
|
|
49
49
|
*/
|
|
50
50
|
capture(dataurlcallback: (name: string) => void): void;
|
|
51
51
|
getDOMElement(): HTMLDivElement;
|
|
52
|
+
getCanvasDOMElement(): HTMLCanvasElement;
|
|
52
53
|
getCameraState(): CameraState;
|
|
53
54
|
setCameraState(transform: Partial<CameraState>): void;
|
|
54
55
|
/**
|
|
55
56
|
* Force a redraw.
|
|
57
|
+
* @param synchronous If true, the redraw will be done synchronously. If false (default), the
|
|
58
|
+
* redraw will be done asynchronously via `requestAnimationFrame`. Redraws should be done async
|
|
59
|
+
* whenever possible for the best performance.
|
|
56
60
|
*/
|
|
57
|
-
redraw(): void;
|
|
61
|
+
redraw(synchronous?: boolean): void;
|
|
58
62
|
unsetImage(): VolumeDrawable | undefined;
|
|
59
63
|
/**
|
|
60
64
|
* Add a new volume image to the viewer. (The viewer currently only supports a single image at a time - adding repeatedly, without removing in between, is a potential resource leak)
|
|
@@ -92,7 +96,7 @@ export declare class View3d {
|
|
|
92
96
|
onVolumeChannelAdded(volume: Volume, newChannelIndex: number): void;
|
|
93
97
|
onVolumeLoadError(volume: Volume, error: unknown): void;
|
|
94
98
|
setLoadErrorHandler(handler: ((volume: Volume, error: unknown) => void) | undefined): void;
|
|
95
|
-
setTime(volume: Volume, time: number, onChannelLoaded?: PerChannelCallback): void
|
|
99
|
+
setTime(volume: Volume, time: number, onChannelLoaded?: PerChannelCallback): Promise<void>;
|
|
96
100
|
/**
|
|
97
101
|
* Nudge the scale level loaded into this volume off the one chosen by the loader.
|
|
98
102
|
* E.g. a bias of `1` will load 1 scale level lower than "ideal."
|
|
@@ -104,6 +108,13 @@ export declare class View3d {
|
|
|
104
108
|
* @param {number} maskChannelIndex
|
|
105
109
|
*/
|
|
106
110
|
setVolumeChannelAsMask(volume: Volume, maskChannelIndex: number): void;
|
|
111
|
+
/**
|
|
112
|
+
* @description Set the necessary data to colorize a segmentation channel, or turn off colorization.
|
|
113
|
+
* @param volume The volume to set the colorize feature for
|
|
114
|
+
* @param channelIndex The channel that will be colorized. This only makes sense for segmentation volumes.
|
|
115
|
+
* @param featureInfo A collection of all parameters necessary to colorize the channel. Pass null to turn off colorization.
|
|
116
|
+
*/
|
|
117
|
+
setChannelColorizeFeature(volume: Volume, channelIndex: number, featureInfo: ColorizeFeature | null): void;
|
|
107
118
|
/**
|
|
108
119
|
* Set voxel dimensions - controls volume scaling. For example, the physical measurements of the voxels from a biological data set
|
|
109
120
|
* @param {Object} volume
|
|
@@ -353,5 +364,26 @@ export declare class View3d {
|
|
|
353
364
|
hasWebGL2(): boolean;
|
|
354
365
|
handleKeydown: (event: KeyboardEvent) => void;
|
|
355
366
|
removeEventListeners(): void;
|
|
367
|
+
/**
|
|
368
|
+
* @description Set the selected ID for a given channel. This is used to change the appearance of the volume where that id is.
|
|
369
|
+
* @param volume the image to set the selected ID on
|
|
370
|
+
* @param channel the channel index where the selected ID is
|
|
371
|
+
* @param id the selected id
|
|
372
|
+
*/
|
|
373
|
+
setSelectedID(volume: Volume, channel: number, id: number): void;
|
|
374
|
+
/**
|
|
375
|
+
* @description Enable or disable picking on a volume. If enabled, the channelIndex is used to determine which channel to pick.
|
|
376
|
+
* @param volume the image to enable picking on
|
|
377
|
+
* @param enabled set true to enable, false to disable
|
|
378
|
+
* @param channelIndex if enabled is set to true, pass the pickable channel index here
|
|
379
|
+
*/
|
|
380
|
+
enablePicking(volume: Volume, enabled: boolean, channelIndex?: number): void;
|
|
381
|
+
/**
|
|
382
|
+
* @description This function is used to determine if a mouse event occurred over a volume object.
|
|
383
|
+
* @param offsetX mouse event x coordinate
|
|
384
|
+
* @param offsetY mouse event y coordinate
|
|
385
|
+
* @returns id of object that is under offsetX, offsetY. -1 if none
|
|
386
|
+
*/
|
|
387
|
+
hitTest(offsetX: number, offsetY: number): number;
|
|
356
388
|
private setupGui;
|
|
357
389
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Chunk, DataType } from "zarrita";
|
|
2
|
+
export type CacheData = ArrayBuffer | Chunk<DataType>;
|
|
3
|
+
export declare const isChunk: (data: CacheData) => data is Chunk<DataType>;
|
|
1
4
|
export default class VolumeCache {
|
|
2
5
|
private entries;
|
|
3
6
|
readonly maxSize: number;
|
|
@@ -31,11 +34,11 @@ export default class VolumeCache {
|
|
|
31
34
|
* Adds a new entry to the cache.
|
|
32
35
|
* @returns {boolean} a boolean indicating whether the insertion succeeded.
|
|
33
36
|
*/
|
|
34
|
-
insert(key: string, data:
|
|
37
|
+
insert(key: string, data: CacheData): boolean;
|
|
35
38
|
/** Internal implementation of `get`. Returns all entry metadata, not just the raw data. */
|
|
36
39
|
private getEntry;
|
|
37
40
|
/** Attempts to get a single entry from the cache. */
|
|
38
|
-
get(key: string):
|
|
41
|
+
get(key: string): CacheData | undefined;
|
|
39
42
|
/** Clears all cache entries whose keys begin with the specified prefix. */
|
|
40
43
|
clearWithPrefix(prefix: string): void;
|
|
41
44
|
/** Clears all data from the cache. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Vector3, Object3D, Euler, DepthTexture, OrthographicCamera, PerspectiveCamera, WebGLRenderer, Texture } from "three";
|
|
1
|
+
import { Vector3, Object3D, Euler, DepthTexture, OrthographicCamera, PerspectiveCamera, WebGLRenderer, WebGLRenderTarget, Texture } from "three";
|
|
2
2
|
import { Pane } from "tweakpane";
|
|
3
3
|
import Volume from "./Volume.js";
|
|
4
|
-
import type { VolumeDisplayOptions, VolumeChannelDisplayOptions } from "./types.js";
|
|
4
|
+
import type { VolumeDisplayOptions, VolumeChannelDisplayOptions, ColorizeFeature } from "./types.js";
|
|
5
5
|
import { RenderMode } from "./types.js";
|
|
6
6
|
import { Light } from "./Light.js";
|
|
7
7
|
import Channel from "./Channel.js";
|
|
@@ -28,9 +28,11 @@ export default class VolumeDrawable {
|
|
|
28
28
|
sceneRoot: Object3D;
|
|
29
29
|
private meshVolume;
|
|
30
30
|
private volumeRendering;
|
|
31
|
+
private pickRendering?;
|
|
31
32
|
private renderMode;
|
|
32
33
|
private renderUpdateListener?;
|
|
33
34
|
constructor(volume: Volume, options: VolumeDisplayOptions);
|
|
35
|
+
getPickBuffer(): WebGLRenderTarget | undefined;
|
|
34
36
|
/**
|
|
35
37
|
* Updates whether a channel's data must be loaded for rendering,
|
|
36
38
|
* based on if its volume or isosurface is enabled, or whether it is needed for masking.
|
|
@@ -57,9 +59,12 @@ export default class VolumeDrawable {
|
|
|
57
59
|
setFlipAxes(flipX: -1 | 1, flipY: -1 | 1, flipZ: -1 | 1): void;
|
|
58
60
|
setMaxProjectMode(isMaxProject: boolean): void;
|
|
59
61
|
onAnimate(renderer: WebGLRenderer, camera: PerspectiveCamera | OrthographicCamera, depthTexture?: DepthTexture | Texture | null): void;
|
|
62
|
+
enablePicking(enabled: boolean, channelIndex: number): void;
|
|
63
|
+
fillPickBuffer(renderer: WebGLRenderer, camera: PerspectiveCamera | OrthographicCamera, depthTexture?: DepthTexture | Texture | null): void;
|
|
60
64
|
getViewMode(): Axis;
|
|
61
65
|
getIsovalue(channel: number): number | undefined;
|
|
62
66
|
hasIsosurface(channel: number): boolean;
|
|
67
|
+
setSelectedID(channelIndex: number, id: number): boolean;
|
|
63
68
|
fuse(): void;
|
|
64
69
|
setRenderUpdateListener(callback?: (iteration: number) => void): void;
|
|
65
70
|
updateShadingMethod(isbrdf: boolean): void;
|
|
@@ -85,6 +90,7 @@ export default class VolumeDrawable {
|
|
|
85
90
|
setBrightness(brightness: number): void;
|
|
86
91
|
getBrightness(): number;
|
|
87
92
|
setChannelAsMask(channelIndex: number): void;
|
|
93
|
+
setChannelColorizeFeature(channelIndex: number, featureInfo: ColorizeFeature | null): void;
|
|
88
94
|
setMaskAlpha(maskAlpha: number): void;
|
|
89
95
|
setShowBoundingBox(showBoundingBox: boolean): void;
|
|
90
96
|
setBoundingBoxColor(color: [number, number, number]): void;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Vector2, Vector3, Matrix4, Texture } from "three";
|
|
2
|
+
export declare const pickVertexShaderSrc: string;
|
|
3
|
+
export declare const pickFragmentShaderSrc: string;
|
|
4
|
+
export declare const pickShaderUniforms: () => {
|
|
5
|
+
iResolution: {
|
|
6
|
+
type: string;
|
|
7
|
+
value: Vector2;
|
|
8
|
+
};
|
|
9
|
+
textureRes: {
|
|
10
|
+
type: string;
|
|
11
|
+
value: Vector2;
|
|
12
|
+
};
|
|
13
|
+
ATLAS_DIMS: {
|
|
14
|
+
type: string;
|
|
15
|
+
value: Vector2;
|
|
16
|
+
};
|
|
17
|
+
AABB_CLIP_MIN: {
|
|
18
|
+
type: string;
|
|
19
|
+
value: Vector3;
|
|
20
|
+
};
|
|
21
|
+
CLIP_NEAR: {
|
|
22
|
+
type: string;
|
|
23
|
+
value: number;
|
|
24
|
+
};
|
|
25
|
+
AABB_CLIP_MAX: {
|
|
26
|
+
type: string;
|
|
27
|
+
value: Vector3;
|
|
28
|
+
};
|
|
29
|
+
CLIP_FAR: {
|
|
30
|
+
type: string;
|
|
31
|
+
value: number;
|
|
32
|
+
};
|
|
33
|
+
textureAtlas: {
|
|
34
|
+
type: string;
|
|
35
|
+
value: Texture;
|
|
36
|
+
};
|
|
37
|
+
textureDepth: {
|
|
38
|
+
type: string;
|
|
39
|
+
value: Texture;
|
|
40
|
+
};
|
|
41
|
+
usingPositionTexture: {
|
|
42
|
+
type: string;
|
|
43
|
+
value: number;
|
|
44
|
+
};
|
|
45
|
+
BREAK_STEPS: {
|
|
46
|
+
type: string;
|
|
47
|
+
value: number;
|
|
48
|
+
};
|
|
49
|
+
SLICES: {
|
|
50
|
+
type: string;
|
|
51
|
+
value: number;
|
|
52
|
+
};
|
|
53
|
+
isOrtho: {
|
|
54
|
+
type: string;
|
|
55
|
+
value: number;
|
|
56
|
+
};
|
|
57
|
+
orthoThickness: {
|
|
58
|
+
type: string;
|
|
59
|
+
value: number;
|
|
60
|
+
};
|
|
61
|
+
orthoScale: {
|
|
62
|
+
type: string;
|
|
63
|
+
value: number;
|
|
64
|
+
};
|
|
65
|
+
maxProject: {
|
|
66
|
+
type: string;
|
|
67
|
+
value: number;
|
|
68
|
+
};
|
|
69
|
+
flipVolume: {
|
|
70
|
+
type: string;
|
|
71
|
+
value: Vector3;
|
|
72
|
+
};
|
|
73
|
+
volumeScale: {
|
|
74
|
+
type: string;
|
|
75
|
+
value: Vector3;
|
|
76
|
+
};
|
|
77
|
+
inverseModelViewMatrix: {
|
|
78
|
+
type: string;
|
|
79
|
+
value: Matrix4;
|
|
80
|
+
};
|
|
81
|
+
inverseProjMatrix: {
|
|
82
|
+
type: string;
|
|
83
|
+
value: Matrix4;
|
|
84
|
+
};
|
|
85
|
+
};
|
package/es/types/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import RequestQueue from "./utils/RequestQueue.js";
|
|
|
8
8
|
import SubscribableRequestQueue from "./utils/SubscribableRequestQueue.js";
|
|
9
9
|
import Histogram from "./Histogram.js";
|
|
10
10
|
import { Lut, remapControlPoints } from "./Lut.js";
|
|
11
|
-
import { ViewportCorner } from "./types.js";
|
|
11
|
+
import { type ColorizeFeature, ViewportCorner } from "./types.js";
|
|
12
12
|
import { VolumeFileFormat, createVolumeLoader, PrefetchDirection } from "./loaders/index.js";
|
|
13
13
|
import { LoadSpec } from "./loaders/IVolumeLoader.js";
|
|
14
14
|
import { OMEZarrLoader } from "./loaders/OmeZarrLoader.js";
|
|
@@ -25,4 +25,4 @@ export type { CreateLoaderOptions } from "./loaders/index.js";
|
|
|
25
25
|
export type { IVolumeLoader, PerChannelCallback, ThreadableVolumeLoader } from "./loaders/IVolumeLoader.js";
|
|
26
26
|
export type { ZarrLoaderFetchOptions } from "./loaders/OmeZarrLoader.js";
|
|
27
27
|
export type { WorkerLoader } from "./workers/VolumeLoaderContext.js";
|
|
28
|
-
export { Histogram, Lut, remapControlPoints, View3d, Volume, VolumeDrawable, LoadSpec, VolumeMaker, VolumeCache, RequestQueue, SubscribableRequestQueue, PrefetchDirection, OMEZarrLoader, JsonImageInfoLoader, RawArrayLoader, type RawArrayData, type RawArrayInfo, type RawArrayLoaderOptions, TiffLoader, VolumeLoaderContext, VolumeLoadError, VolumeLoadErrorType, VolumeFileFormat, createVolumeLoader, Channel, Light, ViewportCorner, AREA_LIGHT, RENDERMODE_PATHTRACE, RENDERMODE_RAYMARCH, SKY_LIGHT, type CameraState, };
|
|
28
|
+
export { Histogram, Lut, remapControlPoints, View3d, Volume, VolumeDrawable, LoadSpec, VolumeMaker, VolumeCache, RequestQueue, SubscribableRequestQueue, PrefetchDirection, OMEZarrLoader, JsonImageInfoLoader, RawArrayLoader, type RawArrayData, type RawArrayInfo, type RawArrayLoaderOptions, TiffLoader, VolumeLoaderContext, VolumeLoadError, VolumeLoadErrorType, VolumeFileFormat, createVolumeLoader, Channel, Light, ViewportCorner, AREA_LIGHT, RENDERMODE_PATHTRACE, RENDERMODE_RAYMARCH, SKY_LIGHT, type CameraState, type ColorizeFeature, };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as zarr from "
|
|
2
|
-
import
|
|
1
|
+
import * as zarr from "zarrita";
|
|
2
|
+
import { AsyncReadable } from "@zarrita/storage";
|
|
3
3
|
import type SubscribableRequestQueue from "../../utils/SubscribableRequestQueue.js";
|
|
4
4
|
export type TCZYX<T> = [T, T, T, T, T];
|
|
5
5
|
export type SubscriberId = ReturnType<SubscribableRequestQueue["addSubscriber"]>;
|
|
@@ -53,16 +53,16 @@ export type OMEMultiscale = {
|
|
|
53
53
|
};
|
|
54
54
|
/** https://ngff.openmicroscopy.org/latest/#omero-md */
|
|
55
55
|
export type OmeroTransitionalMetadata = {
|
|
56
|
-
id
|
|
57
|
-
name
|
|
58
|
-
version
|
|
56
|
+
id?: number;
|
|
57
|
+
name?: string;
|
|
58
|
+
version?: string;
|
|
59
59
|
channels: {
|
|
60
|
-
active
|
|
61
|
-
coefficient
|
|
60
|
+
active?: boolean;
|
|
61
|
+
coefficient?: number;
|
|
62
62
|
color: string;
|
|
63
|
-
family
|
|
64
|
-
inverted
|
|
65
|
-
label
|
|
63
|
+
family?: string;
|
|
64
|
+
inverted?: boolean;
|
|
65
|
+
label?: string;
|
|
66
66
|
window: {
|
|
67
67
|
end: number;
|
|
68
68
|
max: number;
|
|
@@ -73,9 +73,14 @@ export type OmeroTransitionalMetadata = {
|
|
|
73
73
|
};
|
|
74
74
|
export type OMEZarrMetadata = {
|
|
75
75
|
multiscales: OMEMultiscale[];
|
|
76
|
-
omero
|
|
76
|
+
omero?: OmeroTransitionalMetadata;
|
|
77
|
+
};
|
|
78
|
+
export type WrappedArrayOpts = {
|
|
79
|
+
subscriber?: SubscriberId;
|
|
80
|
+
reportChunk?: (coords: number[], subscriber: SubscriberId) => void;
|
|
81
|
+
isPrefetch?: boolean;
|
|
77
82
|
};
|
|
78
|
-
export type NumericZarrArray = zarr.Array<zarr.NumberDataType,
|
|
83
|
+
export type NumericZarrArray = zarr.Array<zarr.NumberDataType, AsyncReadable<RequestInit & WrappedArrayOpts>>;
|
|
79
84
|
/** A record with everything we need to access and use a single remote source of multiscale OME-Zarr data. */
|
|
80
85
|
export type ZarrSource = {
|
|
81
86
|
/** Representations of each scale level in this zarr. We pick one and pass it to zarrita to load data. */
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { OMEZarrMetadata } from "./types.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* If `meta` is the top-level metadata of a zarr node formatted according to the OME-Zarr spec version 0.5, returns
|
|
4
|
+
* the object formatted according to v0.4 of the spec. For our purposes this just means flattening out the `ome` key.
|
|
5
|
+
*
|
|
6
|
+
* Return type is `unknown` because this does no actual validation; use `validateOMEZarrMetadata` for that.
|
|
7
|
+
*/
|
|
8
|
+
export declare const toOMEZarrMetaV4: (meta: unknown) => unknown;
|
|
9
|
+
/** Intermediate stage of validation, before we've picked a single multiscale to validate */
|
|
10
|
+
export type MultiscaleRecord = {
|
|
11
|
+
multiscales: unknown[];
|
|
12
|
+
};
|
|
13
|
+
export declare function assertMetadataHasMultiscales(meta: unknown, name?: string): asserts meta is MultiscaleRecord;
|
|
14
|
+
/**
|
|
15
|
+
* Validates that the `OMEZarrMetadata` record `meta` has the minimal amount of data required to open a volume. Since
|
|
4
16
|
* we only ever open one multiscale, we only validate the multiscale metadata record at index `multiscaleIdx` here.
|
|
5
17
|
* `name` is used in error messages to identify the source of the metadata.
|
|
6
18
|
*/
|
|
7
|
-
export declare function validateOMEZarrMetadata(
|
|
19
|
+
export declare function validateOMEZarrMetadata(meta: MultiscaleRecord, multiscaleIdx?: number, name?: string): asserts meta is OMEZarrMetadata;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Array as ZarrArray, AsyncReadable, DataType } from "zarrita";
|
|
2
|
+
import VolumeCache from "../../VolumeCache.js";
|
|
3
|
+
import type { WrappedArrayOpts } from "./types.js";
|
|
4
|
+
import SubscribableRequestQueue from "../../utils/SubscribableRequestQueue.js";
|
|
5
|
+
type AsyncReadableExt<Opts> = AsyncReadable<Opts & WrappedArrayOpts>;
|
|
6
|
+
export default function wrapArray<T extends DataType, Opts = unknown, Store extends AsyncReadable<Opts> = AsyncReadable<Opts>>(array: ZarrArray<T, Store>, basePath: string, cache?: VolumeCache, queue?: SubscribableRequestQueue): ZarrArray<T, AsyncReadableExt<Opts>>;
|
|
7
|
+
export {};
|
package/es/types/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Camera, OrthographicCamera, PerspectiveCamera, Vector3 } from "three";
|
|
1
|
+
import { Camera, Color, DataTexture, OrthographicCamera, PerspectiveCamera, Vector3 } from "three";
|
|
2
2
|
export interface Bounds {
|
|
3
3
|
bmin: Vector3;
|
|
4
4
|
bmax: Vector3;
|
|
@@ -27,10 +27,26 @@ export declare const ARRAY_CONSTRUCTORS: {
|
|
|
27
27
|
float32: Float32ArrayConstructor;
|
|
28
28
|
float64: Float64ArrayConstructor;
|
|
29
29
|
};
|
|
30
|
+
export interface ColorizeFeature {
|
|
31
|
+
idsToFeatureValue: DataTexture;
|
|
32
|
+
featureValueToColor: DataTexture;
|
|
33
|
+
inRangeIds: DataTexture;
|
|
34
|
+
outlierData: DataTexture;
|
|
35
|
+
featureMin: number;
|
|
36
|
+
featureMax: number;
|
|
37
|
+
outlineColor: Color;
|
|
38
|
+
outlierColor: Color;
|
|
39
|
+
outOfRangeColor: Color;
|
|
40
|
+
outlierDrawMode: number;
|
|
41
|
+
outOfRangeDrawMode: number;
|
|
42
|
+
hideOutOfRange: boolean;
|
|
43
|
+
}
|
|
30
44
|
export interface FuseChannel {
|
|
31
45
|
chIndex: number;
|
|
32
46
|
lut: Uint8Array;
|
|
33
47
|
rgbColor: [number, number, number] | number;
|
|
48
|
+
selectedID: number;
|
|
49
|
+
feature?: ColorizeFeature;
|
|
34
50
|
}
|
|
35
51
|
/** If `FuseChannel.rgbColor` is this value, it is disabled from fusion. */
|
|
36
52
|
export declare const FUSE_DISABLED_RGB_COLOR = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aics/vole-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.14.0",
|
|
4
4
|
"description": "volume renderer for 3d, 4d, or 5d imaging data with OME-Zarr support",
|
|
5
5
|
"main": "es/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"three": "^0.171.0",
|
|
40
40
|
"throttled-queue": "^2.1.4",
|
|
41
41
|
"tweakpane": "^3.1.9",
|
|
42
|
-
"zarrita": "^0.
|
|
42
|
+
"zarrita": "^0.4.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@babel/cli": "^7.25.6",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"typescript": "^4.3.5",
|
|
75
75
|
"url-loader": "^4.1.1",
|
|
76
76
|
"vite": "^6.0.6",
|
|
77
|
-
"vitest": "^2.1.8",
|
|
78
77
|
"vite-plugin-glsl": "^1.3.1",
|
|
78
|
+
"vitest": "^3.0.8",
|
|
79
79
|
"webpack": "^5.69.1",
|
|
80
80
|
"webpack-cli": "^4.9.2",
|
|
81
81
|
"webpack-dev-server": "^4.7.4"
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `Readable` is zarrita's minimal abstraction for any source of data.
|
|
3
|
-
* `WrappedStore` wraps another `Readable` and adds (optional) connections to `VolumeCache` and `RequestQueue`.
|
|
4
|
-
*/
|
|
5
|
-
class WrappedStore {
|
|
6
|
-
constructor(baseStore, cache, queue) {
|
|
7
|
-
this.baseStore = baseStore;
|
|
8
|
-
this.cache = cache;
|
|
9
|
-
this.queue = queue;
|
|
10
|
-
}
|
|
11
|
-
// Dummy implementation to make this class easier to use in tests
|
|
12
|
-
set(_key, _value) {
|
|
13
|
-
return Promise.resolve();
|
|
14
|
-
}
|
|
15
|
-
async getAndCache(key, cacheKey, opts) {
|
|
16
|
-
const result = await this.baseStore.get(key, opts);
|
|
17
|
-
if (this.cache && result) {
|
|
18
|
-
this.cache.insert(cacheKey, result);
|
|
19
|
-
}
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
async get(key, opts) {
|
|
23
|
-
const ZARR_EXTS = [".zarray", ".zgroup", ".zattrs", "zarr.json"];
|
|
24
|
-
if (!this.cache || ZARR_EXTS.some(s => key.endsWith(s))) {
|
|
25
|
-
return this.baseStore.get(key, opts?.options);
|
|
26
|
-
}
|
|
27
|
-
if (opts?.reportKey) {
|
|
28
|
-
opts.reportKey(key, opts.subscriber);
|
|
29
|
-
}
|
|
30
|
-
let keyPrefix = this.baseStore.url ?? "";
|
|
31
|
-
if (keyPrefix !== "" && !(keyPrefix instanceof URL) && !keyPrefix.endsWith("/")) {
|
|
32
|
-
keyPrefix += "/";
|
|
33
|
-
}
|
|
34
|
-
const fullKey = keyPrefix + key.slice(1);
|
|
35
|
-
|
|
36
|
-
// Check the cache
|
|
37
|
-
const cacheResult = this.cache.get(fullKey);
|
|
38
|
-
if (cacheResult) {
|
|
39
|
-
return new Uint8Array(cacheResult);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Not in cache; load the chunk and cache it
|
|
43
|
-
if (this.queue && opts) {
|
|
44
|
-
return this.queue.addRequest(fullKey, opts.subscriber, () => this.getAndCache(key, fullKey, opts?.options), opts.isPrefetch);
|
|
45
|
-
} else {
|
|
46
|
-
// Should we ever hit this code? We should always have a request queue.
|
|
47
|
-
return this.getAndCache(key, fullKey, opts?.options);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
export default WrappedStore;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { AbsolutePath, AsyncMutable, Readable } from "@zarrita/storage";
|
|
2
|
-
import SubscribableRequestQueue from "../../utils/SubscribableRequestQueue";
|
|
3
|
-
import VolumeCache from "../../VolumeCache";
|
|
4
|
-
import { SubscriberId } from "./types";
|
|
5
|
-
type WrappedStoreOpts<Opts> = {
|
|
6
|
-
options?: Opts;
|
|
7
|
-
subscriber: SubscriberId;
|
|
8
|
-
reportKey?: (key: string, subscriber: SubscriberId) => void;
|
|
9
|
-
isPrefetch?: boolean;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* `Readable` is zarrita's minimal abstraction for any source of data.
|
|
13
|
-
* `WrappedStore` wraps another `Readable` and adds (optional) connections to `VolumeCache` and `RequestQueue`.
|
|
14
|
-
*/
|
|
15
|
-
declare class WrappedStore<Opts, S extends Readable<Opts> = Readable<Opts>> implements AsyncMutable<WrappedStoreOpts<Opts>> {
|
|
16
|
-
private baseStore;
|
|
17
|
-
private cache?;
|
|
18
|
-
private queue?;
|
|
19
|
-
constructor(baseStore: S, cache?: VolumeCache | undefined, queue?: SubscribableRequestQueue | undefined);
|
|
20
|
-
set(_key: AbsolutePath, _value: Uint8Array): Promise<void>;
|
|
21
|
-
private getAndCache;
|
|
22
|
-
get(key: AbsolutePath, opts?: WrappedStoreOpts<Opts> | undefined): Promise<Uint8Array | undefined>;
|
|
23
|
-
}
|
|
24
|
-
export default WrappedStore;
|