Skip to content

CappedSphere

vtk-examples/PythonicAPI/Modelling/CappedSphere

Description

Demonstrates how to create a capped sphere.

Firstly a line is created in the x-z plane corresponding to an arc from +z to -z in the +x direction in the x-z plane, the length of the arc is specified in degrees.

Then the line is extended by dropping a perpendicular to the x-axis.

The points generated are then converted to a line and passed through to the vtkRotationalExtrusionFilter to generate the resultant 3D surface.

The parameters are:

  • angle - the arc length in degrees default 90° (a hemisphere)
  • step -the step size of the arc in degrees, default 1°
  • radius - the radius of the arc default 1

Options are provided to:

  • Uncap the sphere (-u, --uncapped)
  • Display the line that was rotationally extruded (-s, --show_line)

Note

The coordinate system for specifying the arc is left-handed with 0° aligned with the positive z-axis, 90° aligned with the positive x-axis.

Note

You can substitute different parametric equations for x and z in the line generating function to get other shapes.

Other languages

See (Cxx), (Python)

Question

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

Code

CappedSphere.py

#!/usr/bin/env python3

import math

# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkPoints
from vtkmodules.vtkCommonDataModel import (
    vtkCellArray,
    vtkLine,
    vtkPolyData
)
from vtkmodules.vtkFiltersModeling import vtkRotationalExtrusionFilter
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer
)


def get_program_parameters():
    import argparse
    description = 'Display a capped sphere.'
    epilogue = '''
   '''
    parser = argparse.ArgumentParser(description=description, epilog=epilogue,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('angle', default=90, type=float, nargs='?',
                        help='The length of the arc in degrees from +z to -z in the +x direction in the x-z plane.')
    parser.add_argument('step', default=1, type=float, nargs='?', help='Step size in degrees.')
    parser.add_argument('radius', default=1, type=float, nargs='?', help='Radius of the arc.')
    parser.add_argument('-u', '--uncapped', action='store_true', help='Uncap the sphere.')
    parser.add_argument('-s', '--show_line', action='store_true',
                        help='Show the line that is rotationally extruded to make the surface.')
    args = parser.parse_args()
    return args.angle, args.step, args.radius, args.uncapped, args.show_line


def main():
    angle, step, radius, uncapped, show_line = get_program_parameters()
    angle = math.radians(abs(angle))
    step = math.radians(abs(step))
    radius = abs(radius)
    # With the default settings, if you set this to 45°,
    # you get a bowl with a flat bottom.
    start = math.radians(90)

    pts = get_line_points(angle, step, radius, uncapped, start)

    # Setup points and lines
    points = vtkPoints()
    lines = vtkCellArray()
    for pt in pts:
        pt_id = points.InsertNextPoint(pt)
        if pt_id < len(pts) - 1:
            line = vtkLine()
            line.GetPointIds().SetId(0, pt_id)
            line.GetPointIds().SetId(1, pt_id + 1)
            lines.InsertNextCell(line)

    polydata = vtkPolyData(points=points, lines=lines)

    # Extrude the profile to make the capped sphere
    extrude = vtkRotationalExtrusionFilter(input_data=polydata, resolution=60)

    #  Visualize
    colors = vtkNamedColors()

    # To see the line.
    line_mapper = vtkPolyDataMapper(input_data=polydata)

    line_actor = vtkActor(mapper=line_mapper)
    line_actor.property.line_width = 4
    line_actor.property.color = colors.GetColor3d('Red')

    # To see the surface.
    surface_mapper = vtkPolyDataMapper()
    extrude >> surface_mapper

    surface_actor = vtkActor(mapper=surface_mapper)
    surface_actor.property.color = colors.GetColor3d('Khaki')

    ren = vtkRenderer(background=colors.GetColor3d('LightSlateGray'))
    ren_win = vtkRenderWindow(size=(600, 600), window_name='CappedSphere')
    ren_win.AddRenderer(ren)
    iren = vtkRenderWindowInteractor()
    iren.render_window = ren_win

    ren.AddActor(surface_actor)
    if show_line:
        ren.AddActor(line_actor)

    ren.ResetCamera()
    if show_line:
        ren.active_camera.Roll(90)
    ren.active_camera.Elevation(60)

    ren.ResetCameraClippingRange()

    ren_win.Render()
    iren.Start()


def get_line_points(angle, step, radius, uncapped, start):
    """
    Get the points for a line.

    :param angle: Length of the arc in degrees.
    :param step: Step size in degrees.
    :param radius: Radius of the arc.
    :param uncapped: True if uncapped.
    :param start: Starting angle.
    :return: A vector of points.
    """
    precision = 1.0e-6
    pts = list()
    # Do the curved line
    theta = 0.0
    while theta <= angle:
        x = radius * math.cos(start - theta)
        z = radius * math.sin(theta - start)
        if x < 0:
            x = 0
            pts.append((x, 0, z))
            break
        if abs(x) < precision:
            x = 0
        if abs(z) < precision:
            z = 0
        pts.append((x, 0, z))
        theta += step

    if not uncapped:
        # Drop a perpendicular from the last point to the x-axis.
        if len(pts) > 1:
            if pts[-1][0] > 0:
                last_point = pts[-1]
                num_pts = 10
                interval = float(num_pts) / radius
                for i in range(1, num_pts):
                    x = last_point[0] - i / interval
                    z = last_point[2]
                    if x < 0:
                        x = 0
                        pts.append((x, 0, z))
                        break
                    if abs(x) < precision:
                        x = 0
                    if abs(z) < precision:
                        z = 0
                    pts.append((x, 0, z))
            if pts[-1][0] > precision:
                pts.append((0, 0, pts[-1][2]))
    return pts


if __name__ == '__main__':
    main()