Mapper

Introduction

vtkMapper is an abstract class to specify interface between data and
graphics primitives. Subclasses of vtkMapper map data through a
lookuptable and control the creation of rendering primitives that
interface to the graphics library. The mapping can be controlled by
supplying a lookup table and specifying a scalar range to map data
through.

There are several important control mechanisms affecting the behavior of
this object. The ScalarVisibility flag controls whether scalar data (if
any) controls the color of the associated actor(s) that refer to the
mapper. The ScalarMode ivar is used to determine whether scalar point data
or cell data is used to color the object. By default, point data scalars
are used unless there are none, then cell scalars are used. Or you can
explicitly control whether to use point or cell scalar data. Finally, the
mapping of scalars through the lookup table varies depending on the
setting of the ColorMode flag. See the documentation for the appropriate
methods for an explanation.

Another important feature of this class is whether to use immediate mode
rendering (ImmediateModeRenderingOn) or display list rendering
(ImmediateModeRenderingOff). If display lists are used, a data structure
is constructed (generally in the rendering library) which can then be
rapidly traversed and rendered by the rendering library. The disadvantage
of display lists is that they require additional memory which may affect
the performance of the system.

Another important feature of the mapper is the ability to shift the
Z-buffer to resolve coincident topology. For example, if you’d like to
draw a mesh with some edges a different color, and the edges lie on the
mesh, this feature can be useful to get nice looking lines. (See the
ResolveCoincidentTopology-related methods.)

Methods

acquireInvertibleLookupTable

canUseTextureMapForColoring

Returns if we can use texture maps for scalar coloring. Note this doesn’t
say we “will” use scalar coloring. It says, if we do use scalar coloring,
we will use a texture.
When rendering multiblock datasets, if any 2 blocks provide different
lookup tables for the scalars, then also we cannot use textures. This case
can be handled if required.

Argument Type Required Description
scalars Yes
cellFlag Yes True when the scalars are per cell instead of per point

clearColorArrays

Call to force a rebuild of color result arrays on next MapScalars.
Necessary when using arrays in the case of multiblock data.

clearInvertibleColor

colorToValue

createDefaultLookupTable

Create default lookup table. Generally used to create one when
none is available with the scalar data.

extend

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

getAbstractScalars

Argument Type Required Description
input Yes
scalarMode ScalarMode Yes
arrayAccessMode Yes
arrayId Yes
arrayName Yes

getAreScalarsMappedFromCells

When scalars are mapped from cells,
there is one color coordinate per cell instead of one per point
in the vtkDataArray getColorCoordinates().
It means that when getAreScalarsMappedFromCells() is true,
the number of tuples in getColorCoordinates() is the number of points,
and when getAreScalarsMappedFromCells() is false,
the number of tuples in getColorCoordinates() is the number of cells.

getArrayAccessMode

getBounds

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

getColorByArrayName

Get the array name to color by.

getColorCoordinates

Provide read access to the color texture coordinate array

getColorMapColors

Provide read access to the color array.

getColorMode

Return the method of coloring scalar data.

getColorModeAsString

Return the method of coloring scalar data.

getColorTextureMap

Provide read access to the color texture array

getCustomShaderAttributes

getFieldDataTupleId

getInterpolateScalarsBeforeMapping

By default, vertex color is used to map colors to a surface.
Colors are interpolated after being mapped.
This option avoids color interpolation by using a one dimensional
texture map for the colors.

getIsOpaque

Check if the mapper does not expect to have translucent geometry. This
may happen when using ColorMode is set to not map scalars i.e. render the
scalar array directly as colors and the scalar array has opacity i.e. alpha
component. Default implementation simply returns true. Note that even if
this method returns true, an actor may treat the geometry as translucent
since a constant translucency is set on the property, for example.

getLookupTable

Get a lookup table for the mapper to use.

getNumberOfColorsInRange

The number of mapped colors in range

getPrimitiveCount

getScalarMode

Return the method for obtaining scalar data.

getScalarModeAsString

Return the method for obtaining scalar data.

getScalarRange

getScalarRangeByReference

getScalarVisibility

Check whether scalar data is used to color objects.

getStatic

Check whether the mapper’s data is static.

getUseLookupTableScalarRange

getViewSpecificProperties

mapScalars

Map the scalars (if there are any scalars and ScalarVisibility is on)
through the lookup table, returning an unsigned char RGBA array. This is
typically done as part of the rendering process. The alpha parameter
allows the blending of the scalars with an additional alpha (typically
which comes from a vtkActor, etc.)
{
rgba: Uint8Array(),
location: 0/1/2, // Points/Cells/Fields
}

Argument Type Required Description
input Yes
alpha Number Yes

newInstance

Method used to create a new instance of vtkMapper

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

setArrayAccessMode

Argument Type Required Description
arrayAccessMode Number Yes

setColorByArrayName

Set the array name to color by.

Argument Type Required Description
colorByArrayName String Yes

setColorMode

Argument Type Required Description
colorMode Number Yes

setColorModeToDefault

Sets colorMode to DEFAULT

setColorModeToDirectScalars

Sets colorMode to DIRECT_SCALARS

setColorModeToMapScalars

Sets colorMode to MAP_SCALARS

setCustomShaderAttributes

Sets point data array names that will be transferred to the VBO

Argument Type Required Description
customShaderAttributes Array. Yes

setFieldDataTupleId

When ScalarMode is set to UseFieldData, set the index of the
tuple by which to color the entire data set. By default, the
index is -1, which means to treat the field data array selected
with SelectColorArray as having a scalar value for each cell.
Indices of 0 or higher mean to use the tuple at the given index
for coloring the entire data set.

Argument Type Required Description
fieldDataTupleI Number Yes

setForceCompileOnly

Argument Type Required Description
forceCompileOnly Number Yes

setInterpolateScalarsBeforeMapping

setLookupTable

Set a lookup table for the mapper to use.

setScalarMode

Control how the filter works with scalar point data and cell attribute
data. By default (ScalarModeToDefault), the filter will use point data,
and if no point data is available, then cell data is used. Alternatively
you can explicitly set the filter to use point data
(ScalarModeToUsePointData) or cell data (ScalarModeToUseCellData).
You can also choose to get the scalars from an array in point field
data (ScalarModeToUsePointFieldData) or cell field data
(ScalarModeToUseCellFieldData). If scalars are coming from a field
data array, you must call SelectColorArray before you call GetColors.

When ScalarMode is set to use Field Data (ScalarModeToFieldData),
you must call SelectColorArray to choose the field data array to
be used to color cells. In this mode, the default behavior is to
treat the field data tuples as being associated with cells. If
the poly data contains triangle strips, the array is expected to
contain the cell data for each mini-cell formed by any triangle
strips in the poly data as opposed to treating them as a single
tuple that applies to the entire strip. This mode can also be
used to color the entire poly data by a single color obtained by
mapping the tuple at a given index in the field data array
through the color map. Use SetFieldDataTupleId() to specify
the tuple index.

Argument Type Required Description
scalarMode Yes

setScalarModeToDefault

Sets scalarMode to DEFAULT

setScalarModeToUseCellData

Sets scalarMode to USE_CELL_DATA

setScalarModeToUseCellFieldData

Sets scalarMode to USE_CELL_FIELD_DATA

setScalarModeToUseFieldData

Sets scalarMode to USE_FIELD_DATA

setScalarModeToUsePointData

Sets scalarMode to USE_POINT_DATA

setScalarModeToUsePointFieldData

Sets scalarMode to USE_POINT_FIELD_DATA

setScalarRange

