Skip to content

Slider3D

Repository source: Slider3D

Description

This example demonstrates how to use a 3D slider widget. Here, the slider controls the resolution of the sphere. The slider is positioned in world coordinates - so if you rotate/translate/scale the scene, the slider will change orientation/position/size. Contrast this with Slider2D that remains at a fixed location in the window.

If the callback is connected to InteractionEvent, the scene will update whenever the mouse is moved on the slider. This is not ideal if the re-rendering takes significant time as it will make the interaction very choppy. If you want to move the slider and have the scene update when the mouse button is released, connect the callback to EndInteractionEvent instead.

Note

This original source code for this example is here.

Other languages

See (Cxx)

Question

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

Code

Slider3D.py

#!/usr/bin/env python3

from dataclasses import dataclass
from typing import Tuple

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkCommand
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkInteractionWidgets import (
    vtkSliderRepresentation3D,
    vtkSliderWidget
)
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)


def main():
    colors = vtkNamedColors()

    # A sphere.
    phi_resolution = 4
    sphere_source = vtkSphereSource(center=(0.0, 0.0, 0.0), radius=4.0,
                                    phi_resolution=phi_resolution,
                                    theta_resolution=phi_resolution * 2)

    mapper = vtkPolyDataMapper()
    sphere_source >> mapper

    actor = vtkActor(mapper=mapper)
    actor.property.SetInterpolationToFlat()
    actor.property.color = colors.GetColor3d('MistyRose')
    actor.property.edge_color = colors.GetColor3d('Tomato')
    actor.property.edge_visibility = True

    # A renderer and render window.
    renderer = vtkRenderer(background=colors.GetColor3d('SlateGray'))
    render_window = vtkRenderWindow(size=(640, 480), window_name='Slider3D')
    render_window.AddRenderer(renderer)

    # An interactor.
    render_window_interactor = vtkRenderWindowInteractor()
    render_window_interactor.render_window = render_window
    style = vtkInteractorStyleTrackballCamera()
    render_window_interactor.interactor_style = style

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

    # Render an image (lights and cameras are created automatically).
    render_window.Render()

    sp = make_slider_properties()

    sp.Range.value = sphere_source.theta_resolution
    sp.Text.title = 'Sphere Resolution'

    widget = make_3d_slider_widget(sp, render_window_interactor)
    cb = SliderCallback(sphere_source)
    widget.AddObserver(vtkCommand.InteractionEvent, cb)

    renderer.Render()
    renderer.active_camera.Dolly(0.9)
    render_window_interactor.Initialize()
    render_window.Render()

    render_window_interactor.Start()


def make_slider_properties():
    sp = Slider3DProperties()
    sp.Range.minimum_value = 3
    sp.Range.maximum_value = 50
    sp.Position.point1 = (-4.0, 5.9, 0.0)
    sp.Position.point2 = (4, 5.9, 0)
    sp.Dimensions.slider_length = 0.075
    sp.Dimensions.slider_width = 0.05
    sp.Dimensions.end_cap_length = 0.05
    sp.Dimensions.title_height = 0.125
    sp.Dimensions.label_height = 0.055
    # Set color properties:
    # Change the color of the knob that slides.
    sp.Colors.slider_color = 'Green'
    # Change the color of the text indicating what the slider controls.
    sp.Colors.title_color = 'AliceBlue'
    # Change the color of the text displaying the value.
    sp.Colors.label_color = 'AliceBlue'
    # Change the color of the knob when the mouse is held on it.
    sp.Colors.selected_color = 'DeepPink'
    # Change the color of the bar.
    sp.Colors.bar_color = 'MistyRose'
    # Change the color of the ends of the bar.
    sp.Colors.bar_ends_color = 'Yellow'

    return sp


def make_3d_slider_widget(properties, interactor):
    """
    Make a 3D slider widget.

    :param properties: The 3D slider properties.
    :param interactor: The vtkInteractor.
    :return: The slider widget.
    """
    colors = vtkNamedColors()

    slider_rep = vtkSliderRepresentation3D(minimum_value=properties.Range.minimum_value,
                                           maximum_value=properties.Range.maximum_value,
                                           value=properties.Range.value,
                                           title_text=properties.Text.title,
                                           tube_width=properties.Dimensions.tube_width,
                                           slider_length=properties.Dimensions.slider_length,
                                           slider_width=properties.Dimensions.slider_width,
                                           end_cap_length=properties.Dimensions.end_cap_length,
                                           end_cap_width=properties.Dimensions.end_cap_width,
                                           title_height=properties.Dimensions.title_height,
                                           label_height=properties.Dimensions.label_height,
                                           )

    # Set the color properties.
    slider_rep.tube_property.color = colors.GetColor3d(properties.Colors.bar_color)
    slider_rep.cap_property.color = colors.GetColor3d(properties.Colors.bar_ends_color)
    slider_rep.slider_property.color = colors.GetColor3d(properties.Colors.slider_color)
    slider_rep.selected_property.color = colors.GetColor3d(properties.Colors.selected_color)

    # Set the position.
    slider_rep.point1_coordinate.coordinate_system = properties.Position.coordinate_system
    slider_rep.point1_coordinate.value = properties.Position.point1
    slider_rep.point2_coordinate.coordinate_system = properties.Position.coordinate_system
    slider_rep.point2_coordinate.value = properties.Position.point2

    widget = vtkSliderWidget(representation=slider_rep, interactor=interactor, enabled=True)
    widget.SetAnimationModeToAnimate()

    return widget


@dataclass(frozen=True)
class Coordinate:
    @dataclass(frozen=True)
    class CoordinateSystem:
        VTK_DISPLAY: int = 0
        VTK_NORMALIZED_DISPLAY: int = 1
        VTK_VIEWPORT: int = 2
        VTK_NORMALIZED_VIEWPORT: int = 3
        VTK_VIEW: int = 4
        VTK_POSE: int = 5
        VTK_WORLD: int = 6
        VTK_USERDEFINED: int = 7


@dataclass
class Slider3DProperties:
    @dataclass
    class Colors:
        title_color: str = 'White'
        label_color: str = 'White'
        slider_color: str = 'White'
        selected_color: str = 'HotPink'
        bar_color: str = 'White'
        bar_ends_color: str = 'White'

    @dataclass
    class Dimensions:
        tube_width: float = 0.008
        slider_length: float = 0.01
        slider_width: float = 0.02
        end_cap_length: float = 0.005
        end_cap_width: float = 0.05
        title_height: float = 0.03
        label_height: float = 0.025

    @dataclass
    class Position:
        coordinate_system: int = Coordinate.CoordinateSystem.VTK_WORLD
        point1: Tuple = (0.1, 0.1, 0.0)
        point2: Tuple = (0.9, 0.1, 0.0)

    @dataclass
    class Range:
        minimum_value: float = 0.0
        maximum_value: float = 1.0
        value: float = 0.0

    @dataclass
    class Text:
        title: str = ''


class SliderCallback:

    def __init__(self, sphere_source):
        """
        """
        self.sphere_source = sphere_source

    def __call__(self, caller, ev):
        slider_widget = caller
        value = int(slider_widget.representation.value)
        self.sphere_source.phi_resolution = value // 2
        self.sphere_source.theta_resolution = value


if __name__ == '__main__':
    main()