VolumeProperty

Introduction

vtkVolumeProperty is used to represent common properties associated
with volume rendering. This includes properties for determining the type
of interpolation to use when sampling a volume, the color of a volume,
the scalar opacity of a volume, the gradient opacity of a volume, and the
shading parameters of a volume.

When the scalar opacity or the gradient opacity of a volume is not set,
then the function is defined to be a constant value of 1.0. When a
scalar and gradient opacity are both set simultaneously, then the opacity
is defined to be the product of the scalar opacity and gradient opacity
transfer functions.

Most properties can be set per “component” for volume mappers that
support multiple independent components. If you are using 2 component
data as LV or 4 component data as RGBV (as specified in the mapper)
only the first scalar opacity and gradient opacity transfer functions
will be used (and all color functions will be ignored). Omitting the
index parameter on the Set/Get methods will access index = 0.

When independent components is turned on, a separate feature (useful
for volume rendering labelmaps) is available. By default all components
have an “opacityMode” of FRACTIONAL, which results in the usual
addition of that components scalar opacity function value to the final
opacity of the fragment. By setting one or more components to have a
PROPORTIONAL “opacityMode” instead, the scalar opacity lookup value
for those components will not be used to adjust the fragment opacity,
but rather used to multiply the color of that fragment. This kind of
rendering makes sense for labelmap components because the gradient of
those fields is meaningless and should not be used in opacity
computation. At the same time, multiplying the color value by the
piecewise scalar opacity function value provides an opportunity to
design piecewise constant opacity functions (step functions) that can
highlight any subset of label values.

vtkColorTransferFunction is a color mapping in RGB or HSV space that
uses piecewise hermite functions to allow interpolation that can be
piecewise constant, piecewise linear, or somewhere in-between
(a modified piecewise hermite function that squishes the function
according to a sharpness parameter). The function also allows for
the specification of the midpoint (the place where the function
reaches the average of the two bounding nodes) as a normalize distance
between nodes.

See the description of class vtkPiecewiseFunction for an explanation of
midpoint and sharpness.

Usage

// create color and opacity transfer functions
const ctfun = vtkColorTransferFunction.newInstance();
ctfun.addRGBPoint(200.0, 1.0, 1.0, 1.0);
ctfun.addRGBPoint(2000.0, 0.0, 0.0, 0.0);
const ofun = vtkPiecewiseFunction.newInstance();
ofun.addPoint(200.0, 0.0);
ofun.addPoint(1200.0, 0.2);
ofun.addPoint(4000.0, 0.4);

// set them on the property
volume.getProperty().setRGBTransferFunction(0, ctfun);
volume.getProperty().setScalarOpacity(0, ofun);
volume.getProperty().setScalarOpacityUnitDistance(0, 4.5);
volume.getProperty().setInterpolationTypeToLinear();

Methods

extend

Method use to decorate a given object (publicAPI+model) with vtkVolumeProperty 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 IVolumePropertyInitialValues No (default: {})

getAmbient

Get the ambient lighting coefficient.

getColorChannels

Argument Type Required Description
index Number Yes

getColorMixPreset

getComponentWeight

Get the scalar component weights.

Argument Type Required Description
index Number Yes

getDiffuse

Get the diffuse lighting coefficient.

getForceNearestInterpolation

Argument Type Required Description
index Number Yes

getGradientOpacityMaximumOpacity

Argument Type Required Description
index Number Yes

getGradientOpacityMaximumValue

Argument Type Required Description
index Number Yes

getGradientOpacityMinimumOpacity

Argument Type Required Description
index Number Yes

getGradientOpacityMinimumValue

Argument Type Required Description
index Number Yes

getGrayTransferFunction

Get the currently set gray transfer function. Create one if none set.

Argument Type Required Description
index Number Yes

getIndependentComponents

getInterpolationType

Get the interpolation type for sampling a volume.

getInterpolationTypeAsString

Get the interpolation type for sampling a volume as a string.

getLabelOutlineThickness

gets the label outline thickness

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? …

getOpacityMode

