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¶
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()
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.
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 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:
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
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
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()