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.
thickness Number No The slice thickness.

getClosestIJKAxis

Get the closest IJK axis

getIsOpaque

getRenderToRectangle

getResolveCoincidentTopology

getResolveCoincidentTopologyAsString

getResolveCoincidentTopologyLineOffsetParameters

getResolveCoincidentTopologyPointOffsetParameters

getResolveCoincidentTopologyPolygonOffsetFaces

getResolveCoincidentTopologyPolygonOffsetParameters

getSlice

Get the slice index.

getSliceAtFocalPoint

Get the slice number at a focal point.

getSliceAtPosition

Argument Type Required Description
pos Array. Yes

getSliceAtPosition

Argument Type Required Description
pos Number Yes

getSlicingModeNormal

getUseCustomExtents

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.

setCustomDisplayExtent

Argument Type Required Description
x1 Yes The x coordinate of the first point.
x2 Yes The x coordinate of the second point.
y1 Yes The y coordinate of the first point.
y2 Yes The y coordinate of the second point.
z1 Yes The z coordinate of the first point.
z2 Yes The z coordinate of the second point.

setCustomDisplayExtentFrom

Argument Type Required Description
customDisplayExtent Yes

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.

setRelativeCoincidentTopologyLineOffsetParameters

Argument Type Required Description
factor Number Yes
offset Number Yes

setRelativeCoincidentTopologyPointOffsetParameters

Argument Type Required Description
factor Number Yes
offset Number Yes

setRelativeCoincidentTopologyPolygonOffsetParameters

Argument Type Required Description
factor Number Yes
offset Number Yes

setRenderToRectangle

Argument Type Required Description
renderToRectangle Boolean Yes

setResolveCoincidentTopology

Argument Type Required Description
resolveCoincidentTopology Yes

setResolveCoincidentTopologyLineOffsetParameters

Argument Type Required Description
factor Number Yes
offset Number Yes

setResolveCoincidentTopologyPointOffsetParameters

Argument Type Required Description
factor Number Yes
offset Number Yes

setResolveCoincidentTopologyPolygonOffsetFaces

Argument Type Required Description
value Yes

setResolveCoincidentTopologyPolygonOffsetParameters

Argument Type Required Description
factor Number Yes
offset Number Yes

setResolveCoincidentTopologyToDefault

setResolveCoincidentTopologyToOff

setResolveCoincidentTopologyToPolygonOffset

setSlice

Argument Type Required Description
slice Number Yes The slice index.

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 Number Yes The slicing mode.

setUseCustomExtents

Argument Type Required Description
useCustomExtents Boolean Yes

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.

sliceAtFocalPoint

Set the slice from a given focal point.

Argument Type Required Description
sliceAtFocalPoint Boolean Yes

Source

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 vtkAbstractMapper from "../AbstractMapper";
import { Bounds } from "../../../types";

export enum SlicingMode {
NONE,
I,
J,
K,
X,
Y,
Z,
}

interface IClosestIJKAxis {
ijkMode: SlicingMode,
flip: boolean
}

interface ICoincidentTopology {
factor: number;
offset: number;
}

interface IImageMapperInitialValues {
displayExtent?: number[];
customDisplayExtent?: number[];
useCustomExtents?: boolean;
slice?: number;
closestIJKAxis?: IClosestIJKAxis;
renderToRectangle?: boolean;
sliceAtFocalPoint?: boolean;
}