Specify range in terms of scalar minimum and maximum (smin,smax). These
values are used to map scalars into lookup table. Has no effect when
UseLookupTableScalarRange is true.

Argument Type Required Description
scalarRange Yes

setScalarRange

Specify range in terms of scalar minimum and maximum (smin,smax). These
values are used to map scalars into lookup table. Has no effect when
UseLookupTableScalarRange is true.

Argument Type Required Description
min Yes
max Yes

setScalarRangeFrom

Argument Type Required Description
scalarRange Yes

setScalarVisibility

Turn on/off flag to control whether scalar data is used to color objects.

Argument Type Required Description
scalarVisibility Boolean Yes

setStatic

Turn on/off flag to control whether the mapper’s data is static. Static data
means that the mapper does not propagate updates down the pipeline, greatly
decreasing the time it takes to update many mappers. This should only be
used if the data never changes.

Argument Type Required Description
static Boolean Yes

setUseLookupTableScalarRange

Control whether the mapper sets the lookuptable range based on its
own ScalarRange, or whether it will use the LookupTable ScalarRange
regardless of it’s own setting. By default the Mapper is allowed to set
the LookupTable range, but users who are sharing LookupTables between
mappers/actors will probably wish to force the mapper to use the
LookupTable unchanged.

Argument Type Required Description
useLookupTableScalarRange Boolean Yes

setViewSpecificProperties

If you want to provide specific properties for rendering engines you can use
viewSpecificProperties.

You can go and have a look in the rendering backend of your choice for details
on specific properties.
For example, for OpenGL/WebGL see OpenGL/PolyDataMapper/api.md
If there is no details, viewSpecificProperties is not supported.

Argument Type Required Description
viewSpecificProperties Yes

useInvertibleColorFor

valueToColor

Source

CoincidentTopologyHelper.d.ts
export interface ICoincidentTopology {
factor: number;
offset: number;
}

export enum Resolve {
Off,
PolygonOffset,
}

export interface StaticCoincidentTopologyMethods {
/**
*
* @param {ICoincidentTopology} params
*/
setResolveCoincidentTopologyPolygonOffsetParameters(
params: ICoincidentTopology
): boolean;

/**
*
* @param {ICoincidentTopology} params
*/
setResolveCoincidentTopologyLineOffsetParameters(
params: ICoincidentTopology
): boolean;

/**
*
* @param {ICoincidentTopology} params
*/
setResolveCoincidentTopologyPointOffsetParameters(
params: ICoincidentTopology
): boolean;

/**
*
* @param {Number} factor
* @param {Number} offset
*/
setResolveCoincidentTopologyPolygonOffsetParameters(
factor: number,
offset: number
): 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;

/**
*
*/
getResolveCoincidentTopologyLineOffsetParameters(): ICoincidentTopology;

/**
*
*/
getResolveCoincidentTopologyPointOffsetParameters(): ICoincidentTopology;

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

/**
*
*/
getResolveCoincidentTopologyPolygonOffsetFaces(): ICoincidentTopology;

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

/**
*
* @param mode
*/
setResolveCoincidentTopology(mode?: Resolve): boolean;

/**
*
*/
getResolveCoincidentTopology(): Resolve;

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

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

/**
*
*/
setResolveCoincidentTopologyToPolygonOffset(): boolean;

/**
*
*/
getResolveCoincidentTopologyAsString(): string;
}

export default interface CoincidentTopologyHelper
extends StaticCoincidentTopologyMethods {
/**
*
* @param {ICoincidentTopology} params
*/
setRelativeCoincidentTopologyLineOffsetParameters(
params: ICoincidentTopology
): boolean;

/**
*
* @param {ICoincidentTopology} params
*/
setRelativeCoincidentTopologyPointOffsetParameters(
params: ICoincidentTopology
): boolean;

/**
*
* @param {ICoincidentTopology} params
*/
setRelativeCoincidentTopologyPolygonOffsetParameters(
params: ICoincidentTopology
): 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;

/**
*
*/
getCoincidentTopologyPolygonOffsetParameters(): ICoincidentTopology;

/**
*
*/
getCoincidentTopologyLineOffsetParameters(): ICoincidentTopology;

/**
*
*/
getCoincidentTopologyPointOffsetParameters(): ICoincidentTopology;
}
CoincidentTopologyHelper.js
/* eslint-disable arrow-body-style */
import otherStaticMethods, {
Resolve,
} from 'vtk.js/Sources/Rendering/Core/Mapper/Static';
import macro from 'vtk.js/Sources/macros';

export { Resolve } from 'vtk.js/Sources/Rendering/Core/Mapper/Static';

function addCoincidentTopologyMethods(publicAPI, model, nameList) {
nameList.forEach((item) => {
publicAPI[`get${item.method}`] = () => model[item.key];
publicAPI[`set${item.method}`] = macro.objectSetterMap.object(
publicAPI,
model,
{
name: item.key,
params: ['factor', 'offset'],
}
);
});
}

export const CATEGORIES = ['Polygon', 'Line', 'Point'];

// CoincidentTopology static methods ------------------------------------------

const staticOffsetModel = {
Polygon: { factor: 2, offset: 0 },
Line: { factor: 1, offset: -1 },
Point: { factor: 0, offset: -2 },
};
const noOp = () => undefined;
const staticOffsetAPI = {
modified: noOp,
};

addCoincidentTopologyMethods(
staticOffsetAPI,
staticOffsetModel,
CATEGORIES.map((key) => ({
key,
method: `ResolveCoincidentTopology${key}OffsetParameters`,
}))
);

function implementCoincidentTopologyMethods(publicAPI, model) {
if (model.resolveCoincidentTopology === undefined) {
model.resolveCoincidentTopology = false;
}

macro.setGet(publicAPI, model, ['resolveCoincidentTopology']);

// Relative methods
model.topologyOffset = {
Polygon: { factor: 0, offset: 0 },
Line: { factor: 0, offset: 0 },
Point: { factor: 0, offset: 0 },
};

// Add Static methods to our instance
Object.keys(otherStaticMethods).forEach((methodName) => {
publicAPI[methodName] = otherStaticMethods[methodName];
});
Object.keys(staticOffsetAPI)
.filter((methodName) => methodName !== 'modified') // don't override instance's modified
.forEach((methodName) => {
publicAPI[methodName] = staticOffsetAPI[methodName];
});

addCoincidentTopologyMethods(
publicAPI,
model.topologyOffset,
CATEGORIES.map((key) => ({
key,
method: `RelativeCoincidentTopology${key}OffsetParameters`,
}))
);

publicAPI.getCoincidentTopologyPolygonOffsetParameters = () => {
const globalValue =
staticOffsetAPI.getResolveCoincidentTopologyPolygonOffsetParameters();
const localValue =
publicAPI.getRelativeCoincidentTopologyPolygonOffsetParameters();
return {
factor: globalValue.factor + localValue.factor,
offset: globalValue.offset + localValue.offset,
};
};

publicAPI.getCoincidentTopologyLineOffsetParameters = () => {
const globalValue =
staticOffsetAPI.getResolveCoincidentTopologyLineOffsetParameters();
const localValue =
publicAPI.getRelativeCoincidentTopologyLineOffsetParameters();
return {
factor: globalValue.factor + localValue.factor,
offset: globalValue.offset + localValue.offset,
};
};

publicAPI.getCoincidentTopologyPointOffsetParameter = () => {
const globalValue =
staticOffsetAPI.getResolveCoincidentTopologyPointOffsetParameters();
const localValue =
publicAPI.getRelativeCoincidentTopologyPointOffsetParameters();
return {
factor: globalValue.factor + localValue.factor,
offset: globalValue.offset + localValue.offset,
};
};
}

