Skip to content

Commit 49bf5c8

Browse files
PaulHaxfloryst
authored andcommitted
feat(ImageSlice): add forceOpacity and forceTransparent
To maintain consistent render effect with stacked image slices no matter the opacity of the slices. closes #3126
1 parent 51faf45 commit 49bf5c8

File tree

5 files changed

+145
-1
lines changed

5 files changed

+145
-1
lines changed

Sources/Rendering/Core/ImageSlice/index.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,18 @@ export interface vtkImageSlice extends vtkProp3D {
135135
* @param {vtkImageProperty} property The vtkImageProperty instance.
136136
*/
137137
setProperty(property: vtkImageProperty): boolean;
138+
139+
/**
140+
*
141+
* @param {boolean} forceOpaque If true, render during opaque pass even if opacity value is below 1.0.
142+
*/
143+
setForceOpaque(forceOpaque: boolean): boolean;
144+
145+
/**
146+
*
147+
* @param {boolean} forceTranslucent If true, render during translucent pass even if opacity value is 1.0.
148+
*/
149+
setForceTranslucent(forceTranslucent: boolean): boolean;
138150
}
139151

140152
/**

Sources/Rendering/Core/ImageSlice/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ const DEFAULT_VALUES = {
179179
mapper: null,
180180
property: null,
181181

182+
forceOpaque: false,
183+
forceTranslucent: false,
184+
182185
bounds: [...vtkBoundingBox.INIT_BOUNDS],
183186
};
184187

@@ -196,7 +199,8 @@ export function extend(publicAPI, model, initialValues = {}) {
196199

197200
// Build VTK API
198201
macro.set(publicAPI, model, ['property']);
199-
macro.setGet(publicAPI, model, ['mapper']);
202+
macro.setGet(publicAPI, model, ['mapper', 'forceOpaque', 'forceTranslucent']);
203+
200204
macro.getArray(publicAPI, model, ['bounds'], 6);
201205

202206
// Object methods
23.4 KB
Loading
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import test from 'tape';
2+
import testUtils from 'vtk.js/Sources/Testing/testUtils';
3+
4+
import vtkImageGridSource from 'vtk.js/Sources/Filters/Sources/ImageGridSource';
5+
import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper';
6+
import vtkImageSlice from 'vtk.js/Sources/Rendering/Core/ImageSlice';
7+
import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow';
8+
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
9+
import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow';
10+
import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction';
11+
12+
import forceOpaqueBaseline from './testForceOpaque.png';
13+
import forceTranslucentBaseline from './testForceTranslucent.png';
14+
import { SlicingMode } from '../../../Core/ImageMapper/Constants';
15+
16+
const setupSlices = (t) => {
17+
const gc = testUtils.createGarbageCollector(t);
18+
t.ok('rendering', 'vtkOpenGLImageMapper testImage');
19+
20+
// Create some control UI
21+
const container = document.querySelector('body');
22+
const renderWindowContainer = gc.registerDOMElement(
23+
document.createElement('div')
24+
);
25+
container.appendChild(renderWindowContainer);
26+
27+
// create what we will view
28+
const renderWindow = gc.registerResource(vtkRenderWindow.newInstance());
29+
const renderer = gc.registerResource(vtkRenderer.newInstance());
30+
renderWindow.addRenderer(renderer);
31+
renderer.setBackground(0.32, 0.34, 0.43);
32+
33+
// ----------------------------------------------------------------------------
34+
// Test code
35+
// ----------------------------------------------------------------------------
36+
37+
const gridSource = gc.registerResource(vtkImageGridSource.newInstance());
38+
const extent = 200;
39+
const gridSpacing = 32;
40+
const dataSpacing = 4;
41+
const origin = 16;
42+
gridSource.setDataExtent(0, extent, 0, extent, 0, 4);
43+
gridSource.setDataSpacing(dataSpacing, dataSpacing, dataSpacing);
44+
gridSource.setGridSpacing(gridSpacing, gridSpacing, gridSpacing);
45+
gridSource.setGridOrigin(origin, origin, 1);
46+
const direction = [0.866, 0.5, 0, -0.5, 0.866, 0, 0, 0, 1];
47+
gridSource.setDataDirection(...direction);
48+
49+
const slice = 0;
50+
51+
// mapperAbove should show above mapperBelow
52+
// scalars, however, should be correct
53+
const mapperBelow = gc.registerResource(vtkImageMapper.newInstance());
54+
mapperBelow.setInputConnection(gridSource.getOutputPort());
55+
mapperBelow.setSlicingMode(SlicingMode.Z);
56+
mapperBelow.setSlice(slice * dataSpacing);
57+
58+
const mapperAbove = gc.registerResource(vtkImageMapper.newInstance());
59+
mapperAbove.setInputConnection(gridSource.getOutputPort());
60+
mapperAbove.setSlicingMode(SlicingMode.Z);
61+
mapperAbove.setSlice(slice * dataSpacing);
62+
63+
const actorBelow = gc.registerResource(vtkImageSlice.newInstance());
64+
const rgb = vtkColorTransferFunction.newInstance();
65+
rgb.addRGBPoint(0, 0, 1, 0);
66+
rgb.addRGBPoint(255, 0, 1, 0);
67+
actorBelow.getProperty().setRGBTransferFunction(rgb);
68+
actorBelow.setMapper(mapperBelow);
69+
actorBelow.setPosition(100, 100, 0);
70+
71+
const actorAbove = gc.registerResource(vtkImageSlice.newInstance());
72+
actorAbove.setMapper(mapperAbove);
73+
actorAbove.setPosition(-100, 0, 0);
74+
75+
renderer.addActor(actorBelow);
76+
renderer.addActor(actorAbove);
77+
renderer.resetCamera();
78+
79+
// create something to view it, in this case webgl
80+
const glWindow = gc.registerResource(vtkOpenGLRenderWindow.newInstance());
81+
glWindow.setContainer(renderWindowContainer);
82+
renderWindow.addView(glWindow);
83+
glWindow.setSize(400, 400);
84+
85+
return { glWindow, renderWindow, actorAbove, actorBelow, gc };
86+
};
87+
88+
test.onlyIfWebGL('Test ImageMapper forceOpaque', (t) => {
89+
const { glWindow, renderWindow, actorBelow, gc } = setupSlices(t);
90+
91+
// If this actor is simply just made transparent, then it will show above the actorAbove
92+
actorBelow.getProperty().setOpacity(0.5);
93+
// Make actorBelow get rendered in same pass as actorAbove
94+
actorBelow.setForceOpaque(true);
95+
96+
glWindow.captureNextImage().then((image) => {
97+
testUtils.compareImages(
98+
image,
99+
[forceOpaqueBaseline],
100+
'Rendering/OpenGL/ImageSlice',
101+
t,
102+
0.5,
103+
gc.releaseResources
104+
);
105+
});
106+
renderWindow.render();
107+
});
108+
109+
test.onlyIfWebGL.only('Test ImageMapper forceTranslucent', (t) => {
110+
const { glWindow, renderWindow, actorAbove, actorBelow, gc } = setupSlices(t);
111+
112+
actorBelow.getProperty().setOpacity(0.9);
113+
actorAbove.getProperty().setOpacity(1);
114+
// keep translucent blending
115+
actorAbove.setForceTranslucent(true);
116+
117+
glWindow.captureNextImage().then((image) => {
118+
testUtils.compareImages(
119+
image,
120+
[forceTranslucentBaseline],
121+
'Rendering/OpenGL/ImageSlice',
122+
t,
123+
0.5,
124+
gc.releaseResources
125+
);
126+
});
127+
renderWindow.render();
128+
});
24.2 KB
Loading

0 commit comments

Comments
 (0)