export interface vtkImageMapper extends vtkAbstractMapper {

/**
*
* @param {Number} pos
*/
getSliceAtPosition(pos: number): number;

/**
*
* @param {Number[]} pos
*/
getSliceAtPosition(pos: 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.
* @param {Number} [thickness] The slice thickness.
* @return {Number[]} The bounds for a given slice.
*/
getBoundsForSlice(slice?: number, thickness?: number): number[];

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


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

/**
*
*/
getResolveCoincidentTopology(): ICoincidentTopology

/**
*
*/
getResolveCoincidentTopologyAsString(): ICoincidentTopology

/**
*
*/
getResolveCoincidentTopologyLineOffsetParameters(): ICoincidentTopology

/**
*
*/
getResolveCoincidentTopologyPointOffsetParameters(): ICoincidentTopology

/**
*
*/
getResolveCoincidentTopologyPolygonOffsetFaces(): ICoincidentTopology

/**
*
*/
getResolveCoincidentTopologyPolygonOffsetParameters(): ICoincidentTopology;

/**
* Get the slice index.
*/
getSlice(): number;

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

/**
*
*/
getUseCustomExtents(): 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 x1 The x coordinate of the first point.
* @param x2 The x coordinate of the second point.
* @param y1 The y coordinate of the first point.
* @param y2 The y coordinate of the second point.
* @param z1 The z coordinate of the first point.
* @param z2 The z coordinate of the second point.
*/
setCustomDisplayExtent(x1: number, x2: number, y1: number, y2: number, z1: number, z2: number): boolean;

/**
*
* @param customDisplayExtent
*/
setCustomDisplayExtentFrom(customDisplayExtent: number[]): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setRelativeCoincidentTopologyLineOffsetParameters(factor: number, offset: number): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setRelativeCoincidentTopologyPointOffsetParameters(factor: number, offset: number): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setRelativeCoincidentTopologyPolygonOffsetParameters(factor: number, offset: number): boolean;

/**
*
* @param resolveCoincidentTopology
* @default false
*/
setResolveCoincidentTopology(resolveCoincidentTopology: boolean): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setResolveCoincidentTopologyLineOffsetParameters(factor: number, offset: number): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setResolveCoincidentTopologyPointOffsetParameters(factor: number, offset: number): boolean;

/**
*
* @param value
*/
setResolveCoincidentTopologyPolygonOffsetFaces(value: number): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setResolveCoincidentTopologyPolygonOffsetParameters(factor: number, offset: number)

/**
*
*/
setResolveCoincidentTopologyToDefault(): boolean;

/**
*
*/
setResolveCoincidentTopologyToOff(): boolean;

/**
*
*/
setResolveCoincidentTopologyToPolygonOffset(): 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
*/
sliceAtFocalPoint(sliceAtFocalPoint: boolean): boolean;

/**
*
* @param {Boolean} useCustomExtents
*/
setUseCustomExtents(useCustomExtents: 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[];

/**
* Set the slicing mode.
* @param {Number} mode The slicing mode.
*/
setSlicingMode(mode: number): 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;
}
export default vtkImageMapper;
index.js
import Constants from 'vtk.js/Sources/Rendering/Core/ImageMapper/Constants';
import macro from 'vtk.js/Sources/macro';
import vtkAbstractMapper from 'vtk.js/Sources/Rendering/Core/AbstractMapper';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import vtkPlane from 'vtk.js/Sources/Common/DataModel/Plane';
import CoincidentTopologyHelper from 'vtk.js/Sources/Rendering/Core/Mapper/CoincidentTopologyHelper';

import { vec3 } from 'gl-matrix';

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

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

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

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

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]);
slice = Math.round(slice);
break;
case SlicingMode.J:
slice = vtkMath.clampValue(ijk[1], ex[2], ex[3]);
slice = Math.round(slice);
break;
case SlicingMode.K:
slice = vtkMath.clampValue(ijk[2], ex[4], ex[5]);
slice = Math.round(slice);
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 a = publicAPI.getInputData().getDirection();
const mat3 = [
[a[0], a[1], a[2]],
[a[3], a[4], a[5]],
[a[6], a[7], a[8]],
];

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 inVec3;
switch (model.slicingMode) {
case SlicingMode.X:
inVec3 = [1, 0, 0];
break;
case SlicingMode.Y:
inVec3 = [0, 1, 0];
break;
case SlicingMode.Z:
inVec3 = [0, 0, 1];
break;
default:
model.closestIJKAxis = {
ijkMode: model.slicingMode,
flip: false,
};
return;
}

// Project vec3 onto direction cosines
const out = [0, 0, 0];
// The direction matrix in vtkImageData is the indexToWorld rotation matrix
// with a column-major data layout since it is stored as a WebGL matrix.
// We need the worldToIndex rotation matrix for the projection, and it needs
// to be in a row-major data layout to use vtkMath for operations.
// To go from the indexToWorld column-major matrix to the worldToIndex
// row-major matrix, we need to transpose it (column -> row) then inverse it.
// However, that 3x3 matrix is a rotation matrix which is orthonormal, meaning
// that its inverse is equal to its transpose. We therefore need to apply two
// transpositions resulting in a no-op.
const a = publicAPI.getInputData().getDirection();
const mat3 = [
[a[0], a[1], a[2]],
[a[3], a[4], a[5]],
[a[6], a[7], a[8]],
];
vtkMath.multiply3x3_vect3(mat3, inVec3, out);

let maxAbs = 0.0;
let ijkMode = -1;
let flip = false;
for (let axis = 0; axis < out.length; ++axis) {
const absValue = Math.abs(out[axis]);
if (absValue > maxAbs) {
maxAbs = absValue;
flip = out[axis] < 0.0;
ijkMode = axis;
}
}

if (maxAbs !== 1.0) {
const xyzLabel = 'IJKXYZ'[model.slicingMode];
const ijkLabel = 'IJKXYZ'[ijkMode];
vtkWarningMacro(
`Unaccurate slicing along ${xyzLabel} axis which ` +
`is not aligned with any IJK axis of the image data. ` +
`Using ${ijkLabel} axis as a fallback (${maxAbs}% aligned). ` +
`Necessitates slice reformat that is not yet implemented. ` +
`You can switch the slicing mode on your mapper to do IJK slicing instead.`
);
}

