How to subclass vtkColorTransferFunction?

I want to implement custom color calculation for color lookup table. I’ve subclassed vtkColorTransferFunction and overrided GetColor and MapValue functions. But the color has no change, whether I use original vtkColorTransferFunction or my subclass. Moreover, these overridded functions have never been called by VTK core. How can I implement custom color calculation?

from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkDataSetMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)
from vtkmodules.vtkRenderingCore import (
    vtkColorTransferFunction
)
from vtkmodules.vtkCommonDataModel import (
    VTK_QUAD,
    vtkUnstructuredGrid,
)
from vtkmodules.vtkCommonCore import vtkPoints
from vtk.numpy_interface import dataset_adapter as dsa
from typing import Tuple, MutableSequence
import math
import numpy as np

class LogarithmicColorTransferFunction(vtkColorTransferFunction):

    def __init__(self):
        super().__init__()
        self.originalMin = 0
        self.originalMax = 1

    def SetOriginalRange(self, min, max):
        self.originalMin = min
        self.originalMax = max

    def MapValue(self, v:float):
        print(v) # Just dummy print, but was never executed
        return super().MapValue(v)
    
    def GetBlueValue(self, x:float):
        print(x) # Just dummy print, but was never executed
        return super().GetBlueValue(x)

    def GetColor(self, x:float) -> Tuple[float, float, float]:
        print(x) # Never executed
        log = math.log
        exp = math.exp
        a = self.originalMin
        b = self.originalMax
        y = -((log(a)-log(b*x-a*x+a))/(log(b)-log(a)))
        # y = (exp(x*(log(b)-log(a))+log(a))-a) / (b-a)
        return super().GetColor(y)
    
    def GetColor(self, x:float, rgb:MutableSequence[float]) -> None:
        print(x) # Never executed
        c = self.GetColor(x)
        rgb[:] = c

def main():
    ctf = LogarithmicColorTransferFunction()
    ctf.SetColorSpaceToRGB()
    ctf.SetScaleToLinear()
    ctf.SetNanColor(0.0, 0.0, 0.0)

    table = np.array([
        [0, 0.01, 0.043, 1],
        [0.2, 0.641, 0.164, 0.725],
        [0.3, 0.988, 0.051, 0.11],
        [0.5, 1, 0.975, 0.5],
        [0.8, 1, 1, 1],
        [1, 1, 1, 1]
    ])

    for point in table:
        ctf.AddRGBPoint(*point)
    ctf.SetOriginalRange(1, 5)

    points = vtkPoints()
    ugrid = vtkUnstructuredGrid()
    data = np.array([[0, 0, 0,],
                     [10, 0, 0],
                     [10, 10, 0],
                     [0, 10, 0]])
    points.SetData(dsa.numpyTovtkDataArray(data))
    quad = [0,1,2,3]
    ugrid.InsertNextCell(VTK_QUAD, 4, quad)
    ugrid.SetPoints(points)
    c = np.array([0, 1, 2, 3])/3
    ugrid.GetPointData().SetScalars(dsa.numpyTovtkDataArray(c))

    mapper = vtkDataSetMapper()
    mapper.SetInputData(ugrid)
    mapper.SetLookupTable(ctf)

    actor = vtkActor()
    actor.SetMapper(mapper)

    renderer = vtkRenderer()
    renderWindow = vtkRenderWindow()
    renderWindow.AddRenderer(renderer)
    renderWindowInteractor = vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)

    renderer.AddActor(actor)
    renderWindow.Render()
    renderWindowInteractor.Start()


if __name__ == '__main__':
    main()

The virtual methods in your Python subclass will never be called, virtual method overrides simply aren’t supported by the VTK Python wrappers. So subclassing vtkColorTransferFunction isn’t going to give you what you need, unless your subclass is written in C++.

For lookup tables and CTFs in general, the mapper has lots of methods that control how the lookup is done, so you’ll have to play around a bit with different methods to get the right result. Try these, for example:

mapper.UseLookupTableScalarRangeOn()
mapper.ScalarVisibilityOn()
mapper.SetColorModeToMapScalars()
mapper.SetScalarModeToUsePointData()

If using C++, which methods are need to be overridden to achieve the desired result?

See the base class, vtkScalarsToColors, and compare to vtkColorTransferFunction to see what was overridden.