StateBuilder

Methods

Source

boundsMixin.js
import macro from 'vtk.js/Sources/macros';
import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox';

function vtkBoundsMixin(publicAPI, model) {
const sourceBounds = [];
const bbox = [...vtkBoundingBox.INIT_BOUNDS];

publicAPI.containsPoint = (x, y, z) => {
if (Array.isArray(x)) {
return vtkBoundingBox.containsPoint(bbox, x[0], x[1], x[2]);
}
return vtkBoundingBox.containsPoint(bbox, x, y, z);
};

publicAPI.placeWidget = (bounds) => {
model.bounds = [];
const center = [
(bounds[0] + bounds[1]) / 2.0,
(bounds[2] + bounds[3]) / 2.0,
(bounds[4] + bounds[5]) / 2.0,
];
for (let i = 0; i < 6; i++) {
const axisCenter = center[Math.floor(i / 2)];
sourceBounds[i] = bounds[i];
model.bounds[i] =
(bounds[i] - axisCenter) * model.placeFactor + axisCenter;
}
vtkBoundingBox.setBounds(bbox, model.bounds);
publicAPI.invokeBoundsChange(model.bounds);
publicAPI.modified();
};

publicAPI.setPlaceFactor = (factor) => {
if (model.placeFactor !== factor) {
model.placeFactor = factor;
model.bounds = [];
const center = [
(sourceBounds[0] + sourceBounds[1]) / 2.0,
(sourceBounds[2] + sourceBounds[3]) / 2.0,
(sourceBounds[4] + sourceBounds[5]) / 2.0,
];
for (let i = 0; i < 6; i++) {
const axisCenter = center[Math.floor(i / 2)];
model.bounds[i] =
(sourceBounds[i] - axisCenter) * model.placeFactor + axisCenter;
}
vtkBoundingBox.setBounds(bbox, model.bounds);
publicAPI.invokeBoundsChange(model.bounds);
publicAPI.modified();
}
};
}

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

const DEFAULT_VALUES = {
bounds: [-1, 1, -1, 1, -1, 1],
placeFactor: 1,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['bounds'], 6);
macro.get(publicAPI, model, ['placeFactor']);
macro.event(publicAPI, model, 'BoundsChange');

model.bounds = model.bounds.slice();
vtkBoundsMixin(publicAPI, model);
}

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

export default { extend };
color3Mixin.js
import macro from 'vtk.js/Sources/macros';
/**
* RGB Uint8 color mixin. Not to be used in conjunction with `color` mixin.
* @see color
*/
const DEFAULT_VALUES = {
color3: [255, 255, 255],
opacity: 255,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['color3'], 3, 255);
macro.setGet(publicAPI, model, ['opacity']);
}

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

export default { extend };
colorMixin.js
import macro from 'vtk.js/Sources/macros';
/**
* Scalar color mixin.
* Scalar value [0, 1] references a color in the mapper LUT.
* Not to be used in conjunction with `color3` mixin.
* @see color3
*/

const DEFAULT_VALUES = {
color: 0.5,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['color']);
}

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

export default { extend };
cornerMixin.js
import macro from 'vtk.js/Sources/macros';

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

function vtkCornerMixin(publicAPI, model) {
publicAPI.translate = (dx, dy, dz) => {
const [x, y, z] = publicAPI.getCornerByReference();
publicAPI.setCorner(x + dx, y + dy, z + dz);
};
}

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

const DEFAULT_VALUES = {
corner: [0, 0, 0],
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['corner'], 3);
vtkCornerMixin(publicAPI, model);
}

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

export default { extend };
directionMixin.js
import macro from 'vtk.js/Sources/macros';
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';

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