export default {
implementCoincidentTopologyMethods,
staticOffsetAPI,
otherStaticMethods,
CATEGORIES,
Resolve,
};
Constants.d.ts
export declare enum ColorMode {
DEFAULT = 0,
MAP_SCALARS = 1,
DIRECT_SCALARS = 2,
}

export declare enum ScalarMode {
DEFAULT = 0,
USE_POINT_DATA = 1,
USE_CELL_DATA = 2,
USE_POINT_FIELD_DATA = 3,
USE_CELL_FIELD_DATA = 4,
USE_FIELD_DATA = 5,
}

export declare enum GetArray {
BY_ID = 0,
BY_NAME = 1,
}

declare const _default: {
ColorMode: typeof ColorMode;
ScalarMode: typeof ScalarMode;
GetArray: typeof GetArray;
};
export default _default;
Constants.js
export const ColorMode = {
DEFAULT: 0,
MAP_SCALARS: 1,
DIRECT_SCALARS: 2,
};

export const ScalarMode = {
DEFAULT: 0,
USE_POINT_DATA: 1,
USE_CELL_DATA: 2,
USE_POINT_FIELD_DATA: 3,
USE_CELL_FIELD_DATA: 4,
USE_FIELD_DATA: 5,
};

export const GetArray = {
BY_ID: 0,
BY_NAME: 1,
};

export default {
ColorMode,
GetArray,
ScalarMode,
};
Static.js
export const Resolve = {
Off: 0,
PolygonOffset: 1,
};

let resolveCoincidentTopologyPolygonOffsetFaces = Resolve.PolygonOffset;
let resolveCoincidentTopology = Resolve.Off;

export const RESOLVE_COINCIDENT_TOPOLOGY_MODE = [
'VTK_RESOLVE_OFF',
'VTK_RESOLVE_POLYGON_OFFSET',
];

export function getResolveCoincidentTopologyPolygonOffsetFaces() {
return resolveCoincidentTopologyPolygonOffsetFaces;
}

export function setResolveCoincidentTopologyPolygonOffsetFaces(value) {
const changed = resolveCoincidentTopologyPolygonOffsetFaces === value;
resolveCoincidentTopologyPolygonOffsetFaces = value;
return changed;
}

export function getResolveCoincidentTopology() {
return resolveCoincidentTopology;
}

export function setResolveCoincidentTopology(mode = 0) {
const changed = resolveCoincidentTopology === mode;
resolveCoincidentTopology = mode;
return changed;
}

export function setResolveCoincidentTopologyToDefault() {
return setResolveCoincidentTopology(Resolve.Off);
}

export function setResolveCoincidentTopologyToOff() {
return setResolveCoincidentTopology(Resolve.Off);
}

export function setResolveCoincidentTopologyToPolygonOffset() {
return setResolveCoincidentTopology(Resolve.PolygonOffset);
}

export function getResolveCoincidentTopologyAsString() {
return RESOLVE_COINCIDENT_TOPOLOGY_MODE[resolveCoincidentTopology];
}

export default {
Resolve,
getResolveCoincidentTopologyAsString,
getResolveCoincidentTopologyPolygonOffsetFaces,
getResolveCoincidentTopology,
setResolveCoincidentTopology,
setResolveCoincidentTopologyPolygonOffsetFaces,
setResolveCoincidentTopologyToDefault,
setResolveCoincidentTopologyToOff,
setResolveCoincidentTopologyToPolygonOffset,
};
index.d.ts
import { Bounds, Nullable, Range } from '../../../types';
import vtkAbstractMapper3D, {
IAbstractMapper3DInitialValues,
} from '../AbstractMapper3D';
import { ColorMode, GetArray, ScalarMode } from './Constants';
import vtkDataArray from '../../../Common/Core/DataArray';
import CoincidentTopologyHelper, {
StaticCoincidentTopologyMethods,
} from './CoincidentTopologyHelper';

interface IPrimitiveCount {
points: number;
verts: number;
lines: number;
triangles: number;
}

interface IAbstractScalars {
cellFlag: boolean;
scalars: Nullable<vtkDataArray>;
}

export interface IMapperInitialValues extends IAbstractMapper3DInitialValues {
static?: boolean;
scalarVisibility?: boolean;
scalarRange?: Range;
useLookupTableScalarRange?: boolean;
colorMode?: number;
scalarMode?: number;
arrayAccessMode?: number;
renderTime?: number;
fieldDataTupleId?: number;
interpolateScalarsBeforeMapping?: boolean;
forceCompileOnly?: number;
useInvertibleColors?: boolean;
customShaderAttributes?: any;
}

