Skip to content

Commit b642773

Browse files
Zaid-Safadiswederik
authored andcommitted
fix: Fix rotation by applying in an iterative rather than additive fashion. (#45)
1 parent bf29210 commit b642773

File tree

6 files changed

+58
-126
lines changed

6 files changed

+58
-126
lines changed

examples/VTKMPRRotateExample.js

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ class VTKMPRRotateExample extends Component {
355355

356356
const viewport = istyle.getViewport();
357357

358-
viewport.setInitialOrientation(
358+
viewport.setOrientation(
359359
volumeData[viewportIndex].slicePlaneNormal,
360360
volumeData[viewportIndex].sliceViewUp
361361
);
@@ -367,15 +367,14 @@ class VTKMPRRotateExample extends Component {
367367
.addEventListener(EVENTS.VIEWPORT_ROTATED, args => {
368368
const rotation = this.state.rotation;
369369

370-
if (
371-
rotation[viewportIndex].x === args.detail.horizontalRotation &&
372-
rotation[viewportIndex].y === args.detail.verticalRotation
373-
) {
370+
if (args.detail.dThetaX === 0 && args.detail.dThetaY === 0) {
374371
return;
375372
}
376373

377-
rotation[viewportIndex].x = args.detail.horizontalRotation;
378-
rotation[viewportIndex].y = args.detail.verticalRotation;
374+
rotation[viewportIndex].x =
375+
(rotation[viewportIndex].x + args.detail.dThetaY) % 360;
376+
rotation[viewportIndex].y =
377+
(rotation[viewportIndex].y + args.detail.dThetaX) % 360;
379378

380379
this.setState({ rotation });
381380
});
@@ -387,8 +386,6 @@ class VTKMPRRotateExample extends Component {
387386
};
388387

389388
handleChangeX = (index, event) => {
390-
volumeData[index].horizontalRotation = +event.target.value;
391-
392389
const rotation = this.state.rotation;
393390

394391
rotation[index].x = +event.target.value;
@@ -399,8 +396,6 @@ class VTKMPRRotateExample extends Component {
399396
};
400397

401398
handleChangeY = (index, event) => {
402-
volumeData[index].verticalRotation = +event.target.value;
403-
404399
const rotation = this.state.rotation;
405400

406401
rotation[index].y = +event.target.value;
@@ -413,24 +408,22 @@ class VTKMPRRotateExample extends Component {
413408
updateRotate = index => {
414409
const api = this.apis[index];
415410
const renderWindow = api.genericRenderWindow.getRenderWindow();
416-
411+
const rotation = this.state.rotation;
417412
const istyle = renderWindow.getInteractor().getInteractorStyle();
413+
const viewport = istyle.getViewport();
414+
const dThetaY = rotation[index].x - volumeData[index].horizontalRotation;
415+
const dThetaX = rotation[index].y - volumeData[index].verticalRotation;
418416

419-
istyle
420-
.getViewport()
421-
.rotate(
422-
volumeData[index].horizontalRotation,
423-
volumeData[index].verticalRotation
424-
);
417+
viewport.rotate(-dThetaX, -dThetaY);
418+
419+
volumeData[index].horizontalRotation = rotation[index].x;
420+
volumeData[index].verticalRotation = rotation[index].y;
425421

426422
this.apis.orientations[index].updateMarkerOrientation();
427423

428424
renderWindow.render();
429425
};
430426

431-
getSliceXRotation = index => {
432-
return volumeData[index].horizontalRotation;
433-
};
434427
render() {
435428
if (!this.state.volumes || !this.state.volumes.length) {
436429
return <h4>Loading...</h4>;
@@ -441,7 +434,7 @@ class VTKMPRRotateExample extends Component {
441434
for (let index = 0; index < volumeData.length; index++) {
442435
columns.push(
443436
<div key={index.toString()} className="col-xs-12 col-sm-6">
444-
<div>
437+
{/* <div>
445438
<input
446439
className="rotate"
447440
type="range"
@@ -468,7 +461,7 @@ class VTKMPRRotateExample extends Component {
468461
}}
469462
/>
470463
<span>{this.state.rotation[index].y}</span>
471-
</div>
464+
</div> */}
472465
<View2D
473466
volumes={this.state.volumes}
474467
onCreated={this.storeApi(index)}

src/Custom/VTKMPRViewport.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export default class VtkMpr extends Component {
135135
this.renderer.addVolume(this.labelPipeline.actor);
136136

137137
istyle.setVolumeMapper(this.pipeline.mapper);
138-
istyle.setSliceNormal(0, 0, 1);
138+
istyle.setSliceNormal([0, 0, 1]);
139139
const range = istyle.getSliceRange();
140140
istyle.setSlice((range[0] + range[1]) / 2);
141141

@@ -239,7 +239,7 @@ export default class VtkMpr extends Component {
239239

240240
if (prevProps.sliceNormal !== this.props.sliceNormal) {
241241
const istyle = this.istyle;
242-
istyle.setSliceNormal(...this.props.sliceNormal);
242+
istyle.setSliceNormal([...this.props.sliceNormal]);
243243

244244
const range = istyle.getSliceRange();
245245
istyle.setSlice((range[0] + range[1]) / 2);

src/VTKViewport/Manipulators/vtkMouseRangeRotateManipulator.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ function vtkMouseRangeRotateManipulator(publicAPI, model) {
1616
-MAX_SAFE_INTEGER,
1717
MAX_SAFE_INTEGER,
1818
1,
19-
model.viewportData.getHRotation,
20-
horizontalRotation => {
21-
let hRotation = horizontalRotation % 360;
19+
() => 0,
20+
dThetaY => {
21+
let thetaY = dThetaY % 360;
2222

23-
model.viewportData.rotate(hRotation, model.viewportData.getVRotation());
23+
model.viewportData.rotate(0, thetaY);
2424

2525
// onInteractiveRotationChanged();
2626
}

src/VTKViewport/ViewportData.js

Lines changed: 25 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,8 @@ function validateNumber(numberValue) {
1414
throw `Invalid number ${numberValue}`;
1515
}
1616

17-
function areInitialRotationValues(horizontalRotation, verticalRotation) {
18-
return horizontalRotation === 0 && verticalRotation === 0;
19-
}
20-
2117
function createNewViewportData() {
2218
return {
23-
horizontalRotation: 0,
24-
verticalRotation: 0,
25-
initialViewUp: [0, 1, 0],
26-
initialSliceNormal: [0, 0, 1],
2719
viewUp: [0, 1, 0],
2820
sliceNormal: [0, 0, 1],
2921
};
@@ -46,71 +38,41 @@ export default class {
4638
return this.eventWindow;
4739
};
4840

49-
getHRotation = () => {
50-
return this._state.horizontalRotation;
51-
};
52-
53-
getVRotation = () => {
54-
return this._state.verticalRotation;
55-
};
41+
rotate = (dThetaX, dThetaY) => {
42+
validateNumber(dThetaX);
43+
validateNumber(dThetaY);
5644

57-
rotate = (horizontalRotation, verticalRotation) => {
58-
validateNumber(horizontalRotation);
59-
validateNumber(verticalRotation);
60-
61-
if (
62-
!areInitialRotationValues(horizontalRotation, verticalRotation) &&
63-
this._state.horizontalRotation === horizontalRotation &&
64-
this._state.verticalRotation === verticalRotation
65-
) {
66-
return;
67-
}
45+
let xAxis = [];
46+
vec3.cross(xAxis, this._state.viewUp, this._state.sliceNormal);
47+
vec3.normalize(xAxis, xAxis);
6848

49+
let yAxis = this._state.viewUp;
6950
// rotate around the vector of the cross product of the
7051
// plane and viewup as the X component
71-
const sliceXRot = [];
52+
7253
const sliceNormal = [];
7354
const sliceViewUp = [];
7455

75-
vec3.cross(
76-
sliceXRot,
77-
this._state.initialViewUp,
78-
this._state.initialSliceNormal
79-
);
80-
vec3.normalize(sliceXRot, sliceXRot);
81-
8256
const planeMat = mat4.create();
8357

84-
// Rotate around the vertical (slice-up) vector
85-
mat4.rotate(
86-
planeMat,
87-
planeMat,
88-
degrees2radians(-horizontalRotation),
89-
this._state.initialViewUp
90-
);
91-
92-
// Rotate around the horizontal (screen-x) vector
93-
mat4.rotate(
94-
planeMat,
95-
planeMat,
96-
degrees2radians(-verticalRotation),
97-
sliceXRot
98-
);
99-
100-
vec3.transformMat4(sliceNormal, this._state.initialSliceNormal, planeMat);
101-
vec3.transformMat4(sliceViewUp, this._state.initialViewUp, planeMat);
102-
103-
this._state.horizontalRotation = horizontalRotation;
104-
this._state.verticalRotation = verticalRotation;
58+
//Rotate around the vertical (slice-up) vector
59+
mat4.rotate(planeMat, planeMat, degrees2radians(dThetaY), yAxis);
60+
61+
//Rotate around the horizontal (screen-x) vector
62+
mat4.rotate(planeMat, planeMat, degrees2radians(dThetaX), xAxis);
63+
64+
vec3.transformMat4(sliceNormal, this._state.sliceNormal, planeMat);
65+
vec3.transformMat4(sliceViewUp, this._state.viewUp, planeMat);
66+
10567
this._state.sliceNormal = sliceNormal;
106-
this._state.sliceViewUp = sliceViewUp;
68+
this._state.viewUp = sliceViewUp;
10769

10870
var event = new CustomEvent(EVENTS.VIEWPORT_ROTATED, {
10971
detail: {
110-
horizontalRotation,
111-
verticalRotation,
11272
sliceNormal,
11373
sliceViewUp,
74+
dThetaX,
75+
dThetaY,
11476
},
11577
bubbles: true,
11678
cancelable: true,
@@ -119,32 +81,19 @@ export default class {
11981
this.eventWindow.dispatchEvent(event);
12082
};
12183

122-
getInitialViewUp = () => {
123-
return this._state.initialViewUp;
124-
};
125-
126-
getInitialSliceNormal = () => {
127-
return this._state.initialSliceNormal;
128-
};
129-
130-
setInitialOrientation = (initialSliceNormal, initialViewUp = [0, 1, 0]) => {
131-
this._state.initialSliceNormal = initialSliceNormal;
132-
this._state.initialViewUp = initialViewUp;
84+
setOrientation = (sliceNormal, viewUp = [0, 1, 0]) => {
85+
this._state.sliceNormal = sliceNormal;
86+
this._state.viewUp = viewUp;
13387
};
13488

135-
getviewUp = () => {
89+
getViewUp = () => {
13690
return this._state.viewUp;
13791
};
13892

139-
getsliceNormal = () => {
93+
getSliceNormal = () => {
14094
return this._state.sliceNormal;
14195
};
14296

143-
// this.setOrientation = (viewUp, sliceNormal) => {
144-
// state.viewUp = viewUp;
145-
// state.sliceNormal = sliceNormal;
146-
// };
147-
14897
getReadOnlyViewPort = () => {
14998
const readOnlyState = JSON.parse(JSON.stringify(this._state));
15099

src/VTKViewport/vtkInteractorStyleMPRRotate.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import vtkInteractorStyleMPRSlice from './vtkInteractorStyleMPRSlice.js';
33
import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants';
44
import { vec3, mat4 } from 'gl-matrix';
55
import { degrees2radians } from '../lib/math/angles.js';
6+
import ViewportData from './ViewportData.js';
67

78
const { States } = Constants;
89
const MAX_SAFE_INTEGER = 2147483647;
@@ -43,16 +44,11 @@ function vtkInteractorStyleMPRRotate(publicAPI, model) {
4344
const size = rwi.getView().getViewportSize(renderer);
4445
const xSensitivity = 100.0 / size[0];
4546
const ySensitivity = 100.0 / size[1];
46-
const dx = Math.round((pos[0] - model.rotateStartPos[0]) * xSensitivity);
47-
const dy = Math.round((pos[1] - model.rotateStartPos[1]) * ySensitivity);
47+
const dThetaX = -((pos[1] - model.rotateStartPos[1]) * ySensitivity);
48+
const dThetaY = -((pos[0] - model.rotateStartPos[0]) * xSensitivity);
4849
const viewport = publicAPI.getViewport();
49-
let horizontalRotation = viewport.getHRotation() + dx;
50-
let verticalRotation = viewport.getVRotation() + dy;
5150

52-
horizontalRotation %= 360;
53-
verticalRotation %= 360;
54-
55-
viewport.rotate(horizontalRotation, verticalRotation);
51+
viewport.rotate(dThetaX, dThetaY);
5652

5753
model.rotateStartPos[0] = Math.round(pos[0]);
5854
model.rotateStartPos[1] = Math.round(pos[1]);

src/VTKViewport/vtkInteractorStyleMPRSlice.js

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import vtkMouseCameraTrackballRotateManipulator from 'vtk.js/Sources/Interaction
66
import vtkMouseCameraTrackballPanManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulator';
77
import vtkMouseCameraTrackballZoomManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseCameraTrackballZoomManipulator';
88
import vtkMouseRangeManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseRangeManipulator';
9-
import vtkMouseRangeRotateManipulator from './Manipulators/vtkMouseRangeRotateManipulator';
9+
//import vtkMouseRangeRotateManipulator from './Manipulators/vtkMouseRangeRotateManipulator';
1010
import ViewportData from './ViewportData';
1111
import EVENTS from '../events';
1212

@@ -226,8 +226,8 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) {
226226
}
227227

228228
if (viewportData) {
229-
setSliceNormalInternal(viewportData.getInitialSliceNormal());
230-
setViewUpInternal(viewportData.getInitialViewUp());
229+
setSliceNormalInternal(viewportData.getSliceNormal());
230+
setViewUpInternal(viewportData.getViewUp());
231231

232232
viewportData
233233
.getEventWindow()
@@ -305,8 +305,8 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) {
305305
const viewportData = publicAPI.getViewport();
306306

307307
if (viewportData) {
308-
setSliceNormalInternal(viewportData.getInitialSliceNormal());
309-
setViewUpInternal(viewportData.getInitialViewUp());
308+
setSliceNormalInternal(viewportData.getSliceNormal());
309+
setViewUpInternal(viewportData.getViewUp());
310310
}
311311

312312
updateScrollManipulator();
@@ -439,10 +439,7 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) {
439439
const viewportData = publicAPI.getViewport();
440440

441441
if (viewportData) {
442-
viewportData.setInitialOrientation(
443-
normal,
444-
viewportData.getInitialViewUp()
445-
);
442+
viewportData.setOrientation(normal, viewportData.getViewUp());
446443
}
447444

448445
setSliceNormalInternal(normal);
@@ -463,10 +460,7 @@ function vtkInteractorStyleMPRSlice(publicAPI, model) {
463460
const viewportData = publicAPI.getViewport();
464461

465462
if (viewportData) {
466-
viewportData.setInitialOrientation(
467-
viewportData.getInitialSliceNormal(),
468-
viewUp
469-
);
463+
viewportData.setOrientation(viewportData.getSliceNormal(), viewUp);
470464
}
471465

472466
setViewUpInternal(viewUp);

0 commit comments

Comments
 (0)