Creation of a `vtkLookupTable` and corresponding `vtkScalarBarActor`

I am trying to create a Look-up table (LUT) in VTK.js similar to how I did it using Python previously.
Screenshot below shows the output in Python.

However I am having issues, whether that be due to the functionality not being present in JS or I am just doing it wrong!

I am looking at creating my own LUT and then adding it to its own actor and also a polydata mapper.

Functioning Python code for the LUT is as follows:

...
self.lut = vtk.vtkLookupTable()
activeRange = self.vtkData.GetPointData().GetArray(self.activeAr).GetRange()
self.lut.SetTableRange(activeRange)
self.lut.SetNumberOfTableValues(numValues)

# Create the RGB array (colours to go from Blue to Yellow, through green)
zero_to_one = np.linspace(0, 1, 128)
one_to_zero = np.linspace(1, 0, 128)
ones = np.ones(128)
zeros = np.zeros(128)
self.rgb = np.array([np.concatenate((zeros, zero_to_one)),
                     np.concatenate((zero_to_one, ones)),
                     np.concatenate((one_to_zero, zeros))])

# Determine equally spaced points including first and last indices
idxs = np.round(np.linspace(0, 255, numValues)).astype(int)
for i in range(numValues):
    self.lut.SetTableValue(i,
                           self.rgb[0, idxs[i]],
                           self.rgb[1, idxs[i]],
                           self.rgb[2, idxs[i]],
                           1)

self.lut.Build()

mapper = vtk.vtkPolyDataMapper()
mapper.SetScalarVisibility(True)
mapper.SetColorModeToMapScalars()
mapper.SetLookupTable(self.lut)
mapper.UseLookupTableScalarRangeOn()
mapper.SetInputConnection(glyphs.GetOutputPort())

self.lutActor = vtk.vtkScalarBarActor()
self.lutActor.SetOrientationToHorizontal()
self.lutActor.SetDisplayPosition(10, 10)
self.lutActor.SetWidth(0.95)
self.lutActor.SetHeight(0.1)
self.lutActor.SetLookupTable(self.lut)
self.lutActor.SetTitle(self.activeAr)
...

The corresponding Javascript code that I have thus far is below. Lines commented with // --> are not working. I realise some of the functionality may not be available, but if not, can you point towards any examples where I can see how to create a LUT and legend?

this.lut = vtk.Common.Core.vtkLookupTable.newInstance()
this.lut.setMappingRange(activeRange)        // this.lut.getTableRange(activeRange)
// --> this.lut.getNumberOfTableValues(numValues)

// Create the RGB array (colours to go from Blue to Yellow, through green)
let zero_to_one = this.linspace(0, 1, 128)
let one_to_zero = this.linspace(1, 0, 128)
let ones = this.linspace(1, 1, 128)
let zeros = this.linspace(0, 0, 128)

this.rgb = [zeros.concat(zero_to_one), zero_to_one.concat(ones), one_to_zero.concat(zeros)]

// Determine equally spaced points including first and last indices
let idxs = Math.round(this.linspace(0, 255, numValues))

// --> for (let index = 0; index < numValues; index++) {
// -->     this.lut.setTableValue(index,
// -->         this.rgb[0, idxs[index]],
// -->         this.rgb[1, idxs[index]],
// -->         this.rgb[2, idxs[index]],
// -->         1)
// --> }

this.lut.build()

let glyph3DMapper = vtk.Rendering.Core.vtkGlyph3DMapper.newInstance();
glyph3DMapper.setInputData(this.vtkData, 0)
glyph3DMapper.setInputConnection(this.sphereSrc.getOutputPort(), 1)
glyph3DMapper.setScalarVisibility(true)
glyph3DMapper.setColorModeToMapScalars()
glyph3DMapper.setLookupTable(this.lut)
// --> glyph3DMapper.useLookupTableScalarRange(true)


