Search code examples
pythonvtkmayavimayavi.mlab

How to rotate actor on tvtk rendered scene with Mayavi in Python?


I played a bit with Mayavi and particularly with tvtk but I struggle finding examples in which glyphs are placed on the scene at different orientation than default.

Based on this example I prepared the following scene with two cyllinders, one red and one blue,

import mayavi.mlab as mlab

from tvtk.api import tvtk
from tvtk.common import configure_input_data

v = mlab.figure()

# Create two cyllinders
cyl1 = tvtk.CylinderSource(center=(0, 0, 0), radius=1.0, height=0.5, capping=True, resolution=24)
cyl2 = tvtk.CylinderSource(center=(3, 0, 0), radius=1.0, height=0.5, capping=True, resolution=24)


# The mapper converts them into position in 3D
cylinder_mapper1 = tvtk.PolyDataMapper()
configure_input_data(cylinder_mapper1, cyl1.output)
cyl1.update()

cylinder_mapper2 = tvtk.PolyDataMapper()
configure_input_data(cylinder_mapper2, cyl2.output)
cyl2.update()

# Assign them differrent colors
p1 = tvtk.Property(opacity=1.0, color=(1, 0, 0))
p2 = tvtk.Property(opacity=1.0, color=(0, 0, 1))

# The actor is the actually object in the scene.
cyl1_actor = tvtk.Actor(mapper=cylinder_mapper1, property=p1)
v.scene.add_actor(cyl1_actor)

cyl2_actor = tvtk.Actor(mapper=cylinder_mapper2, property=p2)
v.scene.add_actor(cyl2_actor)

# Choose a view angle, and display the figure
mlab.view(90, 45, 7.5, [1.5, 0, 0])
mlab.savefig(filename='cylinders.png')

and what I am trying to achieve here is change the orientation of one of them. Either by providing rotation matrix, either by calling a method, doesn't matter as long as I can somehow rotate the glyph independently (and not just rotate entire scene).

The above example renders

rendered output from the code example

Regarding other questions on that have been previously asked, this one seems useful, yet instead of rotating the actor it rotates entire scene about Y-axis. There also is an example in the scipy-cookbook but again - only with default orientation.

The tvtk documentation is not rich in examples and it is almost hard to believe that there is no example that involves alternate orientation. The documentation only states that classes and methods correspond to their C++ counterparts following some conventions.

  1. tvtk class names are essentially similar to VTK classes except there is no annoying ‘vtk’ at the front. The only difficulty is with classes that start with a digit. For example ‘vtk3DSImporter’ becomes ‘3DSImporter’. This is illegal in Python and therefore the class name used is ‘ThreeDSImporter’. So, if the first character is a digit, it is replaced by an equivalent non-digit string. There are very few classes like this so this is not a big deal.
  2. tvtk method names are enthought style names and not CamelCase. That is, if a VTK method is called AddItem, the equivalent tvtk name is add_item. This is done for the sake of consistency with names used in the enthought package.
  3. Many VTK methods are replaced by handy properties. In the above example, we used m.input = cs.output and p.representation = ‘w’ instead of what would have been m.SetInput(cs.GetOutput()) and p.SetRepresentationToWireframe() etc. Some of these properties are really traits.
  4. Unlike VTK objects, one can set the properties of a tvtk object when the object is initialized by passing the properties (traits) of the object as keyword arguments at the time of class instantiation. For example cs = tvtk.ConeSource(radius=0.1, height=0.5).

Maybe someone who has extensively used VTK with C++ could find that useful but for me it is not very helpful. Nevertheless, I tried to look up the VTK C++ documentation and it did give me some clues. The vtkActor class does inherit some types from vtkProp3D and vtkProp. The vtkProp3D does seem to admit methods and attributes related to object orientation on the scene but it is unclear to me how to set them. The tvtk library is just a wrapper on top of C++ one, which makes it impossible to just inspect what attributes are accepted.

I think for the sake of the internet, maybe we should prepare a proper example of rendering a scene with Mayavi and tvtk that admits both positions of actors and their orientations. Most examples ignore orientations and use spheres so orientation seems not relevant.

Let's summarize.

  1. If someone provides a link to a proper example of rendering a scene with Mayavi and tvtk that involves multiple glyphs with different positions and orientations I will accept such answer.
  2. Making a custom example for (1) will also meet acceptance.
  3. Maybe someone could also comment a bit on the difference between positions of the glyph and the actor that maps this glyph on the scene. As you can see in the examples when I create CylinderSource I explicitly specify where the center is location. This is confusing because is should be the actors center that determines the glyphs position on the scene.

Thanks a lot!


Solution

  • Ok, it turned out Actor accepts orientation argument which is in degrees (not in radians!). Also, the position of the actor has to be specified when actor is created and not when glyph is created!

    cyl1_actor = tvtk.Actor(position=(0, 0, 0), mapper=cylinder_mapper1, property=p1, orientation=(0, 0, 90))
    

    renders

    rendered output

    I found it by trial and error... The tvtk code in Mayavi package is generated so it is pointless to try to find what attributes are accepted by studying the GitHub repository. Also, you might confuse the tvtk's Actor with Mayavi's Actor which does not have orientation property. Oh and there is actually a property names "property" so try to search for that and get thousands of results as the word property is extensively used everywhere...

    Edit:

    I noticed I have been specifying positions of actors in the glyph center which was wrong! Here entire source updated:

    import mayavi.mlab as mlab
    
    from tvtk.api import tvtk
    from tvtk.common import configure_input_data
    
    v = mlab.figure()
    
    # Create two cyllinders
    cyl1 = tvtk.CylinderSource(center=(0, 0, 0), radius=1.0, height=0.5, capping=True, resolution=24)
    cyl2 = tvtk.CylinderSource(center=(0, 0, 0), radius=1.0, height=0.5, capping=True, resolution=24)
    
    
    # The mapper converts them into position in 3D
    cylinder_mapper1 = tvtk.PolyDataMapper()
    configure_input_data(cylinder_mapper1, cyl1.output)
    cyl1.update()
    
    cylinder_mapper2 = tvtk.PolyDataMapper()
    configure_input_data(cylinder_mapper2, cyl2.output)
    cyl2.update()
    
    # Assign them differrent colors
    p1 = tvtk.Property(opacity=1.0, color=(1, 0, 0))
    p2 = tvtk.Property(opacity=1.0, color=(0, 0, 1))
    
    # The actor is the actually object in the scene.
    cyl1_actor = tvtk.Actor(position=(0, 0, 0), mapper=cylinder_mapper1, property=p1)
    v.scene.add_actor(cyl1_actor)
    
    cyl2_actor = tvtk.Actor(position=(3, 0, 0), mapper=cylinder_mapper2, property=p2, orientation=(0, 0, 90))
    v.scene.add_actor(cyl2_actor)
    
    # Choose a view angle, and display the figure
    mlab.view(90, 45, 7.5, [1.5, 0, 0])
    mlab.savefig(filename='cylinders.png')