Binary STL
64-bitWrites each triangle's unit normal and three corners.
n = (v₁ − v₀) × (v₂ − v₀) ÷ |(v₁ − v₀) × (v₂ − v₀)|
The normal is computed in 64-bit after the exact Y up to Z up swap; a zero-length normal is divided by 1. Normals and coordinates are written with setFloat32 because binary STL holds 32-bit numbers, so the file keeps about 7 significant digits.
export function stlBinary(mesh, name = 'part') {
const nt = triCount(mesh);
const buf = new ArrayBuffer(84 + nt * 50);
const dv = new DataView(buf);
const head = ('PartCraft binary STL ' + name).slice(0, 79); // i18n-ignore: STL file header
for (let i = 0; i < head.length; i++) dv.setUint8(i, head.charCodeAt(i) & 0x7f);
dv.setUint32(80, nt, true);
const P = mesh.positions, I = mesh.indices;
let o = 84;
for (let t = 0; t < nt; t++) {
const v = [0, 1, 2].map((k) => zUp(P[I[t * 3 + k] * 3], P[I[t * 3 + k] * 3 + 1], P[I[t * 3 + k] * 3 + 2]));
const u = [v[1][0] - v[0][0], v[1][1] - v[0][1], v[1][2] - v[0][2]], w = [v[2][0] - v[0][0], v[2][1] - v[0][1], v[2][2] - v[0][2]];
let n = [u[1] * w[2] - u[2] * w[1], u[2] * w[0] - u[0] * w[2], u[0] * w[1] - u[1] * w[0]];
const l = Math.hypot(...n) || 1;
n = n.map((c) => c / l);
for (const c of n) { dv.setFloat32(o, c, true); o += 4; }
for (const p of v) for (const c of p) { dv.setFloat32(o, c, true); o += 4; }
dv.setUint16(o, 0, true); o += 2;
}
return new Uint8Array(buf);
}