var actor = vtk.Rendering.Core.vtkActor.newInstance();
actor.setMapper(glyph3DMapper)
actor.getProperty().setOpacity(1.0);

this.lutActor = vtk.Rendering.Core.vtkScalarBarActor.newInstance()
// --> this.lutActor.setOrientationToHorizontal()
// --> this.lutActor.setDisplayPosition(10, 10)
// --> this.lutActor.setWidth(0.95)
// --> this.lutActor.setHeight(0.1)
// --> this.lutActor.setLookupTable(this.lut)
// --> this.lutActor.setTitle(this.activeAr)

That example is doing both. It might be a bit complex but hopefully you will manage to extract the only piece that is needed for you.

1 Like

Color presets format and examples can be found here.

Thanks for that @Sebastien_Jourdain. I am happy with the result as is, but some improvements would be great if I could:

  1. Force bar to be horizontal
  2. Change the height of the bar / width of the bar

I see these are set in the defaultAutoLayout function within scalarBarActor in and there is no method available to change them, is this correct?

Here is what the output is looking like now:

A snippet of the code that is working for me now is below for anyone else that is looking in the future

...

this.vtkData = data

this.sphereSrc = vtk.Filters.Sources.vtkSphereSource.newInstance();
this.sphereSrc.setRadius(sphereSize)

const scalars = this.vtkData.getPointData().getScalars();
const dataRange = [].concat(scalars ? scalars.getRange() : [0, 1]);

// Look Up Table
const lutName = 'Viridis (matplotlib)';
const lookupTable = vtk.Rendering.Core.vtkColorTransferFunction.newInstance();

function applyPreset() {
    const preset = vtk.Rendering.Core.vtkColorTransferFunction.vtkColorMaps.getPresetByName(lutName);
    lookupTable.applyColorMap(preset);
    lookupTable.setMappingRange(dataRange[0], dataRange[1]);
    lookupTable.updateRange();
}
applyPreset();

// scalarBarActor
const scalarBarActor = vtk.Rendering.Core.vtkScalarBarActor.newInstance()
renderer.addActor(scalarBarActor);

const mapper = vtk.Rendering.Core.vtkGlyph3DMapper.newInstance({
    interpolateScalarsBeforeMapping: false,
    useLookupTableScalarRange: true,
    lookupTable,
    scalarVisibility: false,
});

const actor = vtk.Rendering.Core.vtkActor.newInstance();
actor.getProperty().setOpacity(1.0);

function updateColorBy(source, colorByArrayName = 'activity') {

    const interpolateScalarsBeforeMapping = true;
    const colorMode  = 1; // vtk.Rendering.Core.Mapper.ColorMode.MAP_SCALARS;
    const scalarMode = 3; // vtk.Rendering.Core.Mapper.Constants.ScalarMode.USE_POINT_FIELD_DATA
    const scalarVisibility = true;

    // Get the array we want to colour by
    const newArray = source.getPointData().getArrayByName(colorByArrayName);

    const newDataRange = newArray.getRange();
    dataRange[0] = newDataRange[0];
    dataRange[1] = newDataRange[1];

    const numberOfComponents = newArray.getNumberOfComponents();
    if (numberOfComponents > 1) {
        // always start on magnitude setting
        if (mapper.getLookupTable()) {
            const lut = mapper.getLookupTable();
            lut.setVectorModeToMagnitude();
        }
    }

    scalarBarActor.setAxisLabel(colorByArrayName);
    scalarBarActor.setVisibility(true);

    mapper.set({
        colorByArrayName,
        colorMode,
        interpolateScalarsBeforeMapping,
        scalarMode,
        scalarVisibility,
    });
    applyPreset();
}
updateColorBy(this.vtkData);

actor.setMapper(mapper);
mapper.setInputData(this.vtkData, 0)
mapper.setInputConnection(this.sphereSrc.getOutputPort(), 1)

renderer.addActor(actor);

scalarBarActor.setScalarsToColors(mapper.getLookupTable());

...