PyVista accessor for the Trimesh library, with robust point-in-mesh, repair, sampling, smoothing, proximity, and more.
Project description
pyvista-trimesh
A PyVista accessor for Trimesh. Point-in-mesh, proximity queries, sampling, repair, smoothing, voxelization, and ICP are all reachable as mesh.trimesh.<op> on any pv.PolyData.
import pyvista as pv
cube = pv.Cube()
cube.trimesh.is_watertight # True
cube.trimesh.volume # 1.0
cube.trimesh.signed_distance([[0, 0, 0]]) # array([-0.5])
Once installed, the accessor registers itself via PyVista's plugin entry-point system. There is nothing to import.
Why
PyVista wraps VTK, and VTK is brittle for several common triangle-mesh operations: vtkSelectEnclosedPoints misclassifies points on non-trivial inputs, there is no proximity or signed-distance API, and repair is awkward. Trimesh has working implementations of all of these. This package wires them up as a single .trimesh accessor that converts on demand, caches the default Trimesh conversion on the dataset, runs the operation, and hands back a fresh pv.PolyData.
mesh.trimesh.select_enclosed_points(...) mirrors pv.PolyData.select_enclosed_points and routes through Trimesh's contains (rtree-accelerated ray cast). It is the implementation originally proposed in pyvista/pyvista#6898.
Install
pip install pyvista-trimesh
For the rtree-backed contains, GLB / GLTF / 3MF readers, voxel grids, and quadric decimation:
pip install "pyvista-trimesh[extras]"
Requires Python 3.10+ and PyVista 0.48+.
Quick start
import numpy as np
import pyvista as pv
# Point-in-mesh on a 3D grid
sphere = pv.Sphere(radius=1.0)
grid = pv.ImageData(dimensions=(21, 21, 21), spacing=(0.1, 0.1, 0.1), origin=(-1, -1, -1))
result = sphere.trimesh.select_enclosed_points(grid)
inside = result.threshold(0.5, scalars='SelectedPoints')
# Signed distance from a query cloud to the surface
queries = np.random.default_rng(0).standard_normal((100, 3))
sdf = sphere.trimesh.signed_distance(queries) # negative inside, positive outside
# Even Poisson-style surface samples
samples = sphere.trimesh.sample_surface(500, even=True, seed=0)
# Voxelize into a PyVista ImageData
vox = sphere.trimesh.voxelized(pitch=0.1)
# Iterative closest point against another mesh
moved = sphere.copy()
moved.translate((0.5, 0.0, 0.0), inplace=True)
matrix, registered, cost = moved.trimesh.register(sphere)
# Drop down to the raw Trimesh when you need it
tmesh = sphere.trimesh.to_trimesh()
Output chains with the rest of PyVista:
finished = cube.trimesh.subdivide(2).clean().compute_normals()
The accessor
mesh.trimesh is a per-instance accessor. It converts the PolyData into a trimesh.Trimesh on demand, caches the default conversion until the dataset is modified (tracked by VTK's MTime), runs the operation, and converts the result back. The input is left untouched.
mesh.trimesh # accessor instance, cached on the dataset
mesh.trimesh.to_trimesh() # raw trimesh.Trimesh
mesh.trimesh.<operation>(...) # any method below
The default conversion triangulates the input. PyVista primitives like pv.Cube and pv.Cylinder work directly.
Point-in-mesh and proximity
| Method | Returns |
|---|---|
select_enclosed_points(points, check_surface=True, inside_out=False) |
copy of points with a SelectedPoints mask |
contains(points) |
boolean array, True where the point is inside the closed surface |
signed_distance(points) |
signed distance per query (negative inside, positive outside) |
closest_point(points) |
(closest, distance, triangle_id) tuple |
Sampling
| Method | Returns |
|---|---|
sample_surface(count, even=False, seed=None) |
vertex-only PolyData on the surface |
sample_volume(count, seed=None) |
vertex-only PolyData inside the volume |
Repair and smoothing
| Method | Notes |
|---|---|
repair(fill_holes=True, fix_normals=True, fix_winding=True, fix_inversion=True) |
hole filling, winding and inversion fixes |
fill_holes() |
patch boundary holes only |
smooth(method='taubin', iterations=10, lamb=0.5, nu=0.5) |
'taubin', 'humphrey', or 'laplacian' |
Subdivision and decimation
| Method | Notes |
|---|---|
subdivide(iterations=1) |
Loop midpoint subdivision |
subdivide_to_size(max_edge, max_iter=10) |
adaptive, edge-length driven |
simplify_quadric(face_count=None, percent=None, aggression=7) |
quadric edge-collapse decimation |
simplify_quadric requires the optional fast-simplification dependency, included in [extras].
Splits and slicing
| Method | Returns |
|---|---|
split(only_watertight=False) |
list of disconnected components |
section(normal, origin=None) |
polylines along one slice |
section_multiplane(plane_origin, plane_normal, heights) |
one PolyData per requested slice height |
Hulls and bounding shapes
| Method | Returns |
|---|---|
convex_hull() |
triangulated convex hull |
bounding_box(oriented=False) |
AABB or minimum-volume oriented box |
bounding_sphere() |
smallest enclosing sphere |
bounding_cylinder() |
minimum-volume bounding cylinder |
Voxelization
| Method | Returns |
|---|---|
voxelized(pitch, fill=True) |
binary pv.ImageData (filled cell-data); fill=False keeps shell |
Ray tracing
| Method | Notes |
|---|---|
ray_intersections(origins, directions, multiple_hits=True) |
NumPy ray engine, no Embree required |
Registration and transforms
| Method | Notes |
|---|---|
register(other, max_iterations=20) |
ICP alignment; returns (matrix, registered, cost) tuple |
apply_transform(matrix) |
apply a 4x4 transform via Trimesh |
other is a pv.DataSet, a trimesh.Trimesh, or an (N, 3) point-cloud array.
Properties
| Property | Returns |
|---|---|
is_watertight, is_winding_consistent, is_volume |
structural validity flags |
volume, area |
mass properties |
euler_number |
V - E + F |
center_mass, moment_inertia |
volumetric centroid and inertia tensor |
principal_inertia_components |
eigenvalues of the inertia tensor |
Module-level helpers
For inputs that do not start from an existing PolyData:
from pyvista_trimesh import (
convex_hull,
load_mesh,
primitive_annulus,
primitive_box,
primitive_capsule,
primitive_icosphere,
)
hull = convex_hull(points) # (N, 3) point cloud
mesh = load_mesh('model.glb') # any format Trimesh supports
ball = primitive_icosphere(subdivisions=4) # uniform-area triangulation
washer = primitive_annulus(r_min=0.4, r_max=1.0, height=0.2)
load_mesh handles GLB, GLTF, DAE, 3MF, OFF, and the rest of Trimesh's reader set. The result is a single pv.PolyData (scenes are concatenated).
For everything that already has a PyVista equivalent (pv.Cube, pv.Sphere, pv.Cylinder, ...), use PyVista directly and chain through .trimesh.
Conversion utilities
The accessor handles conversion automatically. Reach for these when you want a one-shot conversion outside the accessor:
from pyvista_trimesh import from_trimesh, to_trimesh
tmesh = to_trimesh(polydata, triangulate=True) # PolyData -> Trimesh
poly = from_trimesh(tmesh) # Trimesh -> PolyData
These wrap PyVista's built-in pv.to_trimesh / pv.from_trimesh (added in PyVista 0.47). Attribute mappings (vertex / face attributes, metadata, user_dict) match exactly.
Caveats
- Point-in-mesh requires a closed surface.
select_enclosed_pointsraisesRuntimeErrorif the input is not watertight. Passcheck_surface=Falseto skip the check. signed_distancerequires a watertight surface. For open meshes, useclosest_pointand take the unsigned distance.- Optional dependencies enable extra features.
voxelizedandselect_enclosed_pointsusertreewhen available,simplify_quadricrequiresfast-simplification, and the GLB / GLTF readers needshapelyand friends.[extras]installs all of them. - Cell data is dropped by the cached default conversion. Pass
pass_data=Truetoto_trimesh(ormesh.trimesh.to_trimesh(pass_data=True)) when you need round-tripping.
Development
git clone https://github.com/pyvista/pyvista-trimesh
cd pyvista-trimesh
just sync # uv sync --extra dev
just test # pytest with coverage
just lint # pre-commit run --all-files
just typecheck # mypy
just build # wheel + sdist
Acknowledgements
- Trimesh by Michael Dawson-Haggerty and contributors.
- PyVista for the accessor system and the rest of the visualization stack.
- The point-in-mesh implementation is the port of pyvista/pyvista#6898.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyvista_trimesh-0.1.0.tar.gz.
File metadata
- Download URL: pyvista_trimesh-0.1.0.tar.gz
- Upload date:
- Size: 1.9 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ecf6aa4979ba2275aaf1a79a97da8388953cc89b10b51ba867dabc50835ac153
|
|
| MD5 |
8aec3eba007aa81ce67753b356c9efb8
|
|
| BLAKE2b-256 |
9f2296019cde1587280f64b2693797573bdfbdf16a8b1327325d6f917b45f41e
|
Provenance
The following attestation bundles were made for pyvista_trimesh-0.1.0.tar.gz:
Publisher:
ci.yml on pyvista/pyvista-trimesh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyvista_trimesh-0.1.0.tar.gz -
Subject digest:
ecf6aa4979ba2275aaf1a79a97da8388953cc89b10b51ba867dabc50835ac153 - Sigstore transparency entry: 1437816430
- Sigstore integration time:
-
Permalink:
pyvista/pyvista-trimesh@c53b4aa7aa82b626ad464c6470bc7d21b3d2f445 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/pyvista
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@c53b4aa7aa82b626ad464c6470bc7d21b3d2f445 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyvista_trimesh-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pyvista_trimesh-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cd2eb25e12a0b42cfb0e39e41476bc554d4e7058bd3667ab9ac688b2ec44609
|
|
| MD5 |
4c75db61bc4c1e8e1f82178b6e8a9f67
|
|
| BLAKE2b-256 |
2a34f903edcc63775334e08dd43d6c23e9da8944bfd20341b5eea02750f51374
|
Provenance
The following attestation bundles were made for pyvista_trimesh-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on pyvista/pyvista-trimesh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyvista_trimesh-0.1.0-py3-none-any.whl -
Subject digest:
1cd2eb25e12a0b42cfb0e39e41476bc554d4e7058bd3667ab9ac688b2ec44609 - Sigstore transparency entry: 1437816434
- Sigstore integration time:
-
Permalink:
pyvista/pyvista-trimesh@c53b4aa7aa82b626ad464c6470bc7d21b3d2f445 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/pyvista
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@c53b4aa7aa82b626ad464c6470bc7d21b3d2f445 -
Trigger Event:
push
-
Statement type: