Why PyVista?

VTK is an excellent visualization toolkit, and with Python bindings it should be able to combine the speed of C++ with the rapid prototyping of Python. However, despite this VTK code programmed in Python generally looks the same as its C++ counterpart. This module seeks to simplify mesh creation and plotting without losing functionality.

Compare two approaches for loading and plotting a surface mesh from a file:

Plotting a Mesh using Python’s VTK

Using this example as a baseline, loading and plotting an STL file requires a lot of code when using only the vtk library. PyVista on the other hand only requires a few lines of code.

Read and plot STL file using vtk

Read an STL file using PyVista

import vtk
reader = vtk.vtkSTLReader()
reader.SetFileName("bunny.stl")
mapper = vtk.vtkPolyDataMapper()
output_port = reader.GetOutputPort()
mapper.SetInputConnection(output_port)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
ren.AddActor(actor)
iren.Initialize()
renWin.Render()
iren.Start()
del iren, renWin
import pyvista
mesh = pyvista.read('bunny.stl')
mesh.plot()
../_images/why-2_00_00.png

The PyVista data model and API allows you to rapidly load meshes and handles much of the “grunt work” of setting up plots, connecting classes and pipelines, and cleaning up plotting windows. It does this by exposing a simplified, but functional, interface to VTK’s classes.

In pyvista.read(), PyVista automatically determines the correct file reader based on the file extension and returns a DataSet object. This dataset object contains all the methods that are available to a pyvista.PolyData class, including the plot method, allowing you to instantly generate a plot of the mesh. Garbage collection is taken care of automatically and the renderer is cleaned up after the user closes the plotting window.

For more details comparing the two APIs, please see PyVista Data Model and Transitioning from VTK to PyVista.

PyVista API

For example, triangular surface meshes in VTK can be subdivided but every other object in VTK cannot. It then makes sense that a subdivide() method be added to the existing triangular surface mesh. That way, subdivision can be performed with:

import pyvista
mesh = pyvista.Plane().triangulate()
submesh = mesh.subdivide(2, 'linear')
submesh.plot(show_edges=True)

Additionally, the docstrings for all methods in PyVista are intended to be used within interactive coding sessions. This allows users to use sophisticated processing routines on the fly with immediate access to a description of how to use those methods:

../_images/documentation.gif

Interfacing With Other Libraries

PyVista is heavily dependent on numpy and uses it to represent point, cell, field, and other data from the VTK meshes. This data can be easily accessed from the dataset attributes like pyvista.DataSet.points. For example the first 10 points of a circle from pyvista can be accessed with:

circle = pyvista.Circle()
circle.points[:10]
pyvista_ndarray([[0.5       , 0.        , 0.        ],
                 [0.49899334, 0.03171196, 0.        ],
                 [0.49597741, 0.06329623, 0.        ],
                 [0.49096435, 0.09462562, 0.        ],
                 [0.48397435, 0.12557399, 0.        ],
                 [0.47503556, 0.15601672, 0.        ],
                 [0.46418397, 0.18583123, 0.        ],
                 [0.45146327, 0.21489746, 0.        ],
                 [0.43692469, 0.24309837, 0.        ],
                 [0.42062677, 0.27032041, 0.        ]])

And these points can be operated on as if it was a NumPy array, all without losing connection to the underlying VTK data array.

At the same time, a variety of PyVista objects can be generated directly from numpy arrays. For example, below we generate a vector field of arrows using numpy.meshgrid():

import pyvista
import numpy as np

# Make a grid
x, y, z = np.meshgrid(np.linspace(-5, 5, 20),
                      np.linspace(-5, 5, 20),
                      np.linspace(-5, 5, 5))

points = np.empty((x.size, 3))
points[:, 0] = x.ravel('F')
points[:, 1] = y.ravel('F')
points[:, 2] = z.ravel('F')

# Compute a direction for the vector field
direction = np.sin(points)**3

# plot using the plotting class
pl = pyvista.Plotter()
pl.add_arrows(points, direction, 0.5)
pl.show()

PyVista has connections to several other libraries, such as meshio and matplotlib, allowing PyVista to extend VTK with functionality from the Python ecosystem.