ImageSlice

Introduction

vtkImageSlice 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 vtkImageSlice 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 IImageSliceInitialValues No (default: {})

getActors

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

getImages

getIsOpaque

getMTime

Return the Modified Time which is a monotonic increasing integer
global for all vtkObjects.

This allow to solve a question such as:

  • Is that object created/modified after another one?
  • Do I need to re-execute this filter, or not? …

getMapper

getMaxXBound

Get the maximum X bound

getMaxYBound

Get the maximum Y bound

getMaxZBound

Get the maximum Z bound

getMinXBound

Get the minimum X bound

getMinYBound

Get the minimum Y bound

getMinZBound

Get the minimum Z bound

getProperty

getRedrawMTime

getSupportsSelection

hasTranslucentPolygonalGeometry

Always render during opaque pass, to keep the behavior
predictable and because depth-peeling kills alpha-blending.
In the future, the Renderer should render images in layers,
i.e. where each image will have a layer number assigned to it,
and the Renderer will do the images in their own pass.

newInstance

Method use to create a new instance of vtkImageSlice

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

Source

index.d.ts
import { VtkProperty } from "vtk.js/Sources/macro";
import vtkMapper from "vtk.js/Sources/Rendering/Core/Mapper";
import vtkProp3D from "vtk.js/Sources/Rendering/Core/Prop3D";

interface IImageSliceInitialValues{
mapper: null | vtkMapper;
property: null| VtkProperty;
bounds: number[];
}


export interface vtkImageSlice extends vtkProp3D {
/**
*
*/
getActors(): any;

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

/**
* 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[];

/**
*
*/
getImages(): any;

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

/**
*
*/
getProperty(): VtkProperty;

/**
*
*/
getMapper(): vtkMapper;

/**
* Get the minimum X bound
*/
getMinXBound(): number;

/**
* Get the maximum X bound
*/
getMaxXBound(): number;

/**
* Get the minimum Y bound
*/
getMinYBound(): number;

/**
* Get the maximum Y bound
*/
getMaxYBound(): number;

/**
* Get the minimum Z bound
*/
getMinZBound(): number;

/**
* Get the maximum Z bound
*/
getMaxZBound(): number;

/**
* Return the `Modified Time` which is a monotonic increasing integer
* global for all vtkObjects.
*
* This allow to solve a question such as:
* - Is that object created/modified after another one?
* - Do I need to re-execute this filter, or not? ...
*
* @return {Number} the global modified time.
*/
getMTime(): number;

/**
*
*/
getRedrawMTime(): number;

/**
*
*/
getSupportsSelection(): boolean;

/**
* Always render during opaque pass, to keep the behavior
* predictable and because depth-peeling kills alpha-blending.
* In the future, the Renderer should render images in layers,
* i.e. where each image will have a layer number assigned to it,
* and the Renderer will do the images in their own pass.
*/
hasTranslucentPolygonalGeometry(): boolean;
}

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

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

/**
* vtkImageSlice 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 vtkImageSlice: {
newInstance: typeof newInstance;
extend: typeof extend;
}
export default vtkImageSlice;
index.js
import { vec3, mat4 } from 'gl-matrix';
import macro from 'vtk.js/Sources/macro';
import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox';
import vtkProp3D from 'vtk.js/Sources/Rendering/Core/Prop3D';
import vtkImageProperty from 'vtk.js/Sources/Rendering/Core/ImageProperty';

const { vtkDebugMacro } = macro;

// ----------------------------------------------------------------------------
// vtkImageSlice methods
// ----------------------------------------------------------------------------

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

publicAPI.getActors = () => publicAPI;
publicAPI.getImages = () => publicAPI;

publicAPI.getIsOpaque = () => {
if (model.forceOpaque) {
return true;
}
if (model.forceTranslucent) {
return false;
}
// make sure we have a property
if (!model.property) {
// force creation of a property
publicAPI.getProperty();
}

let isOpaque = model.property.getOpacity() >= 1.0;

// are we using an opaque scalar array, if any?
isOpaque = isOpaque && (!model.mapper || model.mapper.getIsOpaque());

return isOpaque;
};

// Always render during opaque pass, to keep the behavior
// predictable and because depth-peeling kills alpha-blending.
// In the future, the Renderer should render images in layers,
// i.e. where each image will have a layer number assigned to it,
// and the Renderer will do the images in their own pass.
publicAPI.hasTranslucentPolygonalGeometry = () => false;

publicAPI.makeProperty = vtkImageProperty.newInstance;

publicAPI.getProperty = () => {
if (model.property === null) {
model.property = publicAPI.makeProperty();
}
return model.property;
};

publicAPI.getBounds = () => {
if (model.mapper === null) {
return model.bounds;
}

// Check for the special case when the mapper's bounds are unknown
const bds = model.mapper.getBounds();
if (!bds || bds.length !== 6) {
return bds;
}

// Check for the special case when the actor is empty.
if (bds[0] > bds[1]) {
model.mapperBounds = bds.concat(); // copy the mapper's bounds
model.bounds = [1, -1, 1, -1, 1, -1];
model.boundsMTime.modified();
return bds;
}

// Check if we have cached values for these bounds - we cache the
// values returned by model.mapper.getBounds() and we store the time
// of caching. If the values returned this time are different, or
// the modified time of this class is newer than the cached time,
// then we need to rebuild.
const zip = (rows) => rows[0].map((_, c) => rows.map((row) => row[c]));
if (
!model.mapperBounds ||
!zip([bds, model.mapperBounds]).reduce(
(a, b) => a && b[0] === b[1],
true
) ||
publicAPI.getMTime() > model.boundsMTime.getMTime()
) {
vtkDebugMacro('Recomputing bounds...');
model.mapperBounds = bds.map((x) => x);
const bbox = [];
vtkBoundingBox.getCorners(bds, bbox);

publicAPI.computeMatrix();
const tmp4 = new Float64Array(16);
mat4.transpose(tmp4, model.matrix);
bbox.forEach((pt) => vec3.transformMat4(pt, pt, tmp4));

/* eslint-disable no-multi-assign */
model.bounds[0] = model.bounds[2] = model.bounds[4] = Number.MAX_VALUE;
model.bounds[1] = model.bounds[3] = model.bounds[5] = -Number.MAX_VALUE;
/* eslint-enable no-multi-assign */
model.bounds = model.bounds.map((d, i) =>
i % 2 === 0
? bbox.reduce((a, b) => (a > b[i / 2] ? b[i / 2] : a), d)
: bbox.reduce((a, b) => (a < b[(i - 1) / 2] ? b[(i - 1) / 2] : a), d)
);
model.boundsMTime.modified();
}
return model.bounds;
};