getRGBTransferFunction

Get the currently set RGB transfer function. Create one if none set.

Argument Type Required Description
index Number Yes

getScalarOpacity

Get the scalar opacity transfer function. Create one if none set.

Argument Type Required Description
index Number Yes

getScalarOpacityUnitDistance

Get the unit distance on which the scalar opacity transfer function is defined.

Argument Type Required Description
index Number Yes

getShade

Get the shading of a volume.

getSpecular

getSpecularPower

Get the specular power.

getUseGradientOpacity

Argument Type Required Description
index Number Yes

getUseLabelOutline

newInstance

Method use to create a new instance of vtkVolumeProperty

setAmbient

Set the ambient lighting coefficient.

Argument Type Required Description
ambient Number Yes The ambient lighting coefficient.

setColorMixPreset

Set the color mix code to a preset value
Set to null to use no preset
See the test testColorMix for an example on how to use this preset.

If set to CUSTOM, a tag //VTK::CustomColorMix is made available to the
user who can user shader replacements to put its own code. The given code
will be used to mix the colors from each component.
Each component is available as a rgba vec4: comp0, comp1
There are other useful functions or variable available. To find them,
see //VTK::CustomComponentsColorMix::Impl tag in vtkVolumeFS.glsl.

setComponentWeight

Set the scalar component weights.

Argument Type Required Description
index Number Yes
value Number Yes

setDiffuse

Set the diffuse lighting coefficient.

Argument Type Required Description
diffuse Number Yes The diffuse lighting coefficient.

setForceNearestInterpolation

Force the nearest neighbor interpolation of one or more of the components
The interpolation for the rest of the volume is set using setInterpolationType

Argument Type Required Description
index Number Yes
value Boolean Yes

setGradientOpacityMaximumOpacity

Argument Type Required Description
index Number Yes
value Number Yes

setGradientOpacityMaximumValue

Argument Type Required Description
index Number Yes
value Number Yes

setGradientOpacityMinimumOpacity

Argument Type Required Description
index Number Yes
value Number Yes

setGradientOpacityMinimumValue

Argument Type Required Description
index Number Yes
value Number Yes

setGrayTransferFunction

Set the color of a volume to a gray transfer function

Argument Type Required Description
index Number Yes
func vtkPiecewiseFunction Yes

setIndependentComponents

Does the data have independent components, or do some define color only?
If IndependentComponents is On (the default) then each component will be
independently passed through a lookup table to determine RGBA, shaded.

Some volume Mappers can handle 1 to 4 component unsigned char or unsigned
short data (see each mapper header file to determine functionality). If
IndependentComponents is Off, then you must have either 2 or 4 component
data. For 2 component data, the first is passed through the first color
transfer function and the second component is passed through the first
scalar opacity (and gradient opacity) transfer function. Normals will be
generated off of the second component. When using gradient based opacity
modulation, the gradients are computed off of the second component. For 4
component data, the first three will directly represent RGB (no lookup
table). The fourth component will be passed through the first scalar
opacity transfer function for opacity and first gradient opacity transfer
function for gradient based opacity modulation. Normals will be generated
from the fourth component. When using gradient based opacity modulation,
the gradients are computed off of the fourth component.

Argument Type Required Description
independentComponents Boolean Yes

setInterpolationType

Set the interpolation type for sampling a volume.

Argument Type Required Description
interpolationType InterpolationType Yes

setInterpolationTypeToFastLinear

Set interpolation type to FAST_LINEAR

setInterpolationTypeToLinear

Set interpolation type to LINEAR

setInterpolationTypeToNearest

Set interpolation type to NEAREST

setLabelOutlineThickness

It will set the label outline thickness for the labelmaps. It can accept
a single number or an array of numbers. If a single number is provided,
it will be used for all the segments. If an array is provided, it indicates
the thickness for each segment index. For instance if you have a labelmap
with 3 segments (0: background 1: liver 2: tumor), you can set the thickness
to [2,4] to have a thicker outline for the tumor (thickness 4). It should be
noted that the thickness is in pixel and also the first array value will
control the default thickness for all labels when 0 or not specified.

