How to color vectors by vector magnitude using vtkGlyph3DMapper?

Hello!!!

I’m using VTK 9.2.6 and Python 3.11.6

I’m trying to display (orient, scale and color) arrows using a vector field and using the vtkGlyph3DMapper (instead of vktGlyph3D).

I have the orientation and scaling working… but I cannot seem to color the vectors by the magnitude of the vector that already using for orientatoin and scale…

Code to demonstrate the issue is shown below. The vector I’m using for orientatin scale and color is “Displacement”. I’m using the vtkGlyph3DMapper “SetScalarModeToUsePointFieldData()” and “SelectColorArray(‘Displacement’)” methods to identify the array to use for coloring and then I’m using the vtkLookupTable
“SetVectorModeToMagnitude()” and setting the range to match the vector values

All this displays all of the vectors in the same color (Red)…

Any ideas wil be greatfully received!!!

# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# # noinspection PyUnresolvedReferences

from vtkmodules.vtkCommonDataModel import vtkPolyData

from vtkmodules.vtkCommonCore import vtkLookupTable, VTK_FLOAT

from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
    vtkGlyph3DMapper,
    vtkInteractorStyle
)
from vtkmodules.vtkFiltersSources import vtkArrowSource

from vtkmodules.numpy_interface import dataset_adapter as dsa
from vtkmodules.util.numpy_support import numpy_to_vtk

import numpy as np

    
if __name__ == "__main__":

    poly=vtkPolyData()
    poly_np=dsa.WrapDataObject(poly)
    poly_np.Points = np.array([[0,0,0],[1,0,0],[1,1,0],[0,0,1]])

    disp_field = numpy_to_vtk(np.array([[1,0,0],[2,0,0],[3,0,0],[1,1,1]]), deep=False, array_type=VTK_FLOAT)
    disp_field.SetNumberOfComponents(3)
    disp_field.SetName('Displacement')
    poly.GetPointData().AddArray(disp_field)

    # Set up an arrow source.     
    arrowSource=vtkArrowSource() 
    arrowSource.SetShaftRadius(0.05)
    arrowSource.SetTipLength(0.2)
    arrowSource.SetTipRadius(0.1)

    # Copy the arrow source to every point on the poly
    glyphmapper=vtkGlyph3DMapper()
    glyphmapper.SetSourceConnection(arrowSource.GetOutputPort())
    glyphmapper.SetInputDataObject(poly)
    glyphmapper.SetOrientationArray("Displacement")

    glyphmapper.SetScaleModeToScaleByMagnitude()
    glyphmapper.SetInputArrayToProcess(0, 0, 0, vtkPolyData.FIELD_ASSOCIATION_POINTS, 'Displacement')

    glyphmapper.SetScalarModeToUsePointFieldData()
    glyphmapper.SelectColorArray('Displacement')

    lut=vtkLookupTable()
    lut.SetTableRange (0, 1)
    lut.SetHueRange (0.667, 1.0)
    lut.SetSaturationRange (1, 1)
    lut.SetVectorModeToMagnitude()
    lut.SetValueRange (1.0, 3.)
    lut.Build()

    glyphmapper.SetLookupTable(lut)

    # Set up an actor for the arrow glyphs
    glyph_actor = vtkActor()
    glyph_actor.SetMapper(glyphmapper)

    # Render window, renderer and interactor
    renderer=vtkRenderer() 
    renwin=vtkRenderWindow()
    renwin.AddRenderer(renderer)
    iren = vtkRenderWindowInteractor()
    iren.SetRenderWindow(renwin)

    # Add an InteractorStyle
    style=vtkInteractorStyle()
    style.SetDefaultRenderer(renderer)

    renderer.AddActor(glyph_actor)

    # Render
    iren.Initialize()
    renwin.Render()
    iren.Start()

I did not try your code but I wonder if the red color is happening because the r,g,b values are expected to be on the 0 to 1 range each in vtk

@mau_igna_06 is on the right track:

Add in this code:

    vecs = np.array([[1, 0, 0], [2, 0, 0], [3, 0, 0], [1, 1, 1]])
    vecs_norm = vecs / (np.linalg.norm(vecs) + 1e-16)

    color_field = numpy_to_vtk(np.array(vecs_norm), deep=False, array_type=VTK_FLOAT)
    color_field.SetNumberOfComponents(3)
    color_field.SetName('Colors')
    poly.GetPointData().AddArray(color_field)