publicAPI.getBoundsForSlice = (slice, thickness = 0) => {
// Check for the special case when the mapper's bounds are unknown
const bds = model.mapper.getBoundsForSlice(slice, thickness);
if (!bds || bds.length !== 6) {
return bds;
}

// Check for the special case when the actor is empty.
if (bds[0] > bds[1]) {
return bds;
}

const bbox = [];
vtkBoundingBox.getCorners(bds, bbox);
publicAPI.computeMatrix();
const tmp4 = new Float64Array(16);
mat4.transpose(tmp4, model.matrix);
bbox.forEach((pt) => vec3.transformMat4(pt, pt, tmp4));

let newBounds = [
Number.MAX_VALUE,
-Number.MAX_VALUE,
Number.MAX_VALUE,
-Number.MAX_VALUE,
Number.MAX_VALUE,
-Number.MAX_VALUE,
];
newBounds = newBounds.map((d, i) =>
i % 2 === 0
? bbox.reduce((a, b) => (a > b[i / 2] ? b[i / 2] : a), d)
: bbox.reduce((a, b) => (a < b[(i - 1) / 2] ? b[(i - 1) / 2] : a), d)
);
return newBounds;
};

//----------------------------------------------------------------------------
// Get the minimum X bound
publicAPI.getMinXBound = () => {
publicAPI.getBounds();
return model.bounds[0];
};

// Get the maximum X bound
publicAPI.getMaxXBound = () => {
publicAPI.getBounds();
return model.bounds[1];
};

// Get the minimum Y bound
publicAPI.getMinYBound = () => {
publicAPI.getBounds();
return model.bounds[2];
};

// Get the maximum Y bound
publicAPI.getMaxYBound = () => {
publicAPI.getBounds();
return model.bounds[3];
};

// Get the minimum Z bound
publicAPI.getMinZBound = () => {
publicAPI.getBounds();
return model.bounds[4];
};

// Get the maximum Z bound
publicAPI.getMaxZBound = () => {
publicAPI.getBounds();
return model.bounds[5];
};

publicAPI.getMTime = () => {
let mt = model.mtime;
if (model.property !== null) {
const time = model.property.getMTime();
mt = time > mt ? time : mt;
}

return mt;
};

publicAPI.getRedrawMTime = () => {
let mt = model.mtime;
if (model.mapper !== null) {
let time = model.mapper.getMTime();
mt = time > mt ? time : mt;
if (model.mapper.getInput() !== null) {
// FIXME !!! getInputAlgorithm / getInput
model.mapper.getInputAlgorithm().update();
time = model.mapper.getInput().getMTime();
mt = time > mt ? time : mt;
}
}
if (model.property !== null) {
let time = model.property.getMTime();
mt = time > mt ? time : mt;
if (model.property.getRGBTransferFunction() !== null) {
time = model.property.getRGBTransferFunction().getMTime();
mt = time > mt ? time : mt;
}
}
return mt;
};

publicAPI.getSupportsSelection = () =>
model.mapper ? model.mapper.getSupportsSelection() : false;
}

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

const DEFAULT_VALUES = {
mapper: null,
property: null,

bounds: [1, -1, 1, -1, 1, -1],
};

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

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

// Inheritance
vtkProp3D.extend(publicAPI, model, initialValues);

// vtkTimeStamp
model.boundsMTime = {};
macro.obj(model.boundsMTime);

// Build VTK API
macro.set(publicAPI, model, ['property']);
macro.setGet(publicAPI, model, ['mapper']);
macro.getArray(publicAPI, model, ['bounds'], 6);

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

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

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

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

export default { newInstance, extend };