WidgetRepresentation

Methods

allocateArray

Allocate or resize a vtkPoint(name=’point’), vtkCellArray (name=
‘line’|’poly’) or vtkDataArray (name=any) and add it to the vtkPolyData.
If allocated, the array is automatically added to the polydata
Connects mapper to actor.

Argument Type Required Description
polyData vtkPolyData Yes The polydata to add array to
name string Yes The name of the array to add (special handling for ‘point’, ‘line, ‘poly’)
numberOfTuples Number Yes The number of tuples to (re)allocate.
dataType String Yes The typed array type name.
numberOfComponents Number Yes The number of components of the array.

connectPipeline

If provided, connects source (dataset or filter) to filter.
If provided, connects filter (otherwise source) to mapper
If provided, connects glyph as 2nd input to mapper. This is typically for the glyph mapper.
Connects mapper to actor.

Argument Type Required Description
pipeline IWidgetPipeline Yes of source, filter, mapper and actor to connect

extend

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

getCoincidentTopologyParameters

Gets the coincident topology parameters applied on the actor mappers

getPixelWorldHeightAtCoord

Static function to get the pixel size of a 3D point.

Argument Type Required Description
worldCoord Array. Yes 3D point in world coordinates
displayScaleParams IDisplayScaleParams Yes Display and camera information

getScaleInPixels

Gets wether actors should have a fix size in display coordinates.

newInstance

Method use to create a new instance of vtkWidgetRepresentation

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

setCoincidentTopologyParameters

Sets the coincident topology parameters applied on the actor mappers

setDisplayScaleParams

Sets the current view and camera scale parameters.
Called by the WidgetManager.

setScaleInPixels

Sets wether actors should have a fix size in display coordinates.

Source

Constants.js
export const Behavior = {
HANDLE: 0,
CONTEXT: 1,
};

export default {
Behavior,
};
index.d.ts
import vtkDataArray from "../../../Common/Core/DataArray";
import vtkPolyData from "../../../Common/DataModel/PolyData";
import { vtkObject } from "../../../interfaces";
import vtkProp from "../../../Rendering/Core/Prop";
export interface IDisplayScaleParams {
dispHeightFactor: number,
cameraPosition: number[],
cameraDir: number[],
isParallel: boolean,
rendererPixelDims: number[],
}

export interface IWidgetRepresentationInitialValues {
labels?: Array<any>,
coincidentTopologyParameters?: object,
displayScaleParams?: IDisplayScaleParams,
scaleInPixels?: boolean
}

export interface vtkWidgetRepresentation extends vtkProp {
getLabels(): Array<any>;
setLabels(labels: Array<any>): void;

/**
* Gets the coincident topology parameters applied on the actor mappers
*/
getCoincidentTopologyParameters(): object;
/**
* Sets the coincident topology parameters applied on the actor mappers
*/
setCoincidentTopologyParameters(parameters: object): boolean;

/**
* Sets the current view and camera scale parameters.
* Called by the WidgetManager.
* @see setScaleInPixels()
*/
setDisplayScaleParams(params: object): boolean;

/**
* Gets wether actors should have a fix size in display coordinates.
* @see setScaleInPixels()
*/
getScaleInPixels(): boolean;

/**
* Sets wether actors should have a fix size in display coordinates.
* @see getScaleInPixels()
*/
setScaleInPixels(scale: boolean): boolean;
}

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

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

/**
* Static function to get the pixel size of a 3D point.
* @param {Number[]} worldCoord 3D point in world coordinates
* @param {IDisplayScaleParams} displayScaleParams Display and camera information
*/
export function getPixelWorldHeightAtCoord(worldCoord: number[], displayScaleParams: IDisplayScaleParams): number[];