function vtkDirectionMixin(publicAPI, model) {
const transform =
model.angleUnit === 'degree'
? vtkMatrixBuilder.buildFromDegree()
: vtkMatrixBuilder.buildFromRadian();

publicAPI.rotateFromDirections = (originDirection, targetDirection) => {
transform
.identity()
.rotateFromDirections(originDirection, targetDirection)
.apply(model.direction);
publicAPI.modified();
};

publicAPI.rotate = (angle, axis) => {
transform.identity().rotate(angle, axis).apply(model.direction);
};

publicAPI.rotateX = (angle) => {
transform.identity().rotateX(angle).apply(model.direction);
};

publicAPI.rotateY = (angle) => {
transform.identity().rotateY(angle).apply(model.direction);
};

publicAPI.rotateZ = (angle) => {
transform.identity().rotateZ(angle).apply(model.direction);
};
}

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

const DEFAULT_VALUES = {
direction: [1, 0, 0],
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['direction'], 3);
vtkDirectionMixin(publicAPI, model);
}

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

export default { extend };
index.d.ts
import vtkWidgetState from '../WidgetState';

export interface StateBuilder {
addDynamicMixinState(buildInfo: {
labels: string[];
mixins: string[];
name: string;
initialValues?: object;
}): StateBuilder;
addStateFromMixin(buildInfo: {
labels: string[];
mixins: string[];
name: string;
initialValues?: object;
}): StateBuilder;
addStateFromInstance(stateInfo: {
labels: string[];
name: string;
instance: vtkWidgetState;
});
addField(field: { name: string; initialValue: any });
build(...mixins: string[]): vtkWidgetState;
}

export function createBuilder(): StateBuilder;

export declare const vtkStateBuilder: {
createBuilder: typeof createBuilder;
};
index.js
import macro from 'vtk.js/Sources/macros';

import vtkWidgetState from 'vtk.js/Sources/Widgets/Core/WidgetState';

import bounds from 'vtk.js/Sources/Widgets/Core/StateBuilder/boundsMixin';
import color from 'vtk.js/Sources/Widgets/Core/StateBuilder/colorMixin';
import color3 from 'vtk.js/Sources/Widgets/Core/StateBuilder/color3Mixin';
import corner from 'vtk.js/Sources/Widgets/Core/StateBuilder/cornerMixin';
import direction from 'vtk.js/Sources/Widgets/Core/StateBuilder/directionMixin';
import manipulator from 'vtk.js/Sources/Widgets/Core/StateBuilder/manipulatorMixin';
import name from 'vtk.js/Sources/Widgets/Core/StateBuilder/nameMixin';
import orientation from 'vtk.js/Sources/Widgets/Core/StateBuilder/orientationMixin';
import origin from 'vtk.js/Sources/Widgets/Core/StateBuilder/originMixin';
import scale1 from 'vtk.js/Sources/Widgets/Core/StateBuilder/scale1Mixin';
import scale3 from 'vtk.js/Sources/Widgets/Core/StateBuilder/scale3Mixin';
import text from 'vtk.js/Sources/Widgets/Core/StateBuilder/textMixin';
import visible from 'vtk.js/Sources/Widgets/Core/StateBuilder/visibleMixin';
import shape from 'vtk.js/Sources/Widgets/Core/StateBuilder/shapeMixin';

const { vtkErrorMacro } = macro;

// ----------------------------------------------------------------------------
// Global type lookup map
// ----------------------------------------------------------------------------

const MIXINS = {
bounds,
color,
color3,
corner,
direction,
manipulator,
name,
orientation,
origin,
scale1,
scale3,
text,
visible,
shape,
};

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

function newInstance(
mixins,
initialValues,
publicAPI = {},
model = {},
skipWidgetState = false
) {
if (!skipWidgetState) {
vtkWidgetState.extend(publicAPI, model, initialValues);
}

for (let i = 0; i < mixins.length; i++) {
const mixin = MIXINS[mixins[i]];
if (mixin) {
mixin.extend(publicAPI, model, initialValues);
} else {
vtkErrorMacro('Invalid mixin name:', mixins[i]);
}
}
macro.safeArrays(model);

return Object.freeze(publicAPI);
}

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