export interface vtkMapper
extends vtkAbstractMapper3D,
CoincidentTopologyHelper {
/**
*
*/
acquireInvertibleLookupTable(): void;

/**
* Returns if we can use texture maps for scalar coloring. Note this doesn’t
* say we “will” use scalar coloring. It says, if we do use scalar coloring,
* we will use a texture.
* When rendering multiblock datasets, if any 2 blocks provide different
* lookup tables for the scalars, then also we cannot use textures. This case
* can be handled if required.
* @param scalars
* @param cellFlag True when the scalars are per cell instead of per point
*/
canUseTextureMapForColoring(
scalars: vtkDataArray,
cellFlag: boolean
): boolean;

/**
* Call to force a rebuild of color result arrays on next MapScalars.
* Necessary when using arrays in the case of multiblock data.
*/
clearColorArrays(): void;

/**
*
*/
clearInvertibleColor(): void;

/**
*
*/
colorToValue(): void;

/**
* Create default lookup table. Generally used to create one when
* none is available with the scalar data.
*/
createDefaultLookupTable(): void;

/**
*
* @param input
* @param {ScalarMode} scalarMode
* @param arrayAccessMode
* @param arrayId
* @param arrayName
*/
getAbstractScalars(
input: any,
scalarMode: ScalarMode,
arrayAccessMode: number,
arrayId: any,
arrayName: any
): IAbstractScalars;

/**
* When scalars are mapped from cells,
* there is one color coordinate per cell instead of one per point
* in the vtkDataArray getColorCoordinates().
* It means that when getAreScalarsMappedFromCells() is true,
* the number of tuples in getColorCoordinates() is the number of points,
* and when getAreScalarsMappedFromCells() is false,
* the number of tuples in getColorCoordinates() is the number of cells.
*/
getAreScalarsMappedFromCells(): boolean;

/**
*
*/
getArrayAccessMode(): number;

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

/**
* Get the array name to color by.
*/
getColorByArrayName(): Nullable<string>;

/**
* Provide read access to the color texture coordinate array
*/
getColorCoordinates(): Nullable<Float32Array>;

/**
* Provide read access to the color array.
*/
getColorMapColors(): Nullable<Uint8Array>;

/**
* Return the method of coloring scalar data.
*/
getColorMode(): ColorMode;

/**
* Return the method of coloring scalar data.
*/
getColorModeAsString(): string;

/**
* Provide read access to the color texture array
* @todo Check the retun type
*/
getColorTextureMap(): any;

/**
*
* @default []
*/
getCustomShaderAttributes(): any;

/**
*
* @default -1
*/
getFieldDataTupleId(): any;

/**
* By default, vertex color is used to map colors to a surface.
* Colors are interpolated after being mapped.
* This option avoids color interpolation by using a one dimensional
* texture map for the colors.
* @default false
*/
getInterpolateScalarsBeforeMapping(): boolean;

/**
* Check if the mapper does not expect to have translucent geometry. This
* may happen when using ColorMode is set to not map scalars i.e. render the
* scalar array directly as colors and the scalar array has opacity i.e. alpha
* component. Default implementation simply returns true. Note that even if
* this method returns true, an actor may treat the geometry as translucent
* since a constant translucency is set on the property, for example.
*/
getIsOpaque(): boolean;

/**
* Get a lookup table for the mapper to use.
*/
getLookupTable(): any;

/**
*
*/
getPrimitiveCount(): IPrimitiveCount;

/**
* Return the method for obtaining scalar data.
*/
getScalarMode(): number;

/**
* Return the method for obtaining scalar data.
*/
getScalarModeAsString(): string;

/**
*
* @default [0, 1]
*/
getScalarRange(): number[];

/**
*
* @default [0, 1]
*/
getScalarRangeByReference(): number[];

/**
* Check whether scalar data is used to color objects.
* @default true
*/
getScalarVisibility(): boolean;

/**
* Check whether the mapper’s data is static.
* @default false
*/
getStatic(): boolean;

/**
*
* @default false
*/
getUseLookupTableScalarRange(): boolean;

/**
*
* @default null
*/
getViewSpecificProperties(): object;

/**
* The number of mapped colors in range
*/
getNumberOfColorsInRange(): number;

/**
* Map the scalars (if there are any scalars and ScalarVisibility is on)
* through the lookup table, returning an unsigned char RGBA array. This is
* typically done as part of the rendering process. The alpha parameter
* allows the blending of the scalars with an additional alpha (typically
* which comes from a vtkActor, etc.)
* {
* rgba: Uint8Array(),
* location: 0/1/2, // Points/Cells/Fields
* }
* @param input
* @param {Number} alpha
*/
mapScalars(input: any, alpha: number): void;

/**
*
* @param {Number} arrayAccessMode
*/
setArrayAccessMode(arrayAccessMode: number): boolean;

/**
* Set the array name to color by.
* @param {String} colorByArrayName
*/
setColorByArrayName(colorByArrayName: string): boolean;

/**
*
* @param {Number} colorMode
*/
setColorMode(colorMode: number): boolean;

/**
* Sets colorMode to `DEFAULT`
*/
setColorModeToDefault(): boolean;

/**
* Sets colorMode to `MAP_SCALARS`
*/
setColorModeToMapScalars(): boolean;

/**
* Sets colorMode to `DIRECT_SCALARS`
*/
setColorModeToDirectScalars(): boolean;

/**
* Sets point data array names that will be transferred to the VBO
* @param {String[]} customShaderAttributes
*/
setCustomShaderAttributes(customShaderAttributes: string[]): boolean;

/**
* When ScalarMode is set to UseFieldData, set the index of the
* tuple by which to color the entire data set. By default, the
* index is -1, which means to treat the field data array selected
* with SelectColorArray as having a scalar value for each cell.
* Indices of 0 or higher mean to use the tuple at the given index
* for coloring the entire data set.
* @param {Number} fieldDataTupleI
* @default -1
*/
setFieldDataTupleId(fieldDataTupleI: number): boolean;

/**
*
* @param {Number} forceCompileOnly
* @default 0
*/
setForceCompileOnly(forceCompileOnly: number): boolean;

/**
* Set a lookup table for the mapper to use.
*/
setLookupTable(lookupTable: any): boolean;

/**
* Control how the filter works with scalar point data and cell attribute
* data. By default (ScalarModeToDefault), the filter will use point data,
* and if no point data is available, then cell data is used. Alternatively
* you can explicitly set the filter to use point data
* (ScalarModeToUsePointData) or cell data (ScalarModeToUseCellData).
* You can also choose to get the scalars from an array in point field
* data (ScalarModeToUsePointFieldData) or cell field data
* (ScalarModeToUseCellFieldData). If scalars are coming from a field
* data array, you must call SelectColorArray before you call GetColors.
*
* When ScalarMode is set to use Field Data (ScalarModeToFieldData),
* you must call SelectColorArray to choose the field data array to
* be used to color cells. In this mode, the default behavior is to
* treat the field data tuples as being associated with cells. If
* the poly data contains triangle strips, the array is expected to
* contain the cell data for each mini-cell formed by any triangle
* strips in the poly data as opposed to treating them as a single
* tuple that applies to the entire strip. This mode can also be
* used to color the entire poly data by a single color obtained by
* mapping the tuple at a given index in the field data array
* through the color map. Use SetFieldDataTupleId() to specify
* the tuple index.
*
* @param scalarMode
*/
setScalarMode(scalarMode: number): boolean;

/**
* Sets scalarMode to DEFAULT
*/
setScalarModeToDefault(): boolean;

/**
* Sets scalarMode to USE_CELL_DATA
*/
setScalarModeToUseCellData(): boolean;

/**
* Sets scalarMode to USE_CELL_FIELD_DATA
*/
setScalarModeToUseCellFieldData(): boolean;

/**
* Sets scalarMode to USE_FIELD_DATA
*/
setScalarModeToUseFieldData(): boolean;

/**
* Sets scalarMode to USE_POINT_DATA
*/
setScalarModeToUsePointData(): boolean;

/**
* Sets scalarMode to USE_POINT_FIELD_DATA
*/
setScalarModeToUsePointFieldData(): boolean;

/**
* Specify range in terms of scalar minimum and maximum (smin,smax). These
* values are used to map scalars into lookup table. Has no effect when
* UseLookupTableScalarRange is true.
*
* @param min
* @param max
* @default [0, 1]
*/
setScalarRange(min: number, max: number): boolean;

/**
* Specify range in terms of scalar minimum and maximum (smin,smax). These
* values are used to map scalars into lookup table. Has no effect when
* UseLookupTableScalarRange is true.
*
* @param scalarRange
* @default [0, 1]
*/
setScalarRange(scalarRange: number[]): boolean;

/**
*
* @param scalarRange
* @default [0, 1]
*/
setScalarRangeFrom(scalarRange: number[]): boolean;

/**
* Turn on/off flag to control whether scalar data is used to color objects.
* @param {Boolean} scalarVisibility
* @default true
*/
setScalarVisibility(scalarVisibility: boolean): boolean;

/**
* Turn on/off flag to control whether the mapper’s data is static. Static data
* means that the mapper does not propagate updates down the pipeline, greatly
* decreasing the time it takes to update many mappers. This should only be
* used if the data never changes.
*
* @param {Boolean} static
* @default false
*/
setStatic(static: boolean): boolean;

/**
* Control whether the mapper sets the lookuptable range based on its
* own ScalarRange, or whether it will use the LookupTable ScalarRange
* regardless of it’s own setting. By default the Mapper is allowed to set
* the LookupTable range, but users who are sharing LookupTables between
* mappers/actors will probably wish to force the mapper to use the
* LookupTable unchanged.
*
* @param {Boolean} useLookupTableScalarRange
* @default false
*/
setUseLookupTableScalarRange(useLookupTableScalarRange: boolean): boolean;

/**
* If you want to provide specific properties for rendering engines you can use
* viewSpecificProperties.
*
* You can go and have a look in the rendering backend of your choice for details
* on specific properties.
* For example, for OpenGL/WebGL see OpenGL/PolyDataMapper/api.md
* If there is no details, viewSpecificProperties is not supported.
* @param viewSpecificProperties
*/
setViewSpecificProperties(viewSpecificProperties: object): boolean;

/**
*
*/
useInvertibleColorFor(): void;

/**
*
*/
valueToColor(): void;

/**
*
*/
setInterpolateScalarsBeforeMapping(
interpolateScalarsBeforeMapping: boolean
): boolean;
}

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

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

