truck_polymesh/
stl.rs

1use crate::*;
2use bytemuck::{Pod, Zeroable};
3use rustc_hash::FxHashMap as HashMap;
4use std::io::{BufRead, BufReader, Lines, Read, Write};
5
6const FACESIZE: usize = size_of::<StlFace>();
7const CHUNKSIZE: usize = FACESIZE + 2;
8
9type Vertex = StandardVertex;
10type Result<T> = std::result::Result<T, errors::Error>;
11
12fn syntax_error() -> std::io::Error {
13    std::io::Error::new(std::io::ErrorKind::InvalidData, "syntax error")
14}
15
16/// STL naive mesh.
17#[repr(C)]
18#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Pod, Zeroable)]
19pub struct StlFace {
20    /// normal vector
21    pub normal: [f32; 3],
22    /// the positions of vertices
23    pub vertices: [[f32; 3]; 3],
24}
25
26impl StlFace {
27    #[inline(always)]
28    fn is_empty(&self) -> bool { self == &StlFace::default() }
29}
30
31/// STL reading iterator.
32#[derive(Debug)]
33pub enum StlReader<R: Read> {
34    #[doc(hidden)]
35    Ascii(Lines<BufReader<R>>),
36    #[doc(hidden)]
37    Binary(R, usize),
38}
39
40/// STL type.
41#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
42pub enum StlType {
43    /// Determine STL type automatically.
44    ///
45    /// # Reading
46    /// If the first 5 bytes are..
47    /// - "solid" => ascii format
48    /// - otherwise => binary format
49    ///
50    /// # Writing
51    /// Always binary format.
52    #[default]
53    Automatic,
54    /// ASCII format.
55    Ascii,
56    /// Binary format.
57    Binary,
58}
59
60impl<R: Read> StlReader<R> {
61    #[inline(always)]
62    fn text_reader(reader: R) -> StlReader<R> { StlReader::Ascii(BufReader::new(reader).lines()) }
63    fn binary_reader(mut reader: R, header_judge: bool) -> Result<StlReader<R>> {
64        let mut header = [0; 5];
65        reader.read_exact(&mut header)?;
66        if header_judge && &header == b"solid" {
67            return Ok(Self::text_reader(reader));
68        }
69        let mut header = [0; 75];
70        reader.read_exact(&mut header)?;
71        let mut length_bytes = [0; 4];
72        reader.read_exact(&mut length_bytes)?;
73        let length = u32::from_le_bytes(length_bytes) as usize;
74        Ok(StlReader::Binary(reader, length))
75    }
76    /// Creates new STL reader.
77    #[inline(always)]
78    pub fn new(reader: R, stl_type: StlType) -> Result<Self> {
79        match stl_type {
80            StlType::Automatic => Self::binary_reader(reader, true),
81            StlType::Binary => Self::binary_reader(reader, false),
82            StlType::Ascii => Ok(Self::text_reader(reader)),
83        }
84    }
85    /// Returns the STL type.
86    #[inline(always)]
87    pub fn stl_type(&self) -> StlType {
88        match self {
89            StlReader::Ascii(_) => StlType::Ascii,
90            StlReader::Binary(_, _) => StlType::Binary,
91        }
92    }
93}
94
95impl<R: Read> Iterator for StlReader<R> {
96    type Item = Result<StlFace>;
97    fn next(&mut self) -> Option<Self::Item> {
98        let res = match self {
99            StlReader::Binary(reader, length) => {
100                if *length == 0 {
101                    Ok(None)
102                } else {
103                    *length -= 1;
104                    binary_one_read(reader)
105                }
106            }
107            StlReader::Ascii(lines) => ascii_one_read(lines),
108        };
109        match res {
110            Ok(Some(got)) => Some(Ok(got)),
111            Ok(None) => None,
112            Err(error) => Some(Err(error)),
113        }
114    }
115}
116
117fn ascii_one_read<R: BufRead>(lines: &mut Lines<R>) -> Result<Option<StlFace>> {
118    let mut face = StlFace::default();
119    let mut num_ver = 0;
120    loop {
121        let line = match lines.next() {
122            Some(got) => got?,
123            None => match face.is_empty() {
124                true => return Ok(None),
125                false => return Err(syntax_error().into()),
126            },
127        };
128        let line = line.trim();
129        if line.len() < 8 {
130            continue;
131        } else if &line[0..5] == "facet" {
132            let args: Vec<_> = line.split_whitespace().collect();
133            face.normal = array![i => args[i + 2].parse::<f32>()?; 3];
134        } else if &line[0..6] == "vertex" {
135            let args: Vec<_> = line.split_whitespace().collect();
136            if num_ver > 2 {
137                return Err(syntax_error().into());
138            }
139            face.vertices[num_ver] = array![i => args[i + 1].parse::<f32>()?; 3];
140            num_ver += 1;
141        } else if &line[0..8] == "endfacet" {
142            if num_ver != 3 {
143                return Err(syntax_error().into());
144            }
145            return Ok(Some(face));
146        }
147    }
148}
149
150fn binary_one_read<R: Read>(reader: &mut R) -> Result<Option<StlFace>> {
151    let mut chunk = [0; CHUNKSIZE];
152    let size = reader.read(&mut chunk)?;
153    if size == CHUNKSIZE {
154        let mut buf = [0; FACESIZE];
155        buf.copy_from_slice(&chunk[..FACESIZE]);
156        Ok(Some(bytemuck::cast(buf)))
157    } else {
158        Err(syntax_error().into())
159    }
160}
161
162/// Write STL file in `stl_type` format.
163///
164/// If `stl_type == StlType::Automatic`, write the binary format.
165#[inline(always)]
166pub fn write<I: IntoStlIterator, W: Write>(
167    iter: I,
168    writer: &mut W,
169    stl_type: StlType,
170) -> Result<()> {
171    match stl_type {
172        StlType::Ascii => write_ascii(iter, writer),
173        _ => write_binary(iter, writer),
174    }
175}
176
177/// Writes ASCII STL data.
178fn write_ascii<I: IntoStlIterator, W: Write>(iter: I, writer: &mut W) -> Result<()> {
179    let mut iter = iter.into_iter();
180    writer.write_all(b"solid\n")?;
181    iter.try_for_each::<_, Result<()>>(|face| {
182        writer.write_fmt(format_args!(
183            "  facet normal {:e} {:e} {:e}\n",
184            face.normal[0], face.normal[1], face.normal[2]
185        ))?;
186        writer.write_all(b"    outer loop\n")?;
187        face.vertices.iter().try_for_each(|pt| {
188            writer.write_fmt(format_args!(
189                "      vertex {:e} {:e} {:e}\n",
190                pt[0], pt[1], pt[2]
191            ))
192        })?;
193        writer.write_all(b"    endloop\n  endfacet\n")?;
194        Ok(())
195    })?;
196    writer.write_all(b"endsolid\n")?;
197    Ok(())
198}
199
200/// Writes binary STL data.
201#[inline(always)]
202fn write_binary<I: IntoStlIterator, W: Write>(iter: I, writer: &mut W) -> Result<()> {
203    let mut iter = iter.into_iter();
204    let len = iter.len() as u32;
205    writer.write_all(&[0u8; 80])?;
206    writer.write_all(&len.to_le_bytes())?;
207    iter.try_for_each(|face| {
208        writer.write_all(bytemuck::cast_slice(&[face]))?;
209        writer.write_all(&[0u8, 0u8])?;
210        Ok(())
211    })
212}
213
214/// By implementing [`IntoStlIterator`] for a type you define how it will be
215/// converted to an iterator.
216///
217/// This is common for types which describe a collection of some kind.
218pub trait IntoStlIterator {
219    /// Which kind of iterator are we turning this into?
220    type IntoIter: ExactSizeIterator<Item = StlFace>;
221    /// Creates an iterator from a value.
222    fn into_iter(self) -> Self::IntoIter;
223}
224
225/// Generate an STL faces from from a [`PolygonMesh`].
226#[derive(Debug)]
227pub struct PolygonMeshStlFaceIterator<'a> {
228    positions: &'a Vec<Point3>,
229    faces: faces::TriangleIterator<'a, Vertex>,
230    len: usize,
231}
232
233impl<'a> Iterator for PolygonMeshStlFaceIterator<'a> {
234    type Item = StlFace;
235    fn next(&mut self) -> Option<StlFace> {
236        self.faces.next().map(|face| {
237            let p = array![i => self.positions[face[i].pos]; 3];
238            let n = (p[1] - p[0]).cross(p[2] - p[0]).normalize();
239            let normal = n.cast().unwrap().into();
240            let vertices = array![i => p[i].cast().unwrap().into(); 3];
241            StlFace { normal, vertices }
242        })
243    }
244    #[inline(always)]
245    fn size_hint(&self) -> (usize, Option<usize>) { (self.len, Some(self.len)) }
246}
247
248impl<'a> ExactSizeIterator for PolygonMeshStlFaceIterator<'a> {}
249
250impl<'a> IntoStlIterator for &'a PolygonMesh {
251    type IntoIter = PolygonMeshStlFaceIterator<'a>;
252    fn into_iter(self) -> Self::IntoIter {
253        let iter = self.faces().triangle_iter();
254        Self::IntoIter {
255            positions: self.positions(),
256            len: iter.len(),
257            faces: iter,
258        }
259    }
260}
261
262impl<I> IntoStlIterator for I
263where
264    I: IntoIterator<Item = StlFace>,
265    I::IntoIter: ExactSizeIterator,
266{
267    type IntoIter = I::IntoIter;
268    fn into_iter(self) -> I::IntoIter { self.into_iter() }
269}
270
271fn signup_vector(vector: [f32; 3], map: &mut HashMap<[i64; 3], usize>) -> usize {
272    let vector = array![i =>
273        ((vector[i] as f64 + TOLERANCE * 0.25) / (TOLERANCE * 0.5)) as i64; 3];
274    let len = map.len();
275    *map.entry(vector).or_insert_with(|| len)
276}
277
278fn decode_vector<T: From<[f64; 3]>>((code, _): ([i64; 3], usize)) -> T {
279    array![i => code[i] as f64 * TOLERANCE * 0.5; 3].into()
280}
281
282impl FromIterator<StlFace> for PolygonMesh {
283    fn from_iter<I: IntoIterator<Item = StlFace>>(iter: I) -> PolygonMesh {
284        let mut positions = HashMap::<[i64; 3], usize>::default();
285        let mut normals = HashMap::<[i64; 3], usize>::default();
286        let closure = |face: StlFace| {
287            let n = signup_vector(face.normal, &mut normals);
288            let p = array![i => signup_vector(face.vertices[i], &mut positions); 3];
289            array![i => (p[i], None, Some(n)).into(); 3]
290        };
291        let faces: Vec<[Vertex; 3]> = iter.into_iter().map(closure).collect();
292        let faces = Faces::from_tri_and_quad_faces(faces, Vec::new());
293        let mut positions: Vec<([i64; 3], usize)> = positions.into_iter().collect();
294        positions.sort_by(|a, b| a.1.cmp(&b.1));
295        let positions: Vec<Point3> = positions.into_iter().map(decode_vector).collect();
296        let mut normals: Vec<([i64; 3], usize)> = normals.into_iter().collect();
297        normals.sort_by(|a, b| a.1.cmp(&b.1));
298        let normals: Vec<Vector3> = normals.into_iter().map(decode_vector).collect();
299        PolygonMesh::debug_new(
300            StandardAttributes {
301                positions,
302                uv_coords: Vec::new(),
303                normals,
304            },
305            faces,
306        )
307    }
308}
309
310/// Read STL file and parse to [`PolygonMesh`].
311#[inline(always)]
312pub fn read<R: Read>(reader: R, stl_type: StlType) -> Result<PolygonMesh> {
313    StlReader::new(reader, stl_type)?.collect()
314}