Argument Type Required Description
labelOutlineThickness Number or Array. Yes

setOpacityMode

Argument Type Required Description
index Number Yes
value Number Yes

setRGBTransferFunction

Set the color of a volume to an RGB transfer function

Argument Type Required Description
index Number Yes
func vtkColorTransferFunction Yes

setScalarOpacity

Set the scalar opacity of a volume to a transfer function

Argument Type Required Description
index Number Yes
func vtkPiecewiseFunction Yes

setScalarOpacityUnitDistance

Set the unit distance on which the scalar opacity transfer function is
defined.

Argument Type Required Description
index Number Yes
value Number Yes

setShade

Set the shading of a volume.

If shading is turned off, then the mapper for the volume will not perform
shading calculations. If shading is turned on, the mapper may perform
shading calculations - in some cases shading does not apply (for example,
in a maximum intensity projection) and therefore shading will not be
performed even if this flag is on. For a compositing type of mapper,
turning shading off is generally the same as setting ambient=1,
diffuse=0, specular=0. Shading can be independently turned on/off per
component.

Argument Type Required Description
shade Boolean Yes

setSpecular

Argument Type Required Description
specular Number Yes

setSpecularPower

Set the specular power.

Argument Type Required Description
specularPower Number Yes

setUseGradientOpacity

Argument Type Required Description
index Number Yes
value Boolean Yes

setUseLabelOutline

Argument Type Required Description
useLabelOutline Boolean Yes

Source

Constants.d.ts
export declare enum InterpolationType {
NEAREST = 0,
LINEAR = 1,
FAST_LINEAR = 2,
}

export declare enum OpacityMode {
FRACTIONAL = 0,
PROPORTIONAL = 1,
}

export declare enum ColorMixPreset {
// Add a `//VTK::CustomColorMix` tag to the Fragment shader
// See usage in file `testColorMix` and in function `setColorMixPreset`
CUSTOM = 0,

// Two components preset
// Out color: sum of colors weighted by opacity
// Out opacity: sum of opacities
ADDITIVE = 1,

// Two components preset
// Out color: color of the first component, colorized by second component with an intensity that is the second component's opacity
// Out opacity: opacity of the first component
COLORIZE = 2,
}

declare const _default: {
InterpolationType: typeof InterpolationType;
OpacityMode: typeof OpacityMode;
ColorMixPreset: typeof ColorMixPreset;
};
export default _default;
Constants.js
export const InterpolationType = {
NEAREST: 0,
LINEAR: 1,
FAST_LINEAR: 2,
};

export const OpacityMode = {
FRACTIONAL: 0,
PROPORTIONAL: 1,
};

export const ColorMixPreset = {
CUSTOM: 0,
ADDITIVE: 1,
COLORIZE: 2,
};

export default {
InterpolationType,
OpacityMode,
ColorMixPreset,
};
index.d.ts
import vtkPiecewiseFunction from '../../../Common/DataModel/PiecewiseFunction';
import { vtkObject } from '../../../interfaces';
import { Nullable } from '../../../types';
import vtkColorTransferFunction from '../ColorTransferFunction';
import { ColorMixPreset, InterpolationType, OpacityMode } from './Constants';

export interface IVolumePropertyInitialValues {
independentComponents?: boolean;
shade?: boolean;
ambient?: number;
diffuse?: number;
specular?: number;
specularPower?: number;
useLabelOutline?: boolean;
labelOutlineThickness?: number | number[];
}