class Builder {
constructor() {
this.publicAPI = {};
this.model = {};

vtkWidgetState.extend(this.publicAPI, this.model);
// The root state should always have the bounds/placeWidget/widgetFactor
bounds.extend(this.publicAPI, this.model);
}

/* eslint-disable no-shadow */
addDynamicMixinState({ labels, mixins, name, initialValues }) {
const listName = `${name}List`;
this.model[listName] = [];
// Create new Instance method
this.publicAPI[`add${macro.capitalize(name)}`] = (values) => {
const instance = newInstance(mixins, {
...initialValues,
...values,
});
this.publicAPI.bindState(instance, labels);
this.model[listName].push(instance);
this.publicAPI.modified();
return instance;
};
this.publicAPI[`remove${macro.capitalize(name)}`] = (instanceOrIndex) => {
let removeIndex = this.model[listName].indexOf(instanceOrIndex);
if (removeIndex === -1 && instanceOrIndex < this.model[listName].length) {
removeIndex = instanceOrIndex;
}
const instance = this.model[listName][removeIndex];
if (instance) {
this.publicAPI.unbindState(instance);
}
this.model[listName].splice(removeIndex, 1);
this.publicAPI.modified();
};
this.publicAPI[`get${macro.capitalize(name)}List`] = () =>
this.model[listName].slice();
this.publicAPI[`clear${macro.capitalize(name)}List`] = () => {
while (this.model[listName].length) {
const instance = this.model[listName].pop();
if (instance) {
this.publicAPI.unbindState(instance);
}
}
this.publicAPI.modified();
};
return this;
}

addStateFromMixin({ labels, mixins, name, initialValues }) {
const instance = newInstance(mixins, initialValues);
this.model[name] = instance;
this.publicAPI.bindState(instance, labels);
macro.setGet(this.publicAPI, this.model, [name]);
return this;
}

addStateFromInstance({ labels, name, instance }) {
this.model[name] = instance;
this.publicAPI.bindState(instance, labels);
macro.setGet(this.publicAPI, this.model, [name]);
return this;
}

addField({ name, initialValue }) {
if (Array.isArray(initialValue)) {
macro.setGetArray(
this.publicAPI,
this.model,
[name],
initialValue.length
);
} else {
macro.setGet(this.publicAPI, this.model, [name]);
}
this.model[name] = initialValue;
return this;
}

build(...mixins) {
return newInstance(mixins, {}, this.publicAPI, this.model, true);
}
}

// ----------------------------------------------------------------------------
// Public API
// ----------------------------------------------------------------------------

export function createBuilder() {
return new Builder();
}

export default {
createBuilder,
};
manipulatorMixin.js
import macro from 'vtk.js/Sources/macros';

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

function vtkManipulatorMixin(publicAPI, model) {
publicAPI.updateManipulator = () => {
if (model.manipulator) {
const { origin, normal, direction } = model;
const {
setHandleOrigin,
setHandleCenter,
setHandleNormal,
setHandleDirection,
} = model.manipulator;

if (origin && setHandleOrigin) {
setHandleOrigin(origin);
} else if (origin && setHandleCenter) {
setHandleCenter(origin);
}

if (direction && setHandleDirection) {
setHandleDirection(direction);
} else if (direction && !normal && setHandleNormal) {
setHandleNormal(direction);
} else if (normal && setHandleDirection) {
setHandleDirection(normal);
}
}
};
}

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

const DEFAULT_VALUES = {
manipulator: null,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['manipulator']);
vtkManipulatorMixin(publicAPI, model);
}

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

export default { extend };
nameMixin.js
import macro from 'vtk.js/Sources/macros';

const DEFAULT_VALUES = {
name: '',
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['name']);
}

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

export default { extend };
orientationMixin.js
import macro from 'vtk.js/Sources/macros';
import * as vtkMath from 'vtk.js/Sources/Common/Core/Math';

function eq(v1, v2) {
return (
v1.length === 3 &&
v2.length === 3 &&
v1[0] === v2[0] &&
v1[1] === v2[1] &&
v1[2] === v2[2]
);
}

function isSame(o, p1, p2, before) {
return eq(o, before.o) && eq(p1, before.p1) && eq(p2, before.p2);
}

