@acransac/vtk.js
Version:
Visualization Toolkit for the Web
303 lines (245 loc) • 9.91 kB
JavaScript
import HandleRepConstants from 'vtk.js/Sources/Interaction/Widgets/HandleRepresentation/Constants';
import macro from 'vtk.js/Sources/macro';
import vtkAbstractWidget from 'vtk.js/Sources/Interaction/Widgets/AbstractWidget';
import vtkHandleWidget from 'vtk.js/Sources/Interaction/Widgets/HandleWidget';
import vtkLineRepresentation from 'vtk.js/Sources/Interaction/Widgets/LineRepresentation';
import { State } from 'vtk.js/Sources/Interaction/Widgets/LineRepresentation/Constants';
import Constants from './Constants';
const { WidgetState } = Constants;
const { InteractionState } = HandleRepConstants;
// ----------------------------------------------------------------------------
// vtkHandleWidget methods
// ----------------------------------------------------------------------------
function vtkLineWidget(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkLineWidget');
const superClass = { ...publicAPI };
publicAPI.setCursor = (state) => {
switch (state) {
case State.OUTSIDE: {
model.interactor.getView().setCursor('default');
break;
}
default: {
model.interactor.getView().setCursor('pointer');
}
}
};
publicAPI.setCurrentHandle = (value) => {
model.currentHandle = value;
};
publicAPI.setInteractor = (i) => {
superClass.setInteractor(i);
model.point1Widget.setInteractor(model.interactor);
model.point2Widget.setInteractor(model.interactor);
publicAPI.modified();
};
publicAPI.setEnabled = (enabling) => {
superClass.setEnabled(enabling);
if (!model.widgetRep) {
return;
}
// Use the representations from the line widget
// for the point widgets to avoid creating
// default representations when setting the
// interactor below
model.point1Widget.setWidgetRep(model.widgetRep.getPoint1Representation());
model.point2Widget.setWidgetRep(model.widgetRep.getPoint2Representation());
if (model.widgetState === WidgetState.START) {
model.point1Widget.setEnabled(0);
model.point2Widget.setEnabled(0);
model.widgetRep.setLineVisibility(0);
model.widgetRep.setPoint1Visibility(1);
model.widgetRep.setPoint2Visibility(0);
} else {
model.point1Widget.setEnabled(enabling);
model.point2Widget.setEnabled(enabling);
model.widgetRep.setLineVisibility(1);
model.widgetRep.setPoint1Visibility(1);
model.widgetRep.setPoint2Visibility(1);
}
};
publicAPI.setProcessEvents = (processEvents) => {
superClass.setProcessEvents(processEvents);
model.point1Widget.setProcessEvents(processEvents);
model.point2Widget.setProcessEvents(processEvents);
};
publicAPI.setWidgetStateToStart = () => {
model.widgetState = WidgetState.START;
publicAPI.setCurrentHandle(0);
publicAPI.setEnabled(model.enabled);
};
publicAPI.setWidgetStateToManipulate = () => {
model.widgetState = WidgetState.MANIPULATE;
publicAPI.setCurrentHandle(-1);
publicAPI.setEnabled(model.enabled);
};
publicAPI.handleMouseMove = (callData) => publicAPI.moveAction(callData);
publicAPI.handleLeftButtonPress = (callData) =>
publicAPI.selectAction(callData);
publicAPI.handleLeftButtonRelease = (callData) =>
publicAPI.endSelectAction(callData);
publicAPI.handleMiddleButtonPress = (callData) =>
publicAPI.translateAction(callData);
publicAPI.handleMiddleButtonRelease = (callData) =>
publicAPI.endSelectAction(callData);
publicAPI.handleRightButtonPress = (callData) =>
publicAPI.scaleAction(callData);
publicAPI.handleRightButtonRelease = (callData) =>
publicAPI.endSelectAction(callData);
publicAPI.selectAction = (callData) => {
const position = [callData.position.x, callData.position.y];
if (model.widgetState === WidgetState.START) {
const pos3D = model.point1Widget
.getWidgetRep()
.displayToWorld(position, 0);
// The first time we click, the method is called twice
if (model.currentHandle < 1) {
model.widgetRep.setLineVisibility(1);
model.widgetRep.setPoint1WorldPosition(pos3D);
// Trick to avoid a line with a zero length
// If the line has a zero length, it appears with bad extremities
pos3D[0] += 0.000000001;
model.widgetRep.setPoint2WorldPosition(pos3D);
publicAPI.setCurrentHandle(model.currentHandle + 1);
} else {
model.widgetRep.setPoint2Visibility(1);
model.widgetRep.setPoint2WorldPosition(pos3D);
// When two points are placed, we go back to the native
model.widgetState = WidgetState.MANIPULATE;
publicAPI.setCurrentHandle(-1);
}
} else {
const state = model.widgetRep.computeInteractionState(position);
if (state === InteractionState.OUTSIDE) {
return;
}
model.widgetState = WidgetState.ACTIVE;
publicAPI.updateHandleWidgets(state);
publicAPI.invokeStartInteractionEvent();
}
model.widgetRep.startComplexWidgetInteraction(position);
publicAPI.render();
};
publicAPI.translateAction = (callData) => {
const position = [callData.position.x, callData.position.y];
const state = model.widgetRep.computeInteractionState(position);
if (state === InteractionState.OUTSIDE) {
return;
}
model.widgetState = WidgetState.ACTIVE;
model.widgetRep.startComplexWidgetInteraction(position);
publicAPI.invokeStartInteractionEvent();
};
publicAPI.scaleAction = (callData) => {
const position = [callData.position.x, callData.position.y];
const state = model.widgetRep.computeInteractionState(position);
if (state === InteractionState.OUTSIDE) {
return;
}
model.widgetState = WidgetState.ACTIVE;
model.widgetRep.startComplexWidgetInteraction(position);
publicAPI.invokeStartInteractionEvent();
};
publicAPI.moveAction = (callData) => {
const position = [callData.position.x, callData.position.y];
let modified = false;
if (model.widgetState === WidgetState.MANIPULATE) {
// In MANIPULATE, we are hovering above the widget
// Check if above a sphere and enable/disable if needed
const state = model.widgetRep.computeInteractionState(position);
modified = publicAPI.updateHandleWidgets(state);
} else if (model.widgetState === WidgetState.START) {
// In START, we are placing the sphere widgets.
// Move current handle along the mouse position.
model.widgetRep.complexWidgetInteraction(position);
const pos3D = model.point1Widget
.getWidgetRep()
.displayToWorld(position, 0);
if (model.currentHandle === 0) {
model.widgetRep.setPoint1WorldPosition(pos3D);
} else {
model.widgetRep.setPoint2WorldPosition(pos3D);
}
modified = true;
} else if (model.widgetState === WidgetState.ACTIVE) {
// In ACTIVE, we are moving a sphere widget.
// Update the line extremities to follow the spheres.
model.widgetRep.setPoint1WorldPosition(
model.point1Widget.getWidgetRep().getWorldPosition()
);
model.widgetRep.setPoint2WorldPosition(
model.point2Widget.getWidgetRep().getWorldPosition()
);
modified = true;
}
if (modified) {
publicAPI.invokeInteractionEvent();
publicAPI.render();
}
};
publicAPI.endSelectAction = (callData) => {
if (model.widgetState === WidgetState.START) {
return;
}
const position = [callData.position.x, callData.position.y];
model.widgetRep.complexWidgetInteraction(position);
model.widgetRep.setPoint1WorldPosition(
model.point1Widget.getWidgetRep().getWorldPosition()
);
model.widgetRep.setPoint2WorldPosition(
model.point2Widget.getWidgetRep().getWorldPosition()
);
model.widgetState = WidgetState.MANIPULATE;
publicAPI.invokeEndInteractionEvent();
publicAPI.render();
};
publicAPI.createDefaultRepresentation = () => {
if (!model.widgetRep) {
model.widgetRep = vtkLineRepresentation.newInstance();
}
};
publicAPI.updateHandleWidgets = (state) => {
let modified = false;
publicAPI.setCursor(state);
const enablePoint1Widget = state === State.ONP1;
const enablePoint2Widget = state === State.ONP2;
if (enablePoint1Widget !== model.point1Widget.getEnabled()) {
model.point1Widget.setEnabled(enablePoint1Widget);
modified = true;
}
if (enablePoint2Widget !== model.point2Widget.getEnabled()) {
model.point2Widget.setEnabled(enablePoint2Widget);
modified = true;
}
return modified;
};
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
widgetState: WidgetState.START,
managesCursor: 1,
currentHandle: 0,
point1Widget: null,
point2Widget: null,
};
// ----------------------------------------------------------------------------
export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Inheritance
vtkAbstractWidget.extend(publicAPI, model, initialValues);
model.point1Widget = vtkHandleWidget.newInstance();
model.point1Widget.setParent(publicAPI);
model.point1Widget.createDefaultRepresentation();
model.point2Widget = vtkHandleWidget.newInstance();
model.point2Widget.setParent(publicAPI);
model.point2Widget.createDefaultRepresentation();
// Object methods
vtkLineWidget(publicAPI, model);
}
// ----------------------------------------------------------------------------
export const newInstance = macro.newInstance(extend, 'vtkLineWidget');
// ----------------------------------------------------------------------------
export default { newInstance, extend };