Triangle (.node / .ele / .poly)
The file formats of Shewchuk's Triangle 2D mesh generator — the planar analogue of TetGen: a .node/.ele sibling pair (points + triangles) and the .poly PSLG format (points + boundary segments).
| Format name | triangle |
| Extensions | .node, .ele (shared with tetgen), .poly |
| Read / Write | ✓ / ✓ |
| Extra dependencies | — |
Reading & writing
import meshioplusplus
mesh = meshioplusplus.read("mesh.node") # 2D pair -> triangle (see dispatch below)
mesh = meshioplusplus.triangle.read("mesh.node") # explicit
meshioplusplus.triangle.write("out.node", mesh) # writes out.node + out.ele
meshioplusplus.triangle.write("out.poly", mesh) # writes the PSLG
meshioplusplus.write("out.node", mesh, file_format="triangle")Extension dispatch (vs tetgen)
.node/.ele are registered to tetgen first (3D pairs stay the default everywhere, including the WASM/C/Fortran flat bindings). Reading through the generic meshioplusplus.read():
- a 3D
.node/.elepair reads via tetgen, exactly as before; - a 2D pair makes tetgen raise (
Need 3D/2D pointsmismatch) and the extension dispatcher falls through totriangleautomatically — no explicit format needed.
Writing to .node/.ele without an explicit format still picks tetgen; pass file_format="triangle" (or call meshioplusplus.triangle.write) for 2D meshes. Only .poly defaults to triangle. The flat bindings (WASM, C API, Fortran) have a single-winner extension map, so reading a 2D .node there always needs format="triangle".
File structure
.node: header npoints 2 nattrs nbmarkers, rows idx x y attrs.. markers... The node index base (0 or 1) is auto-detected from the first row and indices must be exactly consecutive (as in tetgen). A lone .node file (no .ele sibling) reads as a point cloud.
.ele: header ntriangles 3|6 nattrs, rows idx n0..n(k-1) attrs.. — 3-node files become triangle blocks, 6-node files triangle6.
.poly: a vertex section (inline, or the sibling .node when the vertex count is 0), a segment section (idx a b [marker] → one line cell block), a hole section, and optional regional attributes. Holes and regions are skipped with a warning (they describe meshing directives, not mesh entities).
Write: a .node/.ele path writes the pair (all triangle/triangle6 blocks, which must share one type; other cell types are skipped with a warning); a .poly path writes inline vertices, the line blocks as segments, and zero holes. Points must be 2D.
Data mapping
Mirrors tetgen's naming:
point_data["triangle:attr{k}"]— vertex attribute columns.point_data["triangle:ref"]/"triangle:ref2"/ ... — boundary marker columns.cell_data["triangle:ref"]/ ... — element attribute columns (.ele) or segment markers (.poly).
Quirks & limitations
- The format spans multiple files and cannot be read from or written to a buffer.
.eleattribute columns are read asfloat64(Triangle's regional attributes may be non-integer), unlike tetgen's integer parsing.- Mixing
triangleandtriangle6blocks in one mesh raisesWriteError(a.elefile has a single nodes-per-triangle header). .polyfiles with both segments and triangles are not a thing in Triangle itself — triangles are never written to.poly(use the.node/.elepair).