Skip to main content

rscad_csg_traits/
stl.rs

1//! Shared STL framing: binary/ASCII writers over a backend-agnostic
2//! triangle stream. Backends supply `(unit normal, [v0, v1, v2])` triangles
3//! from their own tessellation; the byte/text framing lives once here.
4
5use std::io::Write;
6
7/// One STL facet: `(unit normal, [v0, v1, v2])`.
8pub type StlTriangle = ([f32; 3], [[f32; 3]; 3]);
9
10/// Write binary STL: 80-byte header, `u32` triangle count, then per triangle
11/// the normal, three vertices, and a zero attribute word (all little-endian).
12pub fn write_stl_binary(
13    writer: &mut dyn Write,
14    triangles: impl ExactSizeIterator<Item = StlTriangle>,
15) -> std::io::Result<()> {
16    writer.write_all(&[0u8; 80])?;
17    writer.write_all(&(triangles.len() as u32).to_le_bytes())?;
18    for (normal, verts) in triangles {
19        for c in normal {
20            writer.write_all(&c.to_le_bytes())?;
21        }
22        for v in verts {
23            for c in v {
24                writer.write_all(&c.to_le_bytes())?;
25            }
26        }
27        writer.write_all(&0u16.to_le_bytes())?;
28    }
29    Ok(())
30}
31
32/// Write ASCII STL (`solid rscad` … `endsolid rscad`).
33pub fn write_stl_ascii(
34    writer: &mut dyn Write,
35    triangles: impl Iterator<Item = StlTriangle>,
36) -> std::io::Result<()> {
37    writeln!(writer, "solid rscad")?;
38    for (normal, verts) in triangles {
39        writeln!(
40            writer,
41            "  facet normal {} {} {}",
42            normal[0], normal[1], normal[2]
43        )?;
44        writeln!(writer, "    outer loop")?;
45        for v in verts {
46            writeln!(writer, "      vertex {} {} {}", v[0], v[1], v[2])?;
47        }
48        writeln!(writer, "    endloop")?;
49        writeln!(writer, "  endfacet")?;
50    }
51    writeln!(writer, "endsolid rscad")?;
52    Ok(())
53}