Skip to main content

PyVista accessor for the Trimesh library, with robust point-in-mesh, repair, sampling, smoothing, proximity, and more.

Project description

pyvista-trimesh

banner

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_points raises RuntimeError if the input is not watertight. Pass check_surface=False to skip the check.
  • signed_distance requires a watertight surface. For open meshes, use closest_point and take the unsigned distance.
  • Optional dependencies enable extra features. voxelized and select_enclosed_points use rtree when available, simplify_quadric requires fast-simplification, and the GLB / GLTF readers need shapely and friends. [extras] installs all of them.
  • Cell data is dropped by the cached default conversion. Pass pass_data=True to to_trimesh (or mesh.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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyvista_trimesh-0.1.0.tar.gz (1.9 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyvista_trimesh-0.1.0-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

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

Hashes for pyvista_trimesh-0.1.0.tar.gz
Algorithm Hash digest
SHA256 ecf6aa4979ba2275aaf1a79a97da8388953cc89b10b51ba867dabc50835ac153
MD5 8aec3eba007aa81ce67753b356c9efb8
BLAKE2b-256 9f2296019cde1587280f64b2693797573bdfbdf16a8b1327325d6f917b45f41e

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyvista_trimesh-0.1.0.tar.gz:

Publisher: ci.yml on pyvista/pyvista-trimesh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyvista_trimesh-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pyvista_trimesh-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1cd2eb25e12a0b42cfb0e39e41476bc554d4e7058bd3667ab9ac688b2ec44609
MD5 4c75db61bc4c1e8e1f82178b6e8a9f67
BLAKE2b-256 2a34f903edcc63775334e08dd43d6c23e9da8944bfd20341b5eea02750f51374

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyvista_trimesh-0.1.0-py3-none-any.whl:

Publisher: ci.yml on pyvista/pyvista-trimesh

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page