export interface vtkVolumeProperty extends vtkObject {
/**
* Get the ambient lighting coefficient.
*/
getAmbient(): 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;

/**
*
* @param {Number} index
*/
getColorChannels(index: number): number;

/**
* Get the diffuse lighting coefficient.
*/
getDiffuse(): number;

/**
*
* @param {Number} index
*/
getGradientOpacityMaximumOpacity(index: number): number;

/**
*
* @param {Number} index
*/
getGradientOpacityMaximumValue(index: number): number;

/**
*
* @param {Number} index
*/
getGradientOpacityMinimumOpacity(index: number): number;

/**
*
* @param {Number} index
*/
getGradientOpacityMinimumValue(index: number): number;

/**
*
*/
getColorMixPreset(): Nullable<ColorMixPreset>;

/**
*
*/
getIndependentComponents(): boolean;

/**
* Get the unit distance on which the scalar opacity transfer function is defined.
* @param {Number} index
*/
getScalarOpacityUnitDistance(index: number): number;

/**
* Get the currently set gray transfer function. Create one if none set.
* @param {Number} index
*/
getGrayTransferFunction(index: number): vtkPiecewiseFunction;

/**
*
* @default FRACTIONAL
*/
getOpacityMode(index: number): OpacityMode;

/**
* gets the label outline thickness
*/
getLabelOutlineThickness(): number;

/**
* Get the currently set RGB transfer function. Create one if none set.
* @param {Number} index
*/
getRGBTransferFunction(index: number): vtkColorTransferFunction;

/**
* Get the scalar opacity transfer function. Create one if none set.
* @param {Number} index
*/
getScalarOpacity(index: number): vtkPiecewiseFunction;

/**
* Get the shading of a volume.
*/
getShade(): boolean;

/**
*
*/
getSpecular(): number;

/**
* Get the specular power.
*/
getSpecularPower(): number;

/**
*
* @param {Number} index
*/
getUseGradientOpacity(index: number): boolean;

/**
* @see setForceNearestInterpolation
* @param {Number} index
*/
getForceNearestInterpolation(index: number): boolean;

/**
*
*/
getUseLabelOutline(): boolean;

/**
* Set the ambient lighting coefficient.
* @param {Number} ambient The ambient lighting coefficient.
*/
setAmbient(ambient: number): boolean;

/**
* Set the diffuse lighting coefficient.
* @param {Number} diffuse The diffuse lighting coefficient.
*/
setDiffuse(diffuse: number): boolean;

/**
*
* @param {Number} index
* @param {Number} value
*/
setGradientOpacityMaximumOpacity(index: number, value: number): boolean;

/**
*
* @param {Number} index
* @param {Number} value
*/
setGradientOpacityMaximumValue(index: number, value: number): boolean;

/**
*
* @param {Number} index
* @param {Number} value
*/
setGradientOpacityMinimumOpacity(index: number, value: number): boolean;

/**
*
* @param {Number} index
* @param {Number} value
*/
setGradientOpacityMinimumValue(index: number, value: number): boolean;

/**
* Set the color of a volume to a gray transfer function
* @param {Number} index
* @param {vtkPiecewiseFunction} func
*/
setGrayTransferFunction(index: number, func: vtkPiecewiseFunction): boolean;

/**
* Set the color mix code to a preset value
* Set to null to use no preset
* See the test `testColorMix` for an example on how to use this preset.
*
* If set to `CUSTOM`, a tag `//VTK::CustomColorMix` is made available to the
* user who can user shader replacements to put its own code. The given code
* will be used to mix the colors from each component.
* Each component is available as a rgba vec4: `comp0`, `comp1`...
* There are other useful functions or variable available. To find them,
* see `//VTK::CustomComponentsColorMix::Impl` tag in `vtkVolumeFS.glsl`.
*/
setColorMixPreset(preset: Nullable<ColorMixPreset>): boolean;

/**
* Does the data have independent components, or do some define color only?
* If IndependentComponents is On (the default) then each component will be
* independently passed through a lookup table to determine RGBA, shaded.
*
* Some volume Mappers can handle 1 to 4 component unsigned char or unsigned
* short data (see each mapper header file to determine functionality). If
* IndependentComponents is Off, then you must have either 2 or 4 component
* data. For 2 component data, the first is passed through the first color
* transfer function and the second component is passed through the first
* scalar opacity (and gradient opacity) transfer function. Normals will be
* generated off of the second component. When using gradient based opacity
* modulation, the gradients are computed off of the second component. For 4
* component data, the first three will directly represent RGB (no lookup
* table). The fourth component will be passed through the first scalar
* opacity transfer function for opacity and first gradient opacity transfer
* function for gradient based opacity modulation. Normals will be generated
* from the fourth component. When using gradient based opacity modulation,
* the gradients are computed off of the fourth component.
* @param {Boolean} independentComponents
*/
setIndependentComponents(independentComponents: boolean): boolean;

/**
* It will set the label outline thickness for the labelmaps. It can accept
* a single number or an array of numbers. If a single number is provided,
* it will be used for all the segments. If an array is provided, it indicates
* the thickness for each segment index. For instance if you have a labelmap
* with 3 segments (0: background 1: liver 2: tumor), you can set the thickness
* to [2,4] to have a thicker outline for the tumor (thickness 4). It should be
* noted that the thickness is in pixel and also the first array value will
* control the default thickness for all labels when 0 or not specified.
*
* @param {Number | Number[]} labelOutlineThickness
*/
setLabelOutlineThickness(labelOutlineThickness: number | number[]): boolean;

/**
*
* @param {Number} index
* @param {Number} value
*/
setOpacityMode(index: number, value: number): boolean;

/**
* Set the unit distance on which the scalar opacity transfer function is
* defined.
* @param {Number} index
* @param {Number} value
*/
setScalarOpacityUnitDistance(index: number, value: number): boolean;

/**
* Set the shading of a volume.
*
* If shading is turned off, then the mapper for the volume will not perform
* shading calculations. If shading is turned on, the mapper may perform
* shading calculations - in some cases shading does not apply (for example,
* in a maximum intensity projection) and therefore shading will not be
* performed even if this flag is on. For a compositing type of mapper,
* turning shading off is generally the same as setting ambient=1,
* diffuse=0, specular=0. Shading can be independently turned on/off per
* component.
* @param {Boolean} shade
*/
setShade(shade: boolean): boolean;

/**
*
* @param {Number} specular
*/
setSpecular(specular: number): boolean;

/**
* Set the specular power.
* @param {Number} specularPower
*/
setSpecularPower(specularPower: number): boolean;

/**
*
* @param {Number} index
* @param {Boolean} value
*/
setUseGradientOpacity(index: number, value: boolean): boolean;

/**
*
* @param {Boolean} useLabelOutline
*/
setUseLabelOutline(useLabelOutline: boolean): boolean;

/**
* Set the color of a volume to an RGB transfer function
* @param {Number} index
* @param {vtkColorTransferFunction} func
*/
setRGBTransferFunction(
index: number,
func?: Nullable<vtkColorTransferFunction>
): boolean;

/**
* Set the scalar opacity of a volume to a transfer function
* @param {Number} index
* @param {vtkPiecewiseFunction} func
*/
setScalarOpacity(
index: number,
func?: Nullable<vtkPiecewiseFunction>
): boolean;

/**
* Set the scalar component weights.
* @param {Number} index
* @param {Number} value
*/
setComponentWeight(index: number, value: number): boolean;

/**
* Force the nearest neighbor interpolation of one or more of the components
* The interpolation for the rest of the volume is set using `setInterpolationType`
* @see setInterpolationType
* @param {Number} index
* @param {Boolean} value
*/
setForceNearestInterpolation(index: number, value: boolean): boolean;

/**
* Get the scalar component weights.
* @param {Number} index
*/
getComponentWeight(index: number): number;

/**
* Set the interpolation type for sampling a volume.
* @param {InterpolationType} interpolationType
*/
setInterpolationType(interpolationType: InterpolationType): boolean;

/**
* Set interpolation type to NEAREST
*/
setInterpolationTypeToNearest(): boolean;

/**
* Set interpolation type to LINEAR
*/
setInterpolationTypeToLinear(): boolean;

/**
* Set interpolation type to FAST_LINEAR
*/
setInterpolationTypeToFastLinear(): boolean;

/**
* Get the interpolation type for sampling a volume.
*/
getInterpolationType(): InterpolationType;

/**
* Get the interpolation type for sampling a volume as a string.
*/
getInterpolationTypeAsString(): string;
}

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

