Skip to main content

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

1#[cfg(test)]
2mod test;
3
4use crate::{
5    geometry::{
6        Coordinate, Coordinates, Direction, Directions,
7        mesh::{
8            Connectivity,
9            tessellation::{D, Tessellation},
10        },
11    },
12    io::invalid,
13    math::{Tensor, TensorVec},
14};
15use std::{
16    cell::OnceCell,
17    collections::HashMap,
18    fs::File,
19    io::{BufReader, Error as ErrorIO, Read, Seek, SeekFrom},
20    path::Path,
21    str::SplitWhitespace,
22};
23
24impl TryFrom<&Path> for Tessellation {
25    type Error = ErrorIO;
26    fn try_from(path: &Path) -> Result<Self, Self::Error> {
27        let mut file = File::open(path)?;
28        if let Some(triangle_count) = binary_triangle_count(&mut file)? {
29            read_binary(BufReader::new(file), triangle_count)
30        } else {
31            file.seek(SeekFrom::Start(0))?;
32            let mut contents = String::new();
33            BufReader::new(file).read_to_string(&mut contents)?;
34            read_ascii(&contents)
35        }
36    }
37}
38
39fn binary_triangle_count(file: &mut File) -> Result<Option<usize>, ErrorIO> {
40    let length = file.metadata()?.len();
41    if length < 84 {
42        return Ok(None);
43    }
44    let mut header = [0u8; 84];
45    file.read_exact(&mut header)?;
46    let triangle_count = u32::from_le_bytes([header[80], header[81], header[82], header[83]]);
47    Ok((84 + 50 * triangle_count as u64 == length).then_some(triangle_count as usize))
48}
49
50fn read_binary<R: Read>(mut reader: R, triangle_count: usize) -> Result<Tessellation, ErrorIO> {
51    let mut builder = Builder::with_capacity(triangle_count);
52    (0..triangle_count).try_for_each(|_| {
53        let normal = read_vec3_f32(&mut reader)?;
54        let vertices = [
55            read_vec3_f32(&mut reader)?,
56            read_vec3_f32(&mut reader)?,
57            read_vec3_f32(&mut reader)?,
58        ];
59        let mut attribute_byte_count = [0u8; 2];
60        reader.read_exact(&mut attribute_byte_count)?;
61        builder.push(normal, vertices);
62        Ok::<(), ErrorIO>(())
63    })?;
64    Ok(builder.into())
65}
66
67fn read_ascii(contents: &str) -> Result<Tessellation, ErrorIO> {
68    let mut builder = Builder::with_capacity(contents.len() / 200);
69    let mut tokens = contents.split_whitespace();
70    while let Some(token) = tokens.next() {
71        if token == "facet" {
72            expect(&mut tokens, "normal")?;
73            let normal = read_vec3_ascii(&mut tokens)?;
74            expect(&mut tokens, "outer")?;
75            expect(&mut tokens, "loop")?;
76            let mut vertices = [[0.0; D]; 3];
77            vertices.iter_mut().try_for_each(|vertex| {
78                expect(&mut tokens, "vertex")?;
79                *vertex = read_vec3_ascii(&mut tokens)?;
80                Ok::<(), ErrorIO>(())
81            })?;
82            expect(&mut tokens, "endloop")?;
83            expect(&mut tokens, "endfacet")?;
84            builder.push(normal, vertices);
85        }
86    }
87    Ok(builder.into())
88}
89
90fn expect(tokens: &mut SplitWhitespace, keyword: &str) -> Result<(), ErrorIO> {
91    match tokens.next() {
92        Some(token) if token.eq_ignore_ascii_case(keyword) => Ok(()),
93        token => Err(invalid(format!(
94            "Expected {keyword} but found {}",
95            token.unwrap_or("end of file")
96        ))),
97    }
98}
99
100fn read_vec3_ascii(tokens: &mut SplitWhitespace) -> Result<[f64; D], ErrorIO> {
101    let mut vector = [0.0; D];
102    vector.iter_mut().try_for_each(|entry| {
103        match tokens.next() {
104            Some(token) => {
105                *entry = token
106                    .parse()
107                    .map_err(|_| invalid(format!("Expected a number but found {token}")))?
108            }
109            None => {
110                return Err(invalid(
111                    "Expected a number but found end of file".to_string(),
112                ));
113            }
114        }
115        Ok(())
116    })?;
117    Ok(vector)
118}
119
120fn read_vec3_f32<R: Read>(reader: &mut R) -> Result<[f64; D], ErrorIO> {
121    Ok([read_f32(reader)?, read_f32(reader)?, read_f32(reader)?])
122}
123
124fn read_f32<R: Read>(reader: &mut R) -> Result<f64, ErrorIO> {
125    let mut bytes = [0u8; 4];
126    reader.read_exact(&mut bytes)?;
127    Ok(f32::from_le_bytes(bytes) as f64)
128}
129
130struct Builder {
131    connectivity: Vec<[usize; D]>,
132    normals: Directions<D>,
133    vertex_map: HashMap<[u64; D], usize>,
134    vertices: Coordinates<D>,
135}
136
137impl Builder {
138    fn with_capacity(triangle_count: usize) -> Self {
139        Self {
140            connectivity: Vec::with_capacity(triangle_count),
141            normals: Directions::with_capacity(triangle_count),
142            vertex_map: HashMap::with_capacity(D * triangle_count),
143            vertices: Coordinates::with_capacity(D * triangle_count),
144        }
145    }
146    fn push(&mut self, normal: [f64; D], vertices: [[f64; D]; 3]) {
147        let nodes = [
148            self.dedup(vertices[0]),
149            self.dedup(vertices[1]),
150            self.dedup(vertices[2]),
151        ];
152        self.connectivity.push(nodes);
153        self.normals.push(Direction::const_from(normal));
154    }
155    fn dedup(&mut self, vertex: [f64; D]) -> usize {
156        let key = vertex.map(|entry| entry.to_bits());
157        if let Some(&index) = self.vertex_map.get(&key) {
158            index
159        } else {
160            let index = self.vertices.len();
161            self.vertices.push(Coordinate::const_from(vertex));
162            self.vertex_map.insert(key, index);
163            index
164        }
165    }
166}
167
168impl From<Builder> for Tessellation {
169    fn from(builder: Builder) -> Self {
170        let Builder {
171            connectivity,
172            normals,
173            vertices,
174            ..
175        } = builder;
176        Self {
177            mesh: (
178                vec![Connectivity::Triangular(connectivity.into())],
179                vertices,
180            )
181                .into(),
182            normals: vec![normals].into(),
183            bvh: OnceCell::new(),
184            features: OnceCell::new(),
185        }
186    }
187}