@niivue/niivue
Version:
minimal webgl2 nifti image viewer
1,424 lines (1,410 loc) • 212 kB
TypeScript
import { mat4, vec3, vec4, vec2 } from 'gl-matrix';
import { NIFTI1, NIFTI2, NIFTIEXTENSION } from 'nifti-reader-js';
declare enum COLORMAP_TYPE {
MIN_TO_MAX = 0,
ZERO_TO_MAX_TRANSPARENT_BELOW_MIN = 1,
ZERO_TO_MAX_TRANSLUCENT_BELOW_MIN = 2
}
type ColorMap = {
R: number[];
G: number[];
B: number[];
A: number[];
I: number[];
min?: number;
max?: number;
labels?: string[];
};
type LUT = {
lut: Uint8ClampedArray;
min?: number;
max?: number;
labels?: string[];
};
declare class ColorTables {
gamma: number;
version: number;
cluts: Record<string, ColorMap>;
/**
* Sets cluts to alphabetically sorted cmaps
*/
constructor();
addColormap(key: string, cmap: ColorMap): void;
colormaps(): Array<keyof typeof this.cluts>;
colorMaps(): Array<keyof typeof this.cluts>;
colormapFromKey(name: string): ColorMap;
colormap(key?: string, isInvert?: boolean): Uint8ClampedArray;
makeLabelLut(cm: ColorMap, alphaFill?: number, maxIdx?: number): LUT;
makeLabelLutFromUrl(name: string, alphaFill?: number, maxIdx?: number): Promise<LUT>;
makeDrawLut(name: string | ColorMap): LUT;
makeLut(Rsi: number[], Gsi: number[], Bsi: number[], Asi: number[], Isi: number[], isInvert: boolean): Uint8ClampedArray;
}
declare const cmapper: ColorTables;
type Geometry = {
vertexBuffer: WebGLBuffer;
indexBuffer: WebGLBuffer;
indexCount: number;
vao: WebGLVertexArrayObject | null;
};
/**
* Represents a 3D object rendered with WebGL, including geometry, transformations, and rendering state.
* Used internally by Niivue for rendering meshes and crosshairs.
*/
declare class NiivueObject3D {
static BLEND: number;
static CULL_FACE: number;
static CULL_FRONT: number;
static CULL_BACK: number;
static ENABLE_DEPTH_TEST: number;
sphereIdx: number[];
sphereVtx: number[];
renderShaders: number[];
isVisible: boolean;
isPickable: boolean;
vertexBuffer: WebGLVertexArrayObject;
indexCount: number;
indexBuffer: WebGLVertexArrayObject | null;
vao: WebGLVertexArrayObject | null;
mode: number;
glFlags: number;
id: number;
colorId: [number, number, number, number];
modelMatrix: mat4;
scale: number[];
position: number[];
rotation: number[];
rotationRadians: number;
extentsMin: number[];
extentsMax: number[];
furthestVertexFromOrigin?: number;
originNegate?: vec3;
fieldOfViewDeObliqueMM?: vec3;
mm?: vec4;
constructor(id: number, vertexBuffer: WebGLBuffer, mode: number, indexCount: number, indexBuffer?: WebGLVertexArrayObject | null, vao?: WebGLVertexArrayObject | null);
static generateCrosshairs: (gl: WebGL2RenderingContext, id: number, xyzMM: vec4, xyzMin: vec3, xyzMax: vec3, radius: number, sides?: number, gap?: number) => NiivueObject3D;
static generateCrosshairsGeometry: (gl: WebGL2RenderingContext, xyzMM: vec4, xyzMin: vec3, xyzMax: vec3, radius: number, sides?: number, gap?: number) => Geometry;
static getFirstPerpVector: (v1: vec3) => vec3;
static subdivide: (verts: number[], faces: number[]) => void;
static weldVertices: (verts: number[], faces: number[]) => number[];
static makeSphere: (vertices: number[], indices: number[], radius: number, origin?: vec3 | vec4) => void;
static makeCylinder: (vertices: number[], indices: number[], start: vec3, dest: vec3, radius: number, sides?: number, endcaps?: boolean) => void;
static makeColoredCylinder: (vertices: number[], indices: number[], colors: number[], start: vec3, dest: vec3, radius: number, rgba255?: number[], sides?: number, endcaps?: boolean) => void;
static makeColoredSphere: (vertices: number[], indices: number[], colors: number[], radius: number, origin?: vec3 | vec4, rgba255?: number[]) => void;
}
declare enum LabelTextAlignment {
LEFT = "left",
RIGHT = "right",
CENTER = "center"
}
declare enum LabelLineTerminator {
NONE = "none",
CIRCLE = "circle",
RING = "ring"
}
declare enum LabelAnchorPoint {
NONE = 0,
TOPLEFT = 9,
TOPCENTER = 10,
TOPRIGHT = 12,
MIDDLELEFT = 17,
MIDDLECENTER = 18,
MIDDLERIGHT = 20,
BOTTOMLEFT = 33,
BOTTOMCENTER = 34,
BOTTOMRIGHT = 36
}
/**
* Class representing label style.
* @ignore
*/
declare class NVLabel3DStyle {
textColor: number[];
textScale: number;
textAlignment?: LabelTextAlignment;
lineWidth: number;
lineColor: number[];
lineTerminator: LabelLineTerminator;
bulletScale?: number;
bulletColor?: number[];
backgroundColor?: number[];
/**
* @param textColor - Color of text
* @param textScale - Text Size (0.0..1.0)
* @param lineWidth - Line width
* @param lineColor - Line color
* @param bulletScale - Bullet size respective of text
* @param bulletColor - Bullet color
* @param backgroundColor - Background color of label
*/
constructor(textColor?: number[], textScale?: number, textAlignment?: LabelTextAlignment, lineWidth?: number, lineColor?: number[], lineTerminator?: LabelLineTerminator, bulletScale?: number, bulletColor?: number[], backgroundColor?: number[]);
}
/**
* Label class
* @ignore
*/
declare class NVLabel3D {
text: string;
style: NVLabel3DStyle;
points?: number[] | number[][];
anchor: LabelAnchorPoint;
onClick?: (label: NVLabel3D, e?: MouseEvent) => void;
/**
* @param text - The text of the label
* @param style - The style of the label
* @param points - An array of points label for label lines
*/
constructor(text: string, style: NVLabel3DStyle, points?: number[] | number[][], anchor?: LabelAnchorPoint, onClick?: (label: NVLabel3D, e?: MouseEvent) => void);
}
/**
* Enum for NIfTI datatype codes
* // https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h
*/
declare enum NiiDataType {
DT_NONE = 0,
DT_BINARY = 1,
DT_UINT8 = 2,
DT_INT16 = 4,
DT_INT32 = 8,
DT_FLOAT32 = 16,
DT_COMPLEX64 = 32,
DT_FLOAT64 = 64,
DT_RGB24 = 128,
DT_INT8 = 256,
DT_UINT16 = 512,
DT_UINT32 = 768,
DT_INT64 = 1024,
DT_UINT64 = 1280,
DT_FLOAT128 = 1536,
DT_COMPLEX128 = 1792,
DT_COMPLEX256 = 2048,
DT_RGBA32 = 2304
}
/**
* Enum for supported image types (e.g. NII, NRRD, DICOM)
*/
declare enum ImageType {
UNKNOWN = 0,
NII = 1,
DCM = 2,
DCM_MANIFEST = 3,
MIH = 4,
MIF = 5,
NHDR = 6,
NRRD = 7,
MHD = 8,
MHA = 9,
MGH = 10,
MGZ = 11,
V = 12,
V16 = 13,
VMR = 14,
HEAD = 15,
DCM_FOLDER = 16,
SRC = 17,
FIB = 18,
BMP = 19,
ZARR = 20,
NPY = 21,
NPZ = 22,
HDR = 23
}
type ImageFromUrlOptions = {
url: string;
urlImageData?: string;
headers?: Record<string, string>;
name?: string;
colorMap?: string;
colormap?: string;
opacity?: number;
cal_min?: number;
cal_max?: number;
trustCalMinMax?: boolean;
percentileFrac?: number;
useQFormNotSForm?: boolean;
alphaThreshold?: boolean;
colormapNegative?: string;
colorMapNegative?: string;
cal_minNeg?: number;
cal_maxNeg?: number;
colorbarVisible?: boolean;
ignoreZeroVoxels?: boolean;
imageType?: ImageType;
frame4D?: number;
colormapLabel?: LUT | null;
pairedImgData?: null;
limitFrames4D?: number;
isManifest?: boolean;
urlImgData?: string;
buffer?: ArrayBuffer;
zarrLevel?: number;
zarrMaxVolumeSize?: number;
zarrChannel?: number;
/** Convert OME spatial units to millimeters for NIfTI compatibility (default: true) */
zarrConvertUnits?: boolean;
/** World-space center [x, y, z] in mm where the zarr volume center should be positioned */
zarrCenterMM?: [number, number, number];
};
type ImageFromFileOptions = {
file: File | File[];
name?: string;
colormap?: string;
opacity?: number;
urlImgData?: File | null | FileSystemEntry;
cal_min?: number;
cal_max?: number;
trustCalMinMax?: boolean;
percentileFrac?: number;
ignoreZeroVoxels?: boolean;
useQFormNotSForm?: boolean;
colormapNegative?: string;
imageType?: ImageType;
frame4D?: number;
limitFrames4D?: number;
};
type ImageFromBase64 = {
base64: string;
name?: string;
colormap?: string;
opacity?: number;
cal_min?: number;
cal_max?: number;
trustCalMinMax?: boolean;
percentileFrac?: number;
ignoreZeroVoxels?: boolean;
useQFormNotSForm?: boolean;
colormapNegative?: string;
frame4D?: number;
imageType?: ImageType;
cal_minNeg?: number;
cal_maxNeg?: number;
colorbarVisible?: boolean;
colormapLabel?: LUT | null;
};
type ImageMetadata = {
id: string;
datatypeCode: number;
nx: number;
ny: number;
nz: number;
nt: number;
dx: number;
dy: number;
dz: number;
dt: number;
bpv: number;
};
declare const NVImageFromUrlOptions: (url: string, urlImageData?: string, name?: string, colormap?: string, opacity?: number, cal_min?: number, cal_max?: number, trustCalMinMax?: boolean, percentileFrac?: number, ignoreZeroVoxels?: boolean, useQFormNotSForm?: boolean, colormapNegative?: string, frame4D?: number, imageType?: ImageType, cal_minNeg?: number, cal_maxNeg?: number, colorbarVisible?: boolean, alphaThreshold?: boolean, colormapLabel?: any) => ImageFromUrlOptions;
/**
* ZarrChunkCache - LRU cache for zarr chunks (TypedArrays).
*
* LRU cache that stores TypedArrays.
* TypedArrays are garbage collected automatically, so no explicit cleanup needed.
*/
type TypedArray = Uint8Array | Uint16Array | Int16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
declare class ZarrChunkCache {
private cache;
private loadingSet;
private maxChunks;
constructor(maxChunks?: number);
/**
* Generate a unique key for a chunk.
* Format: "name:level/x/y" for 2D or "name:level/x/y/z" for 3D
*/
static getKey(name: string, level: number, x: number, y: number, z?: number): string;
/**
* Check if a chunk is in the cache
*/
has(key: string): boolean;
/**
* Get a chunk from the cache.
* Also moves the entry to the end (most recently used).
*/
get(key: string): TypedArray | undefined;
/**
* Store a chunk in the cache.
* Evicts oldest entries if capacity is exceeded.
*/
set(key: string, chunk: TypedArray): void;
/**
* Check if a chunk is currently being loaded
*/
isLoading(key: string): boolean;
/**
* Mark a chunk as loading (to prevent duplicate requests)
*/
startLoading(key: string): void;
/**
* Mark a chunk as done loading
*/
doneLoading(key: string): void;
/**
* Get the number of cached chunks
*/
get size(): number;
/**
* Get the number of chunks currently loading
*/
get loadingCount(): number;
/**
* Clear the entire cache
*/
clear(): void;
/**
* Delete a specific chunk from cache
*/
delete(key: string): boolean;
/**
* Get all cached keys
*/
keys(): IterableIterator<string>;
}
/**
* ZarrChunkClient - HTTP client for fetching zarr array data using zarrita.js.
*
* Handles pyramid discovery and chunk fetching for OME-ZARR and regular zarr stores.
*/
interface ZarrChunkClientConfig {
/** Base URL for zarr store (e.g., "http://localhost:8090/lightsheet.zarr") */
baseUrl: string;
}
interface ZarrPyramidLevel {
/** Level index (0 = highest resolution) */
index: number;
/** Path to this level in the zarr hierarchy (e.g., "/0", "/1") */
path: string;
/** Spatial-only shape in OME metadata order (non-spatial dims stripped) */
shape: number[];
/** Spatial-only chunk dimensions matching shape order */
chunks: number[];
/** Data type (e.g., "uint8", "uint16", "float32") */
dtype: string;
/** Physical scale factors per spatial axis in OME metadata order from coordinateTransformations */
scales?: number[];
/** Physical translation offsets per spatial axis in OME metadata order from coordinateTransformations */
translations?: number[];
}
/**
* Mapping from spatial chunk coordinates to full zarr array chunk coordinates.
* Handles non-spatial dimensions like channel (c) and time (t).
*/
interface AxisMapping {
/** Total number of dimensions in the original zarr array */
originalNdim: number;
/** Indices of spatial axes in the original array, in OME metadata order */
spatialIndices: number[];
/** Names of spatial axes in OME metadata order (e.g., ['x', 'y', 'z'] or ['z', 'y', 'x']) */
spatialAxisNames: string[];
/** Non-spatial axes: their index in the original array, chunk size, and default chunk coord */
nonSpatialAxes: Array<{
index: number;
name: string;
chunkSize: number;
defaultChunkCoord: number;
}>;
}
interface ZarrPyramidInfo {
/** Name/URL of the zarr store */
name: string;
/** Pyramid levels (index 0 = highest resolution) */
levels: ZarrPyramidLevel[];
/** Whether this is a 3D dataset (based on spatial dimensions) */
is3D: boolean;
/** Number of spatial dimensions (2 or 3) */
ndim: number;
/** Mapping from spatial to full array coordinates */
axisMapping: AxisMapping;
/** Units for spatial axes in OME metadata order (e.g., "micrometer", "millimeter") */
spatialUnits?: string[];
}
interface ChunkCoord {
/** Pyramid level */
level: number;
/** Chunk X index */
x: number;
/** Chunk Y index */
y: number;
/** Chunk Z index (for 3D) */
z?: number;
}
declare class ZarrChunkClient {
private store;
private baseUrl;
private arrays;
/** Maps level index to actual path in the zarr store */
private levelPaths;
/** Axis mapping for coordinate translation */
private axisMapping;
constructor(config: ZarrChunkClientConfig);
/**
* Discover pyramid structure by reading OME-ZARR multiscales metadata,
* or falling back to probing for arrays at /0, /1, /2, etc.
*/
fetchInfo(): Promise<ZarrPyramidInfo>;
/**
* Build axis mapping from OME axes metadata or infer from array dimensions.
* Identifies spatial (x, y, z) vs non-spatial (c, t) dimensions and returns
* indices for extracting spatial-only shape/chunks.
* Spatial indices are kept in the original OME metadata order (NOT reordered).
*/
private buildAxisMapping;
/**
* Open a zarr array at a specific pyramid level.
* Uses cached arrays when available.
*/
private openLevel;
/**
* Fetch a single chunk by spatial coordinates.
* Uses the axis mapping to build full chunk coordinates including non-spatial dims.
* Returns the spatial-only decoded TypedArray data.
*
* @param level - Pyramid level
* @param x - Spatial X chunk index
* @param y - Spatial Y chunk index
* @param z - Spatial Z chunk index (for 3D)
* @param nonSpatialCoords - Optional overrides for non-spatial dimensions (e.g., channel index)
*/
fetchChunk(level: number, x: number, y: number, z?: number, nonSpatialCoords?: Record<string, number>, signal?: AbortSignal): Promise<TypedArray | null>;
/**
* Fetch multiple chunks in parallel.
* Returns a Map from chunk key to TypedArray.
*/
fetchChunks(name: string, level: number, coords: ChunkCoord[]): Promise<Map<string, TypedArray>>;
/**
* Fetch a rectangular region using zarr.get with slices.
* Useful for fetching exact viewport regions rather than whole chunks.
* Uses axis mapping to handle non-spatial dimensions.
*/
fetchRegion(level: number, region: {
xStart: number;
xEnd: number;
yStart: number;
yEnd: number;
zStart?: number;
zEnd?: number;
}): Promise<{
data: TypedArray;
shape: number[];
} | null>;
/**
* Get the zarr store URL
*/
getUrl(): string;
/**
* Clear cached array references
*/
clearArrayCache(): void;
}
/**
* NVZarrHelper - Simplified zarr chunk management for NVImage.
*
* Attaches to a host NVImage and manages chunked loading of OME-Zarr data.
* No zoom, no prefetching - just pan and level switching.
* All coordinates are in current-level pixel space.
*
* Spatial dimensions are kept in OME metadata order throughout.
* The mapping to NIfTI layout is:
* - OME dim[0] (slowest in C-order) → NIfTI dim 3 (depth, slowest in Fortran-order)
* - OME dim[1] → NIfTI dim 2 (height)
* - OME dim[2] (fastest in C-order) → NIfTI dim 1 (width, fastest in Fortran-order)
* This means chunk data can be copied directly without stride remapping.
* The affine matrix maps NIfTI (i, j, k) indices to physical (x, y, z) space
* using the OME axis names.
*/
interface NVZarrHelperOptions {
url: string;
level: number;
maxVolumeSize?: number;
maxTextureSize?: number;
channel?: number;
cacheSize?: number;
/** Convert OME spatial units to millimeters for NIfTI compatibility (default: true) */
convertUnitsToMm?: boolean;
}
declare class NVZarrHelper {
private hostImage;
private chunkClient;
private chunkCache;
private pyramidInfo;
private datatypeCode;
private pyramidLevel;
/** Level dimensions in OME metadata order: depth=dim[0], height=dim[1], width=dim[2] */
private levelDims;
private volumeDims;
private chunkSize;
/** Voxel scales in OME metadata order: depth=dim[0], height=dim[1], width=dim[2] */
private voxelScales;
/** Voxel translations in OME metadata order */
private voxelTranslations;
private hasTranslations;
private convertUnitsToMm;
private worldOffsetMM;
private centerX;
private centerY;
private centerZ;
private channel;
private nonSpatialCoords;
private isUpdating;
private needsUpdate;
private currentAbortController;
private runningMin;
private runningMax;
private calibrationDone;
private updateDebounceTimer;
private readonly UPDATE_DEBOUNCE_MS;
private pendingChunkCount;
private lastRenderedChunkCount;
centerAtDragStart: {
x: number;
y: number;
z: number;
} | null;
onChunksUpdated?: () => void;
onAllChunksLoaded?: () => void;
private constructor();
static create(hostImage: NVImage, url: string, options: NVZarrHelperOptions): Promise<NVZarrHelper>;
loadInitialChunks(): Promise<void>;
private updateLevelInfo;
private configureHostImage;
/** Get unit-converted voxel scales */
private getConvertedScales;
/** Get unit-converted voxel translations */
private getConvertedTranslations;
/**
* Build the NIfTI affine from OME axis names, scales, and translations.
*
* NIfTI dimensions map to OME spatial dimensions as:
* i (dim 1, width) = OME spatial[-1] (last, fastest in C-order)
* j (dim 2, height) = OME spatial[-2]
* k (dim 3, depth) = OME spatial[-3] (first, slowest in C-order)
*
* The affine maps (i, j, k) → physical (x, y, z):
* physical_axis = scale * nifti_dim + translation
* where nifti_dim is the column index (0=i, 1=j, 2=k) and
* physical_axis row is determined by the OME axis name.
*/
private updateAffine;
beginDrag(): void;
endDrag(): void;
panBy(dx: number, dy: number, dz?: number): Promise<void>;
panTo(newCenterX: number, newCenterY: number, newCenterZ?: number): Promise<void>;
setPyramidLevel(level: number): Promise<void>;
getViewportState(): {
centerX: number;
centerY: number;
centerZ: number;
level: number;
};
getPyramidInfo(): ZarrPyramidInfo;
getPyramidLevel(): number;
getLevelDims(): {
width: number;
height: number;
depth: number;
};
getVolumeDims(): {
width: number;
height: number;
depth: number;
};
getWorldOffset(): [number, number, number];
/**
* Set the world-space offset so the full level's center maps to targetMM in world space.
* Computes the native physical center of the zarr level, then sets worldOffsetMM
* so that center aligns with targetMM. Also centers the viewport on the level center.
*/
setWorldCenter(targetMM: [number, number, number]): void;
/**
* Convert physical (mm) coordinates back to real zarr level pixel coordinates.
* Inverts the affine: levelPixel = (mm - OME_translation) / scale
*/
mmToLevelCoords(mmX: number, mmY: number, mmZ: number): {
width: number;
height: number;
depth: number;
level: number;
levelDims: {
width: number;
height: number;
depth: number;
};
};
private clampCenter;
private getVisibleChunks;
private updateVolume;
private clearVolumeData;
private assembleVisibleChunks;
private assembleChunkIntoVolume;
private updateCalibration;
/**
* Schedule a debounced chunks update callback.
* Batches multiple chunk arrivals within UPDATE_DEBOUNCE_MS into a single GPU update.
*/
private scheduleChunksUpdated;
clearCache(): void;
refresh(): Promise<void>;
}
/**
* Represents an affine transformation in decomposed form.
*/
interface AffineTransform {
translation: [number, number, number];
rotation: [number, number, number];
scale: [number, number, number];
}
/**
* Identity transform with no translation, rotation, or scale change.
*/
declare const identityTransform: AffineTransform;
/**
* Convert degrees to radians.
*/
declare function degToRad(degrees: number): number;
/**
* Create a rotation matrix from Euler angles (XYZ order).
* Angles are in degrees.
*/
declare function eulerToRotationMatrix(rx: number, ry: number, rz: number): mat4;
/**
* Create a 4x4 transformation matrix from decomposed transform components.
* Order: Scale -> Rotate -> Translate
*/
declare function createTransformMatrix(transform: AffineTransform): mat4;
/**
* Convert a 2D array (row-major, as used by NIfTI) to gl-matrix mat4 (column-major).
*/
declare function arrayToMat4(arr: number[][]): mat4;
/**
* Convert gl-matrix mat4 (column-major) to 2D array (row-major, as used by NIfTI).
*/
declare function mat4ToArray(m: mat4): number[][];
/**
* Multiply a transformation matrix by an affine matrix (as 2D array).
* Returns the result as a 2D array.
*
* The transform is applied to the left: result = transform * original
* This means the transform happens in world coordinate space.
*/
declare function multiplyAffine(original: number[][], transform: mat4): number[][];
/**
* Deep copy a 2D affine matrix array.
*/
declare function copyAffine(affine: number[][]): number[][];
/**
* Check if two transforms are approximately equal.
*/
declare function transformsEqual(a: AffineTransform, b: AffineTransform, epsilon?: number): boolean;
type TypedVoxelArray = Float32Array | Uint8Array | Int16Array | Float64Array | Uint16Array | Int32Array | Uint32Array;
/**
* a NVImage encapsulates some image data and provides methods to query and operate on images
*/
declare class NVImage {
name: string;
id: string;
url?: string;
headers?: Record<string, string>;
_colormap: string;
_opacity: number;
percentileFrac: number;
ignoreZeroVoxels: boolean;
trustCalMinMax: boolean;
colormapNegative: string;
colormapLabel: LUT | null;
colormapInvert?: boolean;
nFrame4D?: number;
frame4D: number;
nTotalFrame4D?: number;
cal_minNeg: number;
cal_maxNeg: number;
colorbarVisible: boolean;
modulationImage: number | null;
modulateAlpha: number;
series: any;
nVox3D?: number;
oblique_angle?: number;
maxShearDeg?: number;
useQFormNotSForm: boolean;
colormapType?: number;
pixDims?: number[];
matRAS?: mat4;
pixDimsRAS?: number[];
obliqueRAS?: mat4;
dimsRAS?: number[];
permRAS?: number[];
img2RASstep?: number[];
img2RASstart?: number[];
toRAS?: mat4;
toRASvox?: mat4;
frac2mm?: mat4;
frac2mmOrtho?: mat4;
extentsMinOrtho?: number[];
extentsMaxOrtho?: number[];
mm2ortho?: mat4;
hdr: NIFTI1 | NIFTI2 | null;
extensions?: NIFTIEXTENSION[];
imageType?: ImageType;
img?: TypedVoxelArray;
imaginary?: Float32Array;
v1?: Float32Array;
fileObject?: File | File[];
dims?: number[];
onColormapChange: (img: NVImage) => void;
onOpacityChange: (img: NVImage) => void;
zarrHelper: NVZarrHelper | null;
_hasExplicitZarrCenter: boolean;
mm000?: vec3;
mm100?: vec3;
mm010?: vec3;
mm001?: vec3;
cal_min?: number;
cal_max?: number;
robust_min?: number;
robust_max?: number;
global_min?: number;
global_max?: number;
urlImgData?: string;
isManifest?: boolean;
limitFrames4D?: number;
originalAffine?: number[][];
constructor(dataBuffer?: ArrayBuffer | ArrayBuffer[] | ArrayBufferLike | null, name?: string, colormap?: string, opacity?: number, pairedImgData?: ArrayBuffer | null, cal_min?: number, cal_max?: number, trustCalMinMax?: boolean, percentileFrac?: number, ignoreZeroVoxels?: boolean, useQFormNotSForm?: boolean, colormapNegative?: string, frame4D?: number, imageType?: ImageType, cal_minNeg?: number, cal_maxNeg?: number, colorbarVisible?: boolean, colormapLabel?: LUT | null, colormapType?: number);
init(dataBuffer?: ArrayBuffer | ArrayBuffer[] | ArrayBufferLike | null, name?: string, colormap?: string, opacity?: number, _pairedImgData?: ArrayBuffer | null, cal_min?: number, cal_max?: number, trustCalMinMax?: boolean, percentileFrac?: number, ignoreZeroVoxels?: boolean, useQFormNotSForm?: boolean, colormapNegative?: string, frame4D?: number, imageType?: ImageType, cal_minNeg?: number, cal_maxNeg?: number, colorbarVisible?: boolean, colormapLabel?: LUT | null, colormapType?: number, imgRaw?: ArrayBuffer | ArrayBufferLike | null): void;
static new(dataBuffer: ArrayBuffer | ArrayBuffer[] | ArrayBufferLike | null, name: string, colormap: string, opacity: number, pairedImgData: ArrayBuffer | null, cal_min: number, cal_max: number, trustCalMinMax: boolean, percentileFrac: number, ignoreZeroVoxels: boolean, useQFormNotSForm: boolean, colormapNegative: string, frame4D: number, imageType: ImageType, cal_minNeg: number, cal_maxNeg: number, colorbarVisible: boolean, colormapLabel: LUT | null, colormapType: number, zarrData: null | unknown): Promise<NVImage>;
computeObliqueAngle(mtx44: mat4): number;
/**
* Convert vector field from Float32 to RGBA representation.
* Note: We use RGBA rather than RGB and use least significant bits to store vector polarity.
* This allows a single bitmap to store BOTH (unsigned) color magnitude and signed vector direction.
*
* @param nvImage - The NVImage instance
* @param inImg - Input Float32Array containing vector field data
* @returns Uint8Array with RGBA encoded vector data
*/
float32V1asRGBA(inImg: Float32Array): Uint8Array;
/**
* Load and process diffusion tensor vector (V1) data with optional flips.
* The vectors must be of unit length.
* Modifies the nvImage.img property with the processed RGBA data.
*
* @param nvImage - The NVImage instance
* @param isFlipX - Flip X component (default: false)
* @param isFlipY - Flip Y component (default: false)
* @param isFlipZ - Flip Z component (default: false)
* @example nv1.loadVolumes(volumeList); nv1.volumes[1].loadImgV1();
* @returns true if successful, false if V1 data is not available
* @see {@link https://niivue.com/demos/features/modulate.html | live demo usage}
*/
loadImgV1(isFlipX?: boolean, isFlipY?: boolean, isFlipZ?: boolean): boolean;
calculateOblique(): void;
readECAT(buffer: ArrayBuffer): ArrayBuffer;
readV16(buffer: ArrayBuffer): ArrayBuffer;
readNPY(buffer: ArrayBuffer): Promise<ArrayBuffer>;
readNPZ(buffer: ArrayBuffer): Promise<ArrayBuffer | undefined>;
imageDataFromArrayBuffer(buffer: ArrayBuffer): Promise<ImageData>;
readBMP(buffer: ArrayBuffer): Promise<ArrayBuffer>;
readZARR(buffer: ArrayBuffer, zarrData: unknown): Promise<ArrayBufferLike>;
readVMR(buffer: ArrayBuffer): ArrayBuffer;
readFIB(buffer: ArrayBuffer): Promise<[ArrayBuffer, Float32Array]>;
readSRC(buffer: ArrayBuffer): Promise<ArrayBuffer>;
readHEAD(dataBuffer: ArrayBuffer, pairedImgData: ArrayBuffer | null): Promise<ArrayBuffer>;
readMHA(buffer: ArrayBuffer, pairedImgData: ArrayBuffer | null): Promise<ArrayBuffer>;
readMIF(buffer: ArrayBuffer, pairedImgData: ArrayBuffer | null): Promise<ArrayBuffer>;
calculateRAS(): void;
/**
* Get a deep copy of the current affine matrix.
* @returns A 4x4 affine matrix as a 2D array (row-major)
*/
getAffine(): number[][];
/**
* Set a new affine matrix and recalculate all derived RAS matrices.
* Call updateGLVolume() on the Niivue instance after this to update rendering.
* @param affine - A 4x4 affine matrix as a 2D array (row-major)
*/
setAffine(affine: number[][]): void;
/**
* Apply a transform (translation, rotation, scale) to the current affine matrix.
* The transform is applied in world coordinate space: newAffine = transform * currentAffine
* Call updateGLVolume() on the Niivue instance after this to update rendering.
* @param transform - Transform to apply with translation (mm), rotation (degrees), and scale
*/
applyTransform(transform: AffineTransform): void;
/**
* Reset the affine matrix to its original state when the image was first loaded.
* Call updateGLVolume() on the Niivue instance after this to update rendering.
*/
resetAffine(): void;
hdr2RAS(nVolumes?: number): Promise<NIFTI1 | NIFTI2>;
img2RAS(nVolume?: number): TypedVoxelArray;
vox2mm(XYZ: number[], mtx: mat4): vec3;
mm2vox(mm: number[], frac?: boolean): Float32Array | vec3;
arrayEquals(a: unknown[], b: unknown[]): boolean;
setColormap(cm: string): void;
setColormapLabel(cm: ColorMap): void;
setColormapLabelFromUrl(url: string): Promise<void>;
get colormap(): string;
get colorMap(): string;
set colormap(cm: string);
set colorMap(cm: string);
get opacity(): number;
set opacity(opacity: number);
/**
* set contrast/brightness to robust range (2%..98%)
* @param vol - volume for estimate (use -1 to use estimate on all loaded volumes; use INFINITY for current volume)
* @param isBorder - if true (default) only center of volume used for estimate
* @returns volume brightness and returns array [pct2, pct98, mnScale, mxScale]
* @see {@link https://niivue.com/demos/features/timeseries2.html | live demo usage}
*/
calMinMax(vol?: number, isBorder?: boolean): number[];
intensityRaw2Scaled(raw: number): number;
intensityScaled2Raw(scaled: number): number;
/**
* Converts NVImage to NIfTI compliant byte array, potentially compressed.
* Delegates to ImageWriter.saveToUint8Array.
*/
saveToUint8Array(fnm: string, drawing8?: Uint8Array | null): Promise<Uint8Array>;
/**
* save image as NIfTI volume and trigger download.
* Delegates to ImageWriter.saveToDisk.
*/
saveToDisk(fnm?: string, drawing8?: Uint8Array | null): Promise<Uint8Array>;
static fetchDicomData(url: string, headers?: Record<string, string>): Promise<Array<{
name: string;
data: ArrayBuffer;
}>>;
static readFirstDecompressedBytes(stream: ReadableStream<Uint8Array>, minBytes: number): Promise<Uint8Array>;
static extractFilenameFromUrl(url: string): string | null;
static loadInitialVolumesGz(url?: string, headers?: {}, limitFrames4D?: number): Promise<ArrayBuffer | null>;
static loadInitialVolumes(url?: string, headers?: {}, limitFrames4D?: number): Promise<ArrayBuffer | null>;
/**
* factory function to load and return a new NVImage instance from a given URL
*/
static loadFromUrl({ url, urlImgData, headers, name, colormap, opacity, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, useQFormNotSForm, colormapNegative, frame4D, isManifest, limitFrames4D, imageType, colorbarVisible, buffer, zarrLevel, zarrMaxVolumeSize, zarrChannel, zarrConvertUnits, zarrCenterMM }?: Partial<Omit<ImageFromUrlOptions, 'url'>> & {
url?: string | Uint8Array | ArrayBuffer;
}): Promise<NVImage>;
/**
* Factory method: create a chunked zarr NVImage with an attached NVZarrHelper.
*/
static createChunkedZarr(url: string, options: {
level: number;
maxVolumeSize?: number;
maxTextureSize?: number;
channel?: number;
cacheSize?: number;
convertUnitsToMm?: boolean;
colormap?: string;
opacity?: number;
zarrCenterMM?: [number, number, number];
}): Promise<NVImage>;
static readFileAsync(file: File, bytesToLoad?: number): Promise<ArrayBuffer>;
/**
* factory function to load and return a new NVImage instance from a file in the browser
*/
static loadFromFile({ file, // file can be an array of file objects or a single file object
name, colormap, opacity, urlImgData, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, useQFormNotSForm, colormapNegative, frame4D, limitFrames4D, imageType }: ImageFromFileOptions): Promise<NVImage>;
/**
* Creates a Uint8Array representing a NIFTI file (header + optional image data).
* Delegates to ImageWriter.createNiftiArray.
*/
static createNiftiArray(dims?: number[], pixDims?: number[], affine?: number[], datatypeCode?: NiiDataType, img?: TypedVoxelArray | Uint8Array): Uint8Array;
/**
* Creates a NIFTI1 header object with basic properties.
* Delegates to ImageWriter.createNiftiHeader.
*/
static createNiftiHeader(dims?: number[], pixDims?: number[], affine?: number[], datatypeCode?: NiiDataType): NIFTI1;
/**
* read a 3D slab of voxels from a volume
* @see {@link https://niivue.com/demos/features/slab_selection.html | live demo usage}
*/
/**
* read a 3D slab of voxels from a volume, specified in RAS coordinates.
* Delegates to VolumeUtils.getVolumeData.
*/
getVolumeData(voxStart?: number[], voxEnd?: number[], dataType?: string): [TypedVoxelArray, number[]];
/**
* write a 3D slab of voxels from a volume
* @see {@link https://niivue.com/demos/features/slab_selection.html | live demo usage}
*/
/**
* write a 3D slab of voxels from a volume, specified in RAS coordinates.
* Delegates to VolumeUtils.setVolumeData.
* Input slabData is assumed to be in the correct raw data type for the target image.
*/
setVolumeData(voxStart?: number[], voxEnd?: number[], img?: TypedVoxelArray): void;
/**
* factory function to load and return a new NVImage instance from a base64 encoded string
* @example
* myImage = NVImage.loadFromBase64('SomeBase64String')
*/
static loadFromBase64({ base64, name, colormap, opacity, cal_min, cal_max, trustCalMinMax, percentileFrac, ignoreZeroVoxels, useQFormNotSForm, colormapNegative, frame4D, imageType, cal_minNeg, cal_maxNeg, colorbarVisible, colormapLabel }: ImageFromBase64): Promise<NVImage>;
/**
* make a clone of a NVImage instance and return a new NVImage
* @example
* myImage = NVImage.loadFromFile(SomeFileObject) // files can be from dialogs or drag and drop
* clonedImage = myImage.clone()
*/
clone(): NVImage;
/**
* fill a NVImage instance with zeros for the image data
* @example
* myImage = NVImage.loadFromFile(SomeFileObject) // files can be from dialogs or drag and drop
* clonedImageWithZeros = myImage.clone().zeroImage()
*/
zeroImage(): void;
/**
* get nifti specific metadata about the image
*/
getImageMetadata(): ImageMetadata;
/**
* a factory function to make a zero filled image given a NVImage as a reference
* @example
* myImage = NVImage.loadFromFile(SomeFileObject) // files can be from dialogs or drag and drop
* newZeroImage = NVImage.zerosLike(myImage)
*/
static zerosLike(nvImage: NVImage, dataType?: string): NVImage;
/**
* Returns voxel intensity at specific native coordinates.
* Delegates to VolumeUtils.getValue.
*/
getValue(x: number, y: number, z: number, frame4D?: number, isReadImaginary?: boolean): number;
/**
* Returns voxel intensities at specific native coordinates.
* Delegates to VolumeUtils.getValue.
*/
getValues(x: number, y: number, z: number, frame4D?: number, isReadImaginary?: boolean): number[];
/**
* Update options for image
*/
applyOptionsUpdate(options: ImageFromUrlOptions): void;
getImageOptions(): ImageFromUrlOptions;
/**
* Converts NVImage to NIfTI compliant byte array.
* Handles potential re-orientation of drawing data.
* Delegates to ImageWriter.toUint8Array.
*/
toUint8Array(drawingBytes?: Uint8Array | null): Uint8Array;
convertVox2Frac(vox: vec3): vec3;
convertFrac2Vox(frac: vec3): vec3;
convertFrac2MM(frac: vec3, isForceSliceMM?: boolean): vec4;
convertMM2Frac(mm: vec3 | vec4, isForceSliceMM?: boolean): vec3;
}
type FreeSurferConnectome = {
data_type: string;
points: Array<{
comments?: Array<{
text: string;
}>;
coordinates: {
x: number;
y: number;
z: number;
};
}>;
};
/**
* Represents a connectome
*/
declare class NVConnectome extends NVMesh {
gl: WebGL2RenderingContext;
nodesChanged: EventTarget;
constructor(gl: WebGL2RenderingContext, connectome: LegacyConnectome);
static convertLegacyConnectome(json: LegacyConnectome): Connectome;
static convertFreeSurferConnectome(json: FreeSurferConnectome, colormap?: string): Connectome;
updateLabels(): void;
addConnectomeNode(node: NVConnectomeNode): void;
deleteConnectomeNode(node: NVConnectomeNode): void;
updateConnectomeNodeByIndex(index: number, updatedNode: NVConnectomeNode): void;
updateConnectomeNodeByPoint(point: [number, number, number], updatedNode: NVConnectomeNode): void;
addConnectomeEdge(first: number, second: number, colorValue: number): NVConnectomeEdge;
deleteConnectomeEdge(first: number, second: number): NVConnectomeEdge;
findClosestConnectomeNode(point: number[], distance: number): NVConnectomeNode | null;
updateConnectome(gl: WebGL2RenderingContext): void;
updateMesh(gl: WebGL2RenderingContext): void;
json(): Connectome;
/**
* Factory method to create connectome from options
*/
static loadConnectomeFromUrl(gl: WebGL2RenderingContext, url: string): Promise<NVConnectome>;
}
/**
* Represents a completed measurement between two points
*/
interface CompletedMeasurement {
startMM: vec3;
endMM: vec3;
distance: number;
sliceIndex: number;
sliceType: SLICE_TYPE;
slicePosition: number;
}
/**
* Represents a completed angle measurement between two lines
*/
interface CompletedAngle {
firstLineMM: {
start: vec3;
end: vec3;
};
secondLineMM: {
start: vec3;
end: vec3;
};
angle: number;
sliceIndex: number;
sliceType: SLICE_TYPE;
slicePosition: number;
}
/**
* Slice Type
* @ignore
*/
declare enum SLICE_TYPE {
AXIAL = 0,
CORONAL = 1,
SAGITTAL = 2,
MULTIPLANAR = 3,
RENDER = 4
}
declare enum PEN_TYPE {
PEN = 0,
RECTANGLE = 1,
ELLIPSE = 2
}
declare enum SHOW_RENDER {
NEVER = 0,
ALWAYS = 1,
AUTO = 2
}
/**
* Multi-planar layout
* @ignore
*/
declare enum MULTIPLANAR_TYPE {
AUTO = 0,
COLUMN = 1,
GRID = 2,
ROW = 3
}
/**
* Drag mode
* @ignore
*/
declare enum DRAG_MODE {
none = 0,
contrast = 1,
measurement = 2,
pan = 3,
slicer3D = 4,
callbackOnly = 5,
roiSelection = 6,
angle = 7,
crosshair = 8,
windowing = 9
}
interface MouseEventConfig {
leftButton: {
primary: DRAG_MODE;
withShift?: DRAG_MODE;
withCtrl?: DRAG_MODE;
};
rightButton: DRAG_MODE;
centerButton: DRAG_MODE;
}
interface TouchEventConfig {
singleTouch: DRAG_MODE;
doubleTouch: DRAG_MODE;
}
/**
* NVConfigOptions
*/
type NVConfigOptions = {
textHeight: number;
fontSizeScaling: number;
fontMinPx: number;
colorbarHeight: number;
colorbarWidth: number;
showColorbarBorder: boolean;
crosshairWidth: number;
crosshairWidthUnit: 'voxels' | 'mm' | 'percent';
crosshairGap: number;
rulerWidth: number;
show3Dcrosshair: boolean;
backColor: number[];
crosshairColor: number[];
fontColor: Float32List;
selectionBoxColor: number[];
clipPlaneColor: number[];
isClipPlanesCutaway: boolean;
isClipAllVolumes: boolean;
paqdUniforms: number[];
rulerColor: number[];
colorbarMargin: number;
trustCalMinMax: boolean;
clipPlaneHotKey: string;
cycleClipPlaneHotKey: string;
viewModeHotKey: string;
doubleTouchTimeout: number;
longTouchTimeout: number;
keyDebounceTime: number;
isNearestInterpolation: boolean;
atlasOutline: number;
atlasActiveIndex: number;
isRuler: boolean;
isColorbar: boolean;
isOrientCube: boolean;
tileMargin: number;
multiplanarPadPixels: number;
multiplanarForceRender: boolean;
multiplanarEqualSize: boolean;
multiplanarShowRender: SHOW_RENDER;
isRadiologicalConvention: boolean;
meshThicknessOn2D: number | string;
dragMode: DRAG_MODE;
dragModePrimary: DRAG_MODE;
mouseEventConfig?: MouseEventConfig;
touchEventConfig?: TouchEventConfig;
yoke3Dto2DZoom: boolean;
isDepthPickMesh: boolean;
isCornerOrientationText: boolean;
isOrientationTextVisible: boolean;
showAllOrientationMarkers: boolean;
heroImageFraction: number;
heroSliceType: SLICE_TYPE;
sagittalNoseLeft: boolean;
isSliceMM: boolean;
isV1SliceShader: boolean;
forceDevicePixelRatio: number;
logLevel: 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'silent';
loadingText: string;
isForceMouseClickToVoxelCenters: boolean;
dragAndDropEnabled: boolean;
drawingEnabled: boolean;
penValue: number;
penType: PEN_TYPE;
floodFillNeighbors: number;
isFilledPen: boolean;
thumbnail: string;
maxDrawUndoBitmaps: number;
sliceType: SLICE_TYPE;
isAntiAlias: boolean | null;
isAdditiveBlend: boolean;
isResizeCanvas: boolean;
meshXRay: number;
limitFrames4D: number;
showLegend: boolean;
legendBackgroundColor: number[];
legendTextColor: number[];
multiplanarLayout: MULTIPLANAR_TYPE;
renderOverlayBlend: number;
sliceMosaicString: string;
centerMosaic: boolean;
interactive: boolean;
penSize: number;
clickToSegment: boolean;
clickToSegmentRadius: number;
clickToSegmentBright: boolean;
clickToSegmentAutoIntensity: boolean;
clickToSegmentIntensityMax: number;
clickToSegmentIntensityMin: number;
clickToSegmentPercent: number;
clickToSegmentMaxDistanceMM: number;
clickToSegmentIs2D: boolean;
selectionBoxLineThickness: number;
selectionBoxIsOutline: boolean;
scrollRequiresFocus: boolean;
showMeasureUnits: boolean;
measureTextJustify: 'start' | 'center' | 'end';
measureTextColor: number[];
measureLineColor: number[];
measureTextHeight: number;
isAlphaClipDark: boolean;
gradientOrder: number;
gradientOpacity: number;
renderSilhouette: number;
gradientAmount: number;
invertScrollDirection: boolean;
is2DSliceShader: boolean;
bounds: [[number, number], [number, number]] | null;
showBoundsBorder?: boolean;
boundsBorderColor?: number[];
windowingGainFactor: number;
/** Chunk cache size for zarr viewing (default 500) */
zarrCacheSize: number;
/** Number of chunk rings to prefetch around the visible region for zarr viewing (0 disables, default 1) */
zarrPrefetchRings: number;
/** Smooth drawing surfaces in 3D rendering. 0 = off, > 0 = Box blur radius in voxels (default 0) */
smoothDrawing: number;
};
declare const DEFAULT_OPTIONS: NVConfigOptions;
type EncodeNumbersIn<T> = T extends number ? number | string : T extends Array<infer U> ? Array<EncodeNumbersIn<U>> : T extends object ? {
[K in keyof T]: EncodeNumbersIn<T[K]>;
} : T;
type EncodedNVConfigOptions = EncodeNumbersIn<NVConfigOptions>;
declare const DEFAULT_SCENE_DATA: {};
type SceneData = {
gamma: number;
azimuth: number;
elevation: number;
crosshairPos: vec3;
clipPlanes: number[][];
clipPlaneDepthAziElevs: number[][];
volScaleMultiplier: number;
pan2Dxyzmm: vec4;
};
declare const INITIAL_SCENE_DATA: {
gamma: number;
azimuth: number;
elevation: number;
crosshairPos: vec3;
clipPlanes: number[][];
clipPlaneDepthAziElevs: number[][];
volScaleMultiplier: number;
pan2Dxyzmm: vec4;
};
type Scene = {
onAzimuthElevationChange: (azimuth: number, elevation: number) => void;
onZoom3DChange: (scale: number) => void;
sceneData: SceneData;
renderAzimuth: number;
renderElevation: number;
volScaleMultiplier: number;
crosshairPos: vec3;
clipPlane: number[];
clipPlanes: number[][];
clipPlaneDepthAziElevs: number[][];
pan2Dxyzmm: vec4;
_elevation?: number;
_azimuth?: number;
gamma?: number;
};
/**
* DocumentData / ExportDocumentData types (kept minimal here)
*/
type DocumentData = {
title?: string;
imageOptionsArray?: ImageFromUrlOptions[];
meshOptionsArray?: unknown[];
opts?: Partial<EncodedNVConfigOptions> | Partial<NVConfigOptions>;
previewImageDataURL?: string;
labels?: NVLabel3D[];
encodedImageBlobs?: string[];
encodedDrawingBlob?: string;
meshesString?: string;
sceneData?: Partial<SceneData>;
connectomes?: string[];
customData?: string;
completedMeasurements?: CompletedMeasurement[];
completedAngles?: CompletedAngle[];
};
type ExportDocumentData = {
title?: string;
encodedImageBlobs: string[];
encodedDrawingBlob: string;
previewImageDataURL: string;
imageOptionsMap: Map<string, number>;
imageOptionsArray: ImageFromUrlOptions[];
sceneData: Partial<SceneData>;
opts: EncodedNVConfigOptions | Partial<EncodedNVConfigOptions>;
meshesString: string;
meshOptionsArray?: unknown[];
labels: NVLabel3D[];
connectomes: string[];
customData: string;
completedMeasurements: CompletedMeasurement[];
completedAngles: CompletedAngle[];
};
/**
* Returns a partial configuration object containing only the fields in the provided
* options that differ from the DEFAULT_OPTIONS.
*/
/**
* NVDocument class (main)
*/
declare class NVDocument {
data: DocumentData;
scene: Scene;
volumes: NVImage[];
meshDataObjects?: Array<NVMesh | NVConnectome>;
meshes: Array<NVMesh | NVConnectome>;
drawBitmap: Uint8Array | null;
imageOptionsMap: Map<any, any>;
meshOptionsMap: Map<any, any>;
completedMeasurements: CompletedMeasurement[];
completedAngles: CompletedAngle[];
private _optsProxy;
private _optsChangeCallback;
constructor();
/**
* Title of the document
*/
get title(): string;
/**
* Gets preview image blob
* @returns dataURL of preview image
*/
get previewImageDataURL(): string;
/**
* Sets preview image blob
* @param dataURL - encoded preview image
*/
set previewImageDataURL(dataURL: string);
/**
* @param title - title of document
*/
set title(title: string);
get imageOptionsArray(): ImageFromUrlOptions[];
/**
* Gets the base 64 encoded blobs of associated images
*/
get encodedImageBlobs(): string[];
/**
* Gets the base 64 encoded blob of the associated drawing
*/
get encodedDrawingBlob(): string;
/**
* Gets the options of the {@link Niivue} instance
*/
get opts(): NVConfigOptions;
/**
* Sets the options of the {@link Niivue} instance
*/
set opts(opts: NVConfigOptions);
/**
* Gets the 3D labels of the {@link Niivue} instance
*/
get labels(): NVLabel3D[];
/**
* Sets the 3D labels of the {@link Niivue} instance
*/
set labels(labels: NVLabel3D[]);
get customData(): string | undefined;
set customData(data: string);
/**
* Checks if document has an image by id
*/
hasImage(image: NVImage): boolean;
/**
* Checks if document has an image by url
*/
hasImageFromUrl(url: string): boolean;
/**
* Adds an image and the options an image was created with
*/
addImageOptions(image: NVImage, imageOptions: ImageFromUrlOptions): void;
/**
* Removes image from the document as well as its options
*/
removeImage(image: NVImage): void;
/**
* Fetch any image data that is missing from this document.
*/
fetchLinkedData(): Promise<void>;
/**
* Returns the options for the image if it was added by url
*/
getImageOptions(image: NVImage): ImageFromUrlOptions | null;
/**
* Serialise the document by delegating to NVSerializer.
*/
json(embedImages?: boolean, embedDrawing?: boolean): ExportDocumentData;
download(fileName: string, compress: boolean, opts?: {
embedImages: boolean;
}): Promise<void>;
/**