diff --git a/src/code.rs b/src/code.rs index 43deb86..aab5ba8 100644 --- a/src/code.rs +++ b/src/code.rs @@ -1,4 +1,5 @@ use std::io::{self, Write}; +use std::ops::AddAssign; #[derive(Clone, PartialEq, Eq)] pub enum Direction { @@ -18,7 +19,34 @@ pub enum Distance { Incremental, } -pub type Program = Vec; +#[derive(Default, PartialEq, Clone)] +pub struct Program(Vec); + +impl std::ops::Deref for Program { + type Target = [GCode]; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl AddAssign for Program { + fn add_assign(&mut self, mut other: Program) { + self.0.extend(other.0.drain(..)); + } +} + +impl From> for Program { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl Program { + pub fn push(&mut self, g: GCode) { + self.0.push(g) + } +} macro_rules! write_if_some { ($w:expr, $s:expr, $v:ident) => { @@ -59,7 +87,7 @@ pub enum GCode { pub fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { use GCode::*; - let mut last_feedrate = None; + let mut last_feedrate: Option = None; for code in p.iter() { match code { RapidPositioning { x, y } => { @@ -84,15 +112,15 @@ pub fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { )) } (Some(last), Some(new)) => { - if last != new { - last_feedrate = Some(new); + if (last - *new).abs() < std::f64::EPSILON { + last_feedrate = Some(*new); Some(new) } else { None } } (None, Some(new)) => { - last_feedrate = Some(new); + last_feedrate = Some(*new); Some(new) } (Some(_), None) => None, diff --git a/src/machine.rs b/src/machine.rs index 8934728..687dbfc 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -4,8 +4,8 @@ use crate::code::*; pub struct Machine { tool_state: Option, distance_mode: Option, - pub tool_on_action: Program, - pub tool_off_action: Program, + pub tool_on_action: Vec, + pub tool_off_action: Vec, } impl Default for Machine { @@ -20,7 +20,7 @@ impl Default for Machine { } impl Machine { - pub fn tool_on(&mut self) -> Program { + pub fn tool_on(&mut self) -> Vec { if self.tool_state == Some(Tool::Off) || self.tool_state == None { self.tool_state = Some(Tool::On); self.tool_on_action.clone() @@ -29,7 +29,7 @@ impl Machine { } } - pub fn tool_off(&mut self) -> Program { + pub fn tool_off(&mut self) -> Vec { if self.tool_state == Some(Tool::On) || self.tool_state == None { self.tool_state = Some(Tool::Off); self.tool_off_action.clone() @@ -38,7 +38,7 @@ impl Machine { } } - pub fn distance(&mut self, is_absolute: bool) -> Program { + pub fn distance(&mut self, is_absolute: bool) -> Vec { if is_absolute { self.absolute() } else { @@ -46,7 +46,7 @@ impl Machine { } } - pub fn absolute(&mut self) -> Program { + pub fn absolute(&mut self) -> Vec { if self.distance_mode == Some(Distance::Incremental) || self.distance_mode == None { self.distance_mode = Some(Distance::Absolute); vec![GCode::DistanceMode(Distance::Absolute)] @@ -55,7 +55,7 @@ impl Machine { } } - pub fn incremental(&mut self) -> Program { + pub fn incremental(&mut self) -> Vec { if self.distance_mode == Some(Distance::Absolute) || self.distance_mode == None { self.distance_mode = Some(Distance::Incremental); vec![GCode::DistanceMode(Distance::Incremental)] diff --git a/src/main.rs b/src/main.rs index 9944e29..67304bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,14 +108,13 @@ impl Default for ProgramOptions { } fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> Program { - let mut p = Program::new(); + let mut p = Program::default(); let mut t = Turtle::default(); t.mach = mach; p.push(GCode::UnitsMillimeters); - p.extend(t.mach.tool_off()); - p.extend(t.move_to(true, 0.0, 0.0)); - p.extend(t.mach.tool_on()); + p += t.mach.tool_off().into(); + p += t.move_to(true, 0.0, 0.0).into(); for edge in doc.root().traverse() { let (node, is_start) = match edge { @@ -147,8 +146,10 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P ); } } - if let (ElementId::G, true) = (id, is_start) { - p.push(GCode::Named(Box::new(node.id().to_string()))); + if let ElementId::G = id { + if is_start { + p.push(GCode::Named(Box::new(node.id().to_string()))); + } } if let Some(&AttributeValue::Transform(ref trans)) = attrs.get_value(AttributeId::Transform) { @@ -167,22 +168,20 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P p.push(GCode::Named(Box::new(node.id().to_string()))); t.reset(); for segment in path.iter() { - match segment { - PathSegment::MoveTo { abs, x, y } => { - p.extend(t.move_to(*abs, *x, *y)) - } + let segment_gcode = match segment { + PathSegment::MoveTo { abs, x, y } => t.move_to(*abs, *x, *y), PathSegment::ClosePath { abs } => { // Ignore abs, should have identical effect: https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand - p.extend(t.close(None, opts.feedrate)) + t.close(None, opts.feedrate) } PathSegment::LineTo { abs, x, y } => { - p.extend(t.line(*abs, *x, *y, None, opts.feedrate)); + t.line(*abs, *x, *y, None, opts.feedrate) } PathSegment::HorizontalLineTo { abs, x } => { - p.extend(t.line(*abs, *x, None, None, opts.feedrate)); + t.line(*abs, *x, None, None, opts.feedrate) } PathSegment::VerticalLineTo { abs, y } => { - p.extend(t.line(*abs, None, *y, None, opts.feedrate)); + t.line(*abs, None, *y, None, opts.feedrate) } PathSegment::CurveTo { abs, @@ -192,22 +191,20 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P y2, x, y, - } => { - p.extend(t.cubic_bezier( - *abs, - *x1, - *y1, - *x2, - *y2, - *x, - *y, - opts.tolerance, - None, - opts.feedrate, - )); - } - PathSegment::SmoothCurveTo { abs, x2, y2, x, y } => { - p.extend(t.smooth_cubic_bezier( + } => t.cubic_bezier( + *abs, + *x1, + *y1, + *x2, + *y2, + *x, + *y, + opts.tolerance, + None, + opts.feedrate, + ), + PathSegment::SmoothCurveTo { abs, x2, y2, x, y } => t + .smooth_cubic_bezier( *abs, *x2, *y2, @@ -216,30 +213,26 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P opts.tolerance, None, opts.feedrate, - )); - } - PathSegment::Quadratic { abs, x1, y1, x, y } => { - p.extend(t.quadratic_bezier( - *abs, - *x1, - *y1, - *x, - *y, - opts.tolerance, - None, - opts.feedrate, - )); - } - PathSegment::SmoothQuadratic { abs, x, y } => { - p.extend(t.smooth_quadratic_bezier( + ), + PathSegment::Quadratic { abs, x1, y1, x, y } => t.quadratic_bezier( + *abs, + *x1, + *y1, + *x, + *y, + opts.tolerance, + None, + opts.feedrate, + ), + PathSegment::SmoothQuadratic { abs, x, y } => t + .smooth_quadratic_bezier( *abs, *x, *y, opts.tolerance, None, opts.feedrate, - )); - } + ), PathSegment::EllipticalArc { abs, rx, @@ -249,22 +242,21 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P sweep, x, y, - } => { - p.extend(t.elliptical( - *abs, - *rx, - *ry, - *x_axis_rotation, - *large_arc, - *sweep, - *x, - *y, - None, - opts.feedrate, - opts.tolerance, - )); - } - } + } => t.elliptical( + *abs, + *rx, + *ry, + *x_axis_rotation, + *large_arc, + *sweep, + *x, + *y, + None, + opts.feedrate, + opts.tolerance, + ), + }; + p += segment_gcode.into(); } } } @@ -275,13 +267,12 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P } } - p.extend(t.mach.tool_off()); - p.extend(t.mach.absolute()); + p += t.mach.tool_off().into(); + p += t.mach.absolute().into(); p.push(GCode::RapidPositioning { x: 0.0.into(), y: 0.0.into(), }); - p.extend(t.mach.tool_on()); p.push(GCode::ProgramEnd); p diff --git a/src/turtle.rs b/src/turtle.rs index f49c9e8..59df6f4 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -1,4 +1,4 @@ -use crate::code::{GCode, Program}; +use crate::code::{GCode}; use crate::machine::Machine; use lyon_geom::euclid::{Angle, Transform2D}; use lyon_geom::math::{point, vector, F64Point}; @@ -29,7 +29,7 @@ impl Default for Turtle { } impl Turtle { - pub fn move_to(&mut self, abs: bool, x: X, y: Y) -> Program + pub fn move_to(&mut self, abs: bool, x: X, y: Y) -> Vec where X: Into>, Y: Into>, @@ -64,7 +64,7 @@ impl Turtle { .collect() } - pub fn close(&mut self, z: Z, f: F) -> Program + pub fn close(&mut self, z: Z, f: F) -> Vec where Z: Into>, F: Into>, @@ -85,7 +85,7 @@ impl Turtle { .collect() } - pub fn line(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Program + pub fn line(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Vec where X: Into>, Y: Into>, @@ -129,7 +129,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Program { + ) -> Vec { let z = z.into(); let f = f.into(); let last_point = std::cell::Cell::new(self.curpos); @@ -164,7 +164,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Program + ) -> Vec where Z: Into>, F: Into>, @@ -203,7 +203,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Program + ) -> Vec where Z: Into>, F: Into>, @@ -238,7 +238,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Program + ) -> Vec where Z: Into>, F: Into>, @@ -267,7 +267,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Program + ) -> Vec where Z: Into>, F: Into>, @@ -301,7 +301,7 @@ impl Turtle { z: Z, f: F, tolerance: f64, - ) -> Program + ) -> Vec where Z: Into>, F: Into>,