@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
332 lines (260 loc) • 14.4 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import macro from '../../../macro.js';
import vtkInteractorObserver from '../../../Rendering/Core/InteractorObserver.js';
import vtkLine from '../../../Common/DataModel/Line.js';
import { f as distance2BetweenPoints, l as normalize, d as dot, j as cross, u as uninitializeBounds } from '../../../Common/Core/Math/index.js';
import vtkMatrixBuilder from '../../../Common/Core/MatrixBuilder.js';
import vtkPlane from '../../../Common/DataModel/Plane.js';
import vtkResliceCursorActor from './ResliceCursorActor.js';
import vtkResliceCursorRepresentation from './ResliceCursorRepresentation.js';
import { InteractionState } from './ResliceCursorRepresentation/Constants.js';
// ResliceCursorLineRepresentation methods
// ----------------------------------------------------------------------------
function isAxisPicked(renderer, tolerance, axisPolyData, pickedPosition) {
var points = axisPolyData.getPoints();
var worldP1 = [];
points.getPoint(0, worldP1);
var displayP1 = vtkInteractorObserver.computeWorldToDisplay(renderer, worldP1[0], worldP1[1], worldP1[2]);
var worldP2 = [];
points.getPoint(points.getNumberOfPoints() - 1, worldP2);
var displayP2 = vtkInteractorObserver.computeWorldToDisplay(renderer, worldP2[0], worldP2[1], worldP2[2]);
var xyz = [pickedPosition[0], pickedPosition[1], 0];
var p1 = [displayP1[0], displayP1[1], 0];
var p2 = [displayP2[0], displayP2[1], 0];
var output = vtkLine.distanceToLine(xyz, p1, p2);
return output.distance <= tolerance * tolerance && output.t < 1.0 && output.t > 0.0;
}
function displayToWorld(displayPosition, renderer) {
var activeCamera = renderer.getActiveCamera();
var focalPoint = activeCamera.getFocalPoint();
var displayFocalPoint = vtkInteractorObserver.computeWorldToDisplay(renderer, focalPoint[0], focalPoint[1], focalPoint[2]);
var worldEventPosition = vtkInteractorObserver.computeDisplayToWorld(renderer, displayPosition[0], displayPosition[1], displayFocalPoint[2]);
return worldEventPosition;
}
function vtkResliceCursorLineRepresentation(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkResliceCursorLineRepresentation'); //----------------------------------------------------------------------------
// Public API methods
//----------------------------------------------------------------------------
publicAPI.getResliceCursor = function () {
return model.resliceCursorActor.getCursorAlgorithm().getResliceCursor();
};
publicAPI.getCursorAlgorithm = function () {
return model.resliceCursorActor.getCursorAlgorithm();
};
publicAPI.displayToReslicePlaneIntersection = function (displayPosition) {
var activeCamera = model.renderer.getActiveCamera();
var cameraPosition = activeCamera.getPosition();
var resliceCursor = publicAPI.getResliceCursor();
var worldEventPosition = displayToWorld(displayPosition, model.renderer);
if (!resliceCursor) {
return null;
}
var axisNormal = model.resliceCursorActor.getCursorAlgorithm().getReslicePlaneNormal();
var plane = resliceCursor.getPlane(axisNormal);
var intersection = vtkPlane.intersectWithLine(worldEventPosition, cameraPosition, plane.getOrigin(), plane.getNormal());
return intersection.x;
};
publicAPI.computeInteractionState = function (displayPos) {
model.interactionState = InteractionState.OUTSIDE;
if (!model.renderer || !model.resliceCursorActor.getVisibility()) {
return model.interactionState;
}
var resliceCursor = publicAPI.getResliceCursor();
if (!resliceCursor) {
return model.interactionState;
}
var axis1 = model.resliceCursorActor.getCursorAlgorithm().getAxis1();
var bounds = model.resliceCursorActor.getCenterlineActor(axis1).getBounds();
if (bounds[1] < bounds[0]) {
return model.interactionState;
} // Picking Axis1 interaction:
var axis1PolyData = resliceCursor.getCenterlineAxisPolyData(axis1);
var isAxis1Picked = isAxisPicked(model.renderer, model.tolerance, axis1PolyData, displayPos); // Picking Axis2 interaction:
var axis2 = model.resliceCursorActor.getCursorAlgorithm().getAxis2();
var axis2PolyData = resliceCursor.getCenterlineAxisPolyData(axis2);
var isAxis2Picked = isAxisPicked(model.renderer, model.tolerance, axis2PolyData, displayPos); // Picking center interaction:
var isCenterPicked = isAxis1Picked && isAxis2Picked;
if (isCenterPicked) {
var displayCenterPosition = vtkInteractorObserver.computeWorldToDisplay(model.renderer, resliceCursor.getCenter()[0], resliceCursor.getCenter()[1], resliceCursor.getCenter()[2]);
var distance = distance2BetweenPoints([displayCenterPosition[0], displayCenterPosition[1], 0], [displayPos[0], displayPos[1], 0]);
if (distance <= model.tolerance * model.tolerance) {
model.interactionState = InteractionState.ON_CENTER;
} else {
model.interactionState = InteractionState.ON_AXIS1;
}
} else if (isAxis1Picked) {
model.interactionState = InteractionState.ON_AXIS1;
} else if (isAxis2Picked) {
model.interactionState = InteractionState.ON_AXIS2;
}
model.startPickPosition = publicAPI.displayToReslicePlaneIntersection(displayPos);
if (model.startPickPosition === null) {
model.startPickPosition = [0, 0, 0];
}
return model.interactionState;
};
publicAPI.startComplexWidgetInteraction = function (startEventPos) {
model.startEventPosition[0] = startEventPos[0];
model.startEventPosition[1] = startEventPos[1];
model.startEventPosition[2] = 0.0;
var resliceCursor = publicAPI.getResliceCursor();
if (resliceCursor) {
model.startCenterPosition = resliceCursor.getCenter();
}
model.lastEventPosition[0] = startEventPos[0];
model.lastEventPosition[1] = startEventPos[1];
model.lastEventPosition[2] = 0.0;
};
publicAPI.complexWidgetInteraction = function (displayPosition) {
var resliceCursor = publicAPI.getResliceCursor();
if (model.interactionState === InteractionState.OUTSIDE || !model.renderer || !resliceCursor) {
model.lastEventPosition[0] = displayPosition[0];
model.lastEventPosition[1] = displayPosition[1];
return;
} // Depending on the state, perform different operations
if (model.interactionState === InteractionState.ON_CENTER) {
var intersectionPos = publicAPI.displayToReslicePlaneIntersection(displayPosition);
if (intersectionPos !== null) {
var newCenter = [];
for (var i = 0; i < 3; i++) {
newCenter[i] = model.startCenterPosition[i] + intersectionPos[i] - model.startPickPosition[i];
}
resliceCursor.setCenter(newCenter);
}
}
if (model.interactionState === InteractionState.ON_AXIS1) {
publicAPI.rotateAxis(displayPosition, model.resliceCursorActor.getCursorAlgorithm().getPlaneAxis1());
}
if (model.interactionState === InteractionState.ON_AXIS2) {
publicAPI.rotateAxis(displayPosition, model.resliceCursorActor.getCursorAlgorithm().getPlaneAxis2());
}
model.lastEventPosition = [].concat(_toConsumableArray(displayPosition), [0]);
};
publicAPI.rotateAxis = function (displayPos, axis) {
var resliceCursor = publicAPI.getResliceCursor();
if (!resliceCursor) {
return 0;
}
var center = resliceCursor.getCenter(); // Intersect with the viewing vector. We will use this point and the
// start event point to compute the rotation angle
var currentIntersectionPos = publicAPI.displayToReslicePlaneIntersection(displayPos);
var lastIntersectionPos = publicAPI.displayToReslicePlaneIntersection(model.lastEventPosition);
if (lastIntersectionPos[0] === currentIntersectionPos[0] && lastIntersectionPos[1] === currentIntersectionPos[1] && lastIntersectionPos[2] === currentIntersectionPos[2]) {
return 0;
}
var lastVector = [];
var currVector = [];
for (var i = 0; i < 3; i++) {
lastVector[i] = lastIntersectionPos[i] - center[i];
currVector[i] = currentIntersectionPos[i] - center[i];
}
normalize(lastVector);
normalize(currVector); // Compute the angle between both vectors. This is the amount to
// rotate by.
var angle = Math.acos(dot(lastVector, currVector));
var crossVector = [];
cross(lastVector, currVector, crossVector);
var resliceCursorPlaneId = model.resliceCursorActor.getCursorAlgorithm().getReslicePlaneNormal();
var normalPlane = resliceCursor.getPlane(resliceCursorPlaneId);
var aboutAxis = normalPlane.getNormal();
var align = dot(aboutAxis, crossVector);
var sign = align > 0 ? 1.0 : -1.0;
angle *= sign;
if (angle === 0) {
return 0;
}
publicAPI.applyRotation(axis, angle);
return angle;
};
publicAPI.applyRotation = function (axis, angle) {
var resliceCursor = publicAPI.getResliceCursor();
var resliceCursorPlaneId = model.resliceCursorActor.getCursorAlgorithm().getReslicePlaneNormal();
var planeToBeRotated = resliceCursor.getPlane(axis);
var viewUp = resliceCursor.getViewUp(axis);
var vectorToBeRotated = planeToBeRotated.getNormal();
var normalPlane = resliceCursor.getPlane(resliceCursorPlaneId);
var aboutAxis = normalPlane.getNormal();
var rotatedVector = _toConsumableArray(vectorToBeRotated);
vtkMatrixBuilder.buildFromRadian().rotate(angle, aboutAxis).apply(rotatedVector);
vtkMatrixBuilder.buildFromRadian().rotate(angle, aboutAxis).apply(viewUp);
planeToBeRotated.setNormal(rotatedVector);
};
publicAPI.getBounds = function () {
var bounds = [];
uninitializeBounds(bounds);
var resliceCursor = publicAPI.getResliceCursor();
if (resliceCursor) {
if (resliceCursor.getImage()) {
bounds = resliceCursor.getImage().getBounds();
}
}
return bounds;
};
publicAPI.getActors = function () {
// Update representation
publicAPI.buildRepresentation(); // Update CameraPosition
publicAPI.updateCamera();
return [model.imageActor].concat(_toConsumableArray(model.resliceCursorActor.getActors()));
};
publicAPI.updateCamera = function () {
var normalAxis = model.resliceCursorActor.getCursorAlgorithm().getReslicePlaneNormal(); // When the reslice plane is changed, update the camera to look at the
// normal to the reslice plane always.
var focalPoint = model.renderer.getActiveCamera().getFocalPoint();
var position = model.renderer.getActiveCamera().getPosition();
var normalPlane = publicAPI.getResliceCursor().getPlane(normalAxis);
var normal = normalPlane.getNormal();
var distance = Math.sqrt(distance2BetweenPoints(position, focalPoint));
var estimatedCameraPosition = [focalPoint[0] + distance * normal[0], focalPoint[1] + distance * normal[1], focalPoint[2] + distance * normal[2]]; // intersect with the plane to get updated focal point
var intersection = vtkPlane.intersectWithLine(focalPoint, estimatedCameraPosition, normalPlane.getOrigin(), normalPlane.getNormal());
var newFocalPoint = intersection.x;
model.renderer.getActiveCamera().setFocalPoint(newFocalPoint[0], newFocalPoint[1], newFocalPoint[2]);
var newCameraPosition = [newFocalPoint[0] + distance * normal[0], newFocalPoint[1] + distance * normal[1], newFocalPoint[2] + distance * normal[2]];
model.renderer.getActiveCamera().setPosition(newCameraPosition[0], newCameraPosition[1], newCameraPosition[2]); // Renderer may not have yet actor bounds
var rendererBounds = model.renderer.computeVisiblePropBounds();
var bounds = publicAPI.getBounds();
rendererBounds[0] = Math.min(bounds[0], rendererBounds[0]);
rendererBounds[1] = Math.max(bounds[1], rendererBounds[1]);
rendererBounds[2] = Math.min(bounds[2], rendererBounds[2]);
rendererBounds[3] = Math.max(bounds[3], rendererBounds[3]);
rendererBounds[4] = Math.min(bounds[4], rendererBounds[4]);
rendererBounds[5] = Math.max(bounds[5], rendererBounds[5]); // Don't clip away any part of the data.
model.renderer.resetCameraClippingRange(rendererBounds);
};
/**
* Reimplemented to look at image center instead of reslice cursor.
*/
publicAPI.resetCamera = function () {
if (model.renderer) {
var normalAxis = publicAPI.getCursorAlgorithm().getReslicePlaneNormal();
var normal = publicAPI.getResliceCursor().getPlane(normalAxis).getNormal();
var viewUp = publicAPI.getResliceCursor().getViewUp(normalAxis);
var focalPoint = model.renderer.getActiveCamera().getFocalPoint();
var position = model.renderer.getActiveCamera().getPosition(); // Distance is preserved
var distance = Math.sqrt(distance2BetweenPoints(position, focalPoint));
var newCameraPosition = [focalPoint[0] + distance * normal[0], focalPoint[1] + distance * normal[1], focalPoint[2] + distance * normal[2]];
model.renderer.getActiveCamera().setPosition(newCameraPosition[0], newCameraPosition[1], newCameraPosition[2]);
model.renderer.getActiveCamera().setViewUp(viewUp[0], viewUp[1], viewUp[2]);
}
};
} // ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
var DEFAULT_VALUES = {}; // ----------------------------------------------------------------------------
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues);
vtkResliceCursorRepresentation.extend(publicAPI, model, DEFAULT_VALUES, initialValues);
model.resliceCursorActor = vtkResliceCursorActor.newInstance();
model.startPickPosition = null;
model.startCenterPosition = null;
macro.get(publicAPI, model, ['resliceCursorActor']); // Object methods
vtkResliceCursorLineRepresentation(publicAPI, model);
} // ----------------------------------------------------------------------------
var newInstance = macro.newInstance(extend, 'vtkResliceCursorLineRepresentation'); // ----------------------------------------------------------------------------
var vtkResliceCursorLineRepresentation$1 = {
newInstance: newInstance,
extend: extend
};
export default vtkResliceCursorLineRepresentation$1;
export { extend, newInstance };