Skip to main content

conspire/geometry/mesh/tessellation/write/
mod.rs

1#[cfg(test)]
2mod test;
3
4use crate::{
5    geometry::{
6        Coordinate, Direction,
7        mesh::{
8            Connectivity,
9            tessellation::{D, Tessellation},
10        },
11    },
12    io::Write,
13    math::Tensor,
14};
15use std::{
16    fs::File,
17    io::{BufWriter, Error as ErrorIO, Write as WriteIO},
18    path::Path,
19};
20
21pub enum Stl<P>
22where
23    P: AsRef<Path>,
24{
25    Ascii(P),
26    Binary(P),
27}
28
29impl<P> AsRef<Path> for Stl<P>
30where
31    P: AsRef<Path>,
32{
33    fn as_ref(&self) -> &Path {
34        match self {
35            Stl::Ascii(path) => path.as_ref(),
36            Stl::Binary(path) => path.as_ref(),
37        }
38    }
39}
40
41impl<P> Write<Stl<P>> for Tessellation
42where
43    P: AsRef<Path>,
44{
45    type Error = ErrorIO;
46    fn write(&self, output: Stl<P>) -> Result<(), Self::Error> {
47        match output {
48            Stl::Ascii(path) => self.write_stl_ascii(path)?,
49            Stl::Binary(path) => self.write_stl_binary(path)?,
50        }
51        Ok(())
52    }
53}
54
55impl Tessellation {
56    fn for_each_facet<F>(&self, mut facet: F) -> Result<(), ErrorIO>
57    where
58        F: FnMut(&Direction<D>, [&Coordinate<D>; D]) -> Result<(), ErrorIO>,
59    {
60        self.mesh
61            .connectivities()
62            .iter()
63            .zip(self.normals.iter())
64            .try_for_each(|(connectivity, normals)| match connectivity {
65                Connectivity::Triangular(triangles) => triangles
66                    .iter()
67                    .zip(normals.iter())
68                    .try_for_each(|(nodes, normal)| {
69                        facet(normal, nodes.map(|node| &self.mesh.coordinates()[node]))
70                    }),
71                _ => panic!("STL only supports triangular blocks"),
72            })
73    }
74    fn write_stl_binary<P>(&self, path: P) -> Result<(), ErrorIO>
75    where
76        P: AsRef<Path>,
77    {
78        let mut writer = BufWriter::new(File::create(path)?);
79        writer.write_all(&[0_u8; 80])?;
80        writer.write_all(&(self.mesh.number_of_elements() as u32).to_le_bytes())?;
81        self.for_each_facet(|normal, vertices| {
82            normal.iter().try_for_each(|&component| {
83                writer.write_all(&(component.value() as f32).to_le_bytes())
84            })?;
85            vertices.iter().try_for_each(|vertex| {
86                vertex.iter().try_for_each(|&coordinate| {
87                    writer.write_all(&(coordinate.value() as f32).to_le_bytes())
88                })
89            })?;
90            writer.write_all(&0_u16.to_le_bytes())
91        })?;
92        writer.flush()
93    }
94    fn write_stl_ascii<P>(&self, path: P) -> Result<(), ErrorIO>
95    where
96        P: AsRef<Path>,
97    {
98        let mut writer = BufWriter::new(File::create(path)?);
99        writer.write_all(b"solid conspire\n")?;
100        self.for_each_facet(|normal, vertices| {
101            writeln!(
102                writer,
103                "  facet normal {} {} {}\n    outer loop",
104                normal[0].value() as f32,
105                normal[1].value() as f32,
106                normal[2].value() as f32
107            )?;
108            vertices.iter().try_for_each(|vertex| {
109                writeln!(
110                    writer,
111                    "      vertex {} {} {}",
112                    vertex[0].value() as f32,
113                    vertex[1].value() as f32,
114                    vertex[2].value() as f32
115                )
116            })?;
117            writer.write_all(b"    endloop\n  endfacet\n")
118        })?;
119        writer.write_all(b"endsolid conspire\n")?;
120        writer.flush()
121    }
122}