@thewtex/vtk.js-esm
Version:
Visualization Toolkit for the Web
243 lines (207 loc) • 9.26 kB
JavaScript
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import vtkBoundingBox, { STATIC } from '../../../Common/DataModel/BoundingBox.js';
import vtkCubeSource from '../../../Filters/Sources/CubeSource.js';
import vtkCutter from '../../../Filters/Core/Cutter.js';
import vtkPlane from '../../../Common/DataModel/Plane.js';
import { g as subtract, l as normalize, j as cross, Q as multiplyAccumulate, S as signedAngleBetweenVectors } from '../../../Common/Core/Math/index.js';
import vtkMatrixBuilder from '../../../Common/Core/MatrixBuilder.js';
import { ViewTypes } from '../../Core/WidgetManager/Constants.js';
var EPSILON = 0.00001;
/**
* Fit the plane defined by origin, p1, p2 onto the bounds.
* Plane is untouched if does not intersect bounds.
* @param {Array} bounds
* @param {Array} origin
* @param {Array} p1
* @param {Array} p2
*/
function boundPlane(bounds, origin, p1, p2) {
var v1 = [];
subtract(p1, origin, v1);
normalize(v1);
var v2 = [];
subtract(p2, origin, v2);
normalize(v2);
var n = [0, 0, 1];
cross(v1, v2, n);
normalize(n);
var plane = vtkPlane.newInstance();
plane.setOrigin.apply(plane, _toConsumableArray(origin));
plane.setNormal.apply(plane, n);
var cubeSource = vtkCubeSource.newInstance();
cubeSource.setBounds(bounds);
var cutter = vtkCutter.newInstance();
cutter.setCutFunction(plane);
cutter.setInputConnection(cubeSource.getOutputPort());
var cutBounds = cutter.getOutputData();
if (cutBounds.getNumberOfPoints() === 0) {
return;
}
var localBounds = STATIC.computeLocalBounds(cutBounds.getPoints(), v1, v2, n);
for (var i = 0; i < 3; i += 1) {
origin[i] = localBounds[0] * v1[i] + localBounds[2] * v2[i] + localBounds[4] * n[i];
p1[i] = localBounds[1] * v1[i] + localBounds[2] * v2[i] + localBounds[4] * n[i];
p2[i] = localBounds[0] * v1[i] + localBounds[3] * v2[i] + localBounds[4] * n[i];
}
} // Project point (inPoint) to the bounds of the image according to a plane
// defined by two vectors (v1, v2)
function boundPoint(inPoint, v1, v2, bounds) {
var absT1 = v1.map(function (val) {
return Math.abs(val);
});
var absT2 = v2.map(function (val) {
return Math.abs(val);
});
var o1 = 0.0;
var o2 = 0.0;
for (var i = 0; i < 3; i++) {
var axisOffset = 0;
var useT1 = absT1[i] > absT2[i];
var t = useT1 ? v1 : v2;
var absT = useT1 ? absT1 : absT2;
if (inPoint[i] < bounds[i * 2]) {
axisOffset = absT[i] > EPSILON ? (bounds[2 * i] - inPoint[i]) / t[i] : 0;
} else if (inPoint[i] > bounds[2 * i + 1]) {
axisOffset = absT[i] > EPSILON ? (bounds[2 * i + 1] - inPoint[i]) / t[i] : 0;
}
if (useT1) {
if (Math.abs(axisOffset) > Math.abs(o1)) {
o1 = axisOffset;
}
} else if (Math.abs(axisOffset) > Math.abs(o2)) {
o2 = axisOffset;
}
}
var outPoint = [inPoint[0], inPoint[1], inPoint[2]];
if (o1 !== 0.0) {
multiplyAccumulate(outPoint, v1, o1, outPoint);
}
if (o2 !== 0.0) {
multiplyAccumulate(outPoint, v2, o2, outPoint);
}
return outPoint;
} // Compute the intersection between p1 and p2 on bounds
function boundPointOnPlane(p1, p2, bounds) {
var dir12 = [0, 0, 0];
subtract(p2, p1, dir12);
var out = [0, 0, 0];
var tolerance = [0, 0, 0];
vtkBoundingBox.intersectBox(bounds, p1, dir12, out, tolerance);
return out;
}
/**
* Rotates a vector around another.
* @param {vec3} vectorToBeRotated Vector to rate
* @param {vec3} axis Axis to rotate around
* @param {Number} angle Angle in radian
* @returns The rotated vector
*/
function rotateVector(vectorToBeRotated, axis, angle) {
var rotatedVector = _toConsumableArray(vectorToBeRotated);
vtkMatrixBuilder.buildFromRadian().rotate(angle, axis).apply(rotatedVector);
return rotatedVector;
} // Update the extremities and the rotation point coordinate of the line
function updateLine(lineState, center, axis, lineLength, rotationLength) {
var p1 = [center[0] - lineLength * axis[0], center[1] - lineLength * axis[1], center[2] - lineLength * axis[2]];
var p2 = [center[0] + lineLength * axis[0], center[1] + lineLength * axis[1], center[2] + lineLength * axis[2]];
var rotationP1 = [center[0] - rotationLength * axis[0], center[1] - rotationLength * axis[1], center[2] - rotationLength * axis[2]];
var rotationP2 = [center[0] + rotationLength * axis[0], center[1] + rotationLength * axis[1], center[2] + rotationLength * axis[2]];
lineState.setPoint1(p1);
lineState.setPoint2(p2);
lineState.setRotationPoint1(rotationP1);
lineState.setRotationPoint2(rotationP2);
} // Update the reslice cursor state according to the three planes normals and the origin
function updateState(widgetState) {
// Compute line axis
var xNormal = widgetState.getPlanes()[ViewTypes.YZ_PLANE].normal;
var yNormal = widgetState.getPlanes()[ViewTypes.XZ_PLANE].normal;
var zNormal = widgetState.getPlanes()[ViewTypes.XY_PLANE].normal;
var yzIntersectionLineAxis = cross(yNormal, zNormal, []);
var xzIntersectionLineAxis = cross(zNormal, xNormal, []);
var xyIntersectionLineAxis = cross(xNormal, yNormal, []);
var bounds = widgetState.getImage().getBounds();
var center = widgetState.getCenter(); // Factor used to define where the rotation point will be displayed
// according to the plane size where there will be visible
var factor = 0.5 * 0.85;
var xRotationLength = (bounds[1] - bounds[0]) * factor;
var yRotationLength = (bounds[3] - bounds[2]) * factor;
var zRotationLength = (bounds[5] - bounds[4]) * factor; // Length of the principal diagonal.
var pdLength = 20 * 0.5 * vtkBoundingBox.getDiagonalLength(bounds);
updateLine(widgetState.getAxisXinY(), center, xyIntersectionLineAxis, pdLength, zRotationLength);
updateLine(widgetState.getAxisYinX(), center, xyIntersectionLineAxis, pdLength, zRotationLength);
updateLine(widgetState.getAxisYinZ(), center, yzIntersectionLineAxis, pdLength, xRotationLength);
updateLine(widgetState.getAxisZinY(), center, yzIntersectionLineAxis, pdLength, xRotationLength);
updateLine(widgetState.getAxisXinZ(), center, xzIntersectionLineAxis, pdLength, yRotationLength);
updateLine(widgetState.getAxisZinX(), center, xzIntersectionLineAxis, pdLength, yRotationLength);
}
/**
* First rotate planeToTransform to match targetPlane normal.
* Then rotate around targetNormal to enforce targetViewUp "up" vector (i.e. Origin->p2 ).
* There is an infinite number of options to rotate a plane normal to another. Here we attempt to
* preserve Origin, P1 and P2 when rotating around targetPlane.
* @param {vtkPlaneSource} planeToTransform
* @param {vec3} targetOrigin Center of the plane
* @param {vec3} targetNormal Normal to state to the plane
* @param {vec3} viewType Vector that enforces view up
*/
function transformPlane(planeToTransform, targetCenter, targetNormal, targetViewUp) {
planeToTransform.setNormal(targetNormal);
var viewUp = subtract(planeToTransform.getPoint2(), planeToTransform.getOrigin(), []);
var angle = signedAngleBetweenVectors(viewUp, targetViewUp, targetNormal);
planeToTransform.rotate(angle, targetNormal);
planeToTransform.setCenter(targetCenter);
} // Get name of the line in the same plane as the input
function getAssociatedLinesName(lineName) {
switch (lineName) {
case 'AxisXinY':
return 'AxisZinY';
case 'AxisXinZ':
return 'AxisYinZ';
case 'AxisYinX':
return 'AxisZinX';
case 'AxisYinZ':
return 'AxisXinZ';
case 'AxisZinX':
return 'AxisYinX';
case 'AxisZinY':
return 'AxisXinY';
default:
return '';
}
}
/**
* Get the line name, constructs from the plane name and where the plane is displayed
* Example: planeName='X' rotatedPlaneName='Y', then the return values will be 'AxisXinY'
* @param {String} planeName Value between 'X', 'Y' and 'Z'
* @param {String} rotatedPlaneName Value between 'X', 'Y' and 'Z'
* @returns {String}
*/
function getLineNameFromPlaneAndRotatedPlaneName(planeName, rotatedPlaneName) {
return "Axis".concat(planeName, "in").concat(rotatedPlaneName);
}
/**
* Extract the plane name from the line name
* Example: 'AxisXinY' will return 'X'
* @param {String} lineName Should be following this template : 'Axis_in_' with _ a character
* @returns {String} Value between 'X', 'Y' and 'Z' or null if an error occured
*/
function getPlaneNameFromLineName(lineName) {
var match = lineName.match('([XYZ])in[XYZ]');
if (match) {
return match[1];
}
return null;
}
/**
* Get the orthogonal plane name of 'planeName' in a specific 'rotatedPlaneName'
* Example: planeName='X' on rotatedPlaneName='Z', then the associated plane name
* of 'X' plane is 'Y'
* @param {String} planeName
* @param {String} rotatedPlaneName
*/
function getAssociatedPlaneName(planeName, rotatedPlaneName) {
var lineName = getLineNameFromPlaneAndRotatedPlaneName(planeName, rotatedPlaneName);
var associatedLine = getAssociatedLinesName(lineName);
return getPlaneNameFromLineName(associatedLine);
}
export { boundPlane, boundPoint, boundPointOnPlane, getAssociatedLinesName, getAssociatedPlaneName, getLineNameFromPlaneAndRotatedPlaneName, getPlaneNameFromLineName, rotateVector, transformPlane, updateState };