/**
* vtkMapper is an abstract class to specify interface between data and
* graphics primitives. Subclasses of vtkMapper map data through a
* lookuptable and control the creation of rendering primitives that
* interface to the graphics library. The mapping can be controlled by
* supplying a lookup table and specifying a scalar range to map data
* through.
*
* There are several important control mechanisms affecting the behavior of
* this object. The ScalarVisibility flag controls whether scalar data (if
* any) controls the color of the associated actor(s) that refer to the
* mapper. The ScalarMode ivar is used to determine whether scalar point data
* or cell data is used to color the object. By default, point data scalars
* are used unless there are none, then cell scalars are used. Or you can
* explicitly control whether to use point or cell scalar data. Finally, the
* mapping of scalars through the lookup table varies depending on the
* setting of the ColorMode flag. See the documentation for the appropriate
* methods for an explanation.
*
* Another important feature of this class is whether to use immediate mode
* rendering (ImmediateModeRenderingOn) or display list rendering
* (ImmediateModeRenderingOff). If display lists are used, a data structure
* is constructed (generally in the rendering library) which can then be
* rapidly traversed and rendered by the rendering library. The disadvantage
* of display lists is that they require additional memory which may affect
* the performance of the system.
*
* Another important feature of the mapper is the ability to shift the
* Z-buffer to resolve coincident topology. For example, if you’d like to
* draw a mesh with some edges a different color, and the edges lie on the
* mesh, this feature can be useful to get nice looking lines. (See the
* ResolveCoincidentTopology-related methods.)
*/
export declare const vtkMapper: {
newInstance: typeof newInstance;
extend: typeof extend;
ColorMode: typeof ColorMode;
ScalarMode: typeof ScalarMode;
GetArray: typeof GetArray;
} & StaticCoincidentTopologyMethods;
export default vtkMapper;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkAbstractMapper3D from 'vtk.js/Sources/Rendering/Core/AbstractMapper3D';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData';
import vtkLookupTable from 'vtk.js/Sources/Common/Core/LookupTable';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';
import vtkScalarsToColors from 'vtk.js/Sources/Common/Core/ScalarsToColors/Constants'; // Need to go inside Constants otherwise dependency loop

import CoincidentTopologyHelper from 'vtk.js/Sources/Rendering/Core/Mapper/CoincidentTopologyHelper';
import Constants from 'vtk.js/Sources/Rendering/Core/Mapper/Constants';

import vtkDataSet from 'vtk.js/Sources/Common/DataModel/DataSet';

import { PassTypes } from 'vtk.js/Sources/Rendering/OpenGL/HardwareSelector/Constants';

const { FieldAssociations } = vtkDataSet;

const { staticOffsetAPI, otherStaticMethods } = CoincidentTopologyHelper;

const { ColorMode, ScalarMode, GetArray } = Constants;
const { VectorMode } = vtkScalarsToColors;
const { VtkDataTypes } = vtkDataArray;

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

function notImplemented(method) {
return () => macro.vtkErrorMacro(`vtkMapper::${method} - NOT IMPLEMENTED`);
}

/**
* Increase by one the 3D coordinates
* It will follow a zigzag pattern so that each coordinate is the neighbor of the next coordinate
* This enables interpolation between two texels without issues
* Note: texture coordinates can't be interpolated using this pattern
* @param {vec3} coordinates The 3D coordinates using integers for each coorinate
* @param {vec3} dimensions The 3D dimensions of the volume
*/
function updateZigzaggingCoordinates(coordinates, dimensions) {
const directionX = coordinates[1] % 2 === 0 ? 1 : -1;
coordinates[0] += directionX;
if (coordinates[0] >= dimensions[0] || coordinates[0] < 0) {
const directionY = coordinates[2] % 2 === 0 ? 1 : -1;
coordinates[0] -= directionX;
coordinates[1] += directionY;
if (coordinates[1] >= dimensions[1] || coordinates[1] < 0) {
coordinates[1] -= directionY;
coordinates[2]++;
}
}
}

/**
* Returns the index in the array representing the volume from a 3D coordinate
* @param {vec3} coordinates The 3D integer coordinates
* @param {vec3} dimensions The 3D dimensions of the volume
* @returns The index in a flat array representing the volume
*/
function getIndexFromCoordinates(coordinates, dimensions) {
return (
coordinates[0] +
dimensions[0] * (coordinates[1] + dimensions[1] * coordinates[2])
);
}

/**
* Write texture coordinates for the given `texelIndexPosition` in `textureCoordinate`.
* The `texelIndexPosition` is a floating point number that represents the distance in index space
* from the center of the first texel to the final output position.
* The output is given in texture coordinates and not in index coordinates (this is done at the very end of the function)
* @param {vec3} textureCoordinate The output texture coordinates (to avoid allocating a new Array)
* @param {Number} texelIndexPosition The floating point distance from the center of the first texel, following a zigzag pattern
* @param {vec3} dimensions The 3D dimensions of the volume
*/
function getZigZagTextureCoordinatesFromTexelPosition(
textureCoordinate,
texelIndexPosition,
dimensions
) {
// First compute the integer textureCoordinate
const intTexelIndex = Math.floor(texelIndexPosition);
const xCoordBeforeWrap = intTexelIndex % (2 * dimensions[0]);
let xDirection;
let xEndFlag;
if (xCoordBeforeWrap < dimensions[0]) {
textureCoordinate[0] = xCoordBeforeWrap;
xDirection = 1;
xEndFlag = textureCoordinate[0] === dimensions[0] - 1;
} else {
textureCoordinate[0] = 2 * dimensions[0] - 1 - xCoordBeforeWrap;
xDirection = -1;
xEndFlag = textureCoordinate[0] === 0;
}

const intRowIndex = Math.floor(intTexelIndex / dimensions[0]);
const yCoordBeforeWrap = intRowIndex % (2 * dimensions[1]);
let yDirection;
let yEndFlag;
if (yCoordBeforeWrap < dimensions[1]) {
textureCoordinate[1] = yCoordBeforeWrap;
yDirection = 1;
yEndFlag = textureCoordinate[1] === dimensions[1] - 1;
} else {
textureCoordinate[1] = 2 * dimensions[1] - 1 - yCoordBeforeWrap;
yDirection = -1;
yEndFlag = textureCoordinate[1] === 0;
}

textureCoordinate[2] = Math.floor(intRowIndex / dimensions[1]);

// Now add the remainder either in x, y or z
const remainder = texelIndexPosition - intTexelIndex;
if (xEndFlag) {
if (yEndFlag) {
textureCoordinate[2] += remainder;
} else {
textureCoordinate[1] += yDirection * remainder;
}
} else {
textureCoordinate[0] += xDirection * remainder;
}

// textureCoordinates are in index space, convert to texture space
textureCoordinate[0] = (textureCoordinate[0] + 0.5) / dimensions[0];
textureCoordinate[1] = (textureCoordinate[1] + 0.5) / dimensions[1];
textureCoordinate[2] = (textureCoordinate[2] + 0.5) / dimensions[2];
}