/**
* Method use to create a new instance of vtkVolumeProperty
*/
export function newInstance(
initialValues?: IVolumePropertyInitialValues
): vtkVolumeProperty;

/**
* vtkVolumeProperty is used to represent common properties associated
* with volume rendering. This includes properties for determining the type
* of interpolation to use when sampling a volume, the color of a volume,
* the scalar opacity of a volume, the gradient opacity of a volume, and the
* shading parameters of a volume.

* When the scalar opacity or the gradient opacity of a volume is not set,
* then the function is defined to be a constant value of 1.0. When a
* scalar and gradient opacity are both set simultaneously, then the opacity
* is defined to be the product of the scalar opacity and gradient opacity
* transfer functions.
*
* Most properties can be set per "component" for volume mappers that
* support multiple independent components. If you are using 2 component
* data as LV or 4 component data as RGBV (as specified in the mapper)
* only the first scalar opacity and gradient opacity transfer functions
* will be used (and all color functions will be ignored). Omitting the
* index parameter on the Set/Get methods will access index = 0.
*
* When independent components is turned on, a separate feature (useful
* for volume rendering labelmaps) is available. By default all components
* have an "opacityMode" of `FRACTIONAL`, which results in the usual
* addition of that components scalar opacity function value to the final
* opacity of the fragment. By setting one or more components to have a
* `PROPORTIONAL` "opacityMode" instead, the scalar opacity lookup value
* for those components will not be used to adjust the fragment opacity,
* but rather used to multiply the color of that fragment. This kind of
* rendering makes sense for labelmap components because the gradient of
* those fields is meaningless and should not be used in opacity
* computation. At the same time, multiplying the color value by the
* piecewise scalar opacity function value provides an opportunity to
* design piecewise constant opacity functions (step functions) that can
* highlight any subset of label values.
*
* vtkColorTransferFunction is a color mapping in RGB or HSV space that
* uses piecewise hermite functions to allow interpolation that can be
* piecewise constant, piecewise linear, or somewhere in-between
* (a modified piecewise hermite function that squishes the function
* according to a sharpness parameter). The function also allows for
* the specification of the midpoint (the place where the function
* reaches the average of the two bounding nodes) as a normalize distance
* between nodes.
*
* See the description of class vtkPiecewiseFunction for an explanation of
* midpoint and sharpness.
*
* @example
* ```js
* // create color and opacity transfer functions
* const ctfun = vtkColorTransferFunction.newInstance();
* ctfun.addRGBPoint(200.0, 1.0, 1.0, 1.0);
* ctfun.addRGBPoint(2000.0, 0.0, 0.0, 0.0);
* const ofun = vtkPiecewiseFunction.newInstance();
* ofun.addPoint(200.0, 0.0);
* ofun.addPoint(1200.0, 0.2);
* ofun.addPoint(4000.0, 0.4);
*
* // set them on the property
* volume.getProperty().setRGBTransferFunction(0, ctfun);
* volume.getProperty().setScalarOpacity(0, ofun);
* volume.getProperty().setScalarOpacityUnitDistance(0, 4.5);
* volume.getProperty().setInterpolationTypeToLinear();
* ```
*/
export declare const vtkVolumeProperty: {
newInstance: typeof newInstance;
extend: typeof extend;
InterpolationType: typeof InterpolationType;
OpacityMode: typeof OpacityMode;
};
export default vtkVolumeProperty;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction';
import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction';
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeProperty/Constants';