And update this code using the Colors array instead of the Displacement array:

    glyphmapper.SetScaleModeToScaleByMagnitude()
    glyphmapper.SetInputArrayToProcess(0, 0, 0, vtkPolyData.FIELD_ASSOCIATION_POINTS, 'Colors')

    glyphmapper.SetScalarModeToUsePointFieldData()
    glyphmapper.SelectColorArray('Colors')

Also add in:

    renderer.SetBackground((1.0, 1.0, 1.0))

You will see four colored vectors.

1 Like

I understand why this solution could work - effectively it forces the displacmenet vector into an rgb color vector.

However, I’m specifically using a vtkLookupTable so that I can map the magnitude of the displacement vector into a color series. I’m not trying to use the displacement array directly as the color array.

I use this LUT approach frequently when coloring cells/points in vtkPolyData/vtkPolyDataMapper but for some reason it’s not working with the vtkGlyph3DMapper

Here’s the version using vtkGlyph3D and a regulat mapper instead of the vtkGlyph3DMapper.

This uses the LUT to color the arros by the magnitude of the vector.

I cannot figure out how to get the same approach to work with the vtkGlyph3DMapper…

# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# # noinspection PyUnresolvedReferences

from vtkmodules.vtkCommonDataModel import vtkPolyData

from vtkmodules.vtkCommonCore import vtkLookupTable, VTK_FLOAT

from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
    vtkGlyph3DMapper,
    vtkPolyDataMapper,
    vtkInteractorStyle
)
from vtkmodules.vtkFiltersSources import vtkArrowSource
from vtkmodules.vtkFiltersCore import vtkGlyph3D

from vtkmodules.numpy_interface import dataset_adapter as dsa
from vtkmodules.util.numpy_support import numpy_to_vtk

import numpy as np

if __name__ == "__main__":

    poly=vtkPolyData()
    poly_np=dsa.WrapDataObject(poly)
    poly_np.Points = np.array([[0,0,0],[1,0,0],[1,1,0],[0,0,1]])

    disp_field = numpy_to_vtk(np.array([[1,0,0],[0,2,0],[0,0,3],[1,1,1]]), deep=False, array_type=VTK_FLOAT)
    disp_field.SetNumberOfComponents(3)
    disp_field.SetName('Displacement')
    poly.GetPointData().AddArray(disp_field)

    arrowSource=vtkArrowSource() 
    arrowSource.SetShaftRadius(0.05)
    arrowSource.SetTipLength(0.2)
    arrowSource.SetTipRadius(0.1)
 
    glyph=vtkGlyph3D()
    glyph.SetSourceConnection(arrowSource.GetOutputPort())
    glyph.SetInputDataObject(poly)
    glyph.SetInputArrayToProcess(1, 0, 0, vtkPolyData.FIELD_ASSOCIATION_POINTS, 'Displacement') #Vectors

    glyph.SetVectorModeToUseVector()
    glyph.SetScaleFactor(0.075)
    glyph.SetColorModeToColorByVector()
    glyph.SetScaleModeToScaleByVector()
    glyph.OrientOn()
    glyph.Update()

    lut=vtkLookupTable()
    lut.SetValueRange (1.0, 3.0)
    lut.SetSaturationRange (1, 1)
    lut.SetHueRange (0.667, 1.0)
    lut.SetRampToLinear()
    lut.SetVectorModeToMagnitude()
    lut.Build()

    mapper=vtkPolyDataMapper()
    mapper.SetInputConnection(glyph.GetOutputPort())
    mapper.SetLookupTable(lut)
    mapper.SetScalarModeToUsePointFieldData()
    mapper.SelectColorArray('Displacement')
    mapper.SetScalarRange(1.0, 3.0)

    # Set up an actor for the arrow glyphs
    actor = vtkActor()
    actor.SetMapper(mapper)

    # Render window, renderer and interactor
    renderer=vtkRenderer() 
    renwin=vtkRenderWindow()
    renwin.AddRenderer(renderer)
    iren = vtkRenderWindowInteractor()
    iren.SetRenderWindow(renwin)

    # Add an InteractorStyle
    style=vtkInteractorStyle()
    style.SetDefaultRenderer(renderer)

    renderer.AddActor(actor)

    # Render
    iren.Initialize()
    renwin.Render()
    iren.Start()

Replied on the issue in VTK, please use lut.SetTableRange(1,3) if you want to set the scalar range. What lut.SetValueRange(1,3) really does is it instead sets the range of values for the V component in the HSV color space.

I still make this mistake!

Thanks to Jaswant for providng the solution. Which is basically my error an dnoit a VTK issue at all…