Hello,
I am attempting to change the scalars that are rendered in a vtk.js app but although the setActiveScalars command appears to work (by querying data.getPointData().getScalars().getRange()) my rendered data does not update.
A simple app based on the react-js cone example illustrating my issue is below.
Is this the correct way to change the scalars that are rendered when we have data with multiple data arrays?
In a similar example that I attempted with image data I have the same issue, though the vtkPiecewiseGaussianWidget will update its histogram via:
widget.setDataArray(data.getPointData().getScalars().getData());
widget.render();
App.js
import { useState } from 'react';
import './App.css';
import ShowPolydata from './ShowPolydata.js';
import vtkConeSource from '@kitware/vtk.js/Filters/Sources/ConeSource';
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
function App() {
const [polydata, setPolydata] = useState(null);
// Display a cone
const onShowConeClick = () => {
const coneSource = vtkConeSource.newInstance({ height: 2, radius: 1, resolution: 80 });
const polydata = coneSource.getOutputData();
const N = polydata.getNumberOfPoints();
var valuesRandom = [];
for (let i = 0; i < N; i++) {
valuesRandom[i] = Math.random();
}
var valuesB = [];
for (let i = 0; i < N; i++) {
valuesB[i] = 0.5;
}
const scalarsA = vtkDataArray.newInstance({
values: valuesRandom,
numberOfComponents: 1, // number of channels (grayscale)
dataType: Float32Array, // values encoding
name: 'scalarsA'
});
const scalarsB = vtkDataArray.newInstance({
values: valuesB,
numberOfComponents: 1, // number of channels (grayscale)
dataType: Float32Array, // values encoding
name: 'scalarsB'
});
polydata.getPointData().setScalars(scalarsA);
polydata.getPointData().addArray(scalarsB);
setPolydata(polydata);
}
const onRefresh = () => {
setPolydata(null);
}
return (
<div className="App">
<div id="renderBox">
{polydata &&
<div className="App-header">
<ShowPolydata data={polydata}></ShowPolydata>
</div>
}
</div>
<div className="App-header">
<button onClick={onShowConeClick}>Cone</button>
<button onClick={onRefresh}>Refresh</button>
</div>
</div>
);
}
export default App;
ShowPolyData.js
import { useRef, useEffect, useState } from 'react';
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import vtkRenderWindow from '@kitware/vtk.js/Rendering/Core/RenderWindow';
import vtkRenderer from '@kitware/vtk.js/Rendering/Core/Renderer';
import vtkOpenGLRenderWindow from '@kitware/vtk.js/Rendering/OpenGL/RenderWindow';
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps';
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction';
import vtkRenderWindowInteractor from '@kitware/vtk.js/Rendering/Core/RenderWindowInteractor';
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera';
const ShowPolydata = ({ data }) => {
const vtkContainerRef = useRef(null);
const context = useRef(null);
const [scalarVar, setScalar] = useState('scalarsA')
useEffect(() => {
if ((!context.current) && (data)) {
//
const openglRenderWindow = vtkOpenGLRenderWindow.newInstance();
const renderWindow = vtkRenderWindow.newInstance();
const renderer = vtkRenderer.newInstance({ background: [0, 0, 0], });
renderWindow.addRenderer(renderer);
// mapper - add to actor
const mapper = vtkMapper.newInstance();
const actor = vtkActor.newInstance();
const lookupTable = vtkColorTransferFunction.newInstance();
lookupTable.applyColorMap(vtkColorMaps.getPresetByName('jet'));
lookupTable.updateRange();
mapper.setInputData(data);
mapper.setScalarModeToDefault();
mapper.setScalarRange(0, 1);
mapper.setLookupTable(lookupTable);
actor.setMapper(mapper);
actor.getProperty().setAmbient(1.0);
actor.getProperty().setDiffuse(0.9);
renderer.addActor(actor);
renderWindow.addView(openglRenderWindow);
openglRenderWindow.setSize(600, 500);
openglRenderWindow.setContainer(vtkContainerRef.current)
// Setup interactor and style
const interactor = vtkRenderWindowInteractor.newInstance();
interactor.setView(openglRenderWindow);
interactor.initialize();
interactor.bindEvents(vtkContainerRef.current);
interactor.setInteractorStyle(vtkInteractorStyleTrackballCamera.newInstance());
// all done - reset camera and render
renderer.resetCamera();
renderWindow.render();
context.current = {
openglRenderWindow,
renderWindow,
renderer,
data,
actor,
mapper,
};
}
return () => {
if (context.current) {
const { openglRenderWindow, actor, mapper } = context.current;
actor.delete();
mapper.delete();
openglRenderWindow.delete();
context.current = null;
}
};
}, [vtkContainerRef]);
function resetCamera() {
if (context.current) {
context.current.renderer.resetCamera();
context.current.renderWindow.render();
}
};
useEffect(() => {
if (context.current) {
const { data, mapper, renderWindow } = context.current;
data.getPointData().setActiveScalars(scalarVar);
const lut = mapper.getLookupTable();
lut.updateRange();
console.log("scalar now: ", scalarVar, 'range: ', data.getPointData().getScalars().getRange())
renderWindow.render();
}
}, [scalarVar]);
return (
<div>
<div ref={vtkContainerRef} ></div>
<div id="buttonRow" className="viewA-buttons">
<button onClick={resetCamera}>
Reset Camera
</button>
<select value={scalarVar} style={{ width: '100%' }} onInput={(ev) => setScalar(ev.target.value)}>
<option value="scalarsA">scalarsA</option>
<option value="scalarsB">scalarsB</option>
</select>
</div>
</div>
);
}
export default ShowPolydata;
Thanks for any help / advice.