const { InterpolationType, OpacityMode } = Constants;
const { vtkErrorMacro } = macro;

const VTK_MAX_VRCOMP = 4;

// ----------------------------------------------------------------------------
// vtkVolumeProperty methods
// ----------------------------------------------------------------------------

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

publicAPI.getMTime = () => {
let mTime = model.mtime;
let time;

for (let index = 0; index < VTK_MAX_VRCOMP; index++) {
// Color MTimes
if (model.componentData[index].colorChannels === 1) {
if (model.componentData[index].grayTransferFunction) {
// time that Gray transfer function was last modified
time = model.componentData[index].grayTransferFunction.getMTime();
mTime = mTime > time ? mTime : time;
}
} else if (model.componentData[index].colorChannels === 3) {
if (model.componentData[index].rGBTransferFunction) {
// time that RGB transfer function was last modified
time = model.componentData[index].rGBTransferFunction.getMTime();
mTime = mTime > time ? mTime : time;
}
}

// Opacity MTimes
if (model.componentData[index].scalarOpacity) {
// time that Scalar opacity transfer function was last modified
time = model.componentData[index].scalarOpacity.getMTime();
mTime = mTime > time ? mTime : time;
}

if (model.componentData[index].gradientOpacity) {
if (!model.componentData[index].disableGradientOpacity) {
// time that Gradient opacity transfer function was last modified
time = model.componentData[index].gradientOpacity.getMTime();
mTime = mTime > time ? mTime : time;
}
}
}

return mTime;
};

