Skip to content

Commit bd4fe74

Browse files
committed
feat(ResliceRepresentationProxy): new proxy for ResliceImageMapper
Add a new proxy class (vtkResliceRepresentationProxy) as a representation for the ResliceImageMapper. Add an example using vtkResliceRepresentationProxy.
1 parent e88aa3f commit bd4fe74

File tree

6 files changed

+404
-2
lines changed

6 files changed

+404
-2
lines changed

Sources/Proxy/Core/View2DProxy/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ function vtkView2DProxy(publicAPI, model) {
123123
superAddRepresentation(rep);
124124
if (rep.setSlicingMode) {
125125
rep.setSlicingMode('XYZ'[model.axis]);
126-
publicAPI.bindRepresentationToManipulator(rep);
127126
}
127+
publicAPI.bindRepresentationToManipulator(rep);
128128
};
129129

130130
const superRemoveRepresentation = publicAPI.removeRepresentation;
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import '@kitware/vtk.js/favicon';
2+
3+
// Load the rendering pieces we want to use (for both WebGL and WebGPU)
4+
import '@kitware/vtk.js/Rendering/Profiles/Volume';
5+
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
6+
7+
// Force the loading of HttpDataAccessHelper to support gzip decompression
8+
import '@kitware/vtk.js/IO/Core/DataAccessHelper/HttpDataAccessHelper';
9+
10+
import vtkHttpDataSetReader from '@kitware/vtk.js/IO/Core/HttpDataSetReader';
11+
12+
import vtkProxyManager from '@kitware/vtk.js/Proxy/Core/ProxyManager';
13+
14+
import vtkPlaneWidget from '@kitware/vtk.js/Widgets/Widgets3D/ImplicitPlaneWidget';
15+
import vtkWidgetManager from '@kitware/vtk.js/Widgets/Core/WidgetManager';
16+
import { SlabTypes } from 'vtk.js/Sources/Rendering/Core/ImageResliceMapper/Constants';
17+
18+
import proxyConfiguration from './proxy';
19+
20+
// ----------------------------------------------------------------------------
21+
// Use a vtkHttpDataSetReader to get a promise of vtkImageData
22+
// ----------------------------------------------------------------------------
23+
24+
const imageDataPromise = vtkHttpDataSetReader
25+
.newInstance({ fetchGzip: true })
26+
.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`)
27+
.then((reader) => reader.loadData())
28+
.then((reader) => reader.getOutputData());
29+
30+
// ----------------------------------------------------------------------------
31+
// Create proxy manager
32+
// ----------------------------------------------------------------------------
33+
34+
const proxyManager = vtkProxyManager.newInstance({ proxyConfiguration });
35+
36+
// ----------------------------------------------------------------------------
37+
// Create DOM elements to use as container and a button
38+
// ----------------------------------------------------------------------------
39+
40+
const mainContainer = document.createElement('div');
41+
document.body.appendChild(mainContainer);
42+
43+
// Full screen main container
44+
document.documentElement.style.height = '100%';
45+
document.body.style.height = '100%';
46+
document.body.style.margin = '0';
47+
mainContainer.style.height = '100%';
48+
mainContainer.style.display = 'flex';
49+
50+
const fitCameraButton = document.createElement('button');
51+
fitCameraButton.innerText = 'Toggle camera fitting';
52+
fitCameraButton.style.position = 'absolute';
53+
fitCameraButton.style.top = '10px';
54+
fitCameraButton.style.left = '10px';
55+
fitCameraButton.style.zIndex = '100';
56+
document.body.appendChild(fitCameraButton);
57+
58+
const resetCameraButton = document.createElement('button');
59+
resetCameraButton.innerText = 'Reset Camera';
60+
resetCameraButton.style.position = 'absolute';
61+
resetCameraButton.style.top = '50px';
62+
resetCameraButton.style.left = '10px';
63+
resetCameraButton.style.zIndex = '100';
64+
document.body.appendChild(resetCameraButton);
65+
66+
// ----------------------------------------------------------------------------
67+
// Create a 2D view proxy
68+
// ----------------------------------------------------------------------------
69+
70+
const view2DProxy = proxyManager.createProxy('Views', 'View2D', {
71+
axis: 2,
72+
fitProps: true,
73+
});
74+
view2DProxy.setContainer(mainContainer);
75+
view2DProxy
76+
.getOpenGLRenderWindow()
77+
.setSize(mainContainer.clientWidth, mainContainer.clientHeight);
78+
79+
fitCameraButton.addEventListener('click', () => {
80+
view2DProxy.setFitProps(!view2DProxy.getFitProps());
81+
view2DProxy.resetCamera();
82+
});
83+
84+
resetCameraButton.addEventListener('click', view2DProxy.resetCamera);
85+
86+
view2DProxy.getRenderer().setBackground([65 / 255, 85 / 255, 122 / 255]);
87+
88+
const widgetManager = vtkWidgetManager.newInstance();
89+
widgetManager.setRenderer(view2DProxy.getRenderer());
90+
91+
const repStyle = {
92+
active: {
93+
plane: {
94+
opacity: 0.05,
95+
color: [1, 1, 1],
96+
},
97+
normal: {
98+
opacity: 0.6,
99+
color: [0, 1, 0],
100+
},
101+
origin: {
102+
opacity: 0.6,
103+
color: [0, 1, 0],
104+
},
105+
},
106+
inactive: {
107+
plane: {
108+
opacity: 0.0,
109+
color: [1, 1, 1],
110+
},
111+
normal: {
112+
opacity: 0.3,
113+
color: [0.5, 0, 0],
114+
},
115+
origin: {
116+
opacity: 0.3,
117+
color: [0.5, 0, 0],
118+
},
119+
},
120+
};
121+
122+
const widget = vtkPlaneWidget.newInstance();
123+
widget.getWidgetState().setNormal(0, 0, 1);
124+
widget.setPlaceFactor(1);
125+
const w = widgetManager.addWidget(widget);
126+
w.setRepresentationStyle(repStyle);
127+
128+
// ----------------------------------------------------------------------------
129+
// Define widget setup
130+
// ----------------------------------------------------------------------------
131+
function setupWidget(im, rep) {
132+
const bds = im.getBounds();
133+
const imc = im.getCenter();
134+
const slicePlane = rep.getSlicePlane();
135+
slicePlane.setOrigin(imc);
136+
slicePlane.setNormal(0, 1, 0);
137+
widget.placeWidget(bds);
138+
139+
const renderer = view2DProxy.getRenderer();
140+
renderer.getActiveCamera().setFocalPoint(...imc);
141+
renderer.getActiveCamera().setViewUp([0, 0, -1]);
142+
const planeState = widget.getWidgetState();
143+
planeState.setOrigin(slicePlane.getOrigin());
144+
planeState.setNormal(slicePlane.getNormal());
145+
planeState.onModified(() => {
146+
slicePlane.setOrigin(planeState.getOrigin());
147+
slicePlane.setNormal(planeState.getNormal());
148+
});
149+
const renderWindow = view2DProxy.getRenderWindow();
150+
renderWindow.render();
151+
}
152+
153+
// ----------------------------------------------------------------------------
154+
// Create source proxy
155+
// ----------------------------------------------------------------------------
156+
157+
let representation2DProxy;
158+
const sourceProxy = proxyManager.createProxy('Sources', 'TrivialProducer');
159+
imageDataPromise.then((imageData) => {
160+
sourceProxy.setInputData(imageData);
161+
162+
// ----------------------------------------------------------------------------
163+
// Create representation proxy
164+
// ----------------------------------------------------------------------------
165+
// The representationProxy can be used to change properties, color by an array, set LUTs, etc...
166+
167+
representation2DProxy = proxyManager.getRepresentation(
168+
sourceProxy,
169+
view2DProxy
170+
);
171+
172+
setupWidget(imageData, representation2DProxy);
173+
representation2DProxy.setSlicePolyData(null);
174+
representation2DProxy.setSlabThickness(30);
175+
representation2DProxy.setSlabTrapezoidIntegration(1.5);
176+
representation2DProxy.setSlabType(SlabTypes.MAX);
177+
178+
view2DProxy.setFitProps(false);
179+
view2DProxy.resetCamera();
180+
});
181+
182+
global.mainContainer = mainContainer;
183+
global.proxyManager = proxyManager;
184+
global.sourceProxy = sourceProxy;
185+
global.view2DProxy = view2DProxy;
186+
global.representation2DProxy = representation2DProxy;
187+
global.widget = w;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import vtkSourceProxy from '@kitware/vtk.js/Proxy/Core/SourceProxy';
2+
import vtkView2DProxy from '@kitware/vtk.js/Proxy/Core/View2DProxy';
3+
import vtkResliceRepresentationProxy from '@kitware/vtk.js/Proxy/Representations/ResliceRepresentationProxy';
4+
5+
export default {
6+
definitions: {
7+
Sources: {
8+
TrivialProducer: {
9+
class: vtkSourceProxy,
10+
},
11+
},
12+
Representations: {
13+
ImageSlice: {
14+
class: vtkResliceRepresentationProxy,
15+
},
16+
},
17+
Views: {
18+
View2D: {
19+
class: vtkView2DProxy,
20+
},
21+
},
22+
},
23+
representations: {
24+
View2D: {
25+
vtkImageData: { name: 'ImageSlice' },
26+
},
27+
},
28+
filters: {},
29+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type vtkPolyData from '../../../Common/DataModel/PolyData';
2+
import type vtkPlane from '../../../Common/DataModel/Plane';
3+
import vtkAbstractRepresentationProxy from '../../Core/AbstractRepresentationProxy';
4+
5+
export interface vtkResliceRepresentationProxy
6+
extends vtkAbstractRepresentationProxy {
7+
8+
// proxy property mappings
9+
10+
setVisibility(visible: boolean): boolean;
11+
getVisibility(): boolean;
12+
setWindowWidth(width: number): boolean;
13+
getWindowWidth(): number;
14+
setWindowLevel(level: number): boolean;
15+
getWindowLevel(): number;
16+
setInterpolationType(type: number): boolean;
17+
getInterpolationType(): number;
18+
setSlabType(type: number): boolean;
19+
getSlabtype(): number;
20+
setSlicePlane(plane: vtkPlane): boolean;
21+
getSlicePlane(): vtkPlane;
22+
setSlicePolyData(polydata: vtkPolyData): boolean;
23+
getSlicePolyData(): vtkPolyData;
24+
setSlabThickness(thickness: number): boolean;
25+
getSlabThickness(): number;
26+
setSlabTrapezoidIntegration(slabTrapezoidIntegration: number): boolean;
27+
getSlabTrapezoidIntegration(): number;
28+
}
29+
30+
export default vtkResliceRepresentationProxy;

0 commit comments

Comments
 (0)