ImageMapper

Introduction

vtkImageMapper provides 2D image display support for vtk.
It can be associated with a vtkImageSlice prop and placed within a Renderer.

This class resolves coincident topology with the same methods as vtkMapper.

Methods

extend

Method use to decorate a given object (publicAPI+model) with vtkImageMapper characteristics.

Argument Type Required Description
publicAPI Yes object on which methods will be bounds (public)
model Yes object on which data structure will be bounds (protected)
initialValues IImageMapperInitialValues No (default: {})

getBounds

Get the bounds for this mapper as [xmin, xmax, ymin, ymax,zmin, zmax].

getBoundsForSlice

Get the bounds for a given slice as [xmin, xmax, ymin, ymax,zmin, zmax].

Argument Type Required Description
slice Number No The slice index. If undefined, the current slice is considered.
halfThickness Number No Half the slice thickness in index space (unit voxel spacing). If undefined, 0 is considered.

getClosestIJKAxis

Get the closest IJK axis

getCurrentImage

Return currently active image. By default, there can only be one image
for this mapper, if an input is set.

getIsOpaque

getPreferSizeOverAccuracy

Get the preference to use halfFloat representation of float

getRenderToRectangle

getSliceAtFocalPoint

Get the slice number at a focal point.

getSliceAtPosition

Returns the IJK slice value from a world position or XYZ slice value

Argument Type Required Description
pos Vector3 or number No World point or XYZ slice value

getSlicingMode

Get the slicing mode.

getSlicingModeNormal

intersectWithLineForCellPicking

Argument Type Required Description
p1 Array. Yes The coordinates of the first point.
p2 Array. Yes The coordinates of the second point.

intersectWithLineForPointPicking

Argument Type Required Description
p1 Array. Yes The coordinates of the first point.
p2 Array. Yes The coordinates of the second point.

newInstance

Method use to create a new instance of vtkImageMapper

Argument Type Required Description
initialValues IImageMapperInitialValues No for pre-setting some of its content

setClosestIJKAxis

Set the closest IJK axis

Argument Type Required Description
closestIJKAxis IClosestIJKAxis Yes The axis object.

setISlice

Set the slice for the I axis.

Argument Type Required Description
id Number Yes The slice index.

setJSlice

Set the slice for the J axis.

Argument Type Required Description
id Number Yes The slice index.

setKSlice

Set the slice for the K axis.

Argument Type Required Description
id Number Yes The slice index.

setPreferSizeOverAccuracy

Set the preference to use halfFloat representation of float

Argument Type Required Description
preferSizeOverAccuracy Boolean Yes

setRenderToRectangle

Argument Type Required Description
renderToRectangle Boolean Yes

setSlice

Argument Type Required Description
slice Number Yes The slice index.

setSliceAtFocalPoint

Set the slice from a given focal point.

Argument Type Required Description
sliceAtFocalPoint Boolean Yes

setSliceFromCamera

Set the slice from a given camera.

Argument Type Required Description
cam vtkCamera Yes The camera object.

setSlicingMode

Set the slicing mode.

Argument Type Required Description
mode SlicingMode Yes The slicing mode.

setXSlice

Set the slice for the X axis.

Argument Type Required Description
id Number Yes The slice index.

setYSlice

Set the slice for the Y axis.

Argument Type Required Description
id Number Yes The slice index.

setZSlice

Set the slice for the Z axis.

Argument Type Required Description
id Number Yes The slice index.

Source

Constants.d.ts
export declare enum SlicingMode {
NONE = -1,
I = 0,
J = 1,
K = 2,
X = 3,
Y = 4,
Z = 5,
}

declare const _default: {
SlicingMode: typeof SlicingMode;
};
export default _default;
Constants.js
export const SlicingMode = {
NONE: -1,
I: 0,
J: 1,
K: 2,
X: 3,
Y: 4,
Z: 5,
};

export default {
SlicingMode,
};
index.d.ts
import vtkCamera from '../Camera';
import vtkAbstractImageMapper, {
IAbstractImageMapperInitialValues,
} from '../AbstractImageMapper';
import { Bounds, Nullable, Vector3 } from '../../../types';
import { SlicingMode } from './Constants';
import vtkImageData from '../../../Common/DataModel/ImageData';
import CoincidentTopologyHelper, {
StaticCoincidentTopologyMethods,
} from '../Mapper/CoincidentTopologyHelper';

interface IClosestIJKAxis {
ijkMode: SlicingMode;
flip: boolean;
}

export interface IImageMapperInitialValues
extends IAbstractImageMapperInitialValues {
closestIJKAxis?: IClosestIJKAxis;
renderToRectangle?: boolean;
sliceAtFocalPoint?: boolean;
}

