lapy.TriaMesh¶
- class lapy.TriaMesh(v, t, fsinfo=None)[source]¶
Class representing a triangle mesh.
This is an efficient implementation of a triangle mesh data structure with core functionality using sparse matrices internally (Scipy).
- Parameters:
- varray_like
List of lists of 2 or 3 float coordinates. For 2D vertices (n, 2), they will be automatically padded with z=0 to make them 3D internally. Must not be empty.
- tarray_like
List of lists of 3 int of indices (>= 0) into
varray Ordering is important: All triangles should be oriented in the same way (counter-clockwise, when looking from above). Must not be empty.- fsinfo
dict|None FreeSurfer Surface Header Info.
Attributes
v
(array_like) List of lists of 3 float coordinates (internally always 3D).
t
(array_like) List of lists of 3 int of indices (>= 0) into
varray.adj_sym
(csc_matrix) Symmetric adjacency matrix as csc sparse matrix.
adj_dir
(csc_matrix) Directed adjacency matrix as csc sparse matrix.
fsinfo
(dict | None) FreeSurfer Surface Header Info.
_is_2d
(bool) Internal flag indicating if the mesh was created with 2D vertices.
Methods
area()Compute the total surface area of triangle mesh.
Compute the average edge length of the mesh.
Compute boundary loops of the mesh.
centroid()Compute centroid of triangle mesh as a weighted average of triangle centers.
Compute connected components of the mesh.
Construct directed adjacency matrix (edge graph) of triangle mesh.
curvature([smoothit])Compute various curvature values at vertices.
curvature_tria([smoothit])Compute min and max curvature and directions on triangle faces.
edges([with_boundary])Compute vertices and adjacent triangle ids for each edge.
euler()Compute the Euler Characteristic.
get_vertices([original_dim])Get mesh vertices.
Check if the vertex list has more vertices than what is used in triangles.
is_2d()Check if the mesh was created with 2D vertices.
Check if triangle mesh is closed (no boundary edges).
Check if triangle mesh is manifold (no edges with >2 triangles).
Check if triangle mesh is oriented.
keep_largest_connected_component_([clean])Keep only the largest connected component of the mesh.
level_length(vfunc, level)Compute the length of level sets.
level_path(vfunc, level[, get_tria_idx, ...])Extract levelset of vfunc at a specific level as a path of 3D points.
map_tfunc_to_vfunc(tfunc[, weighted])Map triangle function to vertices by attributing 1/3 to each vertex.
map_vfunc_to_tfunc(vfunc)Map vertex function to triangles by averaging vertex values.
Move vertices along their normals by distance d.
Normalize TriaMesh to unit surface area and centroid at the origin.
orient_()Re-orient triangles of manifold mesh to be consistent.
read_fssurf(filename)Load triangle mesh from FreeSurfer surface geometry file.
read_off(filename)Load triangle mesh from OFF txt file.
read_vtk(filename)Load triangle mesh from VTK txt file.
refine_([it])Refine the triangle mesh by placing new vertex on each edge midpoint.
Remove unused (free) vertices.
smooth_([n])Smooth the mesh iteratively in-place using Taubin smoothing.
smooth_laplace([vfunc, n, lambda_, mat])Smooth the mesh or a vertex function using Laplace smoothing.
smooth_taubin([vfunc, n, lambda_, mu])Smooth the mesh or a vertex function using Taubin smoothing.
smooth_vfunc(vfunc[, n])Smooth the mesh or a vertex function iteratively.
Compute the area of triangles using Heron's formula.
Compute triangle normals.
Compute triangle quality for each triangle in mesh.
Compute the area associated to each vertex (1/3 of one-ring trias).
Compute the vertex degrees (number of edges at each vertex).
Compute vertex normals.
volume()Compute the volume of closed triangle mesh, summing tetrahedra at origin.
write_fssurf(filename[, image])Save as Freesurfer Surface Geometry file (wrap Nibabel).
write_vtk(filename)Save mesh as VTK file.
- Raises:
ValueErrorIf mesh has no triangles or vertices (empty mesh). If vertices don’t have 2 or 3 coordinates. If triangles don’t have exactly 3 vertices. If triangle indices exceed number of vertices.
Notes
The class has static class methods to read triangle meshes from FreeSurfer, OFF, and VTK file formats.
When 2D vertices are provided, they are internally padded with z=0 to create 3D vertices. This allows all geometric operations to work correctly while maintaining compatibility with 2D mesh data.
Empty meshes are not allowed. Use class methods (read_fssurf, read_vtk, read_off) to load mesh data from files.
- area()[source]¶
Compute the total surface area of triangle mesh.
- Returns:
floatTotal surface area (sum of all triangle areas).
- avg_edge_length()[source]¶
Compute the average edge length of the mesh.
- Returns:
floatAverage edge length.
- boundary_loops()[source]¶
Compute boundary loops of the mesh.
Meshes can have 0 or more boundary loops, which are cycles in the directed adjacency graph of the boundary edges. The mesh must be manifold and oriented.
Note
Could fail if loops are connected via a single vertex (like a figure 8). That case needs debugging.
- Returns:
- Raises:
ValueErrorIf mesh is not manifold (edges with more than 2 triangles). If mesh is not oriented.
- centroid()[source]¶
Compute centroid of triangle mesh as a weighted average of triangle centers.
The weight is determined by the triangle area. (This could be done much faster if a FEM lumped mass matrix M is already available where this would be M*v, because it is equivalent with averaging vertices weighted by vertex area)
- Returns:
- centroid
np.ndarray The centroid coordinates of the mesh, shape (3,).
- totalarea
float The total area of the mesh.
- centroid
- connected_components()[source]¶
Compute connected components of the mesh.
Uses scipy’s connected_components on the symmetric adjacency matrix.
- Returns:
- n_components
int Number of connected components.
- labels
np.ndarray Label array of shape (n_vertices,) where labels[i] is the component ID of vertex i. Component IDs are integers from 0 to n_components-1.
- n_components
- construct_adj_dir_tidx()[source]¶
Construct directed adjacency matrix (edge graph) of triangle mesh.
The directed adjacency matrix contains the triangle indices (only for non-manifold meshes). Operates only on triangles.
- Returns:
csc_matrixSimilar to adj_dir, but stores the tria idx+1 instead of one in the matrix (allows lookup of vertex to tria).
- curvature(smoothit=3)[source]¶
Compute various curvature values at vertices.
Computes principal curvature directions and values, mean and Gaussian curvature at each vertex using the anisotropic polygonal remeshing approach.
Note
For the algorithm see: Pierre Alliez, David Cohen-Steiner, Olivier Devillers, Bruno Levy, and Mathieu Desbrun. Anisotropic Polygonal Remeshing. ACM Transactions on Graphics, 2003.
- Parameters:
- smoothit
int, default=3 Number of smoothing iterations on vertex functions.
- smoothit
- Returns:
- u_min
np.ndarray Minimal curvature directions, shape (n_vertices, 3).
- u_max
np.ndarray Maximal curvature directions, shape (n_vertices, 3).
- c_min
np.ndarray Minimal curvature values, shape (n_vertices,).
- c_max
np.ndarray Maximal curvature values, shape (n_vertices,).
- c_mean
np.ndarray Mean curvature
(c_min + c_max) / 2.0, shape (n_vertices,).- c_gauss
np.ndarray Gauss curvature
c_min * c_max, shape (n_vertices,).- normals
np.ndarray Vertex normals, shape (n_vertices, 3).
- u_min
- curvature_tria(smoothit=3)[source]¶
Compute min and max curvature and directions on triangle faces.
First computes curvature values on vertices and smooths them. Then maps them to triangles (averaging) and projects onto the triangle plane, ensuring orthogonality.
- Parameters:
- smoothit
int, default=3 Number of smoothing iterations for curvature computation on vertices.
- smoothit
- Returns:
- u_min
np.ndarray Minimal curvature direction on triangles, shape (n_triangles, 3).
- u_max
np.ndarray Maximal curvature direction on triangles, shape (n_triangles, 3). Orthogonal to u_min and triangle normal.
- c_min
np.ndarray Minimal curvature values on triangles, shape (n_triangles,).
- c_max
np.ndarray Maximal curvature values on triangles, shape (n_triangles,).
- u_min
- edges(with_boundary=False)[source]¶
Compute vertices and adjacent triangle ids for each edge.
- Parameters:
- with_boundary
bool, default=False If True, also return boundary half edges. If False, only return interior edges.
- with_boundary
- Returns:
- vids
np.ndarray Array of shape (n_edges, 2) with starting and end vertex for each unique inner edge.
- tids
np.ndarray Array of shape (n_edges, 2) with triangle containing the half edge from vids[:,0] to vids[:,1] in first column and the neighboring triangle in the second column.
- bdrvids
np.ndarray If with_boundary is True: Array of shape (n_boundary_edges, 2) with each boundary half-edge. Only returned if with_boundary=True.
- bdrtids
np.ndarray If with_boundary is True: Array of shape (n_boundary_edges, 1) with the associated triangle to each boundary edge. Only returned if with_boundary=True.
- vids
- Raises:
ValueErrorIf mesh is not oriented.
- euler()[source]¶
Compute the Euler Characteristic.
The Euler characteristic is the number of vertices minus the number of edges plus the number of triangles (= #V - #E + #T). For example, it is 2 for the sphere and 0 for the torus. This operates only on triangles array.
- Returns:
intEuler characteristic.
- get_vertices(original_dim=False)[source]¶
Get mesh vertices.
- Parameters:
- original_dim
bool, default=False If True and mesh was created with 2D vertices, return vertices in original 2D format (without z-coordinate). If False, always return 3D vertices.
- original_dim
- Returns:
np.ndarrayVertex array of shape (n, 2) or (n, 3) depending on original_dim.
- has_free_vertices()[source]¶
Check if the vertex list has more vertices than what is used in triangles.
Free vertices are vertices that are not referenced by any triangle.
- Returns:
boolTrue if vertex list has more vertices than used in triangles, False otherwise.
- is_2d()[source]¶
Check if the mesh was created with 2D vertices.
- Returns:
boolTrue if mesh was created with 2D vertices, False otherwise.
- is_closed()[source]¶
Check if triangle mesh is closed (no boundary edges).
Operates only on triangles
- Returns:
boolTrue if no boundary edges in adj matrix.
- is_manifold()[source]¶
Check if triangle mesh is manifold (no edges with >2 triangles).
Operates only on triangles
- Returns:
boolTrue if no edges with > 2 triangles.
- is_oriented()[source]¶
Check if triangle mesh is oriented.
True if all triangles are oriented counter-clockwise, when looking from above. Operates only on triangles.
- Returns:
boolTrue if
max(adj_directed)=1.
- keep_largest_connected_component_(clean=True)[source]¶
Keep only the largest connected component of the mesh.
Modifies the mesh in-place.
- Parameters:
- clean
bool, default=True If True, remove vertices that are not used in the largest component. If False, vertices are kept but triangles from other components are removed, creating free vertices.
- clean
- Returns:
- vkeep
np.ndarrayorNone Indices (from original list) of kept vertices if clean=True, else None.
- vdel
np.ndarrayorNone Indices of deleted (unused) vertices if clean=True, else None.
- vkeep
- level_length(vfunc, level)[source]¶
Compute the length of level sets.
For a scalar function defined on mesh vertices, computes the total length of the curve where the function equals the specified level value(s).
- Parameters:
- vfunc
np.ndarray Scalar function values at vertices, shape (n_vertices,). Must be 1D.
- level
floatornp.ndarray Level set value(s). Can be a single float or array of level values.
- vfunc
- Returns:
floatornp.ndarrayLength of level set. Returns float for single level, array for multiple levels.
- Raises:
ValueErrorIf vfunc is not 1-dimensional. If no lengths could be computed.
- level_path(vfunc, level, get_tria_idx=False, get_edges=False, n_points=None)[source]¶
Extract levelset of vfunc at a specific level as a path of 3D points.
For a given real-valued scalar map on the surface mesh (vfunc), this function computes the edges that intersect with a given level set (level). It then finds the point on each mesh edge where the level set intersects. The points are sorted and returned as an ordered array of 3D coordinates together with the length of the level set path.
Note
Only works for level sets that represent a single non-intersecting path with exactly one start and one endpoint (e.g., not closed)!
- Parameters:
- vfunc
np.ndarray Scalar function values at vertices, shape (n_vertices,). Must be 1D.
- level
float Level set value to extract.
- get_tria_idx
bool, default=False If True, also return array of triangle indices for each path segment.
- get_edges
bool, default=False If True, also return arrays of vertex indices (i,j) and relative positions for each 3D point along the intersecting edge.
- n_points
intorNone, default=None If specified, resample level set into n equidistant points. Cannot be combined with get_tria_idx=True or get_edges=True.
- vfunc
- Returns:
- path
np.ndarray Array of shape (n, 3) containing 3D coordinates of vertices on the level path.
- length
float Length of the level set.
- tria_idx
np.ndarray Array of triangle indices for each segment on the path, shape (n-1,) if the path has length n. Only returned if get_tria_idx is True.
- edges_vidxs
np.ndarray Array of shape (n, 2) with vertex indices (i,j) for each 3D point, defining the vertices of the original mesh edge intersecting the level set at this point. Only returned if get_edges is True.
- edges_relpos
np.ndarray Array of floats defining the relative position for each 3D point along the edges of the original mesh (defined by edges_vidxs). Value 0 corresponds to first vertex, 1 to second vertex. The 3D point is computed as: (1 - relpos) * v_i + relpos * v_j. Only returned if get_edges is True.
- path
- Raises:
ValueErrorIf vfunc is not 1-dimensional. If n_points is combined with get_tria_idx=True. If n_points is combined with get_edges=True.
- map_tfunc_to_vfunc(tfunc, weighted=False)[source]¶
Map triangle function to vertices by attributing 1/3 to each vertex.
Distributes triangle values to their three vertices. Useful for converting per-triangle data to per-vertex data.
- Parameters:
- tfunc
np.ndarray Float vector or matrix of shape (n_triangles,) or (n_triangles, N) with values at triangles.
- weighted
bool, default=False If False, weigh only by 1/3 (e.g., to compute vertex areas from triangle areas). If True, weigh by triangle area / 3 (e.g., to integrate a function defined on the triangles).
- tfunc
- Returns:
np.ndarrayFunction on vertices, shape (n_vertices,) or (n_vertices, N).
- Raises:
ValueErrorIf tfunc length doesn’t match number of triangles.
- map_vfunc_to_tfunc(vfunc)[source]¶
Map vertex function to triangles by averaging vertex values.
Computes triangle values by averaging the values at the three vertices of each triangle.
- Parameters:
- vfunc
np.ndarray Float vector or matrix of shape (n_vertices,) or (n_vertices, N) with values at vertices.
- vfunc
- Returns:
np.ndarrayFunction on triangles, shape (n_triangles,) or (n_triangles, N).
- Raises:
ValueErrorIf vfunc length doesn’t match number of vertices.
- normal_offset_(d)[source]¶
Move vertices along their normals by distance d.
Displaces each vertex along its vertex normal. Useful for creating offset surfaces or inflating/deflating meshes.
- Parameters:
- d
floatornp.ndarray Move distance. Can be a scalar (same distance for all vertices) or array of shape (n_vertices,) for per-vertex distances.
- d
- Raises:
ValueErrorIf mesh is not oriented (required for vertex normals).
Notes
This modifies vertices in-place without re-initializing adjacency matrices.
- normalize_()[source]¶
Normalize TriaMesh to unit surface area and centroid at the origin.
Modifies the vertices in-place by: 1. Translating centroid to origin 2. Scaling to unit surface area
- Raises:
ValueErrorIf mesh surface area is not positive.
- orient_()[source]¶
Re-orient triangles of manifold mesh to be consistent.
Re-orients triangles so that vertices are listed counter-clockwise when looking from above (outside). Uses a flood-fill algorithm to propagate orientation from the first triangle.
Algorithm:
Construct list for each half-edge with its triangle and edge direction
Drop boundary half-edges and find half-edge pairs
Construct sparse matrix with triangle neighbors, with entry 1 for opposite half edges and -1 for parallel half-edges (normal flip across this edge)
Flood mesh from first triangle using neighbor matrix, tracking sign
Negative sign for a triangle indicates it needs to be flipped
If global volume is negative, flip everything (first triangle was wrong)
- Returns:
intNumber of triangles flipped.
Notes
This modifies the triangle array in-place to achieve consistent orientation.
- classmethod read_fssurf(filename)[source]¶
Load triangle mesh from FreeSurfer surface geometry file.
- refine_(it=1)[source]¶
Refine the triangle mesh by placing new vertex on each edge midpoint.
Creates 4 similar triangles from each parent triangle (1-to-4 subdivision). Modifies mesh in-place.
- Parameters:
- it
int, default=1 Number of refinement iterations. Each iteration quadruples the number of triangles.
- it
- rm_free_vertices_()[source]¶
Remove unused (free) vertices.
Free vertices are vertices that are not referenced by any triangle. They can produce problems when constructing, e.g., Laplace matrices.
Modifies the mesh in-place by removing unused vertices and re-indexing triangles.
- Returns:
- vkeep
np.ndarray Indices (from original list) of kept vertices, shape (n_kept,).
- vdel
np.ndarray Indices of deleted (unused) vertices, shape (n_deleted,).
- vkeep
- Raises:
ValueErrorIf triangle indices exceed number of vertices.
- smooth_(n=1)[source]¶
Smooth the mesh iteratively in-place using Taubin smoothing.
- Parameters:
- n
int, default=1 Number of smoothing iterations.
- n
- smooth_laplace(vfunc=None, n=1, lambda_=0.5, mat=None)[source]¶
Smooth the mesh or a vertex function using Laplace smoothing.
Applies iterative smoothing: v_new = (1-lambda)*v + lambda * M*v where M is the vertex-area weighted adjacency matrix.
- Parameters:
- vfunc
np.ndarrayorNone, default=None Float vector of values at vertices, shape (n_vertices,) or (n_vertices, N). If None, uses vertex coordinates for mesh smoothing.
- n
int, default=1 Number of smoothing iterations.
- lambda_
float, default=0.5 Diffusion speed parameter in range [0, 1]. lambda_=1 reduces to weighted average of neighboring vertices, while smaller values preserve more of the original values.
- mat
scipy.sparse.csc_matrixorNone, default=None Precomputed smoothing matrix. If None, constructs it internally.
- vfunc
- Returns:
np.ndarraySmoothed vertex function, same shape as input vfunc.
- Raises:
ValueErrorIf vfunc length doesn’t match number of vertices.
- smooth_taubin(vfunc=None, n=1, lambda_=0.5, mu=-0.53)[source]¶
Smooth the mesh or a vertex function using Taubin smoothing.
Taubin smoothing alternates between shrinking (positive lambda) and expanding (negative mu) steps to reduce shrinkage while smoothing. This is a low-pass filter that better preserves mesh volume.
- Parameters:
- vfunc
np.ndarrayorNone, default=None Float vector of values at vertices, shape (n_vertices,) or (n_vertices, N). If None, uses vertex coordinates for mesh smoothing.
- n
int, default=1 Number of smoothing iterations (each iteration applies both lambda and mu steps).
- lambda_
float, default=0.5 Positive diffusion parameter for shrinking step.
- mu
float, default=-0.53 Negative diffusion parameter for expanding step. Should be negative and satisfy mu < -lambda to avoid instability.
- vfunc
- Returns:
np.ndarraySmoothed vertex function, same shape as input vfunc.
- Raises:
ValueErrorIf vfunc length doesn’t match number of vertices.
References
Gabriel Taubin, “A Signal Processing Approach to Fair Surface Design”, SIGGRAPH 1995.
- smooth_vfunc(vfunc, n=1)[source]¶
Smooth the mesh or a vertex function iteratively.
This is the most simple smoothing approach of a weighted average of the neighbors.
- tria_areas()[source]¶
Compute the area of triangles using Heron’s formula.
Heron’s formula computes the area of a triangle by using the three edge lengths.
- Returns:
np.ndarrayArray with areas of each triangle, shape (n_triangles,).
- tria_normals()[source]¶
Compute triangle normals.
Triangle normals are computed using the cross product of two edges. Ordering of triangles is important: normals point outward for counter-clockwise oriented triangles when looking from above.
- Returns:
np.ndarrayTriangle normals of shape (n_triangles, 3). Each normal is normalized to unit length.
- tria_qualities()[source]¶
Compute triangle quality for each triangle in mesh.
Quality measure: q = 4 sqrt(3) A / (e1^2 + e2^2 + e3^2) where A is the triangle area and ei the edge length of the three edges. Constants are chosen so that q=1 for the equilateral triangle.
Note
This measure is used by FEMLAB and can also be found in: R.E. Bank, PLTMG …, Frontiers in Appl. Math. (7), 1990.
- Returns:
np.ndarrayArray with triangle qualities, shape (n_triangles,). Values range from 0 (degenerate) to 1 (equilateral).
- vertex_areas()[source]¶
Compute the area associated to each vertex (1/3 of one-ring trias).
- Returns:
- vareas
array Array of vertex areas.
- vareas
- vertex_degrees()[source]¶
Compute the vertex degrees (number of edges at each vertex).
- Returns:
np.ndarrayArray of vertex degrees, shape (n_vertices,).
- vertex_normals()[source]¶
Compute vertex normals.
Vertex normals are computed by averaging triangle normals around each vertex, weighted by the angle that each triangle contributes at that vertex. The mesh must be oriented for meaningful results.
- Returns:
np.ndarrayVertex normals of shape (n_vertices, 3). Each normal is normalized to unit length.
- Raises:
ValueErrorIf mesh is not oriented.
- volume()[source]¶
Compute the volume of closed triangle mesh, summing tetrahedra at origin.
The mesh must be closed (no boundary edges) and oriented (consistent triangle orientation).
- Returns:
floatTotal enclosed volume.
- Raises:
ValueErrorIf mesh is not closed or not oriented.
- write_fssurf(filename, image=None)[source]¶
Save as Freesurfer Surface Geometry file (wrap Nibabel).
- Parameters:
- filename
str Filename to save to.
- image
str,object,None Path to image, nibabel image object, or image header. If specified, the vertices are assumed to be in voxel coordinates and are converted to surface RAS (tkr) coordinates before saving. The expected order of coordinates is (x, y, z) matching the image voxel indices in nibabel.
- filename
- Raises:
IOErrorIf file cannot be written.
ValueErrorIf image header cannot be processed.
Notes
The surface RAS (tkr) transform is obtained from a header that implements
get_vox2ras_tkr()(e.g.,MGHHeader). For other header types (NIfTI1/2, Analyze/SPM, etc.), we attempt conversion viaMGHHeader.from_header.