LabelWidget

Source

behavior.js
import macro from 'vtk.js/Sources/macros';
import { add } from 'vtk.js/Sources/Common/Core/Math';

export default function widgetBehavior(publicAPI, model) {
model.classHierarchy.push('vtkLabelWidgetProp');
model._isDragging = false;

// --------------------------------------------------------------------------
// Public methods
// --------------------------------------------------------------------------

publicAPI.setText = (text) => {
model.widgetState.getText().setText(text);
model._interactor.render();
};

publicAPI.getText = () => model.widgetState.getText().getText();

// --------------------------------------------------------------------------
// Display 2D
// --------------------------------------------------------------------------

publicAPI.setDisplayCallback = (callback) =>
model.representations[0].setDisplayCallback(callback);

// --------------------------------------------------------------------------
// Interactor events
// --------------------------------------------------------------------------

function ignoreKey(e) {
return e.altKey || e.controlKey || e.shiftKey;
}

// --------------------------------------------------------------------------
// Left press: Select handle to drag / Place text handle
// --------------------------------------------------------------------------

publicAPI.handleLeftButtonPress = (e) => {
if (
!model.activeState ||
!model.activeState.getActive() ||
!model.pickable ||
ignoreKey(e)
) {
return macro.VOID;
}

const manipulator =
model.activeState?.getManipulator?.() ?? model.manipulator;
const { worldCoords } = manipulator.handleEvent(
e,
model._apiSpecificRenderWindow
);

if (
model.activeState === model.widgetState.getMoveHandle() &&
manipulator
) {
// Commit handle to location
const moveHandle = model.widgetState.getMoveHandle();
moveHandle.setOrigin(worldCoords);
model.widgetState.getText().setOrigin(moveHandle.getOrigin());
publicAPI.loseFocus();
} else if (model.dragable) {
model._isDragging = true;
model._apiSpecificRenderWindow.setCursor('grabbing');
model._interactor.requestAnimation(publicAPI);
}

publicAPI.invokeStartInteractionEvent();
return macro.EVENT_ABORT;
};

// --------------------------------------------------------------------------
// Left release: Finish drag
// --------------------------------------------------------------------------

publicAPI.handleLeftButtonRelease = () => {
if (
!model.activeState ||
!model.activeState.getActive() ||
!model.pickable
) {
return macro.VOID;
}

if (model._isDragging) {
model._apiSpecificRenderWindow.setCursor('pointer');
model.widgetState.deactivate();
model._interactor.cancelAnimation(publicAPI);
model._isDragging = false;
} else if (model.activeState !== model.widgetState.getMoveHandle()) {
model.widgetState.deactivate();
}

if (
(model.hasFocus && !model.activeState) ||
(model.activeState && !model.activeState.getActive())
) {
model._widgetManager.enablePicking();
model._interactor.render();
}

publicAPI.invokeEndInteractionEvent();
return macro.EVENT_ABORT;
};

// --------------------------------------------------------------------------
// Mouse move: Drag selected handle / Handle follow the mouse
// --------------------------------------------------------------------------

publicAPI.handleMouseMove = (callData) => {
const manipulator =
model.activeState?.getManipulator?.() ?? model.manipulator;
if (
manipulator &&
model.pickable &&
model.dragable &&
model.activeState &&
model.activeState.getActive() &&
!ignoreKey(callData)
) {
const { worldCoords, worldDelta } = manipulator.handleEvent(
callData,
model._apiSpecificRenderWindow
);

const isHandleMoving =
model.widgetState.getMoveHandle() === model.activeState ||
model._isDragging;

if (!isHandleMoving || !worldCoords.length || !worldDelta.length)
return macro.VOID;

const curOrigin = model.activeState.getOrigin();
const newOrigin = curOrigin
? add(curOrigin, worldDelta, [])
: worldCoords;

model.activeState.setOrigin(newOrigin);
model.widgetState.getText().setOrigin(newOrigin);

publicAPI.invokeInteractionEvent();
return macro.EVENT_ABORT;
}

return macro.VOID;
};

// --------------------------------------------------------------------------
// Focus API
// --------------------------------------------------------------------------

publicAPI.reset = () => {
model.widgetState.getMoveHandle().setOrigin(null);
model.widgetState.getText().setOrigin(null);
model.widgetState.getText().setText('');
};

publicAPI.grabFocus = () => {
if (!model.hasFocus) {
publicAPI.reset();

model.activeState = model.widgetState.getMoveHandle();
model.widgetState.getMoveHandle().activate();
model._interactor.requestAnimation(publicAPI);
publicAPI.invokeStartInteractionEvent();
}
model.hasFocus = true;
};

publicAPI.loseFocus = () => {
if (model.hasFocus) {
model._interactor.cancelAnimation(publicAPI);
publicAPI.invokeEndInteractionEvent();
}
model.widgetState.deactivate();
model.widgetState.getMoveHandle().deactivate();
model.activeState = null;
model.hasFocus = false;
model._widgetManager.enablePicking();
model._interactor.render();
};
}
index.js
import macro from 'vtk.js/Sources/macros';
import vtkAbstractWidgetFactory from 'vtk.js/Sources/Widgets/Core/AbstractWidgetFactory';
import vtkSphereHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/SphereHandleRepresentation';
import vtkPlanePointManipulator from 'vtk.js/Sources/Widgets/Manipulators/PlaneManipulator';

import widgetBehavior from 'vtk.js/Sources/Widgets/Widgets3D/LabelWidget/behavior';
import stateGenerator from 'vtk.js/Sources/Widgets/Widgets3D/LabelWidget/state';

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

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

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

const superClass = { ...publicAPI };

// --- Widget Requirement ---------------------------------------------------
model.methodsToLink = ['scaleInPixels'];

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

// --- Public methods -------------------------------------------------------

publicAPI.setManipulator = (manipulator) => {
superClass.setManipulator(manipulator);
model.widgetState.getMoveHandle().setManipulator(manipulator);
model.widgetState.getText().setManipulator(manipulator);
};

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

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

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

function defaultValues(initialValues) {
return {
behavior: widgetBehavior,
widgetState: stateGenerator(),
...initialValues,
};
}

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

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

vtkAbstractWidgetFactory.extend(publicAPI, model, initialValues);
macro.setGet(publicAPI, model, ['manipulator']);

vtkLabelWidget(publicAPI, model);
}

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

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

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

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

export default function generateState() {
return vtkStateBuilder
.createBuilder()
.addStateFromMixin({
labels: ['moveHandle'],
mixins: ['origin', 'color', 'scale1', 'visible', 'manipulator'],
name: 'moveHandle',
initialValues: {
scale1: 10,
visible: false,
},
})
.addStateFromMixin({
labels: ['SVGtext'],
mixins: ['origin', 'color', 'text', 'visible', 'manipulator'],
name: 'text',
initialValues: {
visible: true,
},
})
.build();
}