export interface vtkImageMapper
extends vtkAbstractImageMapper,
CoincidentTopologyHelper {
/**
* Returns the IJK slice value from a world position or XYZ slice value
* @param {Vector3 | number} [pos] World point or XYZ slice value
*/
getSliceAtPosition(pos: Vector3 | number): number;

/**
* Get the closest IJK axis
* @return {IClosestIJKAxis} The axis object.
*/
getClosestIJKAxis(): IClosestIJKAxis;

/**
* Get the bounds for this mapper as [xmin, xmax, ymin, ymax,zmin, zmax].
* @return {Bounds} The bounds for the mapper.
*/
getBounds(): Bounds;

/**
* Get the bounds for a given slice as [xmin, xmax, ymin, ymax,zmin, zmax].
* @param {Number} [slice] The slice index. If undefined, the current slice is considered.
* @param {Number} [halfThickness] Half the slice thickness in index space (unit voxel
* spacing). If undefined, 0 is considered.
* @return {Bounds} The bounds for a given slice.
*/
getBoundsForSlice(slice?: number, halfThickness?: number): Bounds;

/**
*
*/
getIsOpaque(): boolean;

/**
*
*/
getRenderToRectangle(): boolean;

/**
* Return currently active image. By default, there can only be one image
* for this mapper, if an input is set.
*/
getCurrentImage(): Nullable<vtkImageData>;

/**
* Get the slice number at a focal point.
*/
getSliceAtFocalPoint(): boolean;

/**
*
* @param {Number[]} p1 The coordinates of the first point.
* @param {Number[]} p2 The coordinates of the second point.
*/
intersectWithLineForPointPicking(p1: number[], p2: number[]): any;

/**
*
* @param {Number[]} p1 The coordinates of the first point.
* @param {Number[]} p2 The coordinates of the second point.
*/
intersectWithLineForCellPicking(p1: number[], p2: number[]): any;

/**
* Set the closest IJK axis
* @param {IClosestIJKAxis} closestIJKAxis The axis object.
*/
setClosestIJKAxis(closestIJKAxis: IClosestIJKAxis): boolean;

/**
*
* @param {Boolean} renderToRectangle
*/
setRenderToRectangle(renderToRectangle: boolean): boolean;

/**
*
* @param {Number} slice The slice index.
*/
setSlice(slice: number): boolean;

/**
* Set the slice from a given camera.
* @param {vtkCamera} cam The camera object.
*/
setSliceFromCamera(cam: vtkCamera): boolean;

/**
* Set the slice from a given focal point.
* @param {Boolean} sliceAtFocalPoint
*/
setSliceAtFocalPoint(sliceAtFocalPoint: boolean): boolean;

/**
* Set the slice for the X axis.
* @param {Number} id The slice index.
*/
setXSlice(id: number): boolean;

/**
* Set the slice for the Y axis.
* @param {Number} id The slice index.
*/
setYSlice(id: number): boolean;

/**
* Set the slice for the Z axis.
* @param {Number} id The slice index.
*/
setZSlice(id: number): boolean;

/**
* Set the slice for the I axis.
* @param {Number} id The slice index.
*/
setISlice(id: number): boolean;

/**
* Set the slice for the J axis.
* @param {Number} id The slice index.
*/
setJSlice(id: number): boolean;

/**
* Set the slice for the K axis.
* @param {Number} id The slice index.
*/
setKSlice(id: number): boolean;

/**
*
*/
getSlicingModeNormal(): number[];

/**
* Get the slicing mode.
*/
getSlicingMode(): SlicingMode;

/**
* Set the slicing mode.
* @param {SlicingMode} mode The slicing mode.
*/
setSlicingMode(mode: SlicingMode): boolean;

/**
* Get the preference to use halfFloat representation of float
*/
getPreferSizeOverAccuracy(): boolean;

/**
* Set the preference to use halfFloat representation of float
* @param {Boolean} preferSizeOverAccuracy
*/
setPreferSizeOverAccuracy(preferSizeOverAccuracy: boolean): boolean;
}

/**
* Method use to decorate a given object (publicAPI+model) with vtkImageMapper characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {IImageMapperInitialValues} [initialValues] (default: {})
*/
export function extend(
publicAPI: object,
model: object,
initialValues?: IImageMapperInitialValues
): void;