export interface IWidgetPipeline {
source?: object,
filter?: object,
glyph?: object,
mapper: object,
actor: object
}
/**
* If provided, connects `source` (dataset or filter) to `filter`.
* If provided, connects `filter` (otherwise `source`) to mapper
* If provided, connects glyph as 2nd input to mapper. This is typically for the glyph mapper.
* Connects mapper to actor.
* @param {IWidgetPipeline} pipeline of source, filter, mapper and actor to connect
*/
export function connectPipeline(pipeline: IWidgetPipeline): void;

/**
* Allocate or resize a vtkPoint(name='point'), vtkCellArray (name=
* 'line'|'poly') or vtkDataArray (name=any) and add it to the vtkPolyData.
* If allocated, the array is automatically added to the polydata
* Connects mapper to actor.
* @param {vtkPolyData} polyData The polydata to add array to
* @param {string} name The name of the array to add (special handling for
* 'point', 'line, 'poly')
* @param {Number} numberOfTuples The number of tuples to (re)allocate.
* @param {String} dataType The typed array type name.
* @param {Number} numberOfComponents The number of components of the array.
*/
export function allocateArray(polyData: vtkPolyData,
name: string,
numberOfTuples: number,
dataType?: string,
numberOfComponents?: number): vtkDataArray|null;

export declare const vtkWidgetRepresentation: {
newInstance: typeof newInstance;
extend: typeof extend;
}
export default vtkWidgetRepresentation;
index.js
import macro from 'vtk.js/Sources/macros';
import vtkProp from 'vtk.js/Sources/Rendering/Core/Prop';
import vtkMath from 'vtk.js/Sources/Common/Core/Math';

