diff --git a/src/VTKViewport/vtkInteractorStyleMPRSlice.js b/src/VTKViewport/vtkInteractorStyleMPRSlice.js index dde0e5c8..a829dcec 100644 --- a/src/VTKViewport/vtkInteractorStyleMPRSlice.js +++ b/src/VTKViewport/vtkInteractorStyleMPRSlice.js @@ -9,9 +9,11 @@ import vtkMouseRangeManipulator from 'vtk.js/Sources/Interaction/Manipulators/Mo import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants'; import ViewportData from './ViewportData'; +import { helpers } from '../helpers/index'; import EVENTS from '../events'; const { States } = Constants; +const { isAntiParallel } = helpers; // ---------------------------------------------------------------------------- // Global methods @@ -362,13 +364,19 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { const sliceNormal = publicAPI.getSliceNormal(); // Get rotation matrix from normal to +X (since bounds is aligned to XYZ) - const transform = vtkMatrixBuilder - .buildFromDegree() - .identity() - .rotateFromDirections(sliceNormal, [1, 0, 0]); - + // Special handling if sliceNormal is [-1,0,0] as rotateFromDirections returns zero matrix. const fp = camera.getFocalPoint(); - transform.apply(fp); + if (isAntiParallel(sliceNormal, [1, 0, 0])) { + fp[0] = fp[0] * -1; + } else { + const transform = vtkMatrixBuilder + .buildFromDegree() + .identity() + .rotateFromDirections(sliceNormal, [1, 0, 0]); + + transform.apply(fp); + } + return fp[0]; }; @@ -534,12 +542,17 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) { const points = boundsToCorners(bounds); // Get rotation matrix from normal to +X (since bounds is aligned to XYZ) - const transform = vtkMatrixBuilder - .buildFromDegree() - .identity() - .rotateFromDirections(sliceNormal, [1, 0, 0]); - - points.forEach(pt => transform.apply(pt)); + // Special handling if sliceNormal is [-1,0,0] as rotateFromDirections returns zero matrix. + if (isAntiParallel(sliceNormal, [1, 0, 0])) { + points.forEach(pt => (pt[0] = pt[0] * -1)); + } else { + const transform = vtkMatrixBuilder + .buildFromDegree() + .identity() + .rotateFromDirections(sliceNormal, [1, 0, 0]); + + points.forEach(pt => transform.apply(pt)); + } // range is now maximum X distance let minX = Infinity; diff --git a/src/VTKViewport/vtkSVGCrosshairsWidget.js b/src/VTKViewport/vtkSVGCrosshairsWidget.js index 5b674628..23bdbd72 100644 --- a/src/VTKViewport/vtkSVGCrosshairsWidget.js +++ b/src/VTKViewport/vtkSVGCrosshairsWidget.js @@ -1,6 +1,9 @@ import macro from 'vtk.js/Sources/macro'; import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; +import { helpers } from '../helpers/index'; + +const { isAntiParallel } = helpers; let instanceId = 1; @@ -158,13 +161,20 @@ function vtkSVGCrosshairsWidget(publicAPI, model) { const istyle = renderWindow.getInteractor().getInteractorStyle(); const sliceNormal = istyle.getSliceNormal(); - const transform = vtkMatrixBuilder - .buildFromDegree() - .identity() - .rotateFromDirections(sliceNormal, [1, 0, 0]); const mutatedWorldPos = worldPos.slice(); - transform.apply(mutatedWorldPos); + + // Special handling if sliceNormal is [-1,0,0] as rotateFromDirections returns zero matrix. + if (isAntiParallel(sliceNormal, [1, 0, 0])) { + mutatedWorldPos[0] = mutatedWorldPos[0] * -1; + } else { + const transform = vtkMatrixBuilder + .buildFromDegree() + .identity() + .rotateFromDirections(sliceNormal, [1, 0, 0]); + transform.apply(mutatedWorldPos); + } + const slice = mutatedWorldPos[0]; istyle.setSlice(slice); diff --git a/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js b/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js index 60e183a1..76357ad6 100644 --- a/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js +++ b/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js @@ -4,6 +4,9 @@ import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; import liangBarksyClip from '../helpers/liangBarksyClip'; import { vec2, vec3 } from 'gl-matrix'; import { projectVector2D } from 'vtk.js/Sources/Common/Core/Math'; +import { helpers } from '../helpers/index'; + +const { isAntiParallel } = helpers; let instanceId = 1; @@ -441,13 +444,20 @@ function vtkSVGRotatableCrosshairsWidget(publicAPI, model) { const istyle = renderWindow.getInteractor().getInteractorStyle(); const sliceNormal = istyle.getSliceNormal(); - const transform = vtkMatrixBuilder - .buildFromDegree() - .identity() - .rotateFromDirections(sliceNormal, [1, 0, 0]); const mutatedWorldPos = worldPos.slice(); - transform.apply(mutatedWorldPos); + + // Special handling if sliceNormal is [-1,0,0] as rotateFromDirections returns zero matrix. + if (isAntiParallel(sliceNormal, [1, 0, 0])) { + mutatedWorldPos[0] = mutatedWorldPos[0] * -1; + } else { + const transform = vtkMatrixBuilder + .buildFromDegree() + .identity() + .rotateFromDirections(sliceNormal, [1, 0, 0]); + transform.apply(mutatedWorldPos); + } + const slice = mutatedWorldPos[0]; istyle.setSlice(slice); diff --git a/src/helpers/index.js b/src/helpers/index.js index d161b2ef..f8f21551 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -3,6 +3,7 @@ import formatDA from './formatDA'; import formatTM from './formatTM'; import formatNumberPrecision from './formatNumberPrecision'; import isValidNumber from './isValidNumber'; +import isAntiParallel from './isAntiParallel'; import uuidv4 from './uuidv4.js'; const helpers = { @@ -11,6 +12,7 @@ const helpers = { formatTM, formatNumberPrecision, isValidNumber, + isAntiParallel, }; export { helpers, uuidv4 }; diff --git a/src/helpers/isAntiParallel.js b/src/helpers/isAntiParallel.js new file mode 100644 index 00000000..493b45e2 --- /dev/null +++ b/src/helpers/isAntiParallel.js @@ -0,0 +1,7 @@ +import { vec3 } from 'gl-matrix'; + +export default function isAntiParallel(originalVector, referenceVector) { + vec3.normalize(originalVector, originalVector); + vec3.normalize(referenceVector, referenceVector); + return vec3.dot(originalVector, referenceVector) === -1; +}