@kitware/vtk.js
Version:
Visualization Toolkit for the Web
123 lines (111 loc) • 3.9 kB
JavaScript
import { vec3 } from 'gl-matrix';
import vtkPlane from '../../../Common/DataModel/Plane.js';
/**
* Perform plane-line intersection, where the line is defined by two points (p1, p2),
* and the plane is defined by the imageData and slice number.
*
* @param {Vector3} p1
* @param {Vector3} p2
* @param {vtkImageMapper|vtkImageArrayMapper} mapper
*/
function doPicking(p1, p2, mapper) {
const imageData = mapper.getCurrentImage();
const extent = imageData.getExtent();
// Slice origin
const ijk = [extent[0], extent[2], extent[4]];
const {
ijkMode
} = mapper.getClosestIJKAxis();
let nSlice = mapper.isA('vtkImageArrayMapper') ? mapper.getSubSlice() : mapper.getSlice();
if (ijkMode !== mapper.getSlicingMode()) {
// If not IJK slicing, get the IJK slice from the XYZ position/slice
nSlice = mapper.getSliceAtPosition(nSlice);
}
ijk[ijkMode] += nSlice;
const worldOrigin = [0, 0, 0];
imageData.indexToWorld(ijk, worldOrigin);
// Normal computation
ijk[ijkMode] += 1;
const worldNormal = [0, 0, 0];
imageData.indexToWorld(ijk, worldNormal);
worldNormal[0] -= worldOrigin[0];
worldNormal[1] -= worldOrigin[1];
worldNormal[2] -= worldOrigin[2];
vec3.normalize(worldNormal, worldNormal);
const intersect = vtkPlane.intersectWithLine(p1, p2, worldOrigin, worldNormal);
if (intersect.intersection) {
const point = intersect.x;
const absoluteIJK = [0, 0, 0];
imageData.worldToIndex(point, absoluteIJK);
// `t` is the parametric position along the line
// defined in Plane.intersectWithLine
return {
t: intersect.t,
absoluteIJK
};
}
return null;
}
/**
* Implement point picking for image plane.
* The plane is defined by the imageData and current slice number,
* set in the input mapper.
*
* @param {Vector3} p1
* @param {Vector3} p2
* @param {vtkImageMapper|vtkImageArrayMapper} mapper
*/
function intersectWithLineForPointPicking(p1, p2, mapper) {
const pickingData = doPicking(p1, p2, mapper);
if (pickingData) {
const imageData = mapper.getCurrentImage();
const extent = imageData.getExtent();
// Get closer integer ijk
// NB: point picking means closest slice, means rounding
const ijk = [Math.round(pickingData.absoluteIJK[0]), Math.round(pickingData.absoluteIJK[1]), Math.round(pickingData.absoluteIJK[2])];
// Are we outside our actual extent
if (ijk[0] < extent[0] || ijk[0] > extent[1] || ijk[1] < extent[2] || ijk[1] > extent[3] || ijk[2] < extent[4] || ijk[2] > extent[5]) {
return null;
}
return {
t: pickingData.t,
ijk
};
}
return null;
}
/**
* Implement cell picking for image plane.
* The plane is defined by the imageData and current slice number,
* set in the input mapper.
*
* @param {Vector3} p1
* @param {Vector3} p2
* @param {vtkImageMapper|vtkImageArrayMapper} mapper
*/
function intersectWithLineForCellPicking(p1, p2, mapper) {
const pickingData = doPicking(p1, p2, mapper);
if (pickingData) {
const imageData = mapper.getCurrentImage();
const extent = imageData.getExtent();
const absIJK = pickingData.absoluteIJK;
// Get closer integer ijk
// NB: cell picking means closest voxel, means flooring
const ijk = [Math.floor(absIJK[0]), Math.floor(absIJK[1]), Math.floor(absIJK[2])];
// Are we outside our actual extent
if (ijk[0] < extent[0] || ijk[0] > extent[1] - 1 || ijk[1] < extent[2] || ijk[1] > extent[3] - 1 || ijk[2] < extent[4] ||
// handle single-slice images
ijk[2] > (extent[5] ? extent[5] - 1 : extent[5])) {
return null;
}
// Parametric coordinates within cell
const pCoords = [absIJK[0] - ijk[0], absIJK[1] - ijk[1], absIJK[2] - ijk[2]];
return {
t: pickingData.t,
ijk,
pCoords
};
}
return null;
}
export { intersectWithLineForCellPicking, intersectWithLineForPointPicking };