/**
* Method use to create a new instance of vtkImageMapper
* @param {IImageMapperInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(
initialValues?: IImageMapperInitialValues
): vtkImageMapper;

/**
* vtkImageMapper provides 2D image display support for vtk.
* It can be associated with a vtkImageSlice prop and placed within a Renderer.
*
* This class resolves coincident topology with the same methods as vtkMapper.
*/
export declare const vtkImageMapper: {
newInstance: typeof newInstance;
extend: typeof extend;
SlicingMode: typeof SlicingMode;
} & StaticCoincidentTopologyMethods;
export default vtkImageMapper;
index.js
import Constants from 'vtk.js/Sources/Rendering/Core/ImageMapper/Constants';
import macro from 'vtk.js/Sources/macros';
import vtkAbstractImageMapper from 'vtk.js/Sources/Rendering/Core/AbstractImageMapper';
import * as pickingHelper from 'vtk.js/Sources/Rendering/Core/AbstractImageMapper/helper';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import CoincidentTopologyHelper from 'vtk.js/Sources/Rendering/Core/Mapper/CoincidentTopologyHelper';

const { staticOffsetAPI, otherStaticMethods } = CoincidentTopologyHelper;
const { SlicingMode } = Constants;

// ----------------------------------------------------------------------------
// vtkImageMapper methods
// ----------------------------------------------------------------------------