publicAPI.getColorChannels = (index) => {
if (index < 0 || index > 3) {
vtkErrorMacro('Bad index - must be between 0 and 3');
return 0;
}

return model.componentData[index].colorChannels;
};

// Set the color of a volume to a gray transfer function
publicAPI.setGrayTransferFunction = (index = 0, func = null) => {
let modified = false;
if (model.componentData[index].grayTransferFunction !== func) {
model.componentData[index].grayTransferFunction = func;
modified = true;
}

if (model.componentData[index].colorChannels !== 1) {
model.componentData[index].colorChannels = 1;
modified = true;
}

if (modified) {
publicAPI.modified();
}
return modified;
};

// Get the currently set gray transfer function. Create one if none set.
publicAPI.getGrayTransferFunction = (index = 0) => {
if (model.componentData[index].grayTransferFunction === null) {
model.componentData[index].grayTransferFunction =
vtkPiecewiseFunction.newInstance();
model.componentData[index].grayTransferFunction.addPoint(0, 0.0);
model.componentData[index].grayTransferFunction.addPoint(1024, 1.0);
if (model.componentData[index].colorChannels !== 1) {
model.componentData[index].colorChannels = 1;
}
publicAPI.modified();
}

return model.componentData[index].grayTransferFunction;
};

// Set the color of a volume to an RGB transfer function
publicAPI.setRGBTransferFunction = (index = 0, func = null) => {
let modified = false;
if (model.componentData[index].rGBTransferFunction !== func) {
model.componentData[index].rGBTransferFunction = func;
modified = true;
}

if (model.componentData[index].colorChannels !== 3) {
model.componentData[index].colorChannels = 3;
modified = true;
}

if (modified) {
publicAPI.modified();
}
return modified;
};

// Get the currently set RGB transfer function. Create one if none set.
publicAPI.getRGBTransferFunction = (index = 0) => {
if (model.componentData[index].rGBTransferFunction === null) {
model.componentData[index].rGBTransferFunction =
vtkColorTransferFunction.newInstance();
model.componentData[index].rGBTransferFunction.addRGBPoint(
0,
0.0,
0.0,
0.0
);
model.componentData[index].rGBTransferFunction.addRGBPoint(
1024,
1.0,
1.0,
1.0
);
if (model.componentData[index].colorChannels !== 3) {
model.componentData[index].colorChannels = 3;
}
publicAPI.modified();
}

return model.componentData[index].rGBTransferFunction;
};

// Set the scalar opacity of a volume to a transfer function
publicAPI.setScalarOpacity = (index = 0, func = null) => {
if (model.componentData[index].scalarOpacity !== func) {
model.componentData[index].scalarOpacity = func;
publicAPI.modified();
return true;
}
return false;
};

