rust_3d/io/
ptx.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 operations of the ptx 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 points from .ptx file into IsPushable<Is3D>
37pub fn load_ptx<IP, P, R>(read: &mut R, ip: &mut IP) -> PtxResult<()>
38where
39    IP: IsPushable<P>,
40    P: IsBuildable3D + IsMatrix4Transformable,
41    R: BufRead,
42{
43    let mut i_line = 0;
44    let mut line_buffer = Vec::new();
45
46    let mut line: &[u8];
47
48    loop {
49        let columns: usize;
50        {
51            let first_line = fetch_line(read, &mut line_buffer);
52            if first_line.is_err() {
53                break;
54            }
55            i_line += 1;
56
57            columns = from_ascii(first_line.as_ref().unwrap())
58                .ok_or(PtxError::Columns)
59                .index(i_line)?;
60            // safe, since first_line being err causing break
61        }
62
63        line = fetch_line(read, &mut line_buffer).index(i_line)?;
64        i_line += 1;
65
66        let rows: usize = from_ascii(line).ok_or(PtxError::Rows).line(i_line, line)?;
67
68        // skip scanner position line
69        fetch_line(read, &mut line_buffer).index(i_line)?;
70        i_line += 1;
71
72        // skip scanner x-axis line
73        fetch_line(read, &mut line_buffer).index(i_line)?;
74        i_line += 1;
75
76        // skip scanner y-axis line
77        fetch_line(read, &mut line_buffer).index(i_line)?;
78        i_line += 1;
79
80        // skip scanner z-axis line
81        fetch_line(read, &mut line_buffer).index(i_line)?;
82        i_line += 1;
83
84        line = fetch_line(read, &mut line_buffer).index(i_line)?;
85        i_line += 1;
86        let [m11, m12, m13, m14] = read_matrix_row(line)
87            .ok_or(PtxError::Matrix)
88            .line(i_line, line)?;
89
90        line = fetch_line(read, &mut line_buffer).index(i_line)?;
91        i_line += 1;
92        let [m21, m22, m23, m24] = read_matrix_row(line)
93            .ok_or(PtxError::Matrix)
94            .line(i_line, line)?;
95
96        line = fetch_line(read, &mut line_buffer).index(i_line)?;
97        i_line += 1;
98        let [m31, m32, m33, m34] = read_matrix_row(line)
99            .ok_or(PtxError::Matrix)
100            .line(i_line, line)?;
101
102        line = fetch_line(read, &mut line_buffer).index(i_line)?;
103        i_line += 1;
104        let [m41, m42, m43, m44] = read_matrix_row(line)
105            .ok_or(PtxError::Matrix)
106            .line(i_line, line)?;
107
108        let m = Matrix4 {
109            data: [
110                [m11, m12, m13, m14],
111                [m21, m22, m23, m24],
112                [m31, m32, m33, m34],
113                [m41, m42, m43, m44],
114            ],
115        };
116
117        let must_transform = m != Matrix4::identity();
118
119        let n = columns * rows;
120
121        ip.reserve(n);
122
123        for _ in 0..n {
124            line = fetch_line(read, &mut line_buffer).index(i_line)?;
125            i_line += 1;
126
127            let mut words = to_words_skip_empty(line);
128
129            let x = words
130                .next()
131                .and_then(|w| from_ascii(w))
132                .ok_or(PtxError::Point)
133                .line(i_line, line)?;
134            let y = words
135                .next()
136                .and_then(|w| from_ascii(w))
137                .ok_or(PtxError::Point)
138                .line(i_line, line)?;
139            let z = words
140                .next()
141                .and_then(|w| from_ascii(w))
142                .ok_or(PtxError::Point)
143                .line(i_line, line)?;
144
145            let mut p = P::new(x, y, z);
146
147            if must_transform {
148                p.transform(&m)
149            }
150            ip.push(p)
151        }
152    }
153
154    Ok(())
155}
156
157//------------------------------------------------------------------------------
158
159#[inline(always)]
160fn read_matrix_row(line: &[u8]) -> Option<[f64; 4]> {
161    let mut words = to_words_skip_empty(line);
162
163    let a = from_ascii(words.next()?)?;
164    let b = from_ascii(words.next()?)?;
165    let c = from_ascii(words.next()?)?;
166    let d = from_ascii(words.next()?)?;
167
168    Some([a, b, c, d])
169}
170
171//------------------------------------------------------------------------------
172
173/// Error type for .ptx file operations
174pub enum PtxError {
175    LoadFileEndReached,
176    AccessFile,
177    Columns,
178    Rows,
179    Matrix,
180    Point,
181}
182
183/// Result type for .ptx file operations
184pub type PtxResult<T> = IOResult<T, PtxError>;
185
186impl fmt::Debug for PtxError {
187    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188        match self {
189            Self::LoadFileEndReached => write!(f, "Unexpected reach of .ptx file end"),
190            Self::AccessFile => write!(f, "Unable to access file"),
191            Self::Columns => write!(f, "Columns could not be parsed"),
192            Self::Rows => write!(f, "Rows could not be parsed"),
193            Self::Matrix => write!(f, "Transformation matrix could not be parsed"),
194            Self::Point => write!(f, "Point could not be parsed"),
195        }
196    }
197}
198
199impl fmt::Display for PtxError {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        write!(f, "{:?}", self)
202    }
203}
204
205impl From<ioError> for PtxError {
206    fn from(_error: ioError) -> Self {
207        PtxError::AccessFile
208    }
209}
210
211impl From<FetchLineError> for PtxError {
212    fn from(_error: FetchLineError) -> Self {
213        PtxError::LoadFileEndReached
214    }
215}
216
217impl From<WithLineInfo<FetchLineError>> for WithLineInfo<PtxError> {
218    fn from(other: WithLineInfo<FetchLineError>) -> Self {
219        match other {
220            WithLineInfo::<FetchLineError>::None(x) => WithLineInfo::None(PtxError::from(x)),
221            WithLineInfo::<FetchLineError>::Index(i, x) => {
222                WithLineInfo::Index(i, PtxError::from(x))
223            }
224            WithLineInfo::<FetchLineError>::Line(i, l, x) => {
225                WithLineInfo::Line(i, l, PtxError::from(x))
226            }
227        }
228    }
229}