PLY (.ply)
The Polygon File Format (aka Stanford triangle format): a header describing named element groups with typed properties, followed by ASCII or (little/big-endian) binary data.
| Format name | ply |
| Extensions | .ply |
| Read / Write | ✓ / ✓ |
| Extra dependencies | — |
Reading & writing
python
import meshioplusplus
mesh = meshioplusplus.read("bunny.ply")
meshioplusplus.ply.write("out.ply", mesh, binary=True)binary— binary (True, the default) or ASCII.
File structure
ply
format ascii 1.0 | format binary_little_endian 1.0 | format binary_big_endian 1.0
comment ...
element vertex <N>
property <type> <name> # repeated, x/y/[z] conventionally first
[element face <M>
property list <count_type> <index_type> vertex_indices
[property <type> <name> ...]] # extra per-face scalar properties (Python reader only)
end_header
<vertex data, N rows>
<face data, M rows: count + indices [+ extra properties]>Endianness is read directly from the format line (ascii/ binary_little_endian/binary_big_endian), unlike VTU/VTK which use a separate byte-order attribute.
Cell types
Faces are grouped by vertex count: 1→vertex, 2→line, 3→triangle, 4→quad, else→polygon.
Data mapping
point_data[<name>]— any vertex property beyondx/y/z(e.g.nx,ny,nznormals,confidence,intensity).cell_data[<name>]— any face property beyondvertex_indices(Python reader only — see limitations).
Quirks & limitations
- Ragged face-list parsing in binary mode: since each face row's length is only known by reading its own leading count, the reader first walks the whole buffer computing per-row byte offsets, then groups consecutive constant-length runs into separate cell blocks — this differs from most other formats, where all same-typed cells end up in one block regardless of their position in the file; here, position matters.
- Extra face properties beyond the index list are Python-only — the C++ reader explicitly rejects any face
propertybeyond the single index list, as well as list-typed vertex properties; both force a Python fallback. - 64-bit integer cell data is silently downcast to int32 on write (PLY has no 64-bit integer property type), with a warning; the writer also requires all cell blocks share one dtype.
- Multi-dimensional point-data is not writable (skipped per-key with a warning in Python; silently filtered in C++).
- Only
vertex/line/triangle/quad/polygonare writable cell types; anything else is skipped with a warning. obj_infoheader lines (a Meshlab convention) are skipped without being parsed.- The uchar-property naming:
ucharis mapped to a signed 1-byte type in the count-field parsing path (matching a common "uchar-as-count" convention some tools use) even thoughucharnormally means unsigned — this is deliberate, not an oversight, and only affects the binary list-count field, not general vertex/face data typing.
Notes
tests/meshes/ply/bun_zipper_res4.ply— the decimated Stanford bunny, 948 triangle cells, point-sum ≈34.14584.tests/meshes/ply/tet.ply— a tiny 4-cell tetrahedron surface, point-sum 6 (exercises extra face properties, forcing the Python path).- The C++ core handles ASCII and both binary endiannesses for the common case (index-only faces, non-list vertex properties).