// Get the scalar opacity transfer function. Create one if none set.
publicAPI.getScalarOpacity = (index = 0) => {
if (model.componentData[index].scalarOpacity === null) {
model.componentData[index].scalarOpacity =
vtkPiecewiseFunction.newInstance();
model.componentData[index].scalarOpacity.addPoint(0, 1.0);
model.componentData[index].scalarOpacity.addPoint(1024, 1.0);
publicAPI.modified();
}

return model.componentData[index].scalarOpacity;
};

publicAPI.setComponentWeight = (index = 0, value = 1) => {
if (index < 0 || index >= VTK_MAX_VRCOMP) {
vtkErrorMacro('Invalid index');
return false;
}

const val = Math.min(1, Math.max(0, value));
if (model.componentData[index].componentWeight !== val) {
model.componentData[index].componentWeight = val;
publicAPI.modified();
return true;
}
return false;
};

publicAPI.getComponentWeight = (index = 0) => {
if (index < 0 || index >= VTK_MAX_VRCOMP) {
vtkErrorMacro('Invalid index');
return 0.0;
}

return model.componentData[index].componentWeight;
};

publicAPI.setInterpolationTypeToNearest = () =>
publicAPI.setInterpolationType(InterpolationType.NEAREST);

publicAPI.setInterpolationTypeToLinear = () =>
publicAPI.setInterpolationType(InterpolationType.LINEAR);

publicAPI.setInterpolationTypeToFastLinear = () =>
publicAPI.setInterpolationType(InterpolationType.FAST_LINEAR);

publicAPI.getInterpolationTypeAsString = () =>
macro.enumToString(InterpolationType, model.interpolationType);

const sets = [
'useGradientOpacity',
'scalarOpacityUnitDistance',
'gradientOpacityMinimumValue',
'gradientOpacityMinimumOpacity',
'gradientOpacityMaximumValue',
'gradientOpacityMaximumOpacity',
'opacityMode',
'forceNearestInterpolation',
];
sets.forEach((val) => {
const cap = macro.capitalize(val);
publicAPI[`set${cap}`] = (index, value) => {
if (model.componentData[index][`${val}`] !== value) {
model.componentData[index][`${val}`] = value;
publicAPI.modified();
return true;
}
return false;
};
});

const gets = [
'useGradientOpacity',
'scalarOpacityUnitDistance',
'gradientOpacityMinimumValue',
'gradientOpacityMinimumOpacity',
'gradientOpacityMaximumValue',
'gradientOpacityMaximumOpacity',
'opacityMode',
'forceNearestInterpolation',
];
gets.forEach((val) => {
const cap = macro.capitalize(val);
publicAPI[`get${cap}`] = (index) => model.componentData[index][`${val}`];
});
}

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

const DEFAULT_VALUES = {
colorMixPreset: null,
independentComponents: true,
interpolationType: InterpolationType.FAST_LINEAR,
shade: false,
ambient: 0.1,
diffuse: 0.7,
specular: 0.2,
specularPower: 10.0,
useLabelOutline: false,
labelOutlineThickness: [1],
labelOutlineOpacity: 1.0,
};

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

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

// Build VTK API
macro.obj(publicAPI, model);

if (!model.componentData) {
model.componentData = [];
for (let i = 0; i < VTK_MAX_VRCOMP; ++i) {
model.componentData.push({
colorChannels: 1,
grayTransferFunction: null,
rGBTransferFunction: null,
scalarOpacity: null,
scalarOpacityUnitDistance: 1.0,
opacityMode: OpacityMode.FRACTIONAL,

gradientOpacityMinimumValue: 0,
gradientOpacityMinimumOpacity: 0.0,
gradientOpacityMaximumValue: 1.0,
gradientOpacityMaximumOpacity: 1.0,
useGradientOpacity: false,

componentWeight: 1.0,
forceNearestInterpolation: false,
});
}
}

macro.setGet(publicAPI, model, [
'colorMixPreset',
'independentComponents',
'interpolationType',
'shade',
'ambient',
'diffuse',
'specular',
'specularPower',
'useLabelOutline',
'labelOutlineOpacity',
]);

macro.setGetArray(publicAPI, model, ['labelOutlineThickness']);

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

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

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

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

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