Skip to content

MovableAxes

Repository source: MovableAxes

Other languages

See (Cxx)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

MovableAxes.py

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkCallbackCommand
from vtkmodules.vtkFiltersSources import (
    vtkConeSource
)
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballActor
from vtkmodules.vtkRenderingAnnotation import vtkAxesActor
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer, vtkFollower, vtkAssembly, vtkPropCollection
)
from vtkmodules.vtkRenderingFreeType import vtkVectorText


def main():
    colors = vtkNamedColors()

    cone_source = vtkConeSource()

    # Create a mapper.
    cone_mapper = vtkPolyDataMapper()
    cone_source >> cone_mapper

    # Create an actor.
    cone_actor = vtkActor(mapper=cone_mapper)
    cone_actor.property.color = colors.GetColor3d('Gold')

    # A renderer and render window.
    renderer = vtkRenderer(background=colors.GetColor3d('SlateGray'))
    render_window = vtkRenderWindow(window_name='MovableAxes')
    render_window.AddRenderer(renderer)

    # An interactor.
    render_window_interactor = vtkRenderWindowInteractor()
    render_window_interactor.render_window = render_window

    # Add the actors to the scene.
    renderer.AddActor(cone_actor)

    # vtkAxesActor is currently not designed to work with
    # vtkInteractorStyleTrackballActor since it is a hybrid object containing
    # both vtkProp3D's and vtkActor2D's, the latter of which does not have a 3D
    # position that can be manipulated.
    axes = vtkAxesActor()

    # Get a copy of the axes' constituent 3D actors and put them into a
    # vtkAssembly so they can be manipulated as one prop.
    collection = vtkPropCollection()
    axes.GetActors(collection)

    collection.InitTraversal()

    movable_axes = vtkAssembly()

    for i in range(0, collection.GetNumberOfItems()):
        movable_axes.AddPart(collection.GetNextProp())

    renderer.AddActor(movable_axes)

    # Create our own labels that will follow and face the camera.
    x_text = vtkVectorText(text='X')
    x_text_mapper = vtkPolyDataMapper()
    x_text >> x_text_mapper
    # In order to get the position of the tips of the X, Y, and Z axes we do:
    #  "collection.GetItemAsObject(k).GetCenter()" where k = [3 ... 5].
    x_label = vtkFollower(mapper=x_text_mapper, scale=0.3, camera=renderer.active_camera,
                          position=collection.GetItemAsObject(3).GetCenter(), pickable=False)
    renderer.AddActor(x_label)

    y_text = vtkVectorText(text='Y')
    y_text_mapper = vtkPolyDataMapper()
    y_text >> y_text_mapper
    y_label = vtkFollower(mapper=y_text_mapper, scale=0.3, camera=renderer.active_camera,
                          position=collection.GetItemAsObject(4).GetCenter(), pickable=False)
    renderer.AddActor(y_label)

    z_text = vtkVectorText(text='Z')
    z_text_mapper = vtkPolyDataMapper()
    z_text >> z_text_mapper
    z_label = vtkFollower(mapper=z_text_mapper, scale=0.3, camera=renderer.active_camera,
                          position=collection.GetItemAsObject(5).GetCenter(), pickable=False)
    renderer.AddActor(z_label)

    # The custom callback to set the positions of the labels.
    callback = PositionCallback(x_label, y_label, z_label, movable_axes)

    renderer.ResetCamera()
    render_window.Render()

    style = vtkInteractorStyleTrackballActor()
    render_window_interactor.interactor_style = style
    style.AddObserver('InteractionEvent', callback)

    # Begin mouse interaction.
    render_window_interactor.Start()


class PositionCallback(vtkCallbackCommand):
    def __init__(self, x_label, y_label, z_label, axes):
        super().__init__()

        self.x_label = x_label
        self.y_label = y_label
        self.z_label = z_label
        self.axes = axes
        self.followers = [self.x_label, self.y_label, self.z_label]

    def __call__(self, caller, ev):
        self.Execute(caller, ev)

    def Execute(self, caller, eid, call_data=None):
        self.axes.InitPathTraversal()
        count = 0

        follower_id = -1
        path = self.axes.GetNextPath()
        while path:
            count += 1
            if count > 2:
                prop_3d = path.GetLastNode().GetViewProp()
                if prop_3d:
                    prop_3d.PokeMatrix(path.GetLastNode().GetMatrix())
                    self.followers[follower_id].SetPosition(prop_3d.GetCenter())
                    follower_id += 1
                    prop_3d.PokeMatrix(None)
            path = self.axes.GetNextPath()


if __name__ == '__main__':
    main()