From 8539a1738f212a9b1b2c521fddaccab34842bc07 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Fri, 6 Nov 2020 20:22:46 +0100 Subject: [PATCH 1/2] Re #2127: make crosshair linked views rotations around the focus of the crosshair --- ...tkInteractorStyleRotatableMPRCrosshairs.js | 63 ++++++++++++++++--- .../vtkSVGRotatableCrosshairsWidget.js | 3 + 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js b/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js index b5f3318d..7c3aba6b 100644 --- a/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js +++ b/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js @@ -4,6 +4,7 @@ import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants'; import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate'; import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'; import { vec2, vec3, quat } from 'gl-matrix'; +import vtkMath from 'vtk.js/Sources/Common/Core/Math'; const { States } = Constants; @@ -250,23 +251,69 @@ function vtkInteractorStyleRotatableMPRCrosshairs(publicAPI, model) { const sliceNormal = thisApi.getSliceNormal(); const axis = [-sliceNormal[0], -sliceNormal[1], -sliceNormal[2]]; - const { matrix } = vtkMatrixBuilder.buildFromRadian().rotate(angle, axis); - // Rotate other apis apis.forEach((api, index) => { if (index !== apiIndex) { - // get normal and viewUp. + const cameraForApi = api.genericRenderWindow + .getRenderWindow() + .getInteractor() + .getCurrentRenderer() + .getActiveCamera(); + + const crosshairPointForApi = api.get('cachedCrosshairWorldPosition'); + const initialCrosshairPointForApi = api.get( + 'initialCachedCrosshairWorldPosition' + ); + + const center = []; + vtkMath.subtract( + crosshairPointForApi, + initialCrosshairPointForApi, + center + ); + const translate = []; + vtkMath.add(crosshairPointForApi, center, translate); + + const { matrix } = vtkMatrixBuilder + .buildFromRadian() + .translate(translate[0], translate[1], translate[2]) + .rotate(angle, axis) + .translate(-translate[0], -translate[1], -translate[2]); + + cameraForApi.applyTransform(matrix); const sliceNormalForApi = api.getSliceNormal(); const viewUpForApi = api.getViewUp(); + api.setOrientation(sliceNormalForApi, viewUpForApi); + + /* After rotating the focal point line of sight coordinate is not on the crosshair. + Find nearest point of the crosshair to the line of sight of the camera*/ + + /*p1 = cameraPositionForApi + p2 = cameraFocalPointForApi + q = crosshairPointForApi*/ + + /*Vector3 u = p2 - p1; + Vector3 pq = q - p1; + Vector3 w2 = pq - vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / u2); + + Vector3 point = q - w2;*/ - const newSliceNormalForApi = []; - const newViewUpForApi = []; + const cameraFocalPointForApi = cameraForApi.getFocalPoint(); + const cameraPositionForApi = cameraForApi.getPosition(); - vec3.transformMat4(newSliceNormalForApi, sliceNormalForApi, matrix); - vec3.transformMat4(newViewUpForApi, viewUpForApi, matrix); + const u = []; + vtkMath.subtract(cameraFocalPointForApi, cameraPositionForApi, u); + const pq = []; + vtkMath.subtract(crosshairPointForApi, cameraPositionForApi, pq); + const uLength2 = u[0] * u[0] + u[1] * u[1] + u[2] * u[2]; + vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / uLength2); + const w2 = []; + vtkMath.subtract(pq, u, w2); + const point = []; + vtkMath.subtract(crosshairPointForApi, w2, point); - api.setOrientation(newSliceNormalForApi, newViewUpForApi); + cameraForApi.setFocalPoint(point[0], point[1], point[2]); } }); diff --git a/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js b/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js index 7e764084..60e183a1 100644 --- a/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js +++ b/src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js @@ -429,6 +429,9 @@ function vtkSVGRotatableCrosshairsWidget(publicAPI, model) { // Set camera focal point to world coordinate for linked views apis.forEach((api, apiIndex) => { api.set('cachedCrosshairWorldPosition', worldPos); + if (api.get('initialCachedCrosshairWorldPosition') === undefined) { + api.set('initialCachedCrosshairWorldPosition', worldPos); + } // We are basically doing the same as getSlice but with the world coordinate // that we want to jump to instead of the camera focal point. From 210bb044d14764ba0b90631d7343966b2d0d1546 Mon Sep 17 00:00:00 2001 From: Davide Punzo Date: Fri, 13 Nov 2020 10:03:50 +0100 Subject: [PATCH 2/2] Re #2127: fix focal points position after rotations and pannings of the crosshair. --- ...tkInteractorStyleRotatableMPRCrosshairs.js | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js b/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js index 7c3aba6b..c8d90154 100644 --- a/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js +++ b/src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js @@ -285,40 +285,58 @@ function vtkInteractorStyleRotatableMPRCrosshairs(publicAPI, model) { const sliceNormalForApi = api.getSliceNormal(); const viewUpForApi = api.getViewUp(); api.setOrientation(sliceNormalForApi, viewUpForApi); - - /* After rotating the focal point line of sight coordinate is not on the crosshair. - Find nearest point of the crosshair to the line of sight of the camera*/ - - /*p1 = cameraPositionForApi - p2 = cameraFocalPointForApi - q = crosshairPointForApi*/ - - /*Vector3 u = p2 - p1; - Vector3 pq = q - p1; - Vector3 w2 = pq - vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / u2); - - Vector3 point = q - w2;*/ - - const cameraFocalPointForApi = cameraForApi.getFocalPoint(); - const cameraPositionForApi = cameraForApi.getPosition(); - - const u = []; - vtkMath.subtract(cameraFocalPointForApi, cameraPositionForApi, u); - const pq = []; - vtkMath.subtract(crosshairPointForApi, cameraPositionForApi, pq); - const uLength2 = u[0] * u[0] + u[1] * u[1] + u[2] * u[2]; - vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / uLength2); - const w2 = []; - vtkMath.subtract(pq, u, w2); - const point = []; - vtkMath.subtract(crosshairPointForApi, w2, point); - - cameraForApi.setFocalPoint(point[0], point[1], point[2]); } }); updateCrosshairs(callData); + /* + After the rotations and update of the crosshairs, the focal point of the + camera has a shift along the line of sight coordinate respect to the + crosshair (i.e., the focal point is not on the same slice of the crosshair). + We calculate the new focal point coordinates as the nearest point between + the line of sight of the camera and the crosshair coordinates: + + p1 = cameraPositionForApi + p2 = cameraFocalPointForApi + q = crosshairPointForApi + + Vector3 u = p2 - p1; + Vector3 pq = q - p1; + Vector3 w2 = pq - vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / u2); + + Vector3 newFocalPoint = q - w2; + */ + + apis.forEach(api => { + const cameraForApi = api.genericRenderWindow + .getRenderWindow() + .getInteractor() + .getCurrentRenderer() + .getActiveCamera(); + + const crosshairPointForApi = api.get('cachedCrosshairWorldPosition'); + const cameraFocalPointForApi = cameraForApi.getFocalPoint(); + const cameraPositionForApi = cameraForApi.getPosition(); + + const u = []; + vtkMath.subtract(cameraFocalPointForApi, cameraPositionForApi, u); + const pq = []; + vtkMath.subtract(crosshairPointForApi, cameraPositionForApi, pq); + const uLength2 = u[0] * u[0] + u[1] * u[1] + u[2] * u[2]; + vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / uLength2); + const w2 = []; + vtkMath.subtract(pq, u, w2); + const newFocalPointForApi = []; + vtkMath.subtract(crosshairPointForApi, w2, newFocalPointForApi); + + cameraForApi.setFocalPoint( + newFocalPointForApi[0], + newFocalPointForApi[1], + newFocalPointForApi[2] + ); + }); + operation.prevPosition = newPosition; }