function vtkImageMapper(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkImageMapper');

publicAPI.getSliceAtPosition = (pos) => {
const image = publicAPI.getCurrentImage();

let pos3;
if (pos.length === 3) {
pos3 = pos;
} else if (Number.isFinite(pos)) {
const bds = image.getBounds();
switch (model.slicingMode) {
case SlicingMode.X:
pos3 = [pos, (bds[3] + bds[2]) / 2, (bds[5] + bds[4]) / 2];
break;
case SlicingMode.Y:
pos3 = [(bds[1] + bds[0]) / 2, pos, (bds[5] + bds[4]) / 2];
break;
case SlicingMode.Z:
pos3 = [(bds[1] + bds[0]) / 2, (bds[3] + bds[2]) / 2, pos];
break;
default:
break;
}
}

const ijk = [0, 0, 0];
image.worldToIndex(pos3, ijk);

const ex = image.getExtent();
const { ijkMode } = publicAPI.getClosestIJKAxis();
let slice = 0;
switch (ijkMode) {
case SlicingMode.I:
slice = vtkMath.clampValue(ijk[0], ex[0], ex[1]);
break;
case SlicingMode.J:
slice = vtkMath.clampValue(ijk[1], ex[2], ex[3]);
break;
case SlicingMode.K:
slice = vtkMath.clampValue(ijk[2], ex[4], ex[5]);
break;
default:
return 0;
}

return slice;
};

publicAPI.setSliceFromCamera = (cam) => {
const fp = cam.getFocalPoint();
switch (model.slicingMode) {
case SlicingMode.I:
case SlicingMode.J:
case SlicingMode.K:
{
const slice = publicAPI.getSliceAtPosition(fp);
publicAPI.setSlice(slice);
}
break;
case SlicingMode.X:
publicAPI.setSlice(fp[0]);
break;
case SlicingMode.Y:
publicAPI.setSlice(fp[1]);
break;
case SlicingMode.Z:
publicAPI.setSlice(fp[2]);
break;
default:
break;
}
};

publicAPI.setXSlice = (id) => {
publicAPI.setSlicingMode(SlicingMode.X);
publicAPI.setSlice(id);
};

publicAPI.setYSlice = (id) => {
publicAPI.setSlicingMode(SlicingMode.Y);
publicAPI.setSlice(id);
};

publicAPI.setZSlice = (id) => {
publicAPI.setSlicingMode(SlicingMode.Z);
publicAPI.setSlice(id);
};

publicAPI.setISlice = (id) => {
publicAPI.setSlicingMode(SlicingMode.I);
publicAPI.setSlice(id);
};

publicAPI.setJSlice = (id) => {
publicAPI.setSlicingMode(SlicingMode.J);
publicAPI.setSlice(id);
};

publicAPI.setKSlice = (id) => {
publicAPI.setSlicingMode(SlicingMode.K);
publicAPI.setSlice(id);
};

publicAPI.getSlicingModeNormal = () => {
const out = [0, 0, 0];
const mat3 = publicAPI.getCurrentImage().getDirection();

switch (model.slicingMode) {
case SlicingMode.X:
out[0] = 1;
break;
case SlicingMode.Y:
out[1] = 1;
break;
case SlicingMode.Z:
out[2] = 1;
break;
case SlicingMode.I:
vtkMath.multiply3x3_vect3(mat3, [1, 0, 0], out);
break;
case SlicingMode.J:
vtkMath.multiply3x3_vect3(mat3, [0, 1, 0], out);
break;
case SlicingMode.K:
vtkMath.multiply3x3_vect3(mat3, [0, 0, 1], out);
break;
default:
break;
}
return out;
};

function computeClosestIJKAxis() {
let xyzMode;
switch (model.slicingMode) {
case SlicingMode.X:
xyzMode = 0;
break;
case SlicingMode.Y:
xyzMode = 1;
break;
case SlicingMode.Z:
xyzMode = 2;
break;
default:
model.closestIJKAxis = {
ijkMode: model.slicingMode,
flip: false,
};
return;
}

// The direction matrix in vtkImageData is the indexToWorld rotation matrix
// with a column-major data layout since it is stored as a WebGL matrix.
const direction = publicAPI.getCurrentImage().getDirection();
const newMatrix = vtkMath.getSparseOrthogonalMatrix(direction);
// With {foo}Vector filled with 0s except at {foo}Mode position where it is 1
// We have xyzVector = (+/-) newMatrix * ijkVector
let ijkMode = 0;
for (; ijkMode < 3; ++ijkMode) {
if (newMatrix[xyzMode + 3 * ijkMode] !== 0) {
break;
}
}
const flip = newMatrix[xyzMode + 3 * ijkMode] < 0;
model.closestIJKAxis = { ijkMode, flip };
}

publicAPI.setSlicingMode = (mode) => {
if (model.slicingMode === mode) {
return;
}
model.slicingMode = mode;
if (publicAPI.getCurrentImage()) {
computeClosestIJKAxis();
}
publicAPI.modified();
};

publicAPI.getClosestIJKAxis = () => {
if (
(model.closestIJKAxis === undefined ||
model.closestIJKAxis.ijkMode === SlicingMode.NONE) &&
publicAPI.getCurrentImage()
) {
computeClosestIJKAxis();
}
return model.closestIJKAxis;
};

publicAPI.getBounds = () => {
const image = publicAPI.getCurrentImage();
if (!image) {
return vtkMath.createUninitializedBounds();
}
if (!model.useCustomExtents) {
return image.getBounds();
}

const ex = model.customDisplayExtent.slice();
const { ijkMode } = publicAPI.getClosestIJKAxis();
let nSlice = model.slice;
if (ijkMode !== model.slicingMode) {
// If not IJK slicing, get the IJK slice from the XYZ position/slice
nSlice = publicAPI.getSliceAtPosition(model.slice);
}
switch (ijkMode) {
case SlicingMode.I:
ex[0] = nSlice;
ex[1] = nSlice;
break;
case SlicingMode.J:
ex[2] = nSlice;
ex[3] = nSlice;
break;
case SlicingMode.K:
ex[4] = nSlice;
ex[5] = nSlice;
break;
default:
break;
}

return image.extentToBounds(ex);
};

publicAPI.getBoundsForSlice = (slice = model.slice, halfThickness = 0) => {
const image = publicAPI.getCurrentImage();
if (!image) {
return vtkMath.createUninitializedBounds();
}
const extent = image.getSpatialExtent();
const { ijkMode } = publicAPI.getClosestIJKAxis();
let nSlice = slice;
if (ijkMode !== model.slicingMode) {
// If not IJK slicing, get the IJK slice from the XYZ position/slice
nSlice = publicAPI.getSliceAtPosition(slice);
}
switch (ijkMode) {
case SlicingMode.I:
extent[0] = nSlice - halfThickness;
extent[1] = nSlice + halfThickness;
break;
case SlicingMode.J:
extent[2] = nSlice - halfThickness;
extent[3] = nSlice + halfThickness;
break;
case SlicingMode.K:
extent[4] = nSlice - halfThickness;
extent[5] = nSlice + halfThickness;
break;
default:
break;
}
return image.extentToBounds(extent);
};

publicAPI.intersectWithLineForPointPicking = (p1, p2) =>
pickingHelper.intersectWithLineForPointPicking(p1, p2, publicAPI);

publicAPI.intersectWithLineForCellPicking = (p1, p2) =>
pickingHelper.intersectWithLineForCellPicking(p1, p2, publicAPI);

publicAPI.getCurrentImage = () => publicAPI.getInputData();
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
slicingMode: SlicingMode.NONE,
closestIJKAxis: { ijkMode: SlicingMode.NONE, flip: false },
renderToRectangle: false,
sliceAtFocalPoint: false,
preferSizeOverAccuracy: false, // Whether to use halfFloat representation of float, when it is inaccurate
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

// Build VTK API
vtkAbstractImageMapper.extend(publicAPI, model, initialValues);

macro.get(publicAPI, model, ['slicingMode']);
macro.setGet(publicAPI, model, [
'closestIJKAxis',
'renderToRectangle',
'sliceAtFocalPoint',
'preferSizeOverAccuracy',
]);

CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);

// Object methods
vtkImageMapper(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkImageMapper');

// ----------------------------------------------------------------------------

export default {
newInstance,
extend,
...staticOffsetAPI,
...otherStaticMethods,
...Constants,
};