rust_3d/io/
gcode.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 gcode 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 a IsPushable<Is3D> as x y z coordinates from gcode
37pub fn load_gcode_points<IP, P, R>(read: &mut R, ip: &mut IP) -> GcodeResult<()>
38where
39    IP: IsPushable<P>,
40    P: IsBuildable3D,
41    R: BufRead,
42{
43    let mut line_buffer = Vec::new();
44
45    let mut i_line = 0;
46
47    let mut start_pushed = false;
48    let mut ra = RelativeAbsolute::Absolute;
49    let mut x = 0.0;
50    let mut y = 0.0;
51    let mut z = 0.0;
52
53    while let Ok(line) = fetch_line(read, &mut line_buffer) {
54        i_line += 1;
55
56        if line.len() >= 4 && line[0] == b'G' {
57            if line[2] == b' ' && (line[1] == b'1' || line[1] == b'2' || line[1] == b'3') {
58                // Move according to absolute/relative
59                let mut any_changed = false;
60                let [opt_x, opt_y, opt_z] = command(&line[3..])
61                    .ok_or(GcodeError::Command)
62                    .line(i_line, line)?;
63
64                if let Some(new_x) = opt_x {
65                    any_changed = true;
66                    match ra {
67                        RelativeAbsolute::Absolute => x = new_x,
68                        RelativeAbsolute::Relative => x += new_x,
69                    }
70                }
71
72                if let Some(new_y) = opt_y {
73                    any_changed = true;
74                    match ra {
75                        RelativeAbsolute::Absolute => y = new_y,
76                        RelativeAbsolute::Relative => y += new_y,
77                    }
78                }
79
80                if let Some(new_z) = opt_z {
81                    any_changed = true;
82                    match ra {
83                        RelativeAbsolute::Absolute => z = new_z,
84                        RelativeAbsolute::Relative => z += new_z,
85                    }
86                }
87
88                if any_changed {
89                    if !start_pushed {
90                        ip.push(P::new(0.0, 0.0, 0.0));
91                        start_pushed = true
92                    }
93                    ip.push(P::new(x, y, z));
94                }
95            } else if line[1] == b'9' && line[3] == b' ' {
96                // G9x
97                if line[2] == b'0' {
98                    // G90
99                    ra = RelativeAbsolute::Absolute;
100                } else if line[2] == b'1' {
101                    // G91
102                    ra = RelativeAbsolute::Relative;
103                } else if line[2] == b'2' {
104                    // G92
105                    // Move according absolute
106                    let mut any_changed = false;
107                    let [opt_x, opt_y, opt_z] = command(&line[4..])
108                        .ok_or(GcodeError::Command)
109                        .line(i_line, line)?;
110
111                    if let Some(new_x) = opt_x {
112                        any_changed = true;
113                        x = new_x
114                    }
115
116                    if let Some(new_y) = opt_y {
117                        any_changed = true;
118                        y = new_y
119                    }
120
121                    if let Some(new_z) = opt_z {
122                        any_changed = true;
123                        z = new_z
124                    }
125
126                    if any_changed {
127                        if !start_pushed {
128                            ip.push(P::new(0.0, 0.0, 0.0));
129                            start_pushed = true
130                        }
131                        ip.push(P::new(x, y, z));
132                    }
133                }
134            }
135        }
136    }
137
138    Ok(())
139}
140
141//------------------------------------------------------------------------------
142
143fn command(line: &[u8]) -> Option<[Option<f64>; 3]> {
144    let mut n_found = 0;
145    let mut x = None;
146    let mut y = None;
147    let mut z = None;
148    let words = line.split(|x| *x == b' ');
149
150    for word in words {
151        if n_found == 3 {
152            break;
153        }
154
155        let n = word.len();
156
157        if n == 0 {
158            continue;
159        }
160
161        if word[0] == b';' {
162            break;
163        }
164
165        if n < 2 {
166            continue;
167        }
168
169        match word[0] {
170            b'X' => {
171                x = Some(from_ascii(&word[1..])?);
172                n_found += 1
173            }
174            b'Y' => {
175                y = Some(from_ascii(&word[1..])?);
176                n_found += 1
177            }
178            b'Z' => {
179                z = Some(from_ascii(&word[1..])?);
180                n_found += 1
181            }
182            _ => (),
183        }
184    }
185
186    Some([x, y, z])
187}
188
189enum RelativeAbsolute {
190    Relative,
191    Absolute,
192}
193
194/// Error type for .gcode file operations
195pub enum GcodeError {
196    AccessFile,
197    Command,
198}
199
200/// Result type for .gcode file operations
201pub type GcodeResult<T> = IOResult<T, GcodeError>;
202
203impl fmt::Debug for GcodeError {
204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205        match self {
206            Self::Command => write!(f, "Unable to parse command"),
207            Self::AccessFile => write!(f, "Unable to access file"),
208        }
209    }
210}
211
212impl fmt::Display for GcodeError {
213    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214        write!(f, "{:?}", self)
215    }
216}
217
218impl From<ioError> for GcodeError {
219    fn from(_error: ioError) -> Self {
220        GcodeError::AccessFile
221    }
222}