DataStructureComparison
Repository source: DataStructureComparison
Description¶
In Python use the 'u' key to go up a level and the 'd' key to go down a level.
Info
Using the 'p' key in Python will generate the warning no current renderer on the interactor style.
Other languages
See (Cxx)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
DataStructureComparison.py
#!/usr/bin/env python3
from dataclasses import dataclass
from pathlib import Path
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import (
vtkKdTreePointLocator,
vtkOctreePointLocator,
vtkPolyData
)
from vtkmodules.vtkFiltersFlowPaths import vtkModifiedBSPTree
from vtkmodules.vtkFiltersGeneral import vtkOBBTree
from vtkmodules.vtkFiltersSources import vtkPointSource
from vtkmodules.vtkIOXML import vtkXMLPolyDataReader
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkInteractionWidgets import (
vtkTextRepresentation,
vtkTextWidget
)
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkCamera,
vtkPolyDataMapper,
vtkRenderer,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkTextActor,
vtkTextProperty
)
def get_program_parameters():
import argparse
description = 'Compare data structures.'
epilogue = '''
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('file_name', nargs='?', default=None,
help='The polydata source file name,e.g. Bunny.vtp.')
args = parser.parse_args()
return args.file_name
def main():
colors = vtkNamedColors()
file_name = get_program_parameters()
original_mesh = vtkPolyData()
if file_name:
# If a file name is specified, open and use the file.
if Path(file_name).is_file():
reader = vtkXMLPolyDataReader(file_name=file_name)
original_mesh.ShallowCopy(reader.update().output)
else:
print(f'{file_name} not found.')
else:
# If a file name is not specified, create a random cloud of points.
sphere_source = vtkPointSource()
sphere_source.SetNumberOfPoints(1000)
original_mesh.ShallowCopy(sphere_source.update().output)
number_of_viewports = 4
style = KeyPressInteractorStyle(original_mesh)
camera = vtkCamera()
viewports = dict()
for i in range(0, 4):
viewports[i] = float(i) / number_of_viewports, 0, float(i + 1) / number_of_viewports, 1
render_window = vtkRenderWindow(size=(300 * number_of_viewports, 400), window_name='DataStructureComparison')
for i in range(0, 4):
renderer = vtkRenderer(viewport=viewports[i],
background=colors.GetColor3d('cobalt')) # background=(0.2, 0.3, 0.4))
renderer.active_camera = camera
render_window.AddRenderer(renderer)
style.renderers.append(renderer)
# if i == 0:
# style.current_renderer = renderer
render_window_interactor = vtkRenderWindowInteractor()
render_window_interactor.render_window = render_window
render_window_interactor.interactor_style = style
style.initialize()
style.renderers[0].ResetCamera()
style.renderers[0].active_camera.Zoom(1.0)
render_window_interactor.Start()
class KeyPressInteractorStyle(vtkInteractorStyleTrackballCamera):
def __init__(self, data):
super().__init__(self)
self.colors = vtkNamedColors()
self.data = data
self.renderers = list()
self.trees = list()
self.mappers = list()
self.actors = list()
self.text_actors = list()
self.text_representations = list()
self.text_widgets = list()
self.level = 1
self.trees.append(vtkKdTreePointLocator())
self.trees.append(vtkOBBTree())
self.trees.append(vtkOctreePointLocator())
self.trees.append(vtkModifiedBSPTree())
self.mesh_mapper = vtkPolyDataMapper()
self.mesh_actor = vtkActor(mapper=self.mesh_mapper)
self.mesh_actor.property.color = self.colors.GetColor3d('Peru')
self.AddObserver('KeyPressEvent', self.on_char)
def initialize(self):
self.mesh_mapper.input_data = self.data
for i in range(0, 4):
mapper = vtkPolyDataMapper()
actor = vtkActor(mapper=mapper)
actor.property.SetRepresentationToWireframe()
self.mappers.append(mapper)
self.actors.append(actor)
self.renderers[i].AddActor(actor)
self.renderers[i].AddActor(self.mesh_actor)
# Create the text widgets.
text = {0: 'Kd-tree', 1: 'OBB tree', 2: 'Octree', 3: 'BSP tree'}
# text_actors = list()
# text_representations = list()
# text_widgets = list()
text_property = vtkTextProperty(color=self.colors.GetColor3d('Yellow'), bold=True, italic=False, shadow=False,
font_size=12, font_family_as_string='Courier',
justification=TextProperty.Justification.VTK_TEXT_CENTERED,
vertical_justification=TextProperty.VerticalJustification.VTK_TEXT_CENTERED)
text_positions = get_text_positions(list(text.values()),
justification=TextProperty.Justification.VTK_TEXT_CENTERED,
vertical_justification=TextProperty.VerticalJustification.VTK_TEXT_BOTTOM
)
for k, v in text.items():
self.text_actors.append(
vtkTextActor(input=v, text_scale_mode=vtkTextActor.TEXT_SCALE_MODE_NONE, text_property=text_property))
# Create the text representation. Used for positioning the text actor.
self.text_representations.append(vtkTextRepresentation(enforce_normalized_viewport_bounds=True))
self.text_representations[k].position_coordinate.value = text_positions[v]['p']
self.text_representations[k].position2_coordinate.value = text_positions[v]['p2']
# Create the TextWidget
self.text_widgets.append(
vtkTextWidget(representation=self.text_representations[k], text_actor=self.text_actors[k],
default_renderer=self.renderers[k], interactor=self.interactor, selectable=False))
for k in text.keys():
self.text_widgets[k].On()
print(f'Level = {self.level}')
self.redraw()
def on_char(self, obj, event):
ch = self.interactor.GetKeySym()
if ch == 'd':
self.level += 1
elif ch == 'u':
if self.level > 1:
self.level -= 1
else:
print('An unhandled key was pressed.')
self.redraw()
# Don't forward the "pick" command.
if ch != 'p':
# Forward events
super().OnChar()
def redraw(self):
print(f'Level = {self.level}')
for i in range(0, 4):
tree = self.trees[i]
tree.SetDataSet(self.data)
tree.BuildLocator()
polydata = vtkPolyData()
print(f'Tree {i} has {tree.level} levels.')
if self.level >= tree.GetLevel():
tree.GenerateRepresentation(tree.GetLevel(), polydata)
else:
tree.GenerateRepresentation(self.level, polydata)
self.mappers[i].SetInputData(polydata)
self.interactor.GetRenderWindow().Render()
def get_text_positions(names, justification=0, vertical_justification=0, width=0.96, height=0.1):
"""
Get viewport positioning information for a list of names.
:param names: The list of names.
:param justification: Horizontal justification of the text, default is left.
:param vertical_justification: Vertical justification of the text, default is bottom.
:param width: Width of the bounding_box of the text in screen coordinates.
:param height: Height of the bounding_box of the text in screen coordinates.
:return: A list of positioning information.
"""
# The gap between the left or right edge of the screen and the text.
dx = 0.02
width = abs(width)
if width > 0.96:
width = 0.96
y0 = 0.01
height = abs(height)
if height > 0.9:
height = 0.9
dy = height
if vertical_justification == TextProperty.VerticalJustification.VTK_TEXT_TOP:
y0 = 1.0 - (dy + y0)
dy = height
if vertical_justification == TextProperty.VerticalJustification.VTK_TEXT_CENTERED:
y0 = 0.5 - (dy / 2.0 + y0)
dy = height
name_len_min = 0
name_len_max = 0
first = True
for k in names:
sz = len(k)
if first:
name_len_min = name_len_max = sz
first = False
else:
name_len_min = min(name_len_min, sz)
name_len_max = max(name_len_max, sz)
text_positions = dict()
for k in names:
sz = len(k)
delta_sz = width * sz / name_len_max
if delta_sz > width:
delta_sz = width
if justification == TextProperty.Justification.VTK_TEXT_CENTERED:
x0 = 0.5 - delta_sz / 2.0
elif justification == TextProperty.Justification.VTK_TEXT_RIGHT:
x0 = 1.0 - dx - delta_sz
else:
# Default is left justification.
x0 = dx
# For debugging!
# print(
# f'{k:16s}: (x0, y0) = ({x0:3.2f}, {y0:3.2f}), (x1, y1) = ({x0 + delta_sz:3.2f}, {y0 + dy:3.2f})'
# f', width={delta_sz:3.2f}, height={dy:3.2f}')
text_positions[k] = {'p': [x0, y0, 0], 'p2': [delta_sz, dy, 0]}
return text_positions
@dataclass(frozen=True)
class TextProperty:
@dataclass(frozen=True)
class Justification:
VTK_TEXT_LEFT: int = 0
VTK_TEXT_CENTERED: int = 1
VTK_TEXT_RIGHT: int = 2
@dataclass(frozen=True)
class VerticalJustification:
VTK_TEXT_BOTTOM: int = 0
VTK_TEXT_CENTERED: int = 1
VTK_TEXT_TOP: int = 2
if __name__ == '__main__':
main()