Tetmesh
The Tetmesh module fills the solid a closed surface encloses with tetrahedra. The surface is recovered exactly: every input face is a union of tetrahedron faces, and the tetrahedralization is constrained Delaunay.
import trueform as tf
tet_mesh
mesh = tf.Mesh(faces, points)
tets, points, status = tf.tet_mesh(mesh)
# tets: (T, 4) corners into points; points: (P, 3) float64
if status == tf.TetMeshStatus.MESHED:
...
| Parameter | Type | Description |
|---|---|---|
mesh | Mesh | A 3D triangle mesh; its transformation places it, and its face membership and manifold edge link are reused |
split_constraints | bool, default True | Cut the crossings the recovery finds; without it the surface is meshed as given and a crossing in it refuses |
tolerance | float, default 0.0 | Plane pooling, in the mesh's coordinates: near-coplanar faces pool onto one plane at the entrance, and it is the band of every drain |
site_budget_ratio | int, optional | The sites a run may hold per site its entrance states; a recovery past it refuses. Default: the library's |
return_report | bool, default False | Return the TetMeshReport in the status's place |
return_index_map | bool, default False | Also return a TetMeshIndexMap |
Faces that cross each other are cut where they cross, so a self-intersecting surface meshes too; inside is decided by the surface's winding, a cell kept when the surface winds around it a positive number of times. The points come back in float64 whatever the mesh's dtype: they are exact lattice positions mapped back, finer than a float32 holds. A mesh with a transformation is meshed where the transformation places it, and its points are world positions.
A crossing is found where the constraint recovery refuses it and is cut there, so a surface that crosses nowhere pays nothing for it. With split_constraints=False no crossing is cut (the report's drains is zero) and the first one the recovery names refuses; a clean surface gives the same cells either way.
The Index Map
tets, points, status, imap = tf.tet_mesh(mesh, return_index_map=True)
for facet in imap.face_facets[f]:
rows = imap.facet_triangles[imap.facet_offsets[facet]:imap.facet_offsets[facet + 1]]
# rows: (k, 3) triangles into points, wound as the facet
Coplanar input triangles that meet wound alike are one facet. facet_offsets / facet_triangles hold per facet the boundary triangles of the tet mesh covering it; face_facets (an OffsetBlockedArray) lists per input face, ascending, the facets it lies in — one unless a crossing cut it. The map is only computed when asked for.
The Report
The call's third value is a tf.TetMeshStatus:
| Status | Meaning |
|---|---|
MESHED | Every constraint recovered and the surface winds consistently; the mesh is the cells it winds positively |
REFUSED | Some constraint was refused, the entrance left a carrier without its triangles, or its lattice cannot resolve the crossings its passes kept naming; the report counts each kind. A mesh is returned only when the surface still winds consistently |
OPEN | The recovered surface has a hole, a rim of free edges that hangs off no non-manifold attachment; no mesh. tf.fill_holes closes a hole |
UNWOUND | The surface does not wind consistently, or it holds a sheet such as a flap whose free rim bounds nothing; no mesh |
RANK_DEFICIENT | The points span less than three dimensions |
CAPACITY | The cells or sites outgrew the index type |
tf.tet_mesh(mesh, return_report=True) returns the TetMeshReport in the status's place; its diagnostics are measured only when it is asked for. It mirrors the C++ tf::tet_mesh_report: status is a TetMeshStatus, the refusals are counted by kind (refused_segments, refused_depth, refused_budget, refused_faces), the cuts by drains, pairs_named, pairs_cut, triangles_cut, failed_triangles and pairs_unresolved, the entrance's statements by dropped_degenerate and stitched_triangles, and census is the constraint recovery's census as a dict. See the C++ Tetmesh module for every field.
