Skip to content

Commit 71b04b8

Browse files
committed
feat(VolumeController): add impl
1 parent 7ed06f4 commit 71b04b8

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

src/core/VolumeController.tsx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
2+
import vtkVolumeController from '@kitware/vtk.js/Interaction/UI/VolumeController';
3+
import { useCallback, useContext, useEffect, useRef } from 'react';
4+
import { Contexts } from '..';
5+
import deletionRegistry from '../utils/DeletionRegistry';
6+
import useGetterRef from '../utils/useGetterRef';
7+
import useUnmount from '../utils/useUnmount';
8+
import { useRepresentation } from './contexts';
9+
10+
export interface VolumeControllerProps {
11+
/**
12+
* The size of the widget (width, height)
13+
*/
14+
size?: [number, number];
15+
16+
/**
17+
* Flag for rescaling the colormap.
18+
*/
19+
rescaleColorMap?: boolean;
20+
}
21+
22+
const DefaultProps = {
23+
size: [400, 150],
24+
rescaleColorMap: true,
25+
};
26+
27+
export default function VolumeController(props: VolumeControllerProps) {
28+
const containerRef = useRef<HTMLDivElement | null>(null);
29+
const [controllerRef, getController] = useGetterRef(() => {
30+
const ctrlr = vtkVolumeController.newInstance();
31+
deletionRegistry.markForDeletion(() => ctrlr.delete());
32+
return ctrlr;
33+
});
34+
35+
const view = useContext(Contexts.ViewContext);
36+
if (!view) throw new Error('Need view context');
37+
38+
const volumeRep = useRepresentation();
39+
40+
const updateWidget = useCallback(() => {
41+
const volume = volumeRep.getMapper()?.getInputData() as
42+
| vtkImageData
43+
| undefined;
44+
if (!volume) return;
45+
46+
const widget = getController().getWidget();
47+
const dataArray =
48+
volume.getPointData().getScalars() ||
49+
volume.getPointData().getArrays()[0];
50+
widget.setDataArray(dataArray.getData());
51+
}, [volumeRep, getController]);
52+
53+
// --- set actor --- //
54+
55+
const updateActor = useCallback(() => {
56+
// set the actor prior to setting props, since
57+
// setRescaleColorMap depends on the actor
58+
getController().setActor(volumeRep.getActor());
59+
}, [getController, volumeRep]);
60+
61+
useEffect(() => {
62+
return volumeRep.onDataAvailable(updateActor);
63+
}, [volumeRep, updateActor]);
64+
65+
// --- prop handling --- //
66+
67+
const { size = DefaultProps.size } = props;
68+
69+
useEffect(() => {
70+
if (!size) return;
71+
getController().setSize(size[0], size[1]);
72+
}, [size, getController]);
73+
74+
const { rescaleColorMap = DefaultProps.rescaleColorMap } = props;
75+
76+
useEffect(() => {
77+
if (rescaleColorMap === undefined || !getController().getActor()) return;
78+
79+
getController().setRescaleColorMap(rescaleColorMap);
80+
}, [rescaleColorMap, getController]);
81+
82+
// --- initialization --- //
83+
84+
const init = useCallback(
85+
(volume?: vtkImageData) => {
86+
const container = containerRef.current;
87+
const rw = view.getRenderWindow()?.get();
88+
89+
if (!volume || !rw) return;
90+
91+
const controller = getController();
92+
93+
controller.setContainer(container);
94+
controller.setupContent(rw, volumeRep.getActor(), false);
95+
controller.render();
96+
view.requestRender();
97+
98+
const sub = volume.onModified(() => {
99+
updateWidget();
100+
});
101+
102+
return () => {
103+
controller.setContainer(null);
104+
sub.unsubscribe();
105+
};
106+
},
107+
[view, volumeRep, updateWidget, getController]
108+
);
109+
110+
useEffect(() => {
111+
return volumeRep.onDataAvailable(init);
112+
}, [volumeRep, init]);
113+
114+
useUnmount(() => {
115+
if (controllerRef.current) {
116+
deletionRegistry.markForDeletion(controllerRef.current);
117+
controllerRef.current = null;
118+
}
119+
});
120+
121+
return <div ref={containerRef} />;
122+
}

src/global.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,3 +485,8 @@ declare module '@kitware/vtk.js/Rendering/OpenGL/HardwareSelector' {
485485

486486
export default vtkOpenGLHardwareSelector;
487487
}
488+
489+
declare module '@kitware/vtk.js/Interaction/UI/VolumeController' {
490+
const VolumeController: any;
491+
export default VolumeController;
492+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ export { default as SliceRepresentation } from './core/SliceRepresentation';
4444
export type { SliceRepresentationProps } from './core/SliceRepresentation';
4545
export { default as View } from './core/View';
4646
export type { ViewProps } from './core/View';
47+
export { default as VolumeController } from './core/VolumeController';
4748
export { default as VolumeRepresentation } from './core/VolumeRepresentation';

0 commit comments

Comments
 (0)