// Associate an input vtkDataArray to an object { stringHash, textureCoordinates }
// A single dataArray only caches one array of texture coordinates, so this cache is useless when
// the input data array is used with two different lookup tables (which is very unlikely)
const colorTextureCoordinatesCache = new WeakMap();
/**
* The minimum of the range is mapped to the center of the first texel excluding min texel (texel at index distance 1)
* The maximum of the range is mapped to the center of the last texel excluding max and NaN texels (texel at index distance numberOfColorsInRange)
* The result is cached, and is reused if the arguments are the same and the input doesn't change
* @param {vtkDataArray} input The input data array used for coloring
* @param {Number} component The component of the input data array that is used for coloring (-1 for magnitude of the vectors)
* @param {Range} range The range of the scalars
* @param {Number} numberOfColorsInRange The number of colors that are used in the range
* @param {vec3} dimensions The dimensions of the texture
* @param {boolean} useLogScale If log scale should be used to transform input scalars
* @param {boolean} useZigzagPattern If a zigzag pattern should be used. Otherwise 1 row for colors (including min and max) and 1 row for NaN are used.
* @returns A vtkDataArray containing the texture coordinates (2D or 3D)
*/
function getOrCreateColorTextureCoordinates(
input,
component,
range,
numberOfColorsInRange,
dimensions,
useLogScale,
useZigzagPattern
) {
// Caching using the "arguments" special object (because it is a pure function)
const argStrings = new Array(arguments.length);
for (let argIndex = 0; argIndex < arguments.length; ++argIndex) {
// eslint-disable-next-line prefer-rest-params
const arg = arguments[argIndex];
argStrings[argIndex] = arg.getMTime?.() ?? arg;
}
const stringHash = argStrings.join('/');

const cachedResult = colorTextureCoordinatesCache.get(input);
if (cachedResult && cachedResult.stringHash === stringHash) {
return cachedResult.textureCoordinates;
}

// The range used for computing coordinates have to change
// slightly to accommodate the special above- and below-range
// colors that are the first and last texels, respectively.
const scalarTexelWidth = (range[1] - range[0]) / (numberOfColorsInRange - 1);
const [paddedRangeMin, paddedRangeMax] = [
range[0] - scalarTexelWidth,
range[1] + scalarTexelWidth,
];

// Use the center of the voxel
const textureSOrigin = paddedRangeMin - 0.5 * scalarTexelWidth;
const textureSCoeff =
1.0 / (paddedRangeMax - paddedRangeMin + scalarTexelWidth);

// Compute in index space first
const texelIndexOrigin = paddedRangeMin;
const texelIndexCoeff =
(numberOfColorsInRange + 1) / (paddedRangeMax - paddedRangeMin);

const inputV = input.getData();
const numScalars = input.getNumberOfTuples();
const numComps = input.getNumberOfComponents();
const useMagnitude = component < 0 || component >= numComps;
const numberOfOutputComponents = dimensions[2] <= 1 ? 2 : 3;
const output = vtkDataArray.newInstance({
numberOfComponents: numberOfOutputComponents,
values: new Float32Array(numScalars * numberOfOutputComponents),
});
const outputV = output.getData();

const nanTextureCoordinate = [0, 0, 0];
// Distance of NaN from the beginning:
// min: 0, ...colorsInRange, max: numberOfColorsInRange + 1, NaN = numberOfColorsInRange + 2
getZigZagTextureCoordinatesFromTexelPosition(
nanTextureCoordinate,
numberOfColorsInRange + 2,
dimensions
);

// Set a texture coordinate in the output for each tuple in the input
let inputIdx = 0;
let outputIdx = 0;
const textureCoordinate = [0.5, 0.5, 0.5];
for (let scalarIdx = 0; scalarIdx < numScalars; ++scalarIdx) {
// Get scalar value from magnitude or a single component
let scalarValue;
if (useMagnitude) {
let sum = 0;
for (let compIdx = 0; compIdx < numComps; ++compIdx) {
const compValue = inputV[inputIdx + compIdx];
sum += compValue * compValue;
}
scalarValue = Math.sqrt(sum);
} else {
scalarValue = inputV[inputIdx + component];
}
inputIdx += numComps;

// Apply log scale if necessary
if (useLogScale) {
scalarValue = vtkLookupTable.applyLogScale(scalarValue, range, range);
}

// Convert to texture coordinates and update output
if (vtkMath.isNan(scalarValue)) {
// Last texels are NaN colors (there is at least one NaN color)
textureCoordinate[0] = nanTextureCoordinate[0];
textureCoordinate[1] = nanTextureCoordinate[1];
textureCoordinate[2] = nanTextureCoordinate[2];
} else if (useZigzagPattern) {
// Texel position is in [0, numberOfColorsInRange + 1]
let texelIndexPosition =
(scalarValue - texelIndexOrigin) * texelIndexCoeff;
if (texelIndexPosition < 1) {
// Use min color when smaller than range
texelIndexPosition = 0;
} else if (texelIndexPosition > numberOfColorsInRange) {
// Use max color when greater than range
texelIndexPosition = numberOfColorsInRange + 1;
}

// Convert the texel position into texture coordinate following a zigzag pattern
getZigZagTextureCoordinatesFromTexelPosition(
textureCoordinate,
texelIndexPosition,
dimensions
);
} else {
// 0.0 in t coordinate means not NaN. So why am I setting it to 0.49?
// Because when you are mapping scalars and you have a NaN adjacent to
// anything else, the interpolation everywhere should be NaN. Thus, I
// want the NaN color everywhere except right on the non-NaN neighbors.
// To simulate this, I set the t coord for the real numbers close to
// the threshold so that the interpolation almost immediately looks up
// the NaN value.
textureCoordinate[1] = 0.49;

// Some implementations apparently don't handle relatively large
// numbers (compared to the range [0.0, 1.0]) very well. In fact,
// values above 1122.0f appear to cause texture wrap-around on
// some systems even when edge clamping is enabled. Why 1122.0f? I
// don't know. For safety, we'll clamp at +/- 1000. This will
// result in incorrect images when the texture value should be
// above or below 1000, but I don't have a better solution.
const textureS = (scalarValue - textureSOrigin) * textureSCoeff;
if (textureS > 1000.0) {
textureCoordinate[0] = 1000.0;
} else if (textureS < -1000.0) {
textureCoordinate[0] = -1000.0;
} else {
textureCoordinate[0] = textureS;
}
}
for (let i = 0; i < numberOfOutputComponents; ++i) {
outputV[outputIdx++] = textureCoordinate[i];
}
}

colorTextureCoordinatesCache.set(input, {
stringHash,
textureCoordinates: output,
});
return output;
}

// ----------------------------------------------------------------------------
// vtkMapper methods
// ----------------------------------------------------------------------------

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

publicAPI.getBounds = () => {
const input = publicAPI.getInputData();
if (!input) {
model.bounds = vtkMath.createUninitializedBounds();
} else {
if (!model.static) {
publicAPI.update();
}
model.bounds = input.getBounds();
}
return model.bounds;
};

publicAPI.setForceCompileOnly = (v) => {
model.forceCompileOnly = v;
// make sure we do NOT call modified()
};

publicAPI.setSelectionWebGLIdsToVTKIds = (selectionWebGLIdsToVTKIds) => {
model.selectionWebGLIdsToVTKIds = selectionWebGLIdsToVTKIds;
// make sure we do NOT call modified()
// this attribute is only used when processing a selection made with the hardware selector
// the mtime of the mapper doesn't need to be changed
};

publicAPI.createDefaultLookupTable = () => {
model.lookupTable = vtkLookupTable.newInstance();
};

publicAPI.getColorModeAsString = () =>
macro.enumToString(ColorMode, model.colorMode);

publicAPI.setColorModeToDefault = () => publicAPI.setColorMode(0);
publicAPI.setColorModeToMapScalars = () => publicAPI.setColorMode(1);
publicAPI.setColorModeToDirectScalars = () => publicAPI.setColorMode(2);

publicAPI.getScalarModeAsString = () =>
macro.enumToString(ScalarMode, model.scalarMode);

