Best way to set the mapper scalar range for viewing?

I tried to distill this down to just one short script.
I have attached a temperature array to a vtkPolyData, and I have constructed a vtkColorLookupFunction to go along with it. If my “Temperature” array is in [0, 1], everything works fine, but if the array is the real array and covers a range like [150, 400], things are not as fine.

How should I tell the rendering pipeline what the range of my scalars are?

#!/usr/bin/env python3
import vtkmodules.vtkRenderingOpenGL2
import numpy as np
from vtk.util.numpy_support import numpy_to_vtk
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonDataModel import vtkCellArray, vtkPolyData
from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkIOXML import vtkXMLPolyDataReader
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkColorTransferFunction,
    vtkPolyDataMapper,
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor)

# Make a solid with a Temperature array
pts = vtkPoints()
pts.InsertNextPoint( 1,  1, -1)
pts.InsertNextPoint(-1,  1, -1)
pts.InsertNextPoint(-1, -1, -1)
pts.InsertNextPoint( 1, -1, -1)

pts.InsertNextPoint( 1,  1, 1)
pts.InsertNextPoint(-1,  1, 1)
pts.InsertNextPoint(-1, -1, 1)
pts.InsertNextPoint( 1, -1, 1)

ca = vtkCellArray()
ca.InsertNextCell(4, [0, 1, 2, 3])
ca.InsertNextCell(4, [0, 4, 7, 3])
ca.InsertNextCell(4, [5, 4, 0, 1])
ca.InsertNextCell(4, [6, 5, 1, 2])
ca.InsertNextCell(4, [7, 6, 2, 3])
ca.InsertNextCell(4, [4, 5, 6, 7])

pd = vtkPolyData()
pd.SetPoints(pts)
pd.SetPolys(ca)

t = numpy_to_vtk(100 + np.array([0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1])) # doesn't work
# t = numpy_to_vtk(np.array([0, 0, 0, 0, 1, 1, 1, 1])) # works

t.SetName("Temperature")
pd.GetPointData().AddArray(t)
pd.GetPointData().SetActiveScalars("Temperature")
print(pd.GetPointData().GetArray("Temperature").GetRange())

# Create a colormap for the solid.
ctf = vtkColorTransferFunction()
ctf.SetColorSpaceToDiverging()
ctf.AddRGBPoint(0.0, 0.23137, 0.29803, 0.75294)
ctf.AddRGBPoint(0.5, 0.86499, 0.86499, 0.86499)
ctf.AddRGBPoint(1.0, 0.70588, 0.01568, 0.14901)
ctf.SetNanColor(1.0, 1.0, 0.0)

# Rendering stuff.
mapper = vtkPolyDataMapper()
mapper.SetInputData(pd)
mapper.SetColorModeToMapScalars()
mapper.SetScalarRange(*pd.GetPointData().GetArray("Temperature").GetRange())
mapper.SetLookupTable(ctf)
actor = vtkActor()
actor.SetMapper(mapper)

ren = vtkRenderer()
ren_win = vtkRenderWindow()
ren_win.AddRenderer(ren)
ren_win.SetSize(640, 480)
ren_win.SetWindowName("Test")
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
iren.SetInteractorStyle(vtkInteractorStyleTrackballCamera())
# ren.ResetCamera() # ?

ren.AddActor(actor)
ren_win.Render()
iren.Start()

Hello @rexthor, you can construct the CTF based on your scalar range and ask the mapper to use the CTF’s scalar range.

# ...
# ...
scalarRange = pd.GetPointData().GetArray("Temperature").GetRange()
midScalarRange = 0.5 * (scalarRange[0] + scalarRange[1])

# Create a colormap for the solid.
ctf = vtkColorTransferFunction()
ctf.SetColorSpaceToDiverging()
ctf.AddRGBPoint(scalarRange[0], 0.23137, 0.29803, 0.75294)
ctf.AddRGBPoint(midScalarRange, 0.86499, 0.86499, 0.86499)
ctf.AddRGBPoint(scalarRange[1], 0.70588, 0.01568, 0.14901)
ctf.SetNanColor(1.0, 1.0, 0.0)

# Rendering stuff.
mapper = vtkPolyDataMapper()
# ...
mapper.SetLookupTable(ctf)
mapper.UseLookupTableScalarRangeOn()

Note that this is also how you can share a single lookup table for different mappers.

Hth

It seemed to me that the call below should have been able to do the mapping, but the documentation doesn’t say a whole lot about what it actually does:

mapper.SetScalarRange(*pd.GetPointData().GetArray("Temperature").GetRange())

EDIT: is the way in the script above even the best practice? I’d rather do what everyone else does than some weird thing I invent.

It didn’t work because your CTF was defined for scalars in the range 0…1, when your input data had scalars in the range 100.5…101.0.

Leaving out the details, what really goes on when you call ctf.AddRGBPoint(x, r, g, b) is that a color is inserted for a scalar value of x.

So if I define it on [0, 1] there is no way to map it to a different range correctly downstream?

that’s correct.

the LUT and mapper function by ‘mapping’ scalar values to actual colors. It appears that you’re looking to map scalar field values to a normalized 0…1 range or something like that. If you really want to do it that way, you can use vtkPythonAlgorithm to normalize the input data’s scalar values to 0…1 and send the output to mapper.