From ac4d7121b92bdf5f9a4e5085f082a031f0149025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Bruy=C3=A8re?= Date: Tue, 16 Apr 2024 15:20:35 +0200 Subject: [PATCH] docs(ManyRenderWindows): Add image reslice to the example Also add `addView` call (this didn't cause any bug, but it is better for the example) --- Examples/Rendering/ManyRenderWindows/index.js | 100 ++++++++++++++---- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/Examples/Rendering/ManyRenderWindows/index.js b/Examples/Rendering/ManyRenderWindows/index.js index 46d2fbf1d56..78020a411c8 100644 --- a/Examples/Rendering/ManyRenderWindows/index.js +++ b/Examples/Rendering/ManyRenderWindows/index.js @@ -14,6 +14,12 @@ import vtkRenderWindowInteractor from '@kitware/vtk.js/Rendering/Core/RenderWind import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume'; import vtkVolumeMapper from '@kitware/vtk.js/Rendering/Core/VolumeMapper'; +import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice'; +import vtkImageResliceMapper from '@kitware/vtk.js/Rendering/Core/ImageResliceMapper'; +import vtkPlane from '@kitware/vtk.js/Common/DataModel/Plane'; +import { SlabTypes } from '@kitware/vtk.js/Rendering/Core/ImageResliceMapper/Constants'; +import vtkMath from '@kitware/vtk.js/Common/Core/Math'; + // Force the loading of HttpDataAccessHelper to support gzip decompression import '@kitware/vtk.js/IO/Core/DataAccessHelper/HttpDataAccessHelper'; @@ -21,8 +27,21 @@ import '@kitware/vtk.js/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // Background colors // ---------------------------------------------------------------------------- -async function createActor() { - const actor = vtkVolume.newInstance(); +const sharedOpacityFunction = vtkPiecewiseFunction.newInstance(); +sharedOpacityFunction.addPoint(100.0, 0.0); +sharedOpacityFunction.addPoint(255.0, 1.0); + +function getRandomColorTransferFunction() { + const ctfun = vtkColorTransferFunction.newInstance(); + ctfun.addRGBPoint(0, 0, 0, 0); + ctfun.addRGBPoint(95, Math.random(), Math.random(), Math.random()); + ctfun.addRGBPoint(225, 0.66, 0.66, 0.5); + ctfun.addRGBPoint(255, 0.3, 0.3, 0.5); + return ctfun; +} + +function createVolumeActor(imageData) { + // Create and setup the mapper const mapper = vtkVolumeMapper.newInstance(); mapper.setSampleDistance(0.7); mapper.setVolumetricScatteringBlending(0); @@ -30,34 +49,72 @@ async function createActor() { mapper.setLAOKernelSize(10); mapper.setLAOKernelRadius(5); mapper.setComputeNormalFromOpacity(true); - actor.setMapper(mapper); + mapper.setInputData(imageData); - const ctfun = vtkColorTransferFunction.newInstance(); - ctfun.addRGBPoint(0, 0, 0, 0); - ctfun.addRGBPoint(95, 1.0, 1.0, 1.0); - ctfun.addRGBPoint(225, 0.66, 0.66, 0.5); - ctfun.addRGBPoint(255, 0.3, 0.3, 0.5); - const ofun = vtkPiecewiseFunction.newInstance(); - ofun.addPoint(100.0, 0.0); - ofun.addPoint(255.0, 1.0); - actor.getProperty().setRGBTransferFunction(0, ctfun); - actor.getProperty().setScalarOpacity(0, ofun); + // Create and setup the actor + const actor = vtkVolume.newInstance(); + actor + .getProperty() + .setRGBTransferFunction(0, getRandomColorTransferFunction()); + actor.getProperty().setScalarOpacity(0, sharedOpacityFunction); actor.getProperty().setInterpolationTypeToLinear(); actor.getProperty().setShade(true); actor.getProperty().setAmbient(0.3); - actor.getProperty().setDiffuse(1); + actor.getProperty().setDiffuse(0.8); actor.getProperty().setSpecular(1); - actor.setScale(0.003, 0.003, 0.003); - actor.setPosition(1, 1, -1.1); + actor.getProperty().setSpecularPower(8); + actor.setMapper(mapper); + + return actor; +} + +function createImageActor(imageData) { + // Random plane normal + const randomQuat = [ + Math.random(), + Math.random(), + Math.random(), + Math.random(), + ]; + const randomMat = new Float32Array(9); + vtkMath.quaternionToMatrix3x3(randomQuat, randomMat); // No need to normalize the quaternion + const randomNormal = [randomMat[0], randomMat[1], randomMat[2]]; + + // Create and setup the plane + const slicePlane = vtkPlane.newInstance(); + slicePlane.setNormal(...randomNormal); + slicePlane.setOrigin(imageData.getCenter()); + + // Create and setup mapper + const mapper = vtkImageResliceMapper.newInstance(); + mapper.setSlicePlane(slicePlane); + mapper.setSlabType(SlabTypes.MAX); + mapper.setInputData(imageData); + + // Create and setup actor + const actor = vtkImageSlice.newInstance(); + actor.getProperty().setRGBTransferFunction(getRandomColorTransferFunction()); + actor.setMapper(mapper); + + return actor; +} +async function createActors(numberOfActors) { const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true }); await reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`); await reader.loadData(); const imageData = reader.getOutputData(); - mapper.setInputData(imageData); + const actors = []; + for (let i = 0; i < numberOfActors; ++i) { + if (Math.random() > 0.5) { + actors.push(createVolumeActor(imageData)); + } else { + actors.push(createImageActor(imageData)); + } + } - return actor; + return actors; } const mainRenderWindow = vtkRenderWindow.newInstance(); @@ -88,6 +145,7 @@ function addRenderWindow() { // Create the corresponding view const renderWindowView = mainRenderWindowView.addMissingNode(renderWindow); + renderWindow.addView(renderWindowView); // Create a container for the view const container = applyStyle(document.createElement('div')); @@ -125,12 +183,12 @@ function addRenderWindow() { // ---------------------------------------------------------------------------- const childRenderWindows = []; -createActor().then((actor) => { +createActors(64).then((actors) => { // Main view has to be initialized before the first "render" from a child render window // We initialize before creating the child render windows because the interactor initialization calls "render" on them mainRenderWindowView.initialize(); - for (let i = 0; i < 64; i++) { + actors.forEach((actor) => { const childRenderWindow = addRenderWindow(); // Create the corresponding renderer @@ -151,7 +209,7 @@ createActor().then((actor) => { renderer.resetCamera(); childRenderWindows.push(childRenderWindow); - } + }); mainRenderWindowView.resizeFromChildRenderWindows(); mainRenderWindow.render();