EllipseWidget

Source

behavior.js
import shapeBehavior from 'vtk.js/Sources/Widgets/Widgets3D/ShapeWidget/behavior';
import { vec3 } from 'gl-matrix';

export default function widgetBehavior(publicAPI, model) {
model.shapeHandle = model.widgetState.getEllipseHandle();
model.point1Handle = model.widgetState.getPoint1Handle();
model.point2Handle = model.widgetState.getPoint2Handle();
model.point1Handle.setManipulator(model.manipulator);
model.point2Handle.setManipulator(model.manipulator);

// We inherit shapeBehavior
shapeBehavior(publicAPI, model);
const superClass = { ...publicAPI };

model.classHierarchy.push('vtkEllipseWidgetProp');

publicAPI.setCorners = (point1, point2) => {
if (superClass.setCorners) {
superClass.setCorners(point1, point2);
}

const center = [
0.5 * (point1[0] + point2[0]),
0.5 * (point1[1] + point2[1]),
0.5 * (point1[2] + point2[2]),
];

const diagonal = [0, 0, 0];
vec3.subtract(diagonal, point2, center);

const right = model.shapeHandle.getRight();
const up = model.shapeHandle.getUp();
const dir = model.shapeHandle.getDirection();

const rightComponent = vec3.dot(diagonal, right);
const upComponent = vec3.dot(diagonal, up);
const dirComponent = vec3.dot(diagonal, dir);

model.shapeHandle.setOrigin(center);
model.shapeHandle.setScale3([rightComponent, upComponent, dirComponent]);
};
}
index.js
import macro from 'vtk.js/Sources/macros';
import vtkCircleContextRepresentation from 'vtk.js/Sources/Widgets/Representations/CircleContextRepresentation';
import vtkPlanePointManipulator from 'vtk.js/Sources/Widgets/Manipulators/PlaneManipulator';
import vtkShapeWidget from 'vtk.js/Sources/Widgets/Widgets3D/ShapeWidget';
import vtkSphereHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/SphereHandleRepresentation';
import widgetBehavior from 'vtk.js/Sources/Widgets/Widgets3D/EllipseWidget/behavior';
import stateGenerator from 'vtk.js/Sources/Widgets/Widgets3D/EllipseWidget/state';

import {
BehaviorCategory,
ShapeBehavior,
} from 'vtk.js/Sources/Widgets/Widgets3D/ShapeWidget/Constants';

import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants';

// ----------------------------------------------------------------------------
// Factory
// ----------------------------------------------------------------------------

function vtkEllipseWidget(publicAPI, model) {
model.classHierarchy.push('vtkEllipseWidget');

// --- Widget Requirement ---------------------------------------------------

model.methodsToLink = [
...model.methodsToLink,
'activeScaleFactor',
'activeColor',
'useActiveColor',
'glyphResolution',
'defaultScale',
'drawBorder',
'drawFace',
'opacity',
];

publicAPI.getRepresentationsForViewType = (viewType) => {
switch (viewType) {
case ViewTypes.DEFAULT:
case ViewTypes.GEOMETRY:
case ViewTypes.SLICE:
case ViewTypes.VOLUME:
default:
return [
{
builder: vtkSphereHandleRepresentation,
labels: ['moveHandle'],
},
{
builder: vtkCircleContextRepresentation,
labels: ['ellipseHandle'],
},
];
}
};

// --------------------------------------------------------------------------
// initialization
// --------------------------------------------------------------------------

publicAPI.setManipulator(
model.manipulator ||
vtkPlanePointManipulator.newInstance({ useCameraNormal: true })
);
}

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

function defaultValues(initialValues) {
return {
behavior: widgetBehavior,
widgetState: stateGenerator(),
modifierBehavior: {
None: {
[BehaviorCategory.PLACEMENT]:
ShapeBehavior[BehaviorCategory.PLACEMENT].CLICK_AND_DRAG,
[BehaviorCategory.POINTS]:
ShapeBehavior[BehaviorCategory.POINTS].CENTER_TO_CORNER,
[BehaviorCategory.RATIO]: ShapeBehavior[BehaviorCategory.RATIO].FREE,
},
Shift: {
[BehaviorCategory.RATIO]: ShapeBehavior[BehaviorCategory.RATIO].FIXED,
},
Control: {
[BehaviorCategory.POINTS]:
ShapeBehavior[BehaviorCategory.POINTS].CORNER_TO_CORNER,
},
},
...initialValues,
};
}

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

export function extend(publicAPI, model, initialValues = {}) {
vtkShapeWidget.extend(publicAPI, model, defaultValues(initialValues));
macro.setGet(publicAPI, model, ['widgetState']);

vtkEllipseWidget(publicAPI, model);
}

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

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

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

export default { newInstance, extend };
state.js
import vtkStateBuilder from 'vtk.js/Sources/Widgets/Core/StateBuilder';
import { TextPosition } from 'vtk.js/Sources/Widgets/Widgets3D/ShapeWidget/Constants';

export default function generateState() {
return (
vtkStateBuilder
.createBuilder()
.addStateFromMixin({
labels: ['moveHandle'],
mixins: ['origin', 'color', 'scale1', 'visible', 'manipulator'],
name: 'point1Handle',
initialValues: {
scale1: 10,
visible: false,
},
})
.addStateFromMixin({
labels: ['moveHandle'],
mixins: ['origin', 'color', 'scale1', 'visible', 'manipulator'],
name: 'point2Handle',
initialValues: {
scale1: 10,
visible: false,
},
})
.addStateFromMixin({
labels: ['ellipseHandle'],
mixins: ['origin', 'color', 'scale3', 'visible', 'orientation'],
name: 'ellipseHandle',
initialValues: {
// visible: false,
scale3: [1, 1, 1],
},
})
// FIXME: How to not duplicate with RectangleWidget
.addStateFromMixin({
labels: ['SVGtext'],
mixins: ['origin', 'color', 'text', 'visible'],
name: 'text',
initialValues: {
/* text is empty to set a text filed in the SVGLayer and to avoid
* displaying text before positioning the handles */
text: '',
},
})
// FIXME: to move in text handle sub state
.addField({
name: 'textPosition',
initialValue: [
TextPosition.CENTER,
TextPosition.CENTER,
TextPosition.CENTER,
],
})
.addField({
name: 'textWorldMargin',
initialValue: 0,
})
.build()
);
}