model.closestIJKAxis = { ijkMode, flip };
}

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

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

publicAPI.getBounds = () => {
const image = publicAPI.getInputData();
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, thickness = 0) => {
const image = publicAPI.getInputData();
if (!image) {
return vtkMath.createUninitializedBounds();
}
const extent = image.getExtent();
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 - thickness;
extent[1] = nSlice + thickness;
break;
case SlicingMode.J:
extent[2] = nSlice - thickness;
extent[3] = nSlice + thickness;
break;
case SlicingMode.K:
extent[4] = nSlice - thickness;
extent[5] = nSlice + thickness;
break;
default:
break;
}
return image.extentToBounds(extent);
};

publicAPI.getIsOpaque = () => true;

function doPicking(p1, p2) {
const imageData = publicAPI.getInputData();
const extent = imageData.getExtent();

// Slice origin
const ijk = [extent[0], extent[2], extent[4]];
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(nSlice);
}
ijk[ijkMode] += nSlice;
const worldOrigin = [0, 0, 0];
imageData.indexToWorld(ijk, worldOrigin);

// Normal computation
ijk[ijkMode] += 1;
const worldNormal = [0, 0, 0];
imageData.indexToWorld(ijk, worldNormal);
worldNormal[0] -= worldOrigin[0];
worldNormal[1] -= worldOrigin[1];
worldNormal[2] -= worldOrigin[2];
vec3.normalize(worldNormal, worldNormal);

const intersect = vtkPlane.intersectWithLine(
p1,
p2,
worldOrigin,
worldNormal
);
if (intersect.intersection) {
const point = intersect.x;
const absoluteIJK = [0, 0, 0];
imageData.worldToIndex(point, absoluteIJK);
// `t` is the parametric position along the line
// defined in Plane.intersectWithLine
return {
t: intersect.t,
absoluteIJK,
};
}
return null;
}

publicAPI.intersectWithLineForPointPicking = (p1, p2) => {
const pickingData = doPicking(p1, p2);
if (pickingData) {
const imageData = publicAPI.getInputData();
const extent = imageData.getExtent();

// Get closer integer ijk
// NB: point picking means closest slice, means rounding
const ijk = [
Math.round(pickingData.absoluteIJK[0]),
Math.round(pickingData.absoluteIJK[1]),
Math.round(pickingData.absoluteIJK[2]),
];

// Are we outside our actual extent
if (
ijk[0] < extent[0] ||
ijk[0] > extent[1] ||
ijk[1] < extent[2] ||
ijk[1] > extent[3] ||
ijk[2] < extent[4] ||
ijk[2] > extent[5]
) {
return null;
}

return {
t: pickingData.t,
ijk,
};
}
return null;
};

publicAPI.intersectWithLineForCellPicking = (p1, p2) => {
const pickingData = doPicking(p1, p2);
if (pickingData) {
const imageData = publicAPI.getInputData();
const extent = imageData.getExtent();
const absIJK = pickingData.absoluteIJK;

// Get closer integer ijk
// NB: cell picking means closest voxel, means flooring
const ijk = [
Math.floor(absIJK[0]),
Math.floor(absIJK[1]),
Math.floor(absIJK[2]),
];

// Are we outside our actual extent
if (
ijk[0] < extent[0] ||
ijk[0] > extent[1] - 1 ||
ijk[1] < extent[2] ||
ijk[1] > extent[3] - 1 ||
ijk[2] < extent[4] ||
ijk[2] > extent[5] - 1
) {
return null;
}

// Parametric coordinates within cell
const pCoords = [
absIJK[0] - ijk[0],
absIJK[1] - ijk[1],
absIJK[2] - ijk[2],
];

return {
t: pickingData.t,
ijk,
pCoords,
};
}
return null;
};
}

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

const DEFAULT_VALUES = {
displayExtent: [0, 0, 0, 0, 0, 0],
customDisplayExtent: [0, 0, 0, 0],
useCustomExtents: false,
slice: 0,
slicingMode: SlicingMode.NONE,
closestIJKAxis: { ijkMode: SlicingMode.NONE, flip: false },
renderToRectangle: false,
sliceAtFocalPoint: false,
};

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

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

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

macro.get(publicAPI, model, ['slicingMode']);
macro.setGet(publicAPI, model, [
'slice',
'closestIJKAxis',
'useCustomExtents',
'renderToRectangle',
'sliceAtFocalPoint',
]);
macro.setGetArray(publicAPI, model, ['customDisplayExtent'], 4);

CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);

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

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

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

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

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