Skip to content

Commit 641e27c

Browse files
committed
fix: 🐛 #2127
make crosshair linked views rotations around the focus of the crosshair fix focal points position after rotations and pannings of the crosshair.
1 parent 1296258 commit 641e27c

File tree

2 files changed

+79
-11
lines changed

2 files changed

+79
-11
lines changed

src/VTKViewport/vtkInteractorStyleRotatableMPRCrosshairs.js

+76-11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants';
44
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
55
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
66
import { vec2, vec3, quat } from 'gl-matrix';
7+
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
78

89
const { States } = Constants;
910

@@ -250,28 +251,92 @@ function vtkInteractorStyleRotatableMPRCrosshairs(publicAPI, model) {
250251
const sliceNormal = thisApi.getSliceNormal();
251252
const axis = [-sliceNormal[0], -sliceNormal[1], -sliceNormal[2]];
252253

253-
const { matrix } = vtkMatrixBuilder.buildFromRadian().rotate(angle, axis);
254-
255254
// Rotate other apis
256255
apis.forEach((api, index) => {
257256
if (index !== apiIndex) {
258-
// get normal and viewUp.
257+
const cameraForApi = api.genericRenderWindow
258+
.getRenderWindow()
259+
.getInteractor()
260+
.getCurrentRenderer()
261+
.getActiveCamera();
262+
263+
const crosshairPointForApi = api.get('cachedCrosshairWorldPosition');
264+
const initialCrosshairPointForApi = api.get(
265+
'initialCachedCrosshairWorldPosition'
266+
);
267+
268+
const center = [];
269+
vtkMath.subtract(
270+
crosshairPointForApi,
271+
initialCrosshairPointForApi,
272+
center
273+
);
274+
const translate = [];
275+
vtkMath.add(crosshairPointForApi, center, translate);
276+
277+
const { matrix } = vtkMatrixBuilder
278+
.buildFromRadian()
279+
.translate(translate[0], translate[1], translate[2])
280+
.rotate(angle, axis)
281+
.translate(-translate[0], -translate[1], -translate[2]);
282+
283+
cameraForApi.applyTransform(matrix);
259284

260285
const sliceNormalForApi = api.getSliceNormal();
261286
const viewUpForApi = api.getViewUp();
262-
263-
const newSliceNormalForApi = [];
264-
const newViewUpForApi = [];
265-
266-
vec3.transformMat4(newSliceNormalForApi, sliceNormalForApi, matrix);
267-
vec3.transformMat4(newViewUpForApi, viewUpForApi, matrix);
268-
269-
api.setOrientation(newSliceNormalForApi, newViewUpForApi);
287+
api.setOrientation(sliceNormalForApi, viewUpForApi);
270288
}
271289
});
272290

273291
updateCrosshairs(callData);
274292

293+
/*
294+
After the rotations and update of the crosshairs, the focal point of the
295+
camera has a shift along the line of sight coordinate respect to the
296+
crosshair (i.e., the focal point is not on the same slice of the crosshair).
297+
We calculate the new focal point coordinates as the nearest point between
298+
the line of sight of the camera and the crosshair coordinates:
299+
300+
p1 = cameraPositionForApi
301+
p2 = cameraFocalPointForApi
302+
q = crosshairPointForApi
303+
304+
Vector3 u = p2 - p1;
305+
Vector3 pq = q - p1;
306+
Vector3 w2 = pq - vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / u2);
307+
308+
Vector3 newFocalPoint = q - w2;
309+
*/
310+
311+
apis.forEach(api => {
312+
const cameraForApi = api.genericRenderWindow
313+
.getRenderWindow()
314+
.getInteractor()
315+
.getCurrentRenderer()
316+
.getActiveCamera();
317+
318+
const crosshairPointForApi = api.get('cachedCrosshairWorldPosition');
319+
const cameraFocalPointForApi = cameraForApi.getFocalPoint();
320+
const cameraPositionForApi = cameraForApi.getPosition();
321+
322+
const u = [];
323+
vtkMath.subtract(cameraFocalPointForApi, cameraPositionForApi, u);
324+
const pq = [];
325+
vtkMath.subtract(crosshairPointForApi, cameraPositionForApi, pq);
326+
const uLength2 = u[0] * u[0] + u[1] * u[1] + u[2] * u[2];
327+
vtkMath.multiplyScalar(u, vtkMath.dot(pq, u) / uLength2);
328+
const w2 = [];
329+
vtkMath.subtract(pq, u, w2);
330+
const newFocalPointForApi = [];
331+
vtkMath.subtract(crosshairPointForApi, w2, newFocalPointForApi);
332+
333+
cameraForApi.setFocalPoint(
334+
newFocalPointForApi[0],
335+
newFocalPointForApi[1],
336+
newFocalPointForApi[2]
337+
);
338+
});
339+
275340
operation.prevPosition = newPosition;
276341
}
277342

src/VTKViewport/vtkSVGRotatableCrosshairsWidget.js

+3
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ function vtkSVGRotatableCrosshairsWidget(publicAPI, model) {
429429
// Set camera focal point to world coordinate for linked views
430430
apis.forEach((api, apiIndex) => {
431431
api.set('cachedCrosshairWorldPosition', worldPos);
432+
if (api.get('initialCachedCrosshairWorldPosition') === undefined) {
433+
api.set('initialCachedCrosshairWorldPosition', worldPos);
434+
}
432435

433436
// We are basically doing the same as getSlice but with the world coordinate
434437
// that we want to jump to instead of the camera focal point.

0 commit comments

Comments
 (0)