Skip to content

AffineWidget

Repository source: AffineWidget

Other languages

See (Cxx)

Question

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

Code

AffineWidget.py

#!/usr/bin/env python3

from dataclasses import dataclass

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkCallbackCommand
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersCore import vtkAppendPolyData
from vtkmodules.vtkFiltersSources import (
    vtkPlaneSource,
    vtkSphereSource
)
from vtkmodules.vtkInteractionWidgets import vtkAffineWidget
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindowInteractor,
    vtkRenderWindow,
    vtkRenderer
)


def main():
    colors = vtkNamedColors()

    # Create two spheres: a larger one and a smaller one on top of the larger one
    # to show a reference point while rotating.
    # Then append the two spheres into one vtkPolyData.
    # Create a mapper and actor for the spheres.
    sphere_mapper = vtkPolyDataMapper()
    ((vtkSphereSource(), vtkSphereSource(radius=0.075, center=(0, 0.5, 0))) >>
     vtkAppendPolyData() >> sphere_mapper)
    sphere_actor = vtkActor(mapper=sphere_mapper)
    sphere_actor.property.color = colors.GetColor3d('White')

    # Create a plane centered over the larger sphere with 4x4 subsections.
    plane_source = vtkPlaneSource(x_resolution=4, y_resolution=4, origin=(-1, -1, 0),
                                  point1=(1, -1, 0), point2=(-1, 1, 0))
    # Create a mapper and actor for the plane and show it as a wireframe.
    plane_mapper = vtkPolyDataMapper()
    plane_source >> plane_mapper
    plane_actor = vtkActor(mapper=plane_mapper)
    plane_actor.property.representation = Property.Representation.VTK_WIREFRAME
    plane_actor.property.color = colors.GetColor3d('Red')

    ren = vtkRenderer(background=colors.GetColor3d('LightSkyBlue'),
                      background2=colors.GetColor3d('MidnightBlue'),
                      gradient_background=True)
    ren_win = vtkRenderWindow(size=(640, 480), window_name='AffineWidget')
    ren_win.AddRenderer(ren)
    iren = vtkRenderWindowInteractor()
    iren.render_window = ren_win
    iren.interactor_style.SetCurrentStyleToTrackballCamera()

    ren.AddActor(sphere_actor)
    ren.AddActor(plane_actor)

    ren_win.Render()

    # Create an affine widget to manipulate the actor
    # the widget currently only has a 2D representation and therefore applies
    # transforms in the X-Y plane only
    affine_widget = vtkAffineWidget(interactor=iren)
    affine_widget.CreateDefaultRepresentation()
    affine_widget.representation.PlaceWidget(sphere_actor.GetBounds())

    affine_widget.On()

    affine_callback = AffineCallback(sphere_actor, affine_widget.representation)

    affine_widget.AddObserver(vtkCallbackCommand.InteractionEvent, affine_callback)
    affine_widget.AddObserver(vtkCallbackCommand.EndInteractionEvent, affine_callback)

    iren.Start()


class AffineCallback(vtkCallbackCommand):
    def __init__(self, actor, affine_representation):
        super().__init__()

        self.actor = actor
        self.affine_rep = affine_representation
        self.transform = vtkTransform()

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

    def Execute(self, caller, id, event):
        self.affine_rep.GetTransform(self.transform)
        self.actor.SetUserTransform(self.transform)


@dataclass(frozen=True)
class Property:
    @dataclass(frozen=True)
    class Interpolation:
        VTK_FLAT: int = 0
        VTK_GOURAUD: int = 1
        VTK_PHONG: int = 2
        VTK_PBR: int = 3

    @dataclass(frozen=True)
    class Representation:
        VTK_POINTS: int = 0
        VTK_WIREFRAME: int = 1
        VTK_SURFACE: int = 2


if __name__ == '__main__':
    main()