Skip to content

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 nameply
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 beyond x/y/z (e.g. nx,ny,nz normals, confidence, intensity).
  • cell_data[<name>] — any face property beyond vertex_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 property beyond 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/polygon are writable cell types; anything else is skipped with a warning.
  • obj_info header lines (a Meshlab convention) are skipped without being parsed.
  • The uchar-property naming: uchar is mapped to a signed 1-byte type in the count-field parsing path (matching a common "uchar-as-count" convention some tools use) even though uchar normally 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).

Released under the MIT License.