// function axis(o, p1, p2) {
// if (o[0] === p1[0] && p1[0] === p2[0]) {
// return 'X';
// }
// if (o[1] === p1[1] && p1[1] === p2[1]) {
// return 'Y';
// }
// if (o[2] === p1[2] && p1[2] === p2[2]) {
// return 'Z';
// }
// return '?';
// }

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

function vtkOrientationMixin(publicAPI, model) {
const previousPoints = { o: [], p1: [], p2: [] };

publicAPI.normalize = () => {
vtkMath.normalize(model.up);
vtkMath.normalize(model.right);
vtkMath.normalize(model.direction);
publicAPI.modified();
};

publicAPI.updateFromOriginRightUp = (o, p1, p2) => {
if (isSame(o, p1, p2, previousPoints)) {
return;
}
previousPoints.o = o.slice();
previousPoints.p1 = p1.slice();
previousPoints.p2 = p2.slice();

model.up = [p2[0] - o[0], p2[1] - o[1], p2[2] - o[2]];
model.right = [p1[0] - o[0], p1[1] - o[1], p1[2] - o[2]];
vtkMath.cross(model.up, model.right, model.direction);
vtkMath.cross(model.direction, model.up, model.right);
publicAPI.normalize();
publicAPI.modified();
};
}

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

const DEFAULT_VALUES = {
up: [0, 1, 0],
right: [1, 0, 0],
direction: [0, 0, 1],
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['up', 'right', 'direction'], 3);
vtkOrientationMixin(publicAPI, model);
}

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

export default { extend };
originMixin.js
import macro from 'vtk.js/Sources/macros';
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
import { getPixelWorldHeightAtCoord } from 'vtk.js/Sources/Widgets/Core/WidgetManager';

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

function vtkOriginMixin(publicAPI, model) {
const superClass = { ...publicAPI };
publicAPI.translate = (dx, dy, dz) => {
const [x, y, z] = publicAPI.getOriginByReference();
publicAPI.setOrigin(x + dx, y + dy, z + dz);
};
publicAPI.getOrigin = (displayScaleParams) => {
const origin = superClass.getOrigin();
if (!model.offset) {
return origin;
}
if (!displayScaleParams) {
return vtkMath.add(origin, model.offset, origin);
}
const pixelWorldHeight = getPixelWorldHeightAtCoord(
origin,
displayScaleParams
);
const { rendererPixelDims } = displayScaleParams;
const totalSize = Math.min(rendererPixelDims[0], rendererPixelDims[1]);
return vtkMath.multiplyAccumulate(
origin,
model.offset,
totalSize * pixelWorldHeight,
origin
);
};
}

// ----------------------------------------------------------------------------
/**
* offset: optional offset that can be scaled to pixel screen space.
*/
const DEFAULT_VALUES = {
origin: null,
offset: null,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['origin', 'offset'], 3);
vtkOriginMixin(publicAPI, model);
}

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

export default { extend };
scale1Mixin.js
import macro from 'vtk.js/Sources/macros';

const DEFAULT_VALUES = {
scale1: 0.5,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['scale1']);
}

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

export default { extend };
scale3Mixin.js
import macro from 'vtk.js/Sources/macros';

const DEFAULT_VALUES = {
scale3: [1, 1, 1],
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGetArray(publicAPI, model, ['scale3'], 3);
}

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

export default { extend };
shapeMixin.js
import macro from 'vtk.js/Sources/macros';

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

const DEFAULT_VALUES = {
shape: '',
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['shape']);
}

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

export default { extend };
textMixin.js
import macro from 'vtk.js/Sources/macros';

const DEFAULT_VALUES = {
text: 'DefaultText',
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['text']);
}

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

export default { extend };
visibleMixin.js
import macro from 'vtk.js/Sources/macros';

const DEFAULT_VALUES = {
visible: true,
};

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

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
macro.setGet(publicAPI, model, ['visible']);
publicAPI.isVisible = publicAPI.getVisible;
}

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

export default { extend };