rust_3d/io/
off.rs

1/*
2Copyright 2020 Martin Buck
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation the
7rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the Software
9is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall
12be included all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23//! Module for IO of the off file format
24
25use crate::*;
26
27use std::{
28    fmt,
29    io::{BufRead, Error as ioError},
30};
31
32use super::{types::*, utils::*};
33
34//------------------------------------------------------------------------------
35
36/// Loads an IsMesh3D from the off file format
37pub fn load_off_mesh<EM, P, R>(read: &mut R, mesh: &mut EM) -> OffResult<()>
38where
39    EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
40    P: IsBuildable3D + Clone,
41    R: BufRead,
42{
43    let mut line_buffer = Vec::new();
44    let mut i_line = 0;
45
46    let mut off_seen = false;
47    let mut counts = None;
48
49    while let Ok(line) = fetch_line(read, &mut line_buffer) {
50        i_line += 1;
51
52        if !off_seen && line.starts_with(b"OFF") {
53            off_seen = true;
54            continue;
55        }
56
57        if line.is_empty() || line.starts_with(b"#") {
58            continue;
59        }
60
61        if counts.is_none() {
62            let mut words = to_words_skip_empty(line);
63            let n_vertices = words
64                .next()
65                .and_then(|word| from_ascii(word))
66                .ok_or(OffError::VertexCount)
67                .line(i_line, line)?;
68            let n_faces = words
69                .next()
70                .and_then(|word| from_ascii(word))
71                .ok_or(OffError::FaceCount)
72                .line(i_line, line)?;
73
74            mesh.reserve_vertices(n_vertices);
75            mesh.reserve_faces(n_faces);
76
77            counts = Some([n_vertices, n_faces]);
78            continue;
79        }
80
81        // safe since checked above
82        if mesh.num_vertices() < counts.unwrap()[0] {
83            let mut words = to_words_skip_empty(line);
84
85            let x = words
86                .next()
87                .and_then(|word| from_ascii(word))
88                .ok_or(OffError::Vertex)
89                .line(i_line, line)?;
90
91            let y = words
92                .next()
93                .and_then(|word| from_ascii(word))
94                .ok_or(OffError::Vertex)
95                .line(i_line, line)?;
96
97            let z = words
98                .next()
99                .and_then(|word| from_ascii(word))
100                .ok_or(OffError::Vertex)
101                .line(i_line, line)?;
102
103            mesh.add_vertex(P::new(x, y, z));
104        } else {
105            let mut words = to_words_skip_empty(line);
106
107            let count_face = words
108                .next()
109                .ok_or(OffError::FaceVertexCount)
110                .line(i_line, line)?;
111
112            if count_face == b"3" {
113                let a = words
114                    .next()
115                    .and_then(|word| from_ascii(word))
116                    .ok_or(OffError::Face)
117                    .line(i_line, line)?;
118
119                let b = words
120                    .next()
121                    .and_then(|word| from_ascii(word))
122                    .ok_or(OffError::Face)
123                    .line(i_line, line)?;
124
125                let c = words
126                    .next()
127                    .and_then(|word| from_ascii(word))
128                    .ok_or(OffError::Face)
129                    .line(i_line, line)?;
130
131                mesh.try_add_connection(VId { val: a }, VId { val: b }, VId { val: c })
132                    .or(Err(OffError::InvalidMeshIndices).line(i_line, line))?;
133            }
134        }
135    }
136
137    Ok(())
138}
139
140/// Loads IsPushable<Is3D> from the .off file format
141pub fn load_off_points<IP, P, R>(read: &mut R, ip: &mut IP) -> OffResult<()>
142where
143    IP: IsPushable<P>,
144    P: IsBuildable3D,
145    R: BufRead,
146{
147    let mut line_buffer = Vec::new();
148    let mut i_line = 0;
149
150    let mut off_seen = false;
151    let mut n_vertices = None;
152    let mut n_added = 0;
153
154    while let Ok(line) = fetch_line(read, &mut line_buffer) {
155        i_line += 1;
156
157        if !off_seen && line.starts_with(b"OFF") {
158            off_seen = true;
159            continue;
160        }
161
162        if line.is_empty() || line.starts_with(b"#") {
163            continue;
164        }
165
166        if n_vertices.is_none() {
167            let mut words = to_words_skip_empty(line);
168            n_vertices = Some(
169                words
170                    .next()
171                    .and_then(|word| from_ascii(word))
172                    .ok_or(OffError::VertexCount)
173                    .line(i_line, line)?,
174            );
175            ip.reserve(n_vertices.unwrap());
176
177            continue;
178        }
179
180        // safe since checked above
181        if n_added < n_vertices.unwrap() {
182            let mut words = to_words_skip_empty(line);
183
184            let x = words
185                .next()
186                .and_then(|word| from_ascii(word))
187                .ok_or(OffError::Vertex)
188                .line(i_line, line)?;
189
190            let y = words
191                .next()
192                .and_then(|word| from_ascii(word))
193                .ok_or(OffError::Vertex)
194                .line(i_line, line)?;
195
196            let z = words
197                .next()
198                .and_then(|word| from_ascii(word))
199                .ok_or(OffError::Vertex)
200                .line(i_line, line)?;
201
202            ip.push(P::new(x, y, z));
203            n_added += 1;
204        } else {
205            break;
206        }
207    }
208
209    Ok(())
210}
211
212//------------------------------------------------------------------------------
213
214/// Error type for .off file operations
215pub enum OffError {
216    AccessFile,
217    InvalidMeshIndices,
218    VertexCount,
219    FaceCount,
220    Vertex,
221    Face,
222    FaceVertexCount,
223}
224
225/// Result type for .off file operations
226pub type OffResult<T> = IOResult<T, OffError>;
227
228impl fmt::Debug for OffError {
229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230        match self {
231            Self::AccessFile => write!(f, "Unable to access file"),
232            Self::VertexCount => write!(f, "Unable to parse vertex count"),
233            Self::FaceCount => write!(f, "Unable to parse face count"),
234            Self::Vertex => write!(f, "Unable to parse vertex"),
235            Self::Face => write!(f, "Unable to parse face"),
236            Self::FaceVertexCount => write!(f, "Unable to parse vertex count of face"),
237            Self::InvalidMeshIndices => write!(f, "File contains invalid mesh indices"),
238        }
239    }
240}
241
242impl fmt::Display for OffError {
243    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244        write!(f, "{:?}", self)
245    }
246}
247
248impl From<ioError> for OffError {
249    fn from(_error: ioError) -> Self {
250        OffError::AccessFile
251    }
252}