Skip to content

Commit

Permalink
Merge pull request #135 from Kitware/add-color-opacity-functions
Browse files Browse the repository at this point in the history
feat: add color + opacity funcs to slice + volume
  • Loading branch information
floryst authored Jun 17, 2024
2 parents e5b4e7b + 167843d commit 387f611
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/core/SliceRepresentation.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
import AbstractImageMapper, {
vtkAbstractImageMapper,
} from '@kitware/vtk.js/Rendering/Core/AbstractImageMapper';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkImageArrayMapper from '@kitware/vtk.js/Rendering/Core/ImageArrayMapper';
import vtkImageMapper, {
IImageMapperInitialValues,
Expand Down Expand Up @@ -29,6 +31,7 @@ import {
RepresentationContext,
useRendererContext,
} from './contexts';
import useColorAndOpacity from './modules/useColorAndOpacity';
import useColorTransferFunction from './modules/useColorTransferFunction';
import useDataEvents from './modules/useDataEvents';
import useMapper from './modules/useMapper';
Expand Down Expand Up @@ -70,6 +73,20 @@ export interface SliceRepresentationProps extends PropsWithChildren {
*/
colorDataRange?: 'auto' | Vector2;

/**
* Specify the color transfer functions.
*
* Each index cooresponds to an image component. There are max 4 components supported.
*/
colorTransferFunctions?: Array<vtkColorTransferFunction | null | undefined>;

/**
* Specify the scalar opacity functions.
*
* Each index cooresponds to an image component. There are max 4 components supported.
*/
scalarOpacityFunctions?: Array<vtkPiecewiseFunction | null | undefined>;

/**
* index of the slice along i
*/
Expand Down Expand Up @@ -186,11 +203,22 @@ export default forwardRef(function SliceRepresentation(
getActor().setMapper(getMapper() as vtkImageMapper);
}, [getActor, getMapper]);

// --- color and opacity --- //

const { colorTransferFunctions, scalarOpacityFunctions } = props;

useEffect(() => {
getActor().getProperty().setRGBTransferFunction(0, getLookupTable());
getActor().getProperty().setInterpolationTypeToLinear();
}, [getActor, getLookupTable]);

useColorAndOpacity(
getActor,
colorTransferFunctions,
scalarOpacityFunctions,
trackModified
);

// set actor property props
const { property: propertyProps } = props;
useComparableEffect(
Expand Down
28 changes: 28 additions & 0 deletions src/core/VolumeRepresentation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkVolume, {
IVolumeInitialValues,
} from '@kitware/vtk.js/Rendering/Core/Volume';
Expand All @@ -26,6 +28,7 @@ import {
RepresentationContext,
useRendererContext,
} from './contexts';
import useColorAndOpacity from './modules/useColorAndOpacity';
import useColorTransferFunction from './modules/useColorTransferFunction';
import useDataEvents from './modules/useDataEvents';
import useDataRange from './modules/useDataRange';
Expand Down Expand Up @@ -69,6 +72,20 @@ export interface VolumeRepresentationProps extends PropsWithChildren {
*/
colorDataRange?: 'auto' | Vector2;

/**
* Specify the color transfer functions.
*
* Each index cooresponds to an image component. There are max 4 components supported.
*/
colorTransferFunctions?: Array<vtkColorTransferFunction | null | undefined>;

/**
* Specify the scalar opacity functions.
*
* Each index cooresponds to an image component. There are max 4 components supported.
*/
scalarOpacityFunctions?: Array<vtkPiecewiseFunction | null | undefined>;

/**
* Event callback for when data is made available.
*
Expand Down Expand Up @@ -164,6 +181,10 @@ export default forwardRef(function VolumeRepresentation(
trackModified,
});

// --- color and opacity --- //

const { colorTransferFunctions, scalarOpacityFunctions } = props;

useEffect(() => {
getActor().setMapper(getMapper());
}, [getActor, getMapper]);
Expand All @@ -174,6 +195,13 @@ export default forwardRef(function VolumeRepresentation(
getActor().getProperty().setInterpolationTypeToLinear();
}, [getActor, getLookupTable, getPiecewiseFunction]);

useColorAndOpacity(
getActor,
colorTransferFunctions,
scalarOpacityFunctions,
trackModified
);

// set actor property props
const { property: propertyProps } = props;
useComparableEffect(
Expand Down
90 changes: 90 additions & 0 deletions src/core/modules/useColorAndOpacity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkProp3D from '@kitware/vtk.js/Rendering/Core/Prop3D';
import { useEffect } from 'react';

interface PropertyWithColorAndOpacity {
setRGBTransferFunction(
index: number,
fn: vtkColorTransferFunction | null
): boolean;
getRGBTransferFunction(index?: number): vtkColorTransferFunction | null;
setScalarOpacity(index: number, fn: vtkPiecewiseFunction | null): boolean;
getScalarOpacity(index?: number): vtkPiecewiseFunction | null;
}

interface ActorWithColorAndOpacity<E> extends vtkProp3D {
getProperty(): E;
}

export default function useColorAndOpacity<
E extends PropertyWithColorAndOpacity,
T extends ActorWithColorAndOpacity<E>
>(
getActor: () => T,
colorTransferFunctions:
| Array<vtkColorTransferFunction | null | undefined>
| null
| undefined,
scalarOpacityFunctions:
| Array<vtkPiecewiseFunction | null | undefined>
| null
| undefined,
trackModified: (b: boolean) => void
) {
useEffect(() => {
if (!colorTransferFunctions?.length) return;
const property = getActor().getProperty();

colorTransferFunctions.forEach((fn, component) => {
property.setRGBTransferFunction(component, fn);
});

return () => {
for (let i = 0; i < 4; i++) {
property.setRGBTransferFunction(i, null);
}
};
}, [colorTransferFunctions, getActor]);

useEffect(() => {
if (!scalarOpacityFunctions?.length) return;
const property = getActor().getProperty();

scalarOpacityFunctions.forEach((fn, component) => {
property.setScalarOpacity(component, fn);
});

return () => {
for (let i = 0; i < 4; i++) {
property.setScalarOpacity(i, null);
}
};
}, [scalarOpacityFunctions, getActor]);

// watch for vtk.js object changes

useEffect(() => {
if (!colorTransferFunctions?.length) return;
const subs = colorTransferFunctions
.filter((fn): fn is vtkColorTransferFunction => !!fn)
.map((fn) => {
return fn.onModified(() => {
trackModified(true);
});
});
return () => subs.forEach((sub) => sub.unsubscribe());
});

useEffect(() => {
if (!scalarOpacityFunctions?.length) return;
const subs = scalarOpacityFunctions
.filter((fn): fn is vtkPiecewiseFunction => !!fn)
.map((fn) => {
return fn.onModified(() => {
trackModified(true);
});
});
return () => subs.forEach((sub) => sub.unsubscribe());
});
}
13 changes: 12 additions & 1 deletion usage/src/Volume/VolumeRendering.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import vtkXMLImageDataReader from '@kitware/vtk.js/IO/XML/XMLImageDataReader';
import { useRef } from 'react';
import {
Reader,
View,
Expand All @@ -7,9 +8,19 @@ import {
} from 'react-vtk-js';

function Example() {
const view = useRef();
const run = () => {
const v = view.current;
const camera = v.getCamera();
camera.azimuth(0.5);
v.requestRender();
requestAnimationFrame(run);
};

return (
<div style={{ width: '100%', height: '100%' }}>
<View id='0'>
<button onClick={run}>Rotate</button>
<View id='0' ref={view}>
<VolumeRepresentation>
<VolumeController />
<Reader
Expand Down

0 comments on commit 387f611

Please sign in to comment.