import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkPoints from 'vtk.js/Sources/Common/Core/Points';
import { Behavior } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation/Constants';
import { RenderingTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';
import { CATEGORIES } from 'vtk.js/Sources/Rendering/Core/Mapper/CoincidentTopologyHelper';
import { POLYDATA_FIELDS } from 'vtk.js/Sources/Common/DataModel/PolyData/Constants';

const { vtkErrorMacro, vtkWarningMacro } = macro;

// ----------------------------------------------------------------------------
const STYLE_CATEGORIES = ['active', 'inactive', 'static'];

export function mergeStyles(elementNames, ...stylesToMerge) {
const newStyleObject = { active: {}, inactive: {}, static: {} };
STYLE_CATEGORIES.forEach((category) => {
const cat = newStyleObject[category];
elementNames.forEach((name) => {
if (!cat[name]) {
cat[name] = {};
}
stylesToMerge
.filter((s) => s && s[category] && s[category][name])
.forEach((s) => Object.assign(cat[name], s[category][name]));
});
});

return newStyleObject;
}

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

export function applyStyles(pipelines, styles, activeActor) {
if (!activeActor) {
// static
Object.keys(styles.static).forEach((name) => {
if (pipelines[name]) {
pipelines[name].actor.getProperty().set(styles.static[name]);
}
});
// inactive
Object.keys(styles.inactive).forEach((name) => {
if (pipelines[name]) {
pipelines[name].actor.getProperty().set(styles.inactive[name]);
}
});
} else {
Object.keys(pipelines).forEach((name) => {
const style =
pipelines[name].actor === activeActor
? styles.active[name]
: styles.inactive[name];
if (style) {
pipelines[name].actor.getProperty().set(style);
}
});
}
}

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

export function connectPipeline(pipeline) {
let source = pipeline.source;
if (pipeline.filter) {
if (source.isA('vtkDataSet')) {
pipeline.filter.setInputData(source);
} else {
pipeline.filter.setInputConnection(source.getOutputPort());
}
source = pipeline.filter;
}
if (source) {
if (source.isA('vtkDataSet')) {
pipeline.mapper.setInputData(source);
} else {
pipeline.mapper.setInputConnection(source.getOutputPort());
}
}
if (pipeline.glyph) {
pipeline.mapper.setInputConnection(pipeline.glyph.getOutputPort(), 1);
}
pipeline.actor.setMapper(pipeline.mapper);
}

export function getPixelWorldHeightAtCoord(worldCoord, displayScaleParams) {
const {
dispHeightFactor,
cameraPosition,
cameraDir,
isParallel,
rendererPixelDims,
} = displayScaleParams;
let scale = 1;
if (isParallel) {
scale = dispHeightFactor;
} else {
const worldCoordToCamera = [...worldCoord];
vtkMath.subtract(worldCoordToCamera, cameraPosition, worldCoordToCamera);
scale = vtkMath.dot(worldCoordToCamera, cameraDir) * dispHeightFactor;
}

const rHeight = rendererPixelDims[1];
return scale / rHeight;
}

// Internal convenient function to create a data array:
export function allocateArray(
polyData,
name,
numberOfTuples,
dataType,
numberOfComponents
) {
// Check first whether name is points, verts, lines, polys, otherwise it is a point data array.
let dataArray =
polyData[`get${macro.capitalize(name)}`]?.() ||
polyData.getPointData().getArrayByName(name);
if (
!dataArray ||
(dataType !== undefined && dataArray.getDataType() !== dataType) ||
(numberOfComponents !== undefined &&
dataArray.getNumberOfComponents() !== numberOfComponents)
) {
let arrayType = vtkDataArray;
let arrayDataType = dataType;
let arrayNumberOfComponents = numberOfComponents;
if (name === 'points') {
arrayType = vtkPoints;
arrayDataType = arrayDataType ?? 'Float32Array';
arrayNumberOfComponents = numberOfComponents ?? 3;
} else if (POLYDATA_FIELDS.includes(name)) {
arrayType = vtkCellArray;
arrayDataType = arrayDataType ?? 'Uint16Array';
arrayNumberOfComponents = numberOfComponents ?? 1;
} else {
// data array
arrayDataType = arrayDataType ?? 'Float32Array';
arrayNumberOfComponents = numberOfComponents ?? 1;
}
dataArray = arrayType.newInstance({
name,
dataType: arrayDataType,
numberOfComponents: arrayNumberOfComponents,
size: arrayNumberOfComponents * numberOfTuples,
empty: numberOfTuples === 0,
});
if (name === 'points' || POLYDATA_FIELDS.includes(name)) {
polyData[`set${macro.capitalize(name)}`](dataArray);
} else {
polyData.getPointData().addArray(dataArray);
}
} else if (dataArray.getNumberOfTuples() !== numberOfTuples) {
dataArray.resize(numberOfTuples);
}
return dataArray;
}

// ----------------------------------------------------------------------------
// vtkWidgetRepresentation
// ----------------------------------------------------------------------------

function vtkWidgetRepresentation(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkWidgetRepresentation');
const superclass = { ...publicAPI };
// Internal cache
const cache = { mtimes: {}, states: [] };

// --------------------------------------------------------------------------
publicAPI.getActors = () => model.actors;
publicAPI.getNestedProps = publicAPI.getActors;
// --------------------------------------------------------------------------

publicAPI.setLabels = (...labels) => {
if (labels.length === 1) {
model.labels = [].concat(labels[0]);
} else {
model.labels = labels;
}
publicAPI.modified();
};

publicAPI.getRepresentationStates = (input = model.inputData[0]) => {
if (
cache.mtimes.representation === publicAPI.getMTime() &&
cache.mtimes.input === input.getMTime()
) {
return cache.states;
}

// Reinitialize cache
cache.mtimes.representation = publicAPI.getMTime();
cache.mtimes.input = input.getMTime();
cache.states = [];

// Fill states that are going to be used in the representation
model.labels.forEach((name) => {
cache.states = cache.states.concat(input.getStatesWithLabel(name) || []);
});

return cache.states;
};

publicAPI.getSelectedState = (prop, compositeID) => {
const representationStates = publicAPI.getRepresentationStates();
if (compositeID < representationStates.length) {
return representationStates[compositeID];
}
vtkErrorMacro(
`Representation ${publicAPI.getClassName()} should implement getSelectedState(prop, compositeID) method.`
);
return null;
};

publicAPI.updateActorVisibility = (
renderingType = RenderingTypes.FRONT_BUFFER,
ctxVisible = true,
handleVisible = true
) => {
let otherFlag = true;
switch (model.behavior) {
case Behavior.HANDLE:
otherFlag =
renderingType === RenderingTypes.PICKING_BUFFER || handleVisible;
break;
case Behavior.CONTEXT:
otherFlag = ctxVisible;
break;
default:
otherFlag = true;
break;
}
const visibilityFlag = otherFlag;
for (let i = 0; i < model.actors.length; i++) {
if (model.visibilityFlagArray) {
model.actors[i].setVisibility(
visibilityFlag && model.visibilityFlagArray[i]
);
} else {
model.actors[i].setVisibility(visibilityFlag);
}
}
if (model.alwaysVisibleActors) {
for (let i = 0; i < model.alwaysVisibleActors.length; i++) {
model.alwaysVisibleActors[i].setVisibility(true);
}
}
};

function applyCoincidentTopologyParametersToMapper(mapper, parameters) {
if (mapper && mapper.setResolveCoincidentTopologyToPolygonOffset) {
mapper.setResolveCoincidentTopologyToPolygonOffset();
CATEGORIES.forEach((category) => {
if (parameters[category]) {
const methodName = `setRelativeCoincidentTopology${category}OffsetParameters`;
if (mapper[methodName]) {
const { factor, offset } = parameters[category];
mapper[methodName](factor, offset);
}
}
});
}
}

// Add warning to model.actors.push
model.actors.push = (...args) => {
vtkWarningMacro(
'You should use publicAPI.addActor() to initialize the actor properly'
);
args.forEach((actor) => publicAPI.addActor(actor));
};

publicAPI.addActor = (actor) => {
applyCoincidentTopologyParametersToMapper(
actor.getMapper(),
model.coincidentTopologyParameters
);
Array.prototype.push.apply(model.actors, [actor]);
};

publicAPI.setCoincidentTopologyParameters = (parameters) => {
const modified = superclass.setCoincidentTopologyParameters(parameters);
if (modified) {
publicAPI.getActors().forEach((actor) => {
applyCoincidentTopologyParametersToMapper(
actor.getMapper(),
model.coincidentTopologyParameters
);
});
}
};

// Make sure setting the labels at build time works with string/array...
publicAPI.setLabels(model.labels);
}

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

function defaultValues(initialValues) {
return {
activeScaleFactor: 1.2,
activeColor: 1,
useActiveColor: true,
actors: [],
labels: [],
behavior: Behavior.CONTEXT,
coincidentTopologyParameters: {
Point: {
factor: -1.0,
offset: -1.0,
},
Line: {
factor: -1.0,
offset: -1.0,
},
Polygon: {
factor: -1.0,
offset: -1.0,
},
},
scaleInPixels: false,
displayScaleParams: {
dispHeightFactor: 1,
cameraPosition: [0, 0, 0],
cameraDir: [1, 0, 0],
isParallel: false,
rendererPixelDims: [1, 1],
},
_internalArrays: {},
...initialValues,
};
}

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

export function extend(publicAPI, model, initialValues = {}) {
// Object methods
vtkProp.extend(publicAPI, model, defaultValues(initialValues));
macro.algo(publicAPI, model, 1, 1);
macro.get(publicAPI, model, ['labels']);
macro.set(publicAPI, model, [
{ type: 'object', name: 'displayScaleParams' },
{ type: 'object', name: 'coincidentTopologyParameters' },
]);
macro.setGet(publicAPI, model, [
'scaleInPixels',
'activeScaleFactor',
'activeColor',
'useActiveColor',
]);

// Object specific methods
vtkWidgetRepresentation(publicAPI, model);
}

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

export default {
extend,
mergeStyles,
applyStyles,
connectPipeline,
getPixelWorldHeightAtCoord,
};