Skip to content

Commit 74c4656

Browse files
authored
* fix: 🐛 Using mpr slice tools updates crosshairs (OHIF#84)
1 parent 13b71ad commit 74c4656

6 files changed

+374
-139
lines changed

examples/VTKCrosshairsExample.js

+34
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ function createStudyImageIds(baseUrl, studySearchOptions) {
6363
class VTKCrosshairsExample extends Component {
6464
state = {
6565
volumes: [],
66+
displayCrosshairs: true,
6667
};
6768

6869
async componentDidMount() {
@@ -129,6 +130,15 @@ class VTKCrosshairsExample extends Component {
129130
api.setSlabThickness(0.1);
130131

131132
renderWindow.render();
133+
134+
// Its up to the layout manager of an app to know how many viewports are being created.
135+
if (apis[0] && apis[1] && apis[2]) {
136+
//const api = apis[0];
137+
138+
const api = apis[0];
139+
140+
api.svgWidgets.crosshairsWidget.resetCrosshairs(apis, 0);
141+
}
132142
};
133143
};
134144

@@ -145,6 +155,22 @@ class VTKCrosshairsExample extends Component {
145155
});
146156
}
147157

158+
toggleCrosshairs = () => {
159+
const { displayCrosshairs } = this.state;
160+
const apis = this.apis;
161+
162+
const shouldDisplayCrosshairs = !displayCrosshairs;
163+
164+
apis.forEach(api => {
165+
const { svgWidgetManager, svgWidgets } = api;
166+
svgWidgets.crosshairsWidget.setDisplay(shouldDisplayCrosshairs);
167+
168+
svgWidgetManager.render();
169+
});
170+
171+
this.setState({ displayCrosshairs: shouldDisplayCrosshairs });
172+
};
173+
148174
render() {
149175
if (!this.state.volumes || !this.state.volumes.length) {
150176
return <h4>Loading...</h4>;
@@ -167,6 +193,14 @@ class VTKCrosshairsExample extends Component {
167193
onChange={this.handleSlabThicknessChange.bind(this)}
168194
/>
169195
</div>
196+
<div className="col-xs-4">
197+
<p>Click bellow to toggle crosshairs on/off.</p>
198+
<button onClick={this.toggleCrosshairs}>
199+
{this.state.displayCrosshairs
200+
? 'Hide Crosshairs'
201+
: 'Show Crosshairs'}
202+
</button>
203+
</div>
170204
</div>
171205
<div className="row">
172206
<div className="col-sm-4">

src/VTKViewport/View2D.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export default class View2D extends Component {
5959
this.state = {
6060
voi: this.getVOI(props.volumes[0]),
6161
};
62+
63+
this.apiProperties = {};
6264
}
6365

6466
updatePaintbrush() {
@@ -218,6 +220,8 @@ export default class View2D extends Component {
218220
const boundGetSlabThickness = this.getSlabThickness.bind(this);
219221
const boundSetSlabThickness = this.setSlabThickness.bind(this);
220222
const boundAddSVGWidget = this.addSVGWidget.bind(this);
223+
const boundGetApiProperty = this.getApiProperty.bind(this);
224+
const boundSetApiProperty = this.setApiProperty.bind(this);
221225

222226
this.svgWidgets = {};
223227

@@ -244,12 +248,23 @@ export default class View2D extends Component {
244248
setInteractorStyle: boundSetInteractorStyle,
245249
getSlabThickness: boundGetSlabThickness,
246250
setSlabThickness: boundSetSlabThickness,
251+
get: boundGetApiProperty,
252+
set: boundSetApiProperty,
253+
type: 'VIEW2D',
247254
};
248255

249256
this.props.onCreated(api);
250257
}
251258
}
252259

260+
getApiProperty(propertyName) {
261+
return this.apiProperties[propertyName];
262+
}
263+
264+
setApiProperty(propertyName, value) {
265+
this.apiProperties[propertyName] = value;
266+
}
267+
253268
addSVGWidget(widget, name) {
254269
const { svgWidgetManager } = this;
255270

@@ -312,15 +327,6 @@ export default class View2D extends Component {
312327
}
313328

314329
const slabThickness = this.getSlabThickness();
315-
316-
/*
317-
let currentSlabThickness;
318-
if (currentIStyle.getSlabThickness && istyle.getSlabThickness) {
319-
currentSlabThickness = currentIStyle.getSlabThickness();
320-
this.currentSlabThickness = currentSlabThickness;
321-
}
322-
*/
323-
324330
const interactor = renderWindow.getInteractor();
325331

326332
interactor.setInteractorStyle(istyle);

src/VTKViewport/View3D.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ export default class View3D extends Component {
121121
filters,
122122
actors,
123123
volumes,
124-
_component: this,
124+
type: 'VIEW3D',
125+
_component: this, // Backdoor still open for now whilst the API isn't as mature as View2D.
125126
};
126127

127128
this.props.onCreated(api);

src/VTKViewport/vtkInteractorStyleMPRCrosshairs.js

+8-58
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import macro from 'vtk.js/Sources/macro';
2-
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
32
import vtkInteractorStyleMPRSlice from './vtkInteractorStyleMPRSlice.js';
43
import Constants from 'vtk.js/Sources/Rendering/Core/InteractorStyle/Constants';
54
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
@@ -20,72 +19,30 @@ function vtkInteractorStyleMPRCrosshairs(publicAPI, model) {
2019

2120
function moveCrosshairs(callData) {
2221
const { apis, apiIndex } = model;
23-
const pos = [callData.position.x, callData.position.y];
22+
const api = apis[apiIndex];
23+
24+
const pos = callData.position;
2425
const renderer = callData.pokedRenderer;
2526

2627
const dPos = vtkCoordinate.newInstance();
2728
dPos.setCoordinateSystemToDisplay();
2829

29-
dPos.setValue(pos[0], pos[1], 0);
30+
dPos.setValue(pos.x, pos.y, 0);
3031
let worldPos = dPos.getComputedWorldValue(renderer);
3132

3233
const camera = renderer.getActiveCamera();
3334
const directionOfProjection = camera.getDirectionOfProjection();
3435

35-
const api = apis[apiIndex];
3636
const halfSlabThickness = api.getSlabThickness() / 2;
3737

38-
for (let i = 0; i < worldPos.length; i++) {
39-
worldPos[i] += halfSlabThickness * directionOfProjection[i];
40-
}
41-
4238
// Add half of the slab thickness to the world position, such that we select
4339
// The center of the slice.
4440

45-
if (apis === undefined || apiIndex === undefined) {
46-
console.error(
47-
'apis and apiIndex must be set on the vtkInteractorStyleMPRCrosshairs.'
48-
);
41+
for (let i = 0; i < worldPos.length; i++) {
42+
worldPos[i] += halfSlabThickness * directionOfProjection[i];
4943
}
5044

51-
// Set camera focal point to world coordinate for linked views
52-
apis.forEach((api, viewportIndex) => {
53-
if (viewportIndex !== apiIndex) {
54-
// We are basically doing the same as getSlice but with the world coordinate
55-
// that we want to jump to instead of the camera focal point.
56-
// I would rather do the camera adjustment directly but I keep
57-
// doing it wrong and so this is good enough for now.
58-
const renderWindow = api.genericRenderWindow.getRenderWindow();
59-
60-
const istyle = renderWindow.getInteractor().getInteractorStyle();
61-
const sliceNormal = istyle.getSliceNormal();
62-
const transform = vtkMatrixBuilder
63-
.buildFromDegree()
64-
.identity()
65-
.rotateFromDirections(sliceNormal, [1, 0, 0]);
66-
67-
const mutatedWorldPos = worldPos.slice();
68-
transform.apply(mutatedWorldPos);
69-
const slice = mutatedWorldPos[0];
70-
71-
istyle.setSlice(slice);
72-
73-
renderWindow.render();
74-
}
75-
76-
const renderer = api.genericRenderWindow.getRenderer();
77-
const wPos = vtkCoordinate.newInstance();
78-
wPos.setCoordinateSystemToWorld();
79-
wPos.setValue(worldPos);
80-
81-
const displayPosition = wPos.getComputedDisplayValue(renderer);
82-
const { svgWidgetManager } = api;
83-
api.svgWidgets.crosshairsWidget.setPoint(
84-
displayPosition[0],
85-
displayPosition[1]
86-
);
87-
svgWidgetManager.render();
88-
});
45+
api.svgWidgets.crosshairsWidget.moveCrosshairs(worldPos, apis, apiIndex);
8946

9047
publicAPI.invokeInteractionEvent({ type: 'InteractionEvent' });
9148
}
@@ -125,13 +82,6 @@ function vtkInteractorStyleMPRCrosshairs(publicAPI, model) {
12582
break;
12683
}
12784
};
128-
129-
publicAPI.setApis = apis => {
130-
model.apis = apis;
131-
};
132-
publicAPI.setApiIndex = apiIndex => {
133-
model.apiIndex = apiIndex;
134-
};
13585
}
13686

13787
// ----------------------------------------------------------------------------
@@ -148,7 +98,7 @@ export function extend(publicAPI, model, initialValues = {}) {
14898
// Inheritance
14999
vtkInteractorStyleMPRSlice.extend(publicAPI, model, initialValues);
150100

151-
macro.setGet(publicAPI, model, ['callback']);
101+
macro.setGet(publicAPI, model, ['callback', 'apis', 'apiIndex', 'onScroll']);
152102

153103
// Object specific methods
154104
vtkInteractorStyleMPRCrosshairs(publicAPI, model);

0 commit comments

Comments
 (0)