@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
539 lines (432 loc) • 19.1 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import macro from '../../../macro.js';
import { ShapeBehavior, BehaviorCategory } from './Constants.js';
import vtkLabelRepresentation from '../../../Interaction/Widgets/LabelRepresentation.js';
import { h as distance, a as subtract, d as dot, j as add, c as cross, u as squaredDistance } from '../../../vendor/gl-matrix/esm/vec3.js';
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
var vtkErrorMacro = macro.vtkErrorMacro;
var EPSILON = 1e-6;
function makeBoundsFromPoints(point1, point2) {
return [Math.min(point1[0], point2[0]), Math.max(point1[0], point2[0]), Math.min(point1[1], point2[1]), Math.max(point1[1], point2[1]), Math.min(point1[2], point2[2]), Math.max(point1[2], point2[2])];
}
function widgetBehavior(publicAPI, model) {
model.classHierarchy.push('vtkShapeWidgetProp');
var superClass = _objectSpread({}, publicAPI); // --------------------------------------------------------------------------
// Display 2D
// --------------------------------------------------------------------------
publicAPI.setDisplayCallback = function (callback) {
return model.representations[0].setDisplayCallback(callback);
}; // --------------------------------------------------------------------------
// Public methods
// --------------------------------------------------------------------------
publicAPI.setModifierBehavior = function (behavior) {
Object.assign(model.modifierBehavior, behavior);
};
publicAPI.isBehaviorActive = function (category, flag) {
return Object.keys(model.keysDown).some(function (key) {
return model.keysDown[key] && model.modifierBehavior[key] && model.modifierBehavior[key][category] === flag;
});
};
publicAPI.isOppositeBehaviorActive = function (category, flag) {
return Object.values(ShapeBehavior[category]).some(function (flagToTry) {
return flag !== flagToTry && publicAPI.isBehaviorActive(category, flagToTry);
});
};
publicAPI.getActiveBehaviorFromCategory = function (category) {
return Object.values(ShapeBehavior[category]).find(function (flag) {
return publicAPI.isBehaviorActive(category, flag) || !publicAPI.isOppositeBehaviorActive(category, flag) && model.modifierBehavior.None[category] === flag;
});
};
publicAPI.isRatioFixed = function () {
return publicAPI.getActiveBehaviorFromCategory(BehaviorCategory.RATIO) === ShapeBehavior[BehaviorCategory.RATIO].FIXED;
};
publicAPI.isDraggingEnabled = function () {
var behavior = publicAPI.getActiveBehaviorFromCategory(BehaviorCategory.PLACEMENT);
return behavior === ShapeBehavior[BehaviorCategory.PLACEMENT].DRAG || behavior === ShapeBehavior[BehaviorCategory.PLACEMENT].CLICK_AND_DRAG;
};
publicAPI.isDraggingForced = function () {
return publicAPI.isBehaviorActive(BehaviorCategory.PLACEMENT, ShapeBehavior[BehaviorCategory.PLACEMENT].DRAG) || model.modifierBehavior.None[BehaviorCategory.PLACEMENT] === ShapeBehavior[BehaviorCategory.PLACEMENT].DRAG;
};
publicAPI.setVisibleOnFocus = function (visibleOnFocus) {
model.visibleOnFocus = visibleOnFocus;
};
publicAPI.setLabelTextCallback = function (callback) {
model.labelTextCallback = callback;
};
publicAPI.setResetAfterPointPlacement = function (reset) {
model.resetAfterPointPlacement = reset;
};
publicAPI.getPoint1 = function () {
return model.point1;
};
publicAPI.getPoint2 = function () {
return model.point2;
};
publicAPI.setPoints = function (point1, point2) {
model.point1 = point1;
model.point2 = point2;
model.point1Handle.setOrigin(model.point1);
model.point2Handle.setOrigin(model.point2);
publicAPI.updateShapeBounds();
}; // This method is to be called to place the first point
// for the first time. It is not inlined so that
// the user can specify himself where the first point
// is right after publicAPI.grabFocus() without waiting
// for interactions.
publicAPI.placePoint1 = function (point) {
if (model.hasFocus) {
publicAPI.setPoints(point, point);
model.point1Handle.deactivate();
model.point2Handle.activate();
model.activeState = model.point2Handle;
if (model.useHandles) {
model.point2Handle.setVisible(true);
}
publicAPI.updateShapeBounds();
if (model.visibleOnFocus) {
model.shapeHandle.setVisible(true);
}
}
};
publicAPI.placePoint2 = function (point2) {
if (model.hasFocus) {
model.point2 = point2;
model.point2Handle.setOrigin(model.point2);
publicAPI.updateShapeBounds();
if (model.resetAfterPointPlacement) {
publicAPI.reset();
} else {
publicAPI.loseFocus();
}
}
};
publicAPI.setPixelScale = function (pixelScale) {
model.pixelScale = pixelScale;
publicAPI.updateHandlesSize();
};
publicAPI.updateHandlesSize = function () {
if (model.pixelScale !== null) {
var scale = model.pixelScale * distance(model.apiSpecificRenderWindow.displayToWorld(0, 0, 0, model.renderer), model.apiSpecificRenderWindow.displayToWorld(1, 0, 0, model.renderer));
model.point1Handle.setScale1(scale);
model.point2Handle.setScale1(scale);
}
};
publicAPI.setVisibility = function (visibility) {
var modified = false;
if (model.useHandles) {
modified = superClass.setVisibility(visibility) || modified;
} else {
modified = model.shapeHandle.setVisible(visibility) || modified;
}
if (model.label) {
if (visibility) {
modified = model.label.setContainer(model.interactor.getContainer()) || modified;
} else {
modified = model.label.setContainer(null) || modified;
}
}
return modified;
};
publicAPI.getLabel = function () {
return model.label;
}; // --------------------------------------------------------------------------
// Private methods
// --------------------------------------------------------------------------
publicAPI.makeSquareFromPoints = function (point1, point2) {
var diagonal = [0, 0, 0];
subtract(diagonal, point2, point1);
var dir = model.shapeHandle.getDirection();
var right = model.shapeHandle.getRight();
var up = model.shapeHandle.getUp();
var dirComponent = dot(diagonal, dir);
var rightComponent = dot(diagonal, right);
var upComponent = dot(diagonal, up);
var absRightComponent = Math.abs(rightComponent);
var absUpComponent = Math.abs(upComponent);
if (absRightComponent < EPSILON) {
rightComponent = upComponent;
} else if (absUpComponent < EPSILON) {
upComponent = rightComponent;
} else if (absRightComponent > absUpComponent) {
upComponent = upComponent / absUpComponent * absRightComponent;
} else {
rightComponent = rightComponent / absRightComponent * absUpComponent;
}
return [point1[0] + rightComponent * right[0] + upComponent * up[0] + dirComponent * dir[0], point1[1] + rightComponent * right[1] + upComponent * up[1] + dirComponent * dir[1], point1[2] + rightComponent * right[2] + upComponent * up[2] + dirComponent * dir[2]];
};
var getCornersFromRadius = function getCornersFromRadius(center, pointOnCircle) {
var radius = distance(center, pointOnCircle);
var up = model.shapeHandle.getUp();
var right = model.shapeHandle.getRight();
var point1 = [center[0] + (up[0] - right[0]) * radius, center[1] + (up[1] - right[1]) * radius, center[2] + (up[2] - right[2]) * radius];
var point2 = [center[0] + (right[0] - up[0]) * radius, center[1] + (right[1] - up[1]) * radius, center[2] + (right[2] - up[2]) * radius];
return {
point1: point1,
point2: point2
};
};
var getCornersFromDiameter = function getCornersFromDiameter(point1, point2) {
var center = [0.5 * (point1[0] + point2[0]), 0.5 * (point1[1] + point2[1]), 0.5 * (point1[2] + point2[2])];
return getCornersFromRadius(center, point1);
};
publicAPI.setCorners = function (point1, point2) {
if (model.label && model.labelTextCallback) {
var _model$apiSpecificRen, _model$apiSpecificRen2;
var bounds = makeBoundsFromPoints(point1, point2);
var screenPoint1 = (_model$apiSpecificRen = model.apiSpecificRenderWindow).worldToDisplay.apply(_model$apiSpecificRen, _toConsumableArray(point1).concat([model.renderer]));
var screenPoint2 = (_model$apiSpecificRen2 = model.apiSpecificRenderWindow).worldToDisplay.apply(_model$apiSpecificRen2, _toConsumableArray(point2).concat([model.renderer]));
var screenBounds = [Math.min(screenPoint1[0], screenPoint2[0]), Math.max(screenPoint1[0], screenPoint2[0]), Math.min(screenPoint1[1], screenPoint2[1]), Math.max(screenPoint1[1], screenPoint2[1]), Math.min(screenPoint1[2], screenPoint2[2]), Math.max(screenPoint1[2], screenPoint2[2])];
model.labelTextCallback(bounds, screenBounds, model.label);
}
};
publicAPI.updateShapeBounds = function () {
if (model.point1 && model.point2) {
var point1 = _toConsumableArray(model.point1);
var point2 = _toConsumableArray(model.point2);
if (publicAPI.isRatioFixed()) {
point2 = publicAPI.makeSquareFromPoints(point1, point2);
}
switch (publicAPI.getActiveBehaviorFromCategory(BehaviorCategory.POINTS)) {
case ShapeBehavior[BehaviorCategory.POINTS].CORNER_TO_CORNER:
{
publicAPI.setCorners(point1, point2);
break;
}
case ShapeBehavior[BehaviorCategory.POINTS].CENTER_TO_CORNER:
{
var diagonal = [0, 0, 0];
subtract(diagonal, point1, point2);
add(point1, point1, diagonal);
publicAPI.setCorners(point1, point2);
break;
}
case ShapeBehavior[BehaviorCategory.POINTS].RADIUS:
{
var points = getCornersFromRadius(point1, point2);
publicAPI.setCorners(points.point1, points.point2);
break;
}
case ShapeBehavior[BehaviorCategory.POINTS].DIAMETER:
{
var _points = getCornersFromDiameter(point1, point2);
publicAPI.setCorners(_points.point1, _points.point2);
break;
}
default:
// This should never be executed
vtkErrorMacro('vtk internal error');
}
}
};
/*
* If the widget has the focus, this method reset the widget
* to it's state just after it grabbed the focus. Otherwise
* it resets the widget to its state before it grabbed the focus.
*/
publicAPI.reset = function () {
if (!model.hasFocus) {
model.point1Handle.setVisible(false);
}
model.shapeHandle.setVisible(false);
model.point1 = null;
model.point2 = null;
if (model.label) {
model.label.setLabelText('');
}
model.point1Handle.setOrigin(model.point2Handle.getOrigin());
model.point2Handle.setVisible(false);
model.point2Handle.deactivate();
if (model.hasFocus) {
model.point1Handle.activate();
model.activeState = model.point1Handle;
} else {
model.point1Handle.deactivate();
model.activeState = null;
}
publicAPI.updateShapeBounds();
}; // --------------------------------------------------------------------------
// Interactor events
// --------------------------------------------------------------------------
publicAPI.handleMouseMove = function (callData) {
if (!model.activeState || !model.activeState.getActive() || !model.pickable || !model.dragable || !model.manipulator) {
return macro.VOID;
}
if (!model.point2) {
// Update orientation to match the camera's plane
// if the corners are not yet placed
var normal = model.camera.getDirectionOfProjection();
var up = model.camera.getViewUp();
var right = [];
cross(right, up, normal);
model.shapeHandle.setUp(up);
model.shapeHandle.setRight(right);
model.shapeHandle.setDirection(normal);
model.manipulator.setNormal(normal);
}
var worldCoords = model.manipulator.handleEvent(callData, model.apiSpecificRenderWindow);
if (!worldCoords.length) {
return macro.VOID;
}
if (model.hasFocus) {
if (!model.point1) {
model.point1Handle.setOrigin(worldCoords);
} else {
model.point2Handle.setOrigin(worldCoords);
model.point2 = worldCoords;
publicAPI.updateShapeBounds();
}
} else if (model.useHandles && model.isDragging) {
if (model.activeState === model.point1Handle) {
model.point1Handle.setOrigin(worldCoords);
model.point1 = worldCoords;
} else {
model.point2Handle.setOrigin(worldCoords);
model.point2 = worldCoords;
}
publicAPI.updateShapeBounds();
publicAPI.invokeInteractionEvent();
}
return model.hasFocus ? macro.EVENT_ABORT : macro.VOID;
}; // --------------------------------------------------------------------------
// Left click: Add point / End interaction
// --------------------------------------------------------------------------
publicAPI.handleLeftButtonPress = function (e) {
if (!model.activeState || !model.activeState.getActive() || !model.pickable) {
return macro.VOID;
}
if (model.hasFocus) {
if (!model.point1) {
publicAPI.placePoint1(model.point1Handle.getOrigin());
publicAPI.invokeStartInteractionEvent();
} else {
publicAPI.placePoint2(model.point2Handle.getOrigin());
publicAPI.invokeInteractionEvent();
publicAPI.invokeEndInteractionEvent();
}
return macro.EVENT_ABORT;
}
if (model.point1 && (model.activeState === model.point1Handle || model.activeState === model.point2Handle)) {
model.isDragging = true;
model.apiSpecificRenderWindow.setCursor('grabbing');
model.interactor.requestAnimation(publicAPI);
publicAPI.invokeStartInteractionEvent();
return macro.EVENT_ABORT;
}
return macro.VOID;
}; // --------------------------------------------------------------------------
// Left release: Maybe end interaction
// --------------------------------------------------------------------------
publicAPI.handleLeftButtonRelease = function (e) {
if (model.isDragging) {
model.isDragging = false;
model.apiSpecificRenderWindow.setCursor('pointer');
model.widgetState.deactivate();
model.interactor.cancelAnimation(publicAPI);
publicAPI.invokeEndInteractionEvent();
return macro.EVENT_ABORT;
}
if (!model.hasFocus || !model.pickable) {
return macro.VOID;
}
var viewSize = model.apiSpecificRenderWindow.getSize();
if (e.position.x < 0 || e.position.x > viewSize[0] - 1 || e.position.y < 0 || e.position.y > viewSize[1] - 1) {
return macro.VOID;
}
if (model.point1) {
model.point2 = model.point2Handle.getOrigin();
publicAPI.updateShapeBounds();
if (publicAPI.isDraggingEnabled()) {
var distance = squaredDistance(model.point1, model.point2);
var maxDistance = 100;
if (distance > maxDistance || publicAPI.isDraggingForced()) {
publicAPI.invokeInteractionEvent();
publicAPI.invokeEndInteractionEvent();
if (model.resetAfterPointPlacement) {
publicAPI.reset();
} else {
publicAPI.loseFocus();
}
}
}
}
return macro.EVENT_ABORT;
}; // --------------------------------------------------------------------------
// Register key presses/releases
// --------------------------------------------------------------------------
publicAPI.handleKeyDown = function (_ref) {
var key = _ref.key;
if (key === 'Escape') {
if (model.hasFocus) {
publicAPI.invokeEndInteractionEvent();
publicAPI.reset();
publicAPI.loseFocus();
}
} else {
model.keysDown[key] = true;
}
if (model.hasFocus) {
if (model.point1) {
model.point2 = model.point2Handle.getOrigin();
publicAPI.updateShapeBounds();
}
}
};
publicAPI.handleKeyUp = function (_ref2) {
var key = _ref2.key;
model.keysDown[key] = false;
if (model.hasFocus) {
if (model.point1) {
model.point2 = model.point2Handle.getOrigin();
publicAPI.updateShapeBounds();
}
}
}; // --------------------------------------------------------------------------
// Focus API - follow mouse when widget has focus
// --------------------------------------------------------------------------
publicAPI.grabFocus = function () {
if (!model.hasFocus) {
publicAPI.reset();
if (!model.label) {
model.label = vtkLabelRepresentation.newInstance();
}
model.label.setRenderer(model.renderer);
model.label.buildRepresentation();
model.renderer.addViewProp(model.label);
model.label.setContainer(model.interactor.getContainer());
model.point1Handle.activate();
model.activeState = model.point1Handle;
if (model.useHandles) {
model.point1Handle.setVisible(true);
}
model.shapeHandle.setVisible(false);
model.interactor.requestAnimation(publicAPI);
}
publicAPI.updateHandlesSize();
model.hasFocus = true;
}; // --------------------------------------------------------------------------
publicAPI.loseFocus = function () {
if (model.hasFocus) {
if (model.visibleOnFocus && !model.useHandles) {
model.shapeHandle.setVisible(false);
}
model.interactor.cancelAnimation(publicAPI);
}
if (model.label && !model.useHandles) {
model.label.setContainer(null);
}
if (!model.useHandles || !model.point1) {
model.point1Handle.setVisible(false);
model.point2Handle.setVisible(false);
}
model.widgetState.deactivate();
model.point1Handle.deactivate();
model.point2Handle.deactivate();
model.activeState = null;
model.interactor.render();
model.widgetManager.enablePicking();
model.hasFocus = false;
};
}
export default widgetBehavior;