Skip to content

Commit 387f611

Browse files
authored
Merge pull request #135 from Kitware/add-color-opacity-functions
feat: add color + opacity funcs to slice + volume
2 parents e5b4e7b + 167843d commit 387f611

File tree

4 files changed

+158
-1
lines changed

4 files changed

+158
-1
lines changed

src/core/SliceRepresentation.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
2+
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
23
import AbstractImageMapper, {
34
vtkAbstractImageMapper,
45
} from '@kitware/vtk.js/Rendering/Core/AbstractImageMapper';
6+
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
57
import vtkImageArrayMapper from '@kitware/vtk.js/Rendering/Core/ImageArrayMapper';
68
import vtkImageMapper, {
79
IImageMapperInitialValues,
@@ -29,6 +31,7 @@ import {
2931
RepresentationContext,
3032
useRendererContext,
3133
} from './contexts';
34+
import useColorAndOpacity from './modules/useColorAndOpacity';
3235
import useColorTransferFunction from './modules/useColorTransferFunction';
3336
import useDataEvents from './modules/useDataEvents';
3437
import useMapper from './modules/useMapper';
@@ -70,6 +73,20 @@ export interface SliceRepresentationProps extends PropsWithChildren {
7073
*/
7174
colorDataRange?: 'auto' | Vector2;
7275

76+
/**
77+
* Specify the color transfer functions.
78+
*
79+
* Each index cooresponds to an image component. There are max 4 components supported.
80+
*/
81+
colorTransferFunctions?: Array<vtkColorTransferFunction | null | undefined>;
82+
83+
/**
84+
* Specify the scalar opacity functions.
85+
*
86+
* Each index cooresponds to an image component. There are max 4 components supported.
87+
*/
88+
scalarOpacityFunctions?: Array<vtkPiecewiseFunction | null | undefined>;
89+
7390
/**
7491
* index of the slice along i
7592
*/
@@ -186,11 +203,22 @@ export default forwardRef(function SliceRepresentation(
186203
getActor().setMapper(getMapper() as vtkImageMapper);
187204
}, [getActor, getMapper]);
188205

206+
// --- color and opacity --- //
207+
208+
const { colorTransferFunctions, scalarOpacityFunctions } = props;
209+
189210
useEffect(() => {
190211
getActor().getProperty().setRGBTransferFunction(0, getLookupTable());
191212
getActor().getProperty().setInterpolationTypeToLinear();
192213
}, [getActor, getLookupTable]);
193214

215+
useColorAndOpacity(
216+
getActor,
217+
colorTransferFunctions,
218+
scalarOpacityFunctions,
219+
trackModified
220+
);
221+
194222
// set actor property props
195223
const { property: propertyProps } = props;
196224
useComparableEffect(

src/core/VolumeRepresentation.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
22
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
3+
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
4+
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
35
import vtkVolume, {
46
IVolumeInitialValues,
57
} from '@kitware/vtk.js/Rendering/Core/Volume';
@@ -26,6 +28,7 @@ import {
2628
RepresentationContext,
2729
useRendererContext,
2830
} from './contexts';
31+
import useColorAndOpacity from './modules/useColorAndOpacity';
2932
import useColorTransferFunction from './modules/useColorTransferFunction';
3033
import useDataEvents from './modules/useDataEvents';
3134
import useDataRange from './modules/useDataRange';
@@ -69,6 +72,20 @@ export interface VolumeRepresentationProps extends PropsWithChildren {
6972
*/
7073
colorDataRange?: 'auto' | Vector2;
7174

75+
/**
76+
* Specify the color transfer functions.
77+
*
78+
* Each index cooresponds to an image component. There are max 4 components supported.
79+
*/
80+
colorTransferFunctions?: Array<vtkColorTransferFunction | null | undefined>;
81+
82+
/**
83+
* Specify the scalar opacity functions.
84+
*
85+
* Each index cooresponds to an image component. There are max 4 components supported.
86+
*/
87+
scalarOpacityFunctions?: Array<vtkPiecewiseFunction | null | undefined>;
88+
7289
/**
7390
* Event callback for when data is made available.
7491
*
@@ -164,6 +181,10 @@ export default forwardRef(function VolumeRepresentation(
164181
trackModified,
165182
});
166183

184+
// --- color and opacity --- //
185+
186+
const { colorTransferFunctions, scalarOpacityFunctions } = props;
187+
167188
useEffect(() => {
168189
getActor().setMapper(getMapper());
169190
}, [getActor, getMapper]);
@@ -174,6 +195,13 @@ export default forwardRef(function VolumeRepresentation(
174195
getActor().getProperty().setInterpolationTypeToLinear();
175196
}, [getActor, getLookupTable, getPiecewiseFunction]);
176197

198+
useColorAndOpacity(
199+
getActor,
200+
colorTransferFunctions,
201+
scalarOpacityFunctions,
202+
trackModified
203+
);
204+
177205
// set actor property props
178206
const { property: propertyProps } = props;
179207
useComparableEffect(
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
2+
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
3+
import vtkProp3D from '@kitware/vtk.js/Rendering/Core/Prop3D';
4+
import { useEffect } from 'react';
5+
6+
interface PropertyWithColorAndOpacity {
7+
setRGBTransferFunction(
8+
index: number,
9+
fn: vtkColorTransferFunction | null
10+
): boolean;
11+
getRGBTransferFunction(index?: number): vtkColorTransferFunction | null;
12+
setScalarOpacity(index: number, fn: vtkPiecewiseFunction | null): boolean;
13+
getScalarOpacity(index?: number): vtkPiecewiseFunction | null;
14+
}
15+
16+
interface ActorWithColorAndOpacity<E> extends vtkProp3D {
17+
getProperty(): E;
18+
}
19+
20+
export default function useColorAndOpacity<
21+
E extends PropertyWithColorAndOpacity,
22+
T extends ActorWithColorAndOpacity<E>
23+
>(
24+
getActor: () => T,
25+
colorTransferFunctions:
26+
| Array<vtkColorTransferFunction | null | undefined>
27+
| null
28+
| undefined,
29+
scalarOpacityFunctions:
30+
| Array<vtkPiecewiseFunction | null | undefined>
31+
| null
32+
| undefined,
33+
trackModified: (b: boolean) => void
34+
) {
35+
useEffect(() => {
36+
if (!colorTransferFunctions?.length) return;
37+
const property = getActor().getProperty();
38+
39+
colorTransferFunctions.forEach((fn, component) => {
40+
property.setRGBTransferFunction(component, fn);
41+
});
42+
43+
return () => {
44+
for (let i = 0; i < 4; i++) {
45+
property.setRGBTransferFunction(i, null);
46+
}
47+
};
48+
}, [colorTransferFunctions, getActor]);
49+
50+
useEffect(() => {
51+
if (!scalarOpacityFunctions?.length) return;
52+
const property = getActor().getProperty();
53+
54+
scalarOpacityFunctions.forEach((fn, component) => {
55+
property.setScalarOpacity(component, fn);
56+
});
57+
58+
return () => {
59+
for (let i = 0; i < 4; i++) {
60+
property.setScalarOpacity(i, null);
61+
}
62+
};
63+
}, [scalarOpacityFunctions, getActor]);
64+
65+
// watch for vtk.js object changes
66+
67+
useEffect(() => {
68+
if (!colorTransferFunctions?.length) return;
69+
const subs = colorTransferFunctions
70+
.filter((fn): fn is vtkColorTransferFunction => !!fn)
71+
.map((fn) => {
72+
return fn.onModified(() => {
73+
trackModified(true);
74+
});
75+
});
76+
return () => subs.forEach((sub) => sub.unsubscribe());
77+
});
78+
79+
useEffect(() => {
80+
if (!scalarOpacityFunctions?.length) return;
81+
const subs = scalarOpacityFunctions
82+
.filter((fn): fn is vtkPiecewiseFunction => !!fn)
83+
.map((fn) => {
84+
return fn.onModified(() => {
85+
trackModified(true);
86+
});
87+
});
88+
return () => subs.forEach((sub) => sub.unsubscribe());
89+
});
90+
}

usage/src/Volume/VolumeRendering.jsx

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import vtkXMLImageDataReader from '@kitware/vtk.js/IO/XML/XMLImageDataReader';
2+
import { useRef } from 'react';
23
import {
34
Reader,
45
View,
@@ -7,9 +8,19 @@ import {
78
} from 'react-vtk-js';
89

910
function Example() {
11+
const view = useRef();
12+
const run = () => {
13+
const v = view.current;
14+
const camera = v.getCamera();
15+
camera.azimuth(0.5);
16+
v.requestRender();
17+
requestAnimationFrame(run);
18+
};
19+
1020
return (
1121
<div style={{ width: '100%', height: '100%' }}>
12-
<View id='0'>
22+
<button onClick={run}>Rotate</button>
23+
<View id='0' ref={view}>
1324
<VolumeRepresentation>
1425
<VolumeController />
1526
<Reader

0 commit comments

Comments
 (0)