I/O
The I/O module provides functions for reading and writing mesh data in STL format (binary and ASCII) and OBJ format (ASCII). All operations work with in-memory buffers, making them suitable for browser environments and Node.js alike.
Reading
STL Files
Read an STL file (binary or ASCII) from a buffer. Only vertex positions and face connectivity are read — per-face normals stored in the STL are ignored (use mesh.normals to compute them).
import * as tf from "@polydera/trueform";
// From fetch response
const response = await fetch("model.stl");
const bytes = new Uint8Array(await response.arrayBuffer());
const mesh = tf.readStl(bytes);
console.log(`${mesh.numberOfFaces} faces, ${mesh.numberOfPoints} points`);
OBJ Files
Read an OBJ file from a buffer. Only vertex positions and face connectivity are read — normals (vn), texture coordinates (vt), materials, and group names are ignored.
const response = await fetch("model.obj");
const bytes = new Uint8Array(await response.arrayBuffer());
const mesh = tf.readObj(bytes);
By default, readObj expects triangular faces. To read OBJ files with quads or mixed polygon sizes, pass { dynamic: true } — all polygons are read and automatically triangulated:
const mesh = tf.readObj(bytes, { dynamic: true });
From File Input
In browser environments, read from a file input element:
const file = input.files[0];
const bytes = new Uint8Array(await file.arrayBuffer());
const mesh = tf.readStl(bytes);
| Function | Input | Returns |
|---|---|---|
readStl | ArrayBuffer | Uint8Array | Mesh |
readObj | ArrayBuffer | Uint8Array, ReadObjOptions? | Mesh |
Raw Data Access
readStlData and readObjData return raw faces and points as a MeshLike object instead of a Mesh. This gives direct access to the parsed geometry without creating a WASM-resident mesh.
// STL → MeshLike with NDArrayInt32 faces [F, 3]
const { faces, points } = tf.readStlData(bytes);
// OBJ (triangles only) → MeshLike with NDArrayInt32 faces [F, 3]
const data = tf.readObjData(bytes);
// OBJ (all polygons) → MeshLike with OffsetBlockedBuffer faces
const dynamicData = tf.readObjData(bytes, { dynamic: true });
// Triangulate dynamic data into a Mesh when ready
const mesh = tf.triangulate(dynamicData);
| Function | Input | Returns |
|---|---|---|
readStlData | ArrayBuffer | Uint8Array | MeshLike (NDArrayInt32 faces) |
readObjData | ArrayBuffer | Uint8Array, ReadObjOptions? | MeshLike (NDArrayInt32 or OffsetBlockedBuffer faces) |
Writing
Write functions serialize a mesh to an in-memory buffer. The returned NDArrayInt8 can be converted to a Blob for download or upload.
STL Files
Writes binary STL. Per-face normals are included if available, otherwise zero normals are written.
// Serialize to binary STL
const stlBuffer = tf.writeStl(mesh);
// Download in browser
const blob = new Blob([stlBuffer.toTypedArray()], { type: "model/stl" });
const url = URL.createObjectURL(blob);
OBJ Files
Writes ASCII OBJ with vertex positions and face connectivity only (no normals, UVs, or materials).
// Serialize to ASCII OBJ
const objBuffer = tf.writeObj(mesh);
const blob = new Blob([objBuffer.toTypedArray()], { type: "text/plain" });
| Function | Input | Returns |
|---|---|---|
writeStl | Mesh | NDArrayInt8 (binary STL) |
writeObj | Mesh | NDArrayInt8 (ASCII OBJ) |
transformation set, the transformation is automatically applied to the vertex positions in the output.Round-Trip Example
import * as tf from "@polydera/trueform";
// Read
const response = await fetch("input.stl");
const mesh = tf.readStl(new Uint8Array(await response.arrayBuffer()));
// Process
const decimated = tf.decimated(mesh, 0.1);
// Write back
const stlData = tf.writeStl(decimated);
const blob = new Blob([stlData.toTypedArray()], { type: "model/stl" });
Async
All I/O functions are available as async variants via tf.async for off-main-thread execution:
const mesh = await tf.async.readStl(bytes);
const stlData = await tf.async.writeStl(mesh);
