import '@kitware/vtk.js/favicon'; import * as d3 from 'd3-scale'; import { formatDefaultLocale } from 'd3-format';
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'; import vtkConeSource from '@kitware/vtk.js/Filters/Sources/ConeSource'; import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow'; import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper'; import vtkScalarBarActor from '@kitware/vtk.js/Rendering/Core/ScalarBarActor'; import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'; import { Scale } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/Constants'; import vtkLookupTable from '@kitware/vtk.js/Common/Core/LookupTable'; import vtkAppendPolyData from '@kitware/vtk.js/Filters/General/AppendPolyData'; import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray'; import controlPanel from './controlPanel.html';
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance(); const renderer = fullScreenRenderer.getRenderer(); const renderWindow = fullScreenRenderer.getRenderWindow(); fullScreenRenderer.addController(controlPanel);
const minInput = document.querySelector('#min'); const maxInput = document.querySelector('#max');
const linSteps = 40; const linearCone = vtkConeSource.newInstance({ resolution: linSteps }); linearCone.update(); const npts = linearCone.getOutputData().getPoints().getNumberOfPoints();
const minValue = 0.1; const maxValue = 1000; minInput.value = minValue; maxInput.value = maxValue;
const linStep = (maxValue - minValue) / (linSteps - 1); const scalars = vtkDataArray.newInstance({ size: npts }); for (let i = 0; i < npts; ++i) { scalars.setTuple(i, [minValue + (i - 1) * linStep]); } scalars.setTuple(0, [undefined]);
linearCone.getOutputData().getPointData().setScalars(scalars);
const logSteps = 40; const logCone = vtkConeSource.newInstance({ resolution: logSteps, direction: [-1.0, 0.0, 0.0], center: [-1.0, 0.0, 0.0], height: 0.5, }); logCone.update(); const nptsLog = logCone.getOutputData().getPoints().getNumberOfPoints();
const minOutput = Math.log10(minValue || 0.0001); const maxOutput = Math.log10(maxValue);
const powersCounts = maxOutput - minOutput + 1;
const scalarsLog = vtkDataArray.newInstance({ size: nptsLog }); const stepDivisor = nptsLog / powersCounts; for (let i = 0; i < nptsLog; ++i) { const power = minOutput + Math.floor(i / stepDivisor); scalarsLog.setTuple(i, [10 ** power]); }
scalarsLog.setTuple(0, [undefined]); logCone.getOutputData().getPointData().setScalars(scalarsLog);
const appendPolyData = vtkAppendPolyData.newInstance(); appendPolyData.setInputConnection(linearCone.getOutputPort()); appendPolyData.addInputConnection(logCone.getOutputPort());
const mapper = vtkMapper.newInstance(); mapper.setInputData(appendPolyData.getOutputData());
let lut = mapper.getLookupTable();
const actor = vtkActor.newInstance(); actor.getProperty().setAmbient(0.6); actor.getProperty().setDiffuse(0.4); actor.setMapper(mapper); renderer.addActor(actor); renderer.resetCamera(); renderWindow.render();
const scalarBarActor = vtkScalarBarActor.newInstance();
renderer.addActor(scalarBarActor); scalarBarActor.setScalarsToColors(lut);
function generateTicks(numberOfTicks, useLogScale = false) { return (helper) => { const lastTickBounds = helper.getLastTickBounds(); const scale = useLogScale ? d3.scaleLog() : d3.scaleLinear(); scale.domain([lastTickBounds[0], lastTickBounds[1]]).range([0, 1]);
const ticks = scale.ticks(numberOfTicks); const tickPositions = ticks.map((tick) => scale(tick));
formatDefaultLocale({ minus: '\u002D' }); const format = scale.tickFormat(ticks[0], ticks[ticks.length - 1]); const tickStrings = ticks.map(format).map((tick) => { if (tick === '') { return ''; } return Number(parseFloat(tick).toPrecision(12)).toPrecision(); });
helper.setTicks(ticks); helper.setTickStrings(tickStrings); helper.setTickPositions(tickPositions); }; }
scalarBarActor.setGenerateTicks(generateTicks(10, false));
const logInput = document.querySelector('#log'); const updateMinInputColor = () => { minInput.style.color = logInput.checked && parseFloat(minInput.value) === 0 ? 'red' : null; };
const onMinChanged = () => { mapper.setUseLookupTableScalarRange(true); lut.setRange(parseFloat(minInput.value), lut.getRange()[1]); updateMinInputColor(); renderWindow.render(); }; minInput.addEventListener('input', onMinChanged); onMinChanged();
const onMaxChanged = () => { mapper.setUseLookupTableScalarRange(true); lut.setRange(lut.getRange()[0], parseFloat(maxInput.value)); renderWindow.render(); }; maxInput.addEventListener('input', onMaxChanged); onMaxChanged();
document.querySelector('#automated').addEventListener('change', (event) => { scalarBarActor.setAutomated(event.target.checked); renderWindow.render(); }); document.querySelector('#axisLabel').addEventListener('change', (event) => { scalarBarActor.setAxisLabel(event.target.value); renderWindow.render(); }); document .querySelector('#drawNanAnnotation') .addEventListener('change', (event) => { scalarBarActor.setDrawNanAnnotation(event.target.checked); renderWindow.render(); }); document .querySelector('#drawBelowRangeSwatch') .addEventListener('change', (event) => { scalarBarActor.setDrawBelowRangeSwatch(event.target.checked); renderWindow.render(); }); document .querySelector('#drawAboveRangeSwatch') .addEventListener('change', (event) => { scalarBarActor.setDrawAboveRangeSwatch(event.target.checked); renderWindow.render(); }); document .querySelector('#interpolateScalars') .addEventListener('change', (event) => { mapper.setInterpolateScalarsBeforeMapping(event.target.checked); renderWindow.render(); });
const useColorTransferFunctionInput = document.querySelector( '#useColorTransferFunction' );
function updateScalarBarActor() { const useColorTransferFunction = useColorTransferFunctionInput.checked;
const discretizeInput = document.querySelector('#discretize'); discretizeInput.disabled = !useColorTransferFunction; logInput.disabled = !useColorTransferFunction; const numberOfColorsInput = document.querySelector('#numberOfColors'); const numberOfColors = parseInt(numberOfColorsInput.value, 10);
const scale = logInput.checked ? Scale.LOG10 : Scale.LINEAR; if (useColorTransferFunction) { const discretize = discretizeInput.checked; console.log( `Create colorTransferFunction with, scale: ${scale}, discretize: ${discretize}, numberOfColors: ${numberOfColors}` ); const ctf = vtkColorTransferFunction.newInstance({ discretize, numberOfValues: numberOfColors, scale, }); const preset = vtkColorMaps.getPresetByName('Rainbow Desaturated'); ctf.applyColorMap(preset); ctf.setNanColor([1, 1, 1, 1]);
mapper.setLookupTable(ctf); } else { mapper.setLookupTable(vtkLookupTable.newInstance({ numberOfColors })); } lut = mapper.getLookupTable();
lut.setRange(parseFloat(minInput.value), parseFloat(maxInput.value));
scalarBarActor.setScalarsToColors(lut); scalarBarActor.modified(); renderWindow.render(); }
useColorTransferFunctionInput.addEventListener('change', () => { updateScalarBarActor(); });
document.querySelector('#discretize').addEventListener('change', (event) => { if (lut.isA('vtkColorTransferFunction')) { lut.setDiscretize(event.target.checked); renderWindow.render(); } });
logInput.addEventListener('change', (event) => { const useLog = event.target.checked; if (useLog && parseFloat(minInput.value) === 0) { minInput.value = minValue || 0.0001; } if (useLog && parseFloat(maxInput.value) < 1) { maxInput.value = maxValue; }
scalarBarActor.setGenerateTicks(generateTicks(10, useLog));
updateMinInputColor(); updateScalarBarActor(); });
document .querySelector('#numberOfColors') .addEventListener('change', (event) => { if (lut.isA('vtkLookupTable')) { lut.setNumberOfColors(parseInt(event.target.value, 10)); lut.modified(); lut.build(); } else { lut.setNumberOfValues(parseInt(event.target.value, 10)); } lut.modified(); scalarBarActor.setScalarsToColors(lut); scalarBarActor.modified(); renderWindow.render(); });
|