LinearCellsDemo
Repository source: LinearCellsDemo
Description¶
Linear cell types found in VTK.
The numbers define the ordering of the defining points.
Options are provided to show a wire frame (-w) or to add a back face color (-b). You can also remove the plinth with the (-n) option. If you want a single object, use -o followed by the object number.
With the back face option selected, the back face color will be visible as the objects are semitransparent.
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
LinearCellsDemo.py
# !/usr/bin/env python3
from collections import namedtuple
from dataclasses import dataclass
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonDataModel import (
    VTK_TETRA,
    vtkPolyData,
    vtkCellArray,
    vtkHexagonalPrism,
    vtkHexahedron,
    vtkLine,
    vtkPentagonalPrism,
    vtkPixel,
    vtkPolyLine,
    vtkPolyVertex,
    vtkPolygon,
    vtkPyramid,
    vtkQuad,
    vtkTetra,
    vtkTriangle,
    vtkTriangleStrip,
    vtkUnstructuredGrid,
    vtkVertex,
    vtkVoxel,
    vtkWedge
)
# noinspection PyUnresolvedReferences
from vtkmodules.vtkCommonTransforms import vtkTransform
from vtkmodules.vtkFiltersCore import vtkAppendPolyData
# noinspection PyUnresolvedReferences
from vtkmodules.vtkFiltersGeneral import vtkTransformFilter
from vtkmodules.vtkFiltersSources import (
    vtkCubeSource,
    vtkSphereSource
)
from vtkmodules.vtkInteractionWidgets import vtkCameraOrientationWidget
from vtkmodules.vtkInteractionWidgets import (
    vtkTextRepresentation,
    vtkTextWidget
)
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkActor2D,
    vtkCoordinate,
    vtkDataSetMapper,
    vtkGlyph3DMapper,
    vtkLightKit,
    vtkPolyDataMapper,
    vtkPolyDataMapper2D,
    vtkProperty,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
    vtkTextActor,
    vtkTextProperty,
)
from vtkmodules.vtkRenderingLabel import vtkLabeledDataMapper
def get_program_parameters():
    import argparse
    description = 'Demonstrate the linear cell types found in VTK.'
    epilogue = '''
     The numbers define the ordering of the points making the cell.
    '''
    parser = argparse.ArgumentParser(description=description, epilog=epilogue,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    group1 = parser.add_mutually_exclusive_group()
    group1.add_argument('-w', '--wireframe', action='store_true',
                        help='Render a wireframe.')
    group1.add_argument('-b', '--backface', action='store_true',
                        help='Display the back face in a different colour.')
    parser.add_argument('-o', '--object_number', type=int, default=None,
                        help='The number corresponding to the object.')
    parser.add_argument('-n', '--no_plinth', action='store_true',
                        help='Remove the plinth.')
    args = parser.parse_args()
    return args.wireframe, args.backface, args.object_number, args.no_plinth
def main():
    wireframe_on, backface_on, object_num, plinth_off = get_program_parameters()
    objects = specify_objects()
    # The order here should match the order in specify_objects().
    object_order = list(objects.keys())
    # Check for a single object.
    single_object = None
    if object_num:
        if object_num in object_order:
            single_object = True
        else:
            print('Object not found.\nPlease enter the number corresponding to the object.')
            print('Available objects are:')
            for obj in object_order:
                print(f'{objects[obj]} (={str(obj)})')
            return
    colors = vtkNamedColors()
    # Create one sphere for all.
    sphere = vtkSphereSource(phi_resolution=21, theta_resolution=21, radius=0.04)
    cells = get_unstructured_grids()
    # The text to be displayed in the viewport.
    names = list()
    # The keys of the objects selected for display.
    keys = list()
    if single_object:
        names.append(f'{objects[object_num]} (={str(object_num)})')
        keys.append(object_num)
    else:
        for obj in object_order:
            names.append(f'{objects[obj]} (={str(obj)})')
            keys.append(obj)
    add_plinth = (10, 11, 12, 13, 14, 15, 16,)
    lines = (3, 4)
    # Set up the viewports.
    grid_column_dimensions = 4
    grid_row_dimensions = 4
    renderer_size = 300
    if single_object:
        grid_column_dimensions = 1
        grid_row_dimensions = 1
        renderer_size = 1200
    window_size = (grid_column_dimensions * renderer_size, grid_row_dimensions * renderer_size)
    viewports = dict()
    VP_Params = namedtuple('VP_Params', ['viewport', 'border'])
    last_col = False
    last_row = False
    blank = len(cells)
    blank_viewports = list()
    for row in range(0, grid_row_dimensions):
        if row == grid_row_dimensions - 1:
            last_row = True
        for col in range(0, grid_column_dimensions):
            if col == grid_column_dimensions - 1:
                last_col = True
            index = row * grid_column_dimensions + col
            # Set the renderer's viewport dimensions (xmin, ymin, xmax, ymax) within the render window.
            # Note that for the Y values, we need to subtract the row index from grid_rows
            #  because the viewport Y axis points upwards, and we want to draw the grid from top to down.
            viewport = (float(col) / grid_column_dimensions,
                        float(grid_row_dimensions - (row + 1)) / grid_row_dimensions,
                        float(col + 1) / grid_column_dimensions,
                        float(grid_row_dimensions - row) / grid_row_dimensions)
            if last_row and last_col:
                border = ViewPort.Border.TOP_LEFT_BOTTOM_RIGHT
                last_row = False
                last_col = False
            elif last_col:
                border = ViewPort.Border.RIGHT_TOP_LEFT
                last_col = False
            elif last_row:
                border = ViewPort.Border.TOP_LEFT_BOTTOM
            else:
                border = ViewPort.Border.TOP_LEFT
            vp_params = VP_Params(viewport, border)
            if index < blank:
                viewports[keys[index]] = vp_params
            else:
                s = f'vp_{col:d}_{row:d}'
                viewports[s] = vp_params
                blank_viewports.append(s)
    # Position text according to its length and centered in the viewport.
    text_positions = get_text_positions(names,
                                        justification=TextProperty.Justification.VTK_TEXT_CENTERED,
                                        vertical_justification=TextProperty.VerticalJustification.VTK_TEXT_BOTTOM,
                                        width=0.85, height=0.1)
    ren_win = vtkRenderWindow(size=window_size, window_name='LinearCellDemo')
    ren_win.SetWindowName('LinearCellsDemo')
    iren = vtkRenderWindowInteractor()
    iren.render_window = ren_win
    # Since we always import vtkmodules.vtkInteractionStyle we can do this
    # because vtkInteractorStyleSwitch is automatically imported:
    iren.interactor_style.SetCurrentStyleToTrackballCamera()
    renderers = dict()
    text_representations = list()
    text_actors = list()
    text_widgets = list()
    # Create and link the mappers, actors and renderers together.
    single_object_key = None
    for idx, key in enumerate(keys):
        print('Creating:', names[idx])
        if single_object:
            single_object_key = key
        mapper = vtkDataSetMapper()
        cells[key][0] >> mapper
        actor = vtkActor(mapper=mapper, property=get_actor_property())
        if wireframe_on or key in lines:
            actor.property.representation = Property.Representation.VTK_WIREFRAME
            actor.property.line_width = 2
            actor.property.opacity = 1
            actor.property.color = colors.GetColor3d('Black')
        else:
            if backface_on:
                actor.backface_property = get_back_face_property()
        # Label the points.
        label_property = get_label_property()
        if single_object:
            label_property.SetFontSize(renderer_size // 36)
        else:
            label_property.SetFontSize(renderer_size // 16)
        label_mapper = vtkLabeledDataMapper(label_text_property=label_property)
        cells[key][0] >> label_mapper
        label_actor = vtkActor2D(mapper=label_mapper)
        # Glyph the points.
        point_mapper = vtkGlyph3DMapper(scaling=True, scalar_visibility=False,
                                        source_connection=sphere.output_port)
        cells[key][0] >> point_mapper
        point_actor = vtkActor(mapper=point_mapper, property=get_point_actor_property())
        viewport = viewports[key].viewport
        border = viewports[key].border
        renderer = vtkRenderer(background=colors.GetColor3d('LightSteelBlue'), viewport=viewport)
        draw_viewport_border(renderer, border=border, color=colors.GetColor3d('MidnightBlue'), line_width=4)
        light_kit = vtkLightKit()
        light_kit.AddLightsToRenderer(renderer)
        renderer.AddActor(actor)
        renderer.AddActor(label_actor)
        renderer.AddActor(point_actor)
        if not plinth_off:
            # Add a plinth.
            if key in add_plinth:
                tile_actor = make_tile(cells[key][0].GetBounds(),
                                       expansion_factor=0.5, thickness_ratio=0.01, shift_y=-0.05)
                tile_actor.property = get_tile_property()
                renderer.AddActor(tile_actor)
        # Create the text actor and representation.
        text_property = get_text_property()
        if single_object:
            single_object_key = key
        text_property = get_text_property()
        if single_object:
            text_property.SetFontSize(renderer_size // 28)
        else:
            text_property.SetFontSize(renderer_size // 24)
        text_actor = vtkTextActor(input=names[idx],
                                  text_scale_mode=vtkTextActor.TEXT_SCALE_MODE_NONE,
                                  text_property=text_property)
        # Create the text representation. Used for positioning the text actor.
        text_representation = vtkTextRepresentation(enforce_normalized_viewport_bounds=True)
        text_representation.position_coordinate.value = text_positions[names[idx]]['p']
        text_representation.position2_coordinate.value = text_positions[names[idx]]['p2']
        # Create the text widget, setting the default renderer and interactor.
        text_widget = vtkTextWidget(representation=text_representation, text_actor=text_actor,
                                    default_renderer=renderer, interactor=iren, selectable=False)
        text_actors.append(text_actor)
        text_representations.append(text_representation)
        text_widgets.append(text_widget)
        renderer.ResetCamera()
        renderer.active_camera.Azimuth(cells[key][1].azimuth)
        renderer.active_camera.Elevation(cells[key][1].elevation)
        renderer.active_camera.Dolly(cells[key][1].zoom)
        renderer.ResetCameraClippingRange()
        renderers[key] = renderer
        ren_win.AddRenderer(renderers[key])
    for name in blank_viewports:
        viewport = viewports[name].viewport
        border = viewports[name].border
        renderer = vtkRenderer(background=colors.GetColor3d('LightSteelBlue'), viewport=viewport)
        draw_viewport_border(renderer, border=border, color=colors.GetColor3d('MidnightBlue'), line_width=4)
        renderers[name] = renderer
        ren_win.AddRenderer(renderers[name])
    for i in range(0, len(text_widgets)):
        text_widgets[i].On()
    if single_object:
        cam_orient_manipulator = vtkCameraOrientationWidget(parent_renderer=renderers[single_object_key],
                                                            interactor=iren)
        cam_orient_manipulator.On()
    ren_win.Render()
    iren.Initialize()
    iren.Start()
def specify_objects():
    """
    Link the unstructured grid number to the unstructured grid name.
    :return: A dictionary: {index number: unstructured grid name}.
    """
    return {
        1: 'VTK_VERTEX',
        2: 'VTK_POLY_VERTEX',
        3: 'VTK_LINE',
        4: 'VTK_POLY_LINE',
        5: 'VTK_TRIANGLE',
        6: 'VTK_TRIANGLE_STRIP',
        7: 'VTK_POLYGON',
        8: 'VTK_PIXEL',
        9: 'VTK_QUAD',
        10: 'VTK_TETRA',
        11: 'VTK_VOXEL',
        12: 'VTK_HEXAHEDRON',
        13: 'VTK_WEDGE',
        14: 'VTK_PYRAMID',
        15: 'VTK_PENTAGONAL_PRISM',
        16: 'VTK_HEXAGONAL_PRISM',
    }
def get_unstructured_grids():
    """
    Get the unstructured grid names, the unstructured grid and initial orientations.
    :return: A dictionary: {index number: (unstructured grid, orientation)}.
    """
    def make_orientation(azimuth: float = 0, elevation: float = 0, zoom: float = 1.0):
        return Orientation(azimuth, elevation, zoom)
    return {
        1: (make_vertex(), make_orientation(30, -30, 0.1)),
        2: (make_poly_vertex(), make_orientation(30, -30, 0.8)),
        3: (make_line(), make_orientation(30, -30, 0.4)),
        4: (make_polyline(), make_orientation(30, -30, 1.0)),
        5: (make_triangle(), make_orientation(30, -30, 0.7)),
        6: (make_triangle_strip(), make_orientation(30, -30, 1.1)),
        7: (make_polygon(), make_orientation(0, -45, 1.0)),
        8: (make_pixel(), make_orientation(0, -45, 1.0)),
        9: (make_quad(), make_orientation(0, -45, 0)),
        10: (make_tetra(), make_orientation(20, 20, 1.0)),
        11: (make_voxel(), make_orientation(-22.5, 15, 0.95)),
        12: (make_hexahedron(), make_orientation(-22.5, 15, 0.95)),
        13: (make_wedge(), make_orientation(-30, 15, 1.0)),
        14: (make_pyramid(), make_orientation(-60, 15, 1.0)),
        15: (make_pentagonal_prism(), make_orientation(-60, 10, 1.0)),
        16: (make_hexagonal_prism(), make_orientation(-60, 15, 1.0)),
    }
@dataclass(frozen=True)
class Orientation:
    azimuth: float
    elevation: float
    zoom: float
# These functions return a vtkUnstructured grid corresponding to the object.
def make_vertex():
    # A vertex is a cell that represents a 3D point
    number_of_vertices = 1
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    vertex = vtkVertex()
    for i in range(0, number_of_vertices):
        vertex.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(vertex.GetCellType(), vertex.GetPointIds())
    return ug
def make_poly_vertex():
    # A polyvertex is a cell representing a set of 0D vertices
    number_of_vertices = 6
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(1, 0, 0)
    points.InsertNextPoint(0, 1, 0)
    points.InsertNextPoint(0, 0, 1)
    points.InsertNextPoint(1, 0, 0.4)
    points.InsertNextPoint(0, 1, 0.6)
    poly_vertex = vtkPolyVertex()
    poly_vertex.point_ids.SetNumberOfIds(number_of_vertices)
    for i in range(0, number_of_vertices):
        poly_vertex.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(poly_vertex.GetCellType(), poly_vertex.GetPointIds())
    return ug
def make_line():
    # A line is a cell that represents a 1D point.
    number_of_vertices = 2
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(0.5, 0.5, 0)
    line = vtkLine()
    for i in range(0, number_of_vertices):
        line.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(line.GetCellType(), line.GetPointIds())
    return ug
def make_polyline():
    # A polyline is a cell that represents a set of 1D lines.
    number_of_vertices = 5
    points = vtkPoints()
    points.InsertNextPoint(0, 0.5, 0)
    points.InsertNextPoint(0.5, 0, 0)
    points.InsertNextPoint(1, 0.3, 0)
    points.InsertNextPoint(1.5, 0.4, 0)
    points.InsertNextPoint(2.0, 0.4, 0)
    polyline = vtkPolyLine()
    polyline.point_ids.SetNumberOfIds(number_of_vertices)
    for i in range(0, number_of_vertices):
        polyline.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(polyline.GetCellType(), polyline.GetPointIds())
    return ug
def make_triangle():
    # A triangle is a cell that represents a triangle.
    number_of_vertices = 3
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(0.5, 0.5, 0)
    points.InsertNextPoint(.2, 1, 0)
    triangle = vtkTriangle()
    for i in range(0, number_of_vertices):
        triangle.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(triangle.GetCellType(), triangle.GetPointIds())
    return ug
def make_triangle_strip():
    # A triangle is a cell that represents a triangle strip.
    number_of_vertices = 10
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(1, -.1, 0)
    points.InsertNextPoint(0.5, 1, 0)
    points.InsertNextPoint(2.0, -0.1, 0)
    points.InsertNextPoint(1.5, 0.8, 0)
    points.InsertNextPoint(3.0, 0, 0)
    points.InsertNextPoint(2.5, 0.9, 0)
    points.InsertNextPoint(4.0, -0.2, 0)
    points.InsertNextPoint(3.5, 0.8, 0)
    points.InsertNextPoint(4.5, 1.1, 0)
    triangle_strip = vtkTriangleStrip()
    triangle_strip.point_ids.SetNumberOfIds(number_of_vertices)
    for i in range(0, number_of_vertices):
        triangle_strip.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(triangle_strip.GetCellType(), triangle_strip.GetPointIds())
    return ug
def make_polygon():
    # A polygon is a cell that represents a polygon.
    number_of_vertices = 6
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(1, -0.1, 0)
    points.InsertNextPoint(0.8, 0.5, 0)
    points.InsertNextPoint(1, 1, 0)
    points.InsertNextPoint(0.6, 1.2, 0)
    points.InsertNextPoint(0, 0.8, 0)
    polygon = vtkPolygon()
    polygon.point_ids.SetNumberOfIds(number_of_vertices)
    for i in range(0, number_of_vertices):
        polygon.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(polygon.GetCellType(), polygon.GetPointIds())
    return ug
def make_pixel():
    # A pixel is a cell that represents a pixel.
    number_of_vertices = 4
    pixel = vtkPixel()
    pixel.points.SetPoint(0, 0, 0, 0)
    pixel.points.SetPoint(1, 1, 0, 0)
    pixel.points.SetPoint(2, 0, 1, 0)
    pixel.points.SetPoint(3, 1, 1, 0)
    for i in range(0, number_of_vertices):
        pixel.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=pixel.points)
    ug.InsertNextCell(pixel.GetCellType(), pixel.GetPointIds())
    return ug
def make_quad():
    # A quad is a cell that represents a quad.
    number_of_vertices = 4
    quad = vtkQuad()
    quad.points.SetPoint(0, 0, 0, 0)
    quad.points.SetPoint(1, 1, 0, 0)
    quad.points.SetPoint(2, 1, 1, 0)
    quad.points.SetPoint(3, 0, 1, 0)
    for i in range(0, number_of_vertices):
        quad.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=quad.points)
    ug.InsertNextCell(quad.cell_type, quad.point_ids)
    return ug
def make_tetra():
    number_of_vertices = 4
    # Make a tetrahedron.
    points = vtkPoints()
    # points.InsertNextPoint(0, 0, 0)
    # points.InsertNextPoint(1, 0, 0)
    # points.InsertNextPoint(1, 1, 0)
    # points.InsertNextPoint(0, 1, 1)
    # Rotate the above points -90° about the X-axis.
    points.InsertNextPoint((0.0, 0.0, 0.0))
    points.InsertNextPoint((1.0, 0.0, 0.0))
    points.InsertNextPoint((1.0, 0.0, -1.0))
    points.InsertNextPoint((0.0, 1.0, -1.0))
    tetra = vtkTetra()
    for i in range(0, number_of_vertices):
        tetra.point_ids.SetId(i, i)
    cell_array = vtkCellArray()
    cell_array.InsertNextCell(tetra)
    ug = vtkUnstructuredGrid(points=points)
    ug.SetCells(VTK_TETRA, cell_array)
    # pd = vtkPolyData(points=points)
    # t = vtkTransform()
    # t.RotateX(-90)
    # t.Translate(0, 0, 0)
    # tf = vtkTransformFilter(transform=t)
    # (pd >> tf).update()
    # pts = tf.output.GetPoints()
    # for i in range(0, pts.number_of_points):
    #     print(f'points.InsertNextPoint({pts.GetPoint(i)})')
    return ug
def make_voxel():
    # A voxel is a representation of a regular grid in 3-D space.
    number_of_vertices = 8
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(1, 0, 0)
    points.InsertNextPoint(0, 1, 0)
    points.InsertNextPoint(1, 1, 0)
    points.InsertNextPoint(0, 0, 1)
    points.InsertNextPoint(1, 0, 1)
    points.InsertNextPoint(0, 1, 1)
    points.InsertNextPoint(1, 1, 1)
    voxel = vtkVoxel()
    for i in range(0, number_of_vertices):
        voxel.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(voxel.cell_type, voxel.point_ids)
    return ug
def make_hexahedron():
    """
    A regular hexagon (cube) with all faces square and three squares
     around each vertex is created below.
    Set up the coordinates of eight points, (the two faces must be
     in counter-clockwise order as viewed from the outside).
    :return:
    """
    number_of_vertices = 8
    # Create the points.
    points = vtkPoints()
    points.InsertNextPoint(0, 0, 0)
    points.InsertNextPoint(1, 0, 0)
    points.InsertNextPoint(1, 1, 0)
    points.InsertNextPoint(0, 1, 0)
    points.InsertNextPoint(0, 0, 1)
    points.InsertNextPoint(1, 0, 1)
    points.InsertNextPoint(1, 1, 1)
    points.InsertNextPoint(0, 1, 1)
    # Create a hexahedron from the points.
    hexhedr = vtkHexahedron()
    for i in range(0, number_of_vertices):
        hexhedr.point_ids.SetId(i, i)
    # Add the points and hexahedron to an unstructured grid
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(hexhedr.cell_type, hexhedr.point_ids)
    return ug
def make_wedge():
    number_of_vertices = 6
    # A wedge consists of two triangular ends and three rectangular faces.
    points = vtkPoints()
    # Original Points.
    # points.InsertNextPoint(0, 1, 0)
    # points.InsertNextPoint(0, 0, 0)
    # points.InsertNextPoint(0, 0.5, 0.5)
    # points.InsertNextPoint(1, 1, 0)
    # points.InsertNextPoint(1, 0.0, 0.0)
    # points.InsertNextPoint(1, 0.5, 0.5)
    # Rotate the above points -90° about the X-axis
    # and translate -1 along the Y-axis.
    points.InsertNextPoint(0.0, 0.0, 0.0)
    points.InsertNextPoint(0.0, 0, 1.0)
    points.InsertNextPoint(0.0, 0.5, 0.5)
    points.InsertNextPoint(1.0, 0.0, 0.0)
    points.InsertNextPoint(1.0, 0, 1.0)
    points.InsertNextPoint(1.0, 0.5, 0.5)
    wedge = vtkWedge()
    for i in range(0, number_of_vertices):
        wedge.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(wedge.cell_type, wedge.point_ids)
    # pd = vtkPolyData(points=points)
    # t = vtkTransform()
    # t.RotateX(-90)
    # t.Translate(0,-1,0)
    # tf = vtkTransformFilter(transform=t)
    # (pd >> tf).update()
    # pts = tf.output.GetPoints()
    # for i in range(0, pts.number_of_points):
    #     print(f'points.InsertNextPoint{pts.GetPoint(i)}')
    return ug
def make_pyramid():
    # Make a regular square pyramid.
    number_of_vertices = 5
    points = vtkPoints()
    # Original points.
    # p0 = [1.0, 1.0, 0.0]
    # p1 = [-1.0, 1.0, 0.0]
    # p2 = [-1.0, -1.0, 0.0]
    # p3 = [1.0, -1.0, 0.0]
    # p4 = [0.0, 0.0, 1.0]
    # Rotate the above points -90° about the X-axis.
    p0 = (1.0, 0, -1.0)
    p1 = (-1.0, 0, -1.0)
    p2 = (-1.0, 0, 1.0)
    p3 = (1.0, 0, 1.0)
    p4 = (0.0, 2.0, 0)
    points.InsertNextPoint(p0)
    points.InsertNextPoint(p1)
    points.InsertNextPoint(p2)
    points.InsertNextPoint(p3)
    points.InsertNextPoint(p4)
    pyramid = vtkPyramid()
    for i in range(0, number_of_vertices):
        pyramid.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=points)
    ug.InsertNextCell(pyramid.cell_type, pyramid.point_ids)
    # pd = vtkPolyData(points=points)
    # t = vtkTransform()
    # t.RotateX(-90)
    # t.Translate(0,0,0)
    # tf = vtkTransformFilter(transform=t)
    # (pd >> tf).update()
    # pts = tf.output.GetPoints()
    # for i in range(0, pts.number_of_points):
    #     print(f'p{i} = {pts.GetPoint(i)}')
    return ug
def make_pentagonal_prism():
    number_of_vertices = 10
    pentagonal_prism = vtkPentagonalPrism()
    scale = 2.0
    pentagonal_prism.points.SetPoint(0, 11 / scale, 10 / scale, 10 / scale)
    pentagonal_prism.points.SetPoint(1, 13 / scale, 10 / scale, 10 / scale)
    pentagonal_prism.points.SetPoint(2, 14 / scale, 12 / scale, 10 / scale)
    pentagonal_prism.points.SetPoint(3, 12 / scale, 14 / scale, 10 / scale)
    pentagonal_prism.points.SetPoint(4, 10 / scale, 12 / scale, 10 / scale)
    pentagonal_prism.points.SetPoint(5, 11 / scale, 10 / scale, 14 / scale)
    pentagonal_prism.points.SetPoint(6, 13 / scale, 10 / scale, 14 / scale)
    pentagonal_prism.points.SetPoint(7, 14 / scale, 12 / scale, 14 / scale)
    pentagonal_prism.points.SetPoint(8, 12 / scale, 14 / scale, 14 / scale)
    pentagonal_prism.points.SetPoint(9, 10 / scale, 12 / scale, 14 / scale)
    for i in range(0, number_of_vertices):
        pentagonal_prism.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=pentagonal_prism.points)
    ug.InsertNextCell(pentagonal_prism.cell_type, pentagonal_prism.point_ids)
    return ug
def make_hexagonal_prism():
    number_of_vertices = 12
    hexagonal_prism = vtkHexagonalPrism()
    scale = 2.0
    hexagonal_prism.points.SetPoint(0, 11 / scale, 10 / scale, 10 / scale)
    hexagonal_prism.points.SetPoint(1, 13 / scale, 10 / scale, 10 / scale)
    hexagonal_prism.points.SetPoint(2, 14 / scale, 12 / scale, 10 / scale)
    hexagonal_prism.points.SetPoint(3, 13 / scale, 14 / scale, 10 / scale)
    hexagonal_prism.points.SetPoint(4, 11 / scale, 14 / scale, 10 / scale)
    hexagonal_prism.points.SetPoint(5, 10 / scale, 12 / scale, 10 / scale)
    hexagonal_prism.points.SetPoint(6, 11 / scale, 10 / scale, 14 / scale)
    hexagonal_prism.points.SetPoint(7, 13 / scale, 10 / scale, 14 / scale)
    hexagonal_prism.points.SetPoint(8, 14 / scale, 12 / scale, 14 / scale)
    hexagonal_prism.points.SetPoint(9, 13 / scale, 14 / scale, 14 / scale)
    hexagonal_prism.points.SetPoint(10, 11 / scale, 14 / scale, 14 / scale)
    hexagonal_prism.points.SetPoint(11, 10 / scale, 12 / scale, 14 / scale)
    for i in range(0, number_of_vertices):
        hexagonal_prism.point_ids.SetId(i, i)
    ug = vtkUnstructuredGrid(points=hexagonal_prism.points)
    ug.InsertNextCell(hexagonal_prism.cell_type, hexagonal_prism.point_ids)
    return ug
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
def draw_viewport_border(renderer, border, color=(0, 0, 0), line_width=2):
    """
    Draw a border around the viewport of a renderer.
    :param renderer: The renderer.
    :param border: The border to draw, it must be one of the constants in ViewPort.Border.
    :param color: The color.
    :param line_width: The line width of the border.
    :return:
    """
    def generate_border_lines(border_type):
        """
        Generate the lines for the border.
        :param border_type:  The border type to draw, it must be one of the constants in ViewPort.Border
        :return: The points and lines.
        """
        if border_type >= ViewPort.Border.NUMBER_OF_BORDER_TYPES:
            print('Not a valid border type.')
            return None
        # Points start at upper right and proceed anti-clockwise.
        pts = (
            (1, 1, 0),
            (0, 1, 0),
            (0, 0, 0),
            (1, 0, 0),
            (1, 1, 0),
        )
        pt_orders = {
            ViewPort.Border.TOP: (0, 1),
            ViewPort.Border.LEFT: (1, 2),
            ViewPort.Border.BOTTOM: (2, 3),
            ViewPort.Border.RIGHT: (3, 4),
            ViewPort.Border.LEFT_BOTTOM: (1, 2, 3),
            ViewPort.Border.BOTTOM_RIGHT: (2, 3, 4),
            ViewPort.Border.RIGHT_TOP: (3, 4, 1),
            ViewPort.Border.RIGHT_TOP_LEFT: (3, 4, 1, 2),
            ViewPort.Border.TOP_LEFT: (0, 1, 2),
            ViewPort.Border.TOP_LEFT_BOTTOM: (0, 1, 2, 3),
            ViewPort.Border.TOP_LEFT_BOTTOM_RIGHT: (0, 1, 2, 3, 4)
        }
        pt_order = pt_orders[border_type]
        number_of_points = len(pt_order)
        points = vtkPoints(number_of_points=number_of_points)
        i = 0
        for pt_id in pt_order:
            points.InsertPoint(i, *pts[pt_id])
            i += 1
        lines = vtkPolyLine()
        lines.point_ids.SetNumberOfIds(number_of_points)
        for i in range(0, number_of_points):
            lines.point_ids.id = (i, i)
        cells = vtkCellArray()
        cells.InsertNextCell(lines)
        # Make the polydata and return.
        return vtkPolyData(points=points, lines=cells)
    # Use normalized viewport coordinates since
    # they are independent of window size.
    coordinate = vtkCoordinate(coordinate_system=Coordinate.CoordinateSystem.VTK_NORMALIZED_VIEWPORT)
    poly = vtkAppendPolyData()
    if border == ViewPort.Border.TOP_BOTTOM:
        (
            generate_border_lines(ViewPort.Border.TOP),
            generate_border_lines(ViewPort.Border.BOTTOM)
        ) >> poly
    elif border == ViewPort.Border.LEFT_RIGHT:
        (
            generate_border_lines(ViewPort.Border.LEFT),
            generate_border_lines(ViewPort.Border.RIGHT)
        ) >> poly
    else:
        generate_border_lines(border) >> poly
    mapper = vtkPolyDataMapper2D(transform_coordinate=coordinate)
    poly >> mapper
    actor = vtkActor2D(mapper=mapper)
    actor.property.color = color
    # Line width should be at least 2 to be visible at the extremes.
    actor.property.line_width = line_width
    renderer.AddViewProp(actor)
def make_tile(bounds, expansion_factor=0.5, thickness_ratio=0.05, shift_y=-0.05):
    """
    Make a tile slightly larger or smaller than the bounds in the
      X and Z directions and thinner or thicker in the Y direction.
    A thickness_ratio of zero reduces the tile to an XZ plane.
    :param bounds: The bounds for the tile.
    :param expansion_factor: The expansion factor in the XZ plane.
    :param thickness_ratio: The thickness ratio in the Y direction, >= 0.
    :param shift_y: Used to shift the centre of the plinth in the Y-direction.
    :return: An actor corresponding to the tile.
    """
    d_xyz = (
        bounds[1] - bounds[0],
        bounds[3] - bounds[2],
        bounds[5] - bounds[4]
    )
    thickness = d_xyz[2] * abs(thickness_ratio)
    center = ((bounds[1] + bounds[0]) / 2.0,
              bounds[2] - thickness / 2.0 + shift_y,
              (bounds[5] + bounds[4]) / 2.0)
    x_length = bounds[1] - bounds[0] + (d_xyz[0] * expansion_factor)
    z_length = bounds[5] - bounds[4] + (d_xyz[2] * expansion_factor)
    plane = vtkCubeSource(center=center, x_length=x_length, y_length=thickness, z_length=z_length)
    plane_mapper = vtkPolyDataMapper()
    plane >> plane_mapper
    return vtkActor(mapper=plane_mapper)
def get_text_property():
    colors = vtkNamedColors()
    return vtkTextProperty(color=colors.GetColor3d('Black'),
                           bold=True, italic=False, shadow=False,
                           font_family_as_string='Courier',
                           justification=TextProperty.Justification.VTK_TEXT_CENTERED)
def get_label_property():
    colors = vtkNamedColors()
    return vtkTextProperty(color=colors.GetColor3d('DeepPink'),
                           bold=True, italic=False, shadow=True,
                           font_family_as_string='Courier',
                           justification=TextProperty.Justification.VTK_TEXT_CENTERED)
def get_back_face_property():
    colors = vtkNamedColors()
    return vtkProperty(
        ambient_color=colors.GetColor3d('LightSalmon'),
        diffuse_color=colors.GetColor3d('OrangeRed'),
        specular_color=colors.GetColor3d('White'),
        specular=0.2, diffuse=1.0, ambient=0.2, specular_power=20.0,
        opacity=1.0)
def get_actor_property():
    colors = vtkNamedColors()
    return vtkProperty(
        ambient_color=colors.GetColor3d('DarkSalmon'),
        diffuse_color=colors.GetColor3d('Seashell'),
        specular_color=colors.GetColor3d('White'),
        specular=0.5, diffuse=0.7, ambient=0.5, specular_power=20.0,
        opacity=0.9, edge_visibility=True, line_width=3)
def get_point_actor_property():
    colors = vtkNamedColors()
    return vtkProperty(
        ambient_color=colors.GetColor3d('Gold'),
        diffuse_color=colors.GetColor3d('Yellow'),
        specular_color=colors.GetColor3d('White'),
        specular=0.5, diffuse=0.7, ambient=0.5, specular_power=20.0,
        opacity=1.0)
def get_tile_property():
    colors = vtkNamedColors()
    return vtkProperty(
        ambient_color=colors.GetColor3d('SteelBlue'),
        diffuse_color=colors.GetColor3d('LightSteelBlue'),
        specular_color=colors.GetColor3d('White'),
        specular=0.5, diffuse=0.7, ambient=0.5, specular_power=20.0,
        opacity=0.8, edge_visibility=True, line_width=1)
@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(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
@dataclass(frozen=True)
class TextProperty:
    @dataclass(frozen=True)
    class FontFamily:
        VTK_ARIAL: int = 0
        VTK_COURIER: int = 1
        VTK_TIMES: int = 2
        VTK_UNKNOWN_FONT: int = 3
    @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
@dataclass(frozen=True)
class ViewPort:
    @dataclass(frozen=True)
    class Border:
        TOP: int = 0
        LEFT: int = 1
        BOTTOM: int = 2
        RIGHT: int = 3
        LEFT_BOTTOM: int = 4
        BOTTOM_RIGHT: int = 5
        RIGHT_TOP: int = 6
        RIGHT_TOP_LEFT: int = 7
        TOP_LEFT: int = 8
        TOP_LEFT_BOTTOM: int = 9
        TOP_LEFT_BOTTOM_RIGHT: int = 10
        TOP_BOTTOM: int = 11
        LEFT_RIGHT: int = 12
        NUMBER_OF_BORDER_TYPES: int = 13
if __name__ == '__main__':
    main()
