KochanekSplineDemo
Repository source: KochanekSplineDemo
Description¶
The example provides vtkSliderWidgets to change tension, continuity and bias of the vtkKochanekSpline.
- tension - Changes the length of the tangent vector
- continuity - Changes the sharpness in change between tangents
- bias - Primarily changes the direction of the tangent vector
Seealso
This wikipedia article describes the controls in detail.
Other languages
See (Cxx)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
KochanekSplineDemo.py
#!/usr/bin/env python3
import math
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.vtkCommonComputationalGeometry import (
vtkKochanekSpline,
vtkParametricSpline
)
from vtkmodules.vtkCommonCore import (
vtkCommand,
vtkPoints
)
from vtkmodules.vtkCommonDataModel import vtkPolyData
from vtkmodules.vtkFiltersSources import vtkSphereSource, vtkParametricFunctionSource
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkInteractionWidgets import (
vtkSliderRepresentation2D,
vtkSliderWidget
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer, vtkGlyph3DMapper
)
def main():
colors = vtkNamedColors()
number_of_points = 7
points = vtkPoints(number_of_points=number_of_points)
radius = 1.0
delta = 2.0 * math.pi / float(number_of_points)
for n in range(0, number_of_points):
theta = delta * n
x = radius * math.cos(theta)
y = radius * math.sin(theta)
z = n * (math.pi / number_of_points)
points.SetPoint(n, x, y, z)
x_spline = vtkKochanekSpline()
y_spline = vtkKochanekSpline()
z_spline = vtkKochanekSpline()
spline = vtkParametricSpline(x_spline=x_spline, y_spline=y_spline, z_spline=z_spline, points=points)
resolution = 50 * number_of_points
function_source = vtkParametricFunctionSource(parametric_function=spline,
u_resolution=resolution, v_resolution=resolution,
w_resolution=resolution)
function_source.Update()
# Set up the actor and mapper.
mapper = vtkPolyDataMapper()
function_source >> mapper
actor = vtkActor(mapper=mapper)
actor.property.color = colors.GetColor3d('DarkSlateGrey')
actor.property.line_width = 3.0
# Glyph the points.
sphere = vtkSphereSource(phi_resolution=21, theta_resolution=21, radius=0.1)
# Create a polydata to store everything in.
poly_data = vtkPolyData(points=points)
point_mapper = vtkGlyph3DMapper(input_data=poly_data, source_connection=sphere.output_port)
point_actor = vtkActor(mapper=point_mapper)
point_actor.property.color = colors.GetColor3d('Gold')
point_actor.property.opacity = 0.75
# Set up the renderer, render window, and interactor.
renderer = vtkRenderer(background=colors.GetColor3d('SlateGray'))
render_window = vtkRenderWindow(size=(640, 480), window_name='KochanekSpline')
render_window.AddRenderer(renderer)
render_window_interactor = vtkRenderWindowInteractor()
render_window_interactor.render_window = render_window
style = vtkInteractorStyleTrackballCamera()
render_window_interactor.interactor_style = style
renderer.AddActor(actor)
renderer.AddActor(point_actor)
render_window.Render()
sp = make_slider_properties()
sp.Text.title = 'Tension'
sp.Range.value = spline.x_spline.default_tension
tension_widget = make_2d_slider_widget(sp, render_window_interactor)
tension_cb = SliderCallbackTension(spline, function_source)
tension_widget.AddObserver(vtkCommand.InteractionEvent, tension_cb)
sp.Text.title = 'Continuity'
sp.Range.value = spline.x_spline.default_continuity
sp.Position.point1 = (0.4, 0.1)
sp.Position.point2 = (0.6, 0.1)
continuity_widget = make_2d_slider_widget(sp, render_window_interactor)
continuity_cb = SliderCallbackContinuity(spline, function_source)
continuity_widget.AddObserver(vtkCommand.InteractionEvent, continuity_cb)
sp.Text.title = 'Bias'
sp.Range.value = spline.x_spline.default_bias
sp.Position.point1 = (0.7, 0.1)
sp.Position.point2 = (0.9, 0.1)
bias_widget = make_2d_slider_widget(sp, render_window_interactor)
bias_cb = SliderCallbackBias(spline, function_source)
bias_widget.AddObserver(vtkCommand.InteractionEvent, bias_cb)
render_window.Render()
render_window_interactor.Start()
def make_slider_properties():
tube_width = 0.01
slider_length = 0.05
title_height = 0.05
label_height = 0.045
# Setup a slider widget for each varying parameter.
sp = Slider2DProperties()
sp.Text.title_bold = True
sp.Text.title_italic = False
sp.Text.title_shadow = True
sp.Text.label_bold = True
sp.Text.label_italic = False
sp.Text.label_shadow = True
sp.Range.minimum_value = -1.0
sp.Range.maximum_value = 1.0
sp.Position.point1 = (0.1, 0.1)
sp.Position.point2 = (0.3, 0.1)
sp.Dimensions.slider_length = slider_length
sp.Dimensions.slider_width = tube_width * 2.5
sp.Dimensions.tube_width = tube_width
sp.Dimensions.end_cap_length = tube_width * 1.25
sp.Dimensions.end_cap_width = tube_width * 3.0
sp.Dimensions.title_height = title_height
sp.Dimensions.label_height = label_height
# 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 = 'LemonChiffon'
# Change the color of the text displaying the value.
sp.Colors.label_color = 'PapayaWhip'
# 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 = 'Beige'
# Change the color of the ends of the bar.
sp.Colors.bar_ends_color = 'PeachPuff'
return sp
def make_2d_slider_widget(properties, interactor):
"""
Make a 2D slider widget.
:param properties: The 2D slider properties.
:param interactor: The vtkInteractor.
:return: The slider widget.
"""
colors = vtkNamedColors()
slider_rep = vtkSliderRepresentation2D(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.title_property.color = colors.GetColor3d(properties.Colors.title_color)
slider_rep.label_property.color = colors.GetColor3d(properties.Colors.label_color)
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
title_font_family = properties.Text.title_font_family
match title_font_family:
case 'Courier':
slider_rep.title_property.SetFontFamilyToCourier()
case 'Times':
slider_rep.title_property.SetFontFamilyToTimes()
case _:
slider_rep.title_property.SetFontFamilyToArial()
slider_rep.title_property.bold = properties.Text.title_bold
slider_rep.title_property.italic = properties.Text.title_italic
slider_rep.title_property.shadow = properties.Text.title_shadow
label_font_family = properties.Text.label_font_family
match label_font_family:
case 'Courier':
slider_rep.label_property.SetFontFamilyToCourier()
case 'Times':
slider_rep.label_property.SetFontFamilyToTimes()
case _:
slider_rep.label_property.SetFontFamilyToArial()
slider_rep.label_property.bold = properties.Text.label_bold
slider_rep.label_property.italic = properties.Text.label_italic
slider_rep.label_property.shadow = properties.Text.label_shadow
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 Slider2DProperties:
@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_NORMALIZED_VIEWPORT
point1: Tuple = (0.1, 0.1)
point2: Tuple = (0.9, 0.1)
@dataclass
class Range:
minimum_value: float = 0.0
maximum_value: float = 1.0
value: float = 0.0
@dataclass
class Text:
# Font families are: Ariel, Courier and Times
title: str = ''
title_font_family = 'Arial'
title_bold: bool = True
title_italic: bool = False
title_shadow: bool = True
label_font_family = 'Arial'
label_bold: bool = True
label_italic: bool = False
label_shadow: bool = True
class SliderCallbackTension:
def __init__(self, parametric_spline, parametric_source):
"""
"""
self.parametric_spline = parametric_spline
self.parametric_source = parametric_source
def __call__(self, caller, ev):
slider_widget = caller
value = slider_widget.representation.value
self.parametric_spline.x_spline.default_tension = value
self.parametric_spline.y_spline.default_tension = value
self.parametric_spline.z_spline.default_tension = value
self.parametric_source.Modified()
self.parametric_source.Update()
class SliderCallbackContinuity:
def __init__(self, parametric_spline, parametric_source):
"""
"""
self.parametric_spline = parametric_spline
self.parametric_source = parametric_source
def __call__(self, caller, ev):
slider_widget = caller
value = slider_widget.representation.value
self.parametric_spline.x_spline.default_continuity = value
self.parametric_spline.y_spline.default_continuity = value
self.parametric_spline.z_spline.default_continuity = value
self.parametric_source.Modified()
self.parametric_source.Update()
class SliderCallbackBias:
def __init__(self, parametric_spline, parametric_source):
"""
"""
self.parametric_spline = parametric_spline
self.parametric_source = parametric_source
def __call__(self, caller, ev):
slider_widget = caller
value = slider_widget.representation.value
self.parametric_spline.x_spline.default_bias = value
self.parametric_spline.y_spline.default_bias = value
self.parametric_spline.z_spline.default_bias = value
self.parametric_source.Modified()
self.parametric_source.Update()
if __name__ == '__main__':
main()