Skip to content

Commit

Permalink
feat(ImageSlice): add forceOpacity and forceTransparent
Browse files Browse the repository at this point in the history
To maintain consistent render effect with stacked image slices
no matter the opacity of the slices.

closes #3126
  • Loading branch information
PaulHax authored and floryst committed Sep 30, 2024
1 parent 51faf45 commit 49bf5c8
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 1 deletion.
12 changes: 12 additions & 0 deletions Sources/Rendering/Core/ImageSlice/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ export interface vtkImageSlice extends vtkProp3D {
* @param {vtkImageProperty} property The vtkImageProperty instance.
*/
setProperty(property: vtkImageProperty): boolean;

/**
*
* @param {boolean} forceOpaque If true, render during opaque pass even if opacity value is below 1.0.
*/
setForceOpaque(forceOpaque: boolean): boolean;

/**
*
* @param {boolean} forceTranslucent If true, render during translucent pass even if opacity value is 1.0.
*/
setForceTranslucent(forceTranslucent: boolean): boolean;
}

/**
Expand Down
6 changes: 5 additions & 1 deletion Sources/Rendering/Core/ImageSlice/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ const DEFAULT_VALUES = {
mapper: null,
property: null,

forceOpaque: false,
forceTranslucent: false,

bounds: [...vtkBoundingBox.INIT_BOUNDS],
};

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

// Build VTK API
macro.set(publicAPI, model, ['property']);
macro.setGet(publicAPI, model, ['mapper']);
macro.setGet(publicAPI, model, ['mapper', 'forceOpaque', 'forceTranslucent']);

macro.getArray(publicAPI, model, ['bounds'], 6);

// Object methods
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions Sources/Rendering/OpenGL/ImageSlice/test/testForceRenderPass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import test from 'tape';
import testUtils from 'vtk.js/Sources/Testing/testUtils';

import vtkImageGridSource from 'vtk.js/Sources/Filters/Sources/ImageGridSource';
import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper';
import vtkImageSlice from 'vtk.js/Sources/Rendering/Core/ImageSlice';
import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow';
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow';
import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction';

import forceOpaqueBaseline from './testForceOpaque.png';
import forceTranslucentBaseline from './testForceTranslucent.png';
import { SlicingMode } from '../../../Core/ImageMapper/Constants';

const setupSlices = (t) => {
const gc = testUtils.createGarbageCollector(t);
t.ok('rendering', 'vtkOpenGLImageMapper testImage');

// Create some control UI
const container = document.querySelector('body');
const renderWindowContainer = gc.registerDOMElement(
document.createElement('div')
);
container.appendChild(renderWindowContainer);

// create what we will view
const renderWindow = gc.registerResource(vtkRenderWindow.newInstance());
const renderer = gc.registerResource(vtkRenderer.newInstance());
renderWindow.addRenderer(renderer);
renderer.setBackground(0.32, 0.34, 0.43);

// ----------------------------------------------------------------------------
// Test code
// ----------------------------------------------------------------------------

const gridSource = gc.registerResource(vtkImageGridSource.newInstance());
const extent = 200;
const gridSpacing = 32;
const dataSpacing = 4;
const origin = 16;
gridSource.setDataExtent(0, extent, 0, extent, 0, 4);
gridSource.setDataSpacing(dataSpacing, dataSpacing, dataSpacing);
gridSource.setGridSpacing(gridSpacing, gridSpacing, gridSpacing);
gridSource.setGridOrigin(origin, origin, 1);
const direction = [0.866, 0.5, 0, -0.5, 0.866, 0, 0, 0, 1];
gridSource.setDataDirection(...direction);

const slice = 0;

// mapperAbove should show above mapperBelow
// scalars, however, should be correct
const mapperBelow = gc.registerResource(vtkImageMapper.newInstance());
mapperBelow.setInputConnection(gridSource.getOutputPort());
mapperBelow.setSlicingMode(SlicingMode.Z);
mapperBelow.setSlice(slice * dataSpacing);

const mapperAbove = gc.registerResource(vtkImageMapper.newInstance());
mapperAbove.setInputConnection(gridSource.getOutputPort());
mapperAbove.setSlicingMode(SlicingMode.Z);
mapperAbove.setSlice(slice * dataSpacing);

const actorBelow = gc.registerResource(vtkImageSlice.newInstance());
const rgb = vtkColorTransferFunction.newInstance();
rgb.addRGBPoint(0, 0, 1, 0);
rgb.addRGBPoint(255, 0, 1, 0);
actorBelow.getProperty().setRGBTransferFunction(rgb);
actorBelow.setMapper(mapperBelow);
actorBelow.setPosition(100, 100, 0);

const actorAbove = gc.registerResource(vtkImageSlice.newInstance());
actorAbove.setMapper(mapperAbove);
actorAbove.setPosition(-100, 0, 0);

renderer.addActor(actorBelow);
renderer.addActor(actorAbove);
renderer.resetCamera();

// create something to view it, in this case webgl
const glWindow = gc.registerResource(vtkOpenGLRenderWindow.newInstance());
glWindow.setContainer(renderWindowContainer);
renderWindow.addView(glWindow);
glWindow.setSize(400, 400);

return { glWindow, renderWindow, actorAbove, actorBelow, gc };
};

test.onlyIfWebGL('Test ImageMapper forceOpaque', (t) => {
const { glWindow, renderWindow, actorBelow, gc } = setupSlices(t);

// If this actor is simply just made transparent, then it will show above the actorAbove
actorBelow.getProperty().setOpacity(0.5);
// Make actorBelow get rendered in same pass as actorAbove
actorBelow.setForceOpaque(true);

glWindow.captureNextImage().then((image) => {
testUtils.compareImages(
image,
[forceOpaqueBaseline],
'Rendering/OpenGL/ImageSlice',
t,
0.5,
gc.releaseResources
);
});
renderWindow.render();
});

test.onlyIfWebGL.only('Test ImageMapper forceTranslucent', (t) => {
const { glWindow, renderWindow, actorAbove, actorBelow, gc } = setupSlices(t);

actorBelow.getProperty().setOpacity(0.9);
actorAbove.getProperty().setOpacity(1);
// keep translucent blending
actorAbove.setForceTranslucent(true);

glWindow.captureNextImage().then((image) => {
testUtils.compareImages(
image,
[forceTranslucentBaseline],
'Rendering/OpenGL/ImageSlice',
t,
0.5,
gc.releaseResources
);
});
renderWindow.render();
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 49bf5c8

Please sign in to comment.