publicAPI.setScalarModeToDefault = () => publicAPI.setScalarMode(0);
publicAPI.setScalarModeToUsePointData = () => publicAPI.setScalarMode(1);
publicAPI.setScalarModeToUseCellData = () => publicAPI.setScalarMode(2);
publicAPI.setScalarModeToUsePointFieldData = () => publicAPI.setScalarMode(3);
publicAPI.setScalarModeToUseCellFieldData = () => publicAPI.setScalarMode(4);
publicAPI.setScalarModeToUseFieldData = () => publicAPI.setScalarMode(5);

publicAPI.getAbstractScalars = (
input,
scalarMode,
arrayAccessMode,
arrayId,
arrayName
) => {
// make sure we have an input
if (!input || !model.scalarVisibility) {
return { scalars: null, cellFlag: false };
}

let scalars = null;
let cellFlag = false;

// get and scalar data according to scalar mode
if (scalarMode === ScalarMode.DEFAULT) {
scalars = input.getPointData().getScalars();
if (!scalars) {
scalars = input.getCellData().getScalars();
cellFlag = true;
}
} else if (scalarMode === ScalarMode.USE_POINT_DATA) {
scalars = input.getPointData().getScalars();
} else if (scalarMode === ScalarMode.USE_CELL_DATA) {
scalars = input.getCellData().getScalars();
cellFlag = true;
} else if (scalarMode === ScalarMode.USE_POINT_FIELD_DATA) {
const pd = input.getPointData();
if (arrayAccessMode === GetArray.BY_ID) {
scalars = pd.getArrayByIndex(arrayId);
} else {
scalars = pd.getArrayByName(arrayName);
}
} else if (scalarMode === ScalarMode.USE_CELL_FIELD_DATA) {
const cd = input.getCellData();
cellFlag = true;
if (arrayAccessMode === GetArray.BY_ID) {
scalars = cd.getArrayByIndex(arrayId);
} else {
scalars = cd.getArrayByName(arrayName);
}
} else if (scalarMode === ScalarMode.USE_FIELD_DATA) {
const fd = input.getFieldData();
if (arrayAccessMode === GetArray.BY_ID) {
scalars = fd.getArrayByIndex(arrayId);
} else {
scalars = fd.getArrayByName(arrayName);
}
}

return { scalars, cellFlag };
};

publicAPI.mapScalars = (input, alpha) => {
const { scalars, cellFlag } = publicAPI.getAbstractScalars(
input,
model.scalarMode,
model.arrayAccessMode,
model.arrayId,
model.colorByArrayName
);
model.areScalarsMappedFromCells = cellFlag;

if (!scalars) {
model.colorCoordinates = null;
model.colorTextureMap = null;
model.colorMapColors = null;
return;
}

// we want to only recompute when something has changed
const toString = `${publicAPI.getMTime()}${scalars.getMTime()}${alpha}`;
if (model.colorBuildString === toString) return;

if (!model.useLookupTableScalarRange) {
publicAPI
.getLookupTable()
.setRange(model.scalarRange[0], model.scalarRange[1]);
}

// Decide between texture color or vertex color.
// Cell data always uses vertex color.
// Only point data can use both texture and vertex coloring.
if (publicAPI.canUseTextureMapForColoring(scalars, cellFlag)) {
model.mapScalarsToTexture(scalars, cellFlag, alpha);
} else {
model.colorCoordinates = null;
model.colorTextureMap = null;

const lut = publicAPI.getLookupTable();
if (lut) {
// Ensure that the lookup table is built
lut.build();
model.colorMapColors = lut.mapScalars(
scalars,
model.colorMode,
model.fieldDataTupleId
);
}
}
model.colorBuildString = `${publicAPI.getMTime()}${scalars.getMTime()}${alpha}`;
};

// Protected method
model.mapScalarsToTexture = (scalars, cellFlag, alpha) => {
const range = model.lookupTable.getRange();
const useLogScale = model.lookupTable.usingLogScale();
if (useLogScale) {
// convert range to log.
vtkLookupTable.getLogRange(range, range);
}

const origAlpha = model.lookupTable.getAlpha();

// Get rid of vertex color array. Only texture or vertex coloring
// can be active at one time. The existence of the array is the
// signal to use that technique.
model.colorMapColors = null;

// If the lookup table has changed, then recreate the color texture map.
// Set a new lookup table changes this->MTime.
if (
model.colorTextureMap == null ||
publicAPI.getMTime() > model.colorTextureMap.getMTime() ||
model.lookupTable.getMTime() > model.colorTextureMap.getMTime() ||
model.lookupTable.getAlpha() !== alpha
) {
model.lookupTable.setAlpha(alpha);
model.colorTextureMap = null;

// Get the texture map from the lookup table.
// Create a dummy ramp of scalars.
// In the future, we could extend vtkScalarsToColors.
model.lookupTable.build();
const numberOfAvailableColors =
model.lookupTable.getNumberOfAvailableColors();

// Maximum dimensions and number of colors in range
const maxTextureWidthForCells = 2048;
const maxColorsInRangeForCells = maxTextureWidthForCells ** 3 - 3; // 3D but keep a color for min, max and NaN
const maxTextureWidthForPoints = 4096;
const maxColorsInRangeForPoints = maxTextureWidthForPoints - 2; // 1D but keep a color for min and max (NaN is in a different row)
// Minimum number of colors in range (excluding special colors like minColor, maxColor and NaNColor)
const minColorsInRange = 2;
// Maximum number of colors, limited by the maximum possible texture size
const maxColorsInRange = cellFlag
? maxColorsInRangeForCells
: maxColorsInRangeForPoints;

model.numberOfColorsInRange = Math.min(
Math.max(numberOfAvailableColors, minColorsInRange),
maxColorsInRange
);
const numberOfColorsForCells = model.numberOfColorsInRange + 3; // Add min, max and NaN
const numberOfColorsInUpperRowForPoints = model.numberOfColorsInRange + 2; // Add min and max ; the lower row will be used for NaN color
const textureDimensions = cellFlag
? [
Math.min(
Math.ceil(numberOfColorsForCells / maxTextureWidthForCells ** 0),
maxTextureWidthForCells
),
Math.min(
Math.ceil(numberOfColorsForCells / maxTextureWidthForCells ** 1),
maxTextureWidthForCells
),
Math.min(
Math.ceil(numberOfColorsForCells / maxTextureWidthForCells ** 2),
maxTextureWidthForCells
),
]
: [numberOfColorsInUpperRowForPoints, 2, 1];
const textureSize =
textureDimensions[0] * textureDimensions[1] * textureDimensions[2];

const scalarsArray = new Float64Array(textureSize);

// Colors for NaN by default
scalarsArray.fill(NaN);

// Colors in range
// Add 2 to also get color for min and max
const numberOfNonSpecialColors = model.numberOfColorsInRange;
const numberOfNonNaNColors = numberOfNonSpecialColors + 2;
const textureCoordinates = [0, 0, 0];
const rangeMin = range[0];
const rangeDifference = range[1] - range[0];
for (let i = 0; i < numberOfNonNaNColors; ++i) {
const scalarsArrayIndex = getIndexFromCoordinates(
textureCoordinates,
textureDimensions
);

// Minus 1 start at min color
const scalarValue =
rangeMin +
(rangeDifference * (i - 1)) / (numberOfNonSpecialColors - 1);
scalarsArray[scalarsArrayIndex] = useLogScale
? 10.0 ** scalarValue
: scalarValue;

// Colors are zigzagging to allow interpolation between two neighbor colors when coloring cells
updateZigzaggingCoordinates(textureCoordinates, textureDimensions);
}

const scalarsDataArray = vtkDataArray.newInstance({
numberOfComponents: 1,
values: scalarsArray,
});
const colorsDataArray = model.lookupTable.mapScalars(
scalarsDataArray,
model.colorMode,
0
);

model.colorTextureMap = vtkImageData.newInstance();
model.colorTextureMap.setDimensions(textureDimensions);
model.colorTextureMap.getPointData().setScalars(colorsDataArray);

model.lookupTable.setAlpha(origAlpha);
}

// Although I like the feature of applying magnitude to single component
// scalars, it is not how the old MapScalars for vertex coloring works.
const scalarComponent =
model.lookupTable.getVectorMode() === VectorMode.MAGNITUDE &&
scalars.getNumberOfComponents() > 1
? -1
: model.lookupTable.getVectorComponent();

// Create new coordinates if necessary, this function uses cache if possible.
// A zigzag pattern can't be used with point data, as interpolation of texture coordinates will be wrong
// A zigzag pattern can be used with cell data, as there will be no texture coordinates interpolation
// The texture generated using a zigzag pattern in one dimension is the same as without zigzag
// Therefore, the same code can be used for texture generation of point/cell data but not for texture coordinates
model.colorCoordinates = getOrCreateColorTextureCoordinates(
scalars,
scalarComponent,
range,
model.numberOfColorsInRange,
model.colorTextureMap.getDimensions(),
useLogScale,
cellFlag
);
};

publicAPI.getIsOpaque = () => {
const input = publicAPI.getInputData();
const gasResult = publicAPI.getAbstractScalars(
input,
model.scalarMode,
model.arrayAccessMode,
model.arrayId,
model.colorByArrayName
);
const scalars = gasResult.scalars;
if (!model.scalarVisibility || scalars == null) {
// No scalar colors.
return true;
}
const lut = publicAPI.getLookupTable();
if (lut) {
// Ensure that the lookup table is built
lut.build();
return lut.areScalarsOpaque(scalars, model.colorMode, -1);
}
return true;
};

publicAPI.canUseTextureMapForColoring = (scalars, cellFlag) => {
if (cellFlag && !(model.colorMode === ColorMode.DIRECT_SCALARS)) {
return true; // cell data always use textures.
}

if (!model.interpolateScalarsBeforeMapping) {
return false; // user doesn't want us to use texture maps at all.
}

// index color does not use textures
if (model.lookupTable && model.lookupTable.getIndexedLookup()) {
return false;
}

if (!scalars) {
// no scalars on this dataset, we don't care if texture is used at all.
return false;
}

if (
(model.colorMode === ColorMode.DEFAULT &&
scalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR) ||
model.colorMode === ColorMode.DIRECT_SCALARS
) {
// Don't use texture is direct coloring using RGB unsigned chars is
// requested.
return false;
}

return true;
};

publicAPI.clearColorArrays = () => {
model.colorMapColors = null;
model.colorCoordinates = null;
model.colorTextureMap = null;
};

publicAPI.getLookupTable = () => {
if (!model.lookupTable) {
publicAPI.createDefaultLookupTable();
}
return model.lookupTable;
};

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

publicAPI.getPrimitiveCount = () => {
const input = publicAPI.getInputData();
const pcount = {
points: input.getPoints().getNumberOfValues() / 3,
verts:
input.getVerts().getNumberOfValues() -
input.getVerts().getNumberOfCells(),
lines:
input.getLines().getNumberOfValues() -
2 * input.getLines().getNumberOfCells(),
triangles:
input.getPolys().getNumberOfValues() -
3 * input.getPolys().getNumberOfCells(),
};
return pcount;
};

publicAPI.acquireInvertibleLookupTable = notImplemented(
'AcquireInvertibleLookupTable'
);
publicAPI.valueToColor = notImplemented('ValueToColor');
publicAPI.colorToValue = notImplemented('ColorToValue');
publicAPI.useInvertibleColorFor = notImplemented('UseInvertibleColorFor');
publicAPI.clearInvertibleColor = notImplemented('ClearInvertibleColor');

publicAPI.processSelectorPixelBuffers = (selector, pixelOffsets) => {
/* eslint-disable no-bitwise */
if (
!selector ||
!model.selectionWebGLIdsToVTKIds ||
!model.populateSelectionSettings
) {
return;
}

const rawLowData = selector.getRawPixelBuffer(PassTypes.ID_LOW24);
const rawHighData = selector.getRawPixelBuffer(PassTypes.ID_HIGH24);
const currentPass = selector.getCurrentPass();
const fieldAssociation = selector.getFieldAssociation();

let idMap = null;
if (fieldAssociation === FieldAssociations.FIELD_ASSOCIATION_POINTS) {
idMap = model.selectionWebGLIdsToVTKIds.points;
} else if (fieldAssociation === FieldAssociations.FIELD_ASSOCIATION_CELLS) {
idMap = model.selectionWebGLIdsToVTKIds.cells;
}

if (!idMap) {
return;
}

pixelOffsets.forEach((pos) => {
if (currentPass === PassTypes.ID_LOW24) {
let inValue = 0;
if (rawHighData) {
inValue += rawHighData[pos];
inValue *= 256;
}
inValue += rawLowData[pos + 2];
inValue *= 256;
inValue += rawLowData[pos + 1];
inValue *= 256;
inValue += rawLowData[pos];

const outValue = idMap[inValue];
const lowData = selector.getPixelBuffer(PassTypes.ID_LOW24);
lowData[pos] = outValue & 0xff;
lowData[pos + 1] = (outValue & 0xff00) >> 8;
lowData[pos + 2] = (outValue & 0xff0000) >> 16;
} else if (currentPass === PassTypes.ID_HIGH24 && rawHighData) {
let inValue = 0;
inValue += rawHighData[pos];
inValue *= 256;
inValue += rawLowData[pos + 2];
inValue *= 256;
inValue += rawLowData[pos + 1];
inValue *= 256;
inValue += rawLowData[pos];

const outValue = idMap[inValue];
const highData = selector.getPixelBuffer(PassTypes.ID_HIGH24);
highData[pos] = (outValue & 0xff000000) >> 24;
}
});
/* eslint-enable no-bitwise */
};
}

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

const DEFAULT_VALUES = {
colorMapColors: null, // Same as this->Colors
areScalarsMappedFromCells: false,

static: false,
lookupTable: null,

scalarVisibility: true,
scalarRange: [0, 1],
useLookupTableScalarRange: false,

colorMode: 0,
scalarMode: 0,
arrayAccessMode: 1, // By_NAME

renderTime: 0,

colorByArrayName: null,

fieldDataTupleId: -1,

populateSelectionSettings: true,
selectionWebGLIdsToVTKIds: null,

interpolateScalarsBeforeMapping: false,
colorCoordinates: null,
colorTextureMap: null,
numberOfColorsInRange: 0,

forceCompileOnly: 0,

useInvertibleColors: false,
invertibleScalars: null,

customShaderAttributes: [],
};

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

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

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

macro.get(publicAPI, model, [
'areScalarsMappedFromCells',
'colorCoordinates',
'colorMapColors',
'colorTextureMap',
'numberOfColorsInRange',
'selectionWebGLIdsToVTKIds',
]);
macro.setGet(publicAPI, model, [
'colorByArrayName',
'arrayAccessMode',
'colorMode',
'fieldDataTupleId',
'interpolateScalarsBeforeMapping',
'lookupTable',
'populateSelectionSettings',
'renderTime',
'scalarMode',
'scalarVisibility',
'static',
'useLookupTableScalarRange',
'customShaderAttributes', // point data array names that will be transferred to the VBO
]);
macro.setGetArray(publicAPI, model, ['scalarRange'], 2);

CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);

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

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

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

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

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