From 125d1facf7bfb25cfbd051150dfd9b765dc84f9f Mon Sep 17 00:00:00 2001 From: Sameer Puri Date: Thu, 25 Apr 2019 12:09:06 -0500 Subject: [PATCH] Separate out machine state management logic --- src/code.rs | 124 +++++++++++++++++++++++++++++++++++++++++++++++ src/machine.rs | 63 ++++++++++++++++++++++++ src/main.rs | 129 ++++++++++++++++++------------------------------- 3 files changed, 234 insertions(+), 82 deletions(-) create mode 100644 src/code.rs create mode 100644 src/machine.rs diff --git a/src/code.rs b/src/code.rs new file mode 100644 index 0000000..d9f02d0 --- /dev/null +++ b/src/code.rs @@ -0,0 +1,124 @@ +use std::io::{self, Write}; + +#[derive(Clone, PartialEq, Eq)] +pub enum Direction { + Clockwise, + AntiClockwise, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum Tool { + Off, + On, +} + +#[derive(Clone, PartialEq)] +pub enum Distance { + Absolute, + Incremental, +} + +pub type Program = Vec; + +macro_rules! write_if_some { + ($w:expr, $s:expr, $v:ident) => { + if let Some(v) = $v { + write!($w, $s, v) + } else { + Ok(()) + } + }; +} + +#[derive(Clone, PartialEq)] +pub enum GCode { + RapidPositioning { + x: Option, + y: Option, + }, + LinearInterpolation { + x: Option, + y: Option, + z: Option, + f: Option, + }, + Dwell { + p: f64, + }, + UnitsInches, + UnitsMillimeters, + ProgramEnd, + StartSpindle { + d: Direction, + s: f64, + }, + StopSpindle, + DistanceMode(Distance), + Named(Box), +} + +pub fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { + use GCode::*; + for code in p.iter() { + match code { + RapidPositioning { x, y } => { + if let (None, None) = (x, y) { + continue; + } + write!(w, "G0")?; + write_if_some!(w, " X{}", x)?; + write_if_some!(w, " Y{}", y)?; + writeln!(w)?; + } + LinearInterpolation { x, y, z, f } => { + if let (None, None, None, None) = (x, y, z, f) { + continue; + } + write!(w, "G1")?; + write_if_some!(w, " X{}", x)?; + write_if_some!(w, " Y{}", y)?; + write_if_some!(w, " Z{}", z)?; + write_if_some!(w, " F{}", f)?; + writeln!(w)?; + } + Dwell { p } => { + writeln!(w, "G4 P{}", p)?; + } + UnitsInches => { + writeln!(w, "G20")?; + } + UnitsMillimeters => { + writeln!(w, "G21")?; + } + ProgramEnd => { + writeln!(w, "M20")?; + } + StartSpindle { d, s } => { + let d = match d { + Direction::Clockwise => 3, + Direction::AntiClockwise => 4, + }; + writeln!(w, "M{} S{}", d, s)?; + } + StopSpindle => { + writeln!(w, "M5")?; + } + DistanceMode(mode) => { + writeln!( + w, + "G{}", + match mode { + Distance::Absolute => 90, + Distance::Incremental => 91, + } + )?; + } + Named(name) => { + if name.len() > 0 { + writeln!(w, "({})", name)?; + } + } + } + } + Ok(()) +} diff --git a/src/machine.rs b/src/machine.rs new file mode 100644 index 0000000..0a65013 --- /dev/null +++ b/src/machine.rs @@ -0,0 +1,63 @@ +use super::code::*; + +pub struct Machine { + tool_state: Tool, + distance_mode: Distance, + pub tool_on_action: Program, + pub tool_off_action: Program, +} + +impl Default for Machine { + fn default() -> Self { + Self { + tool_state: Tool::Off, + distance_mode: Distance::Absolute, + tool_on_action: vec![], + tool_off_action: vec![], + } + } +} + +impl Machine { + pub fn tool_on(&mut self, p: &mut Program) { + if self.tool_state == Tool::Off { + self.tool_on_action + .iter() + .map(Clone::clone) + .for_each(|x| p.push(x)); + } + self.tool_state = Tool::On; + } + + pub fn tool_off(&mut self, p: &mut Program) { + if self.tool_state == Tool::On { + self.tool_off_action + .iter() + .map(Clone::clone) + .for_each(|x| p.push(x)); + } + self.tool_state = Tool::Off; + } + + pub fn distance(&mut self, p: &mut Program, is_absolute: bool) { + if is_absolute { + self.absolute(p); + } else { + self.incremental(p); + } + } + + pub fn absolute(&mut self, p: &mut Program) { + if self.distance_mode == Distance::Incremental { + p.push(GCode::DistanceMode(Distance::Absolute)); + } + self.distance_mode = Distance::Absolute; + } + + pub fn incremental(&mut self, p: &mut Program) { + if self.distance_mode == Distance::Absolute { + p.push(GCode::DistanceMode(Distance::Incremental)); + } + self.distance_mode = Distance::Incremental; + } +} diff --git a/src/main.rs b/src/main.rs index 2c61fba..369e7c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,8 +14,10 @@ use lyon_geom::math; use svgdom::{AttributeId, AttributeValue, ElementId, ElementType, PathSegment}; mod code; +mod machine; use code::*; +use machine::*; fn main() -> io::Result<()> { if let Err(_) = env::var("RUST_LOG") { @@ -48,7 +50,8 @@ fn main() -> io::Result<()> { } }; - let mut opts = MachineOptions::default(); + let mut opts = ProgramOptions::default(); + let mut mach = Machine::default(); if let Some(tolerance) = matches.value_of("tolerance").and_then(|x| x.parse().ok()) { opts.tolerance = tolerance; @@ -60,94 +63,55 @@ fn main() -> io::Result<()> { opts.dpi = dpi; } + if true { + mach.tool_on_action = vec![GCode::StopSpindle, GCode::Dwell { p: 1.5 }]; + } + if true { + mach.tool_off_action = vec![ + GCode::Dwell { p: 0.1 }, + GCode::StartSpindle { + d: Direction::Clockwise, + s: 40.0, + }, + GCode::Dwell { p: 0.2 }, + ]; + } + let doc = svgdom::Document::from_str(&input).expect("Invalid or unsupported SVG file"); - let prog = svg2program(&doc, opts); + let prog = svg2program(&doc, opts, mach); program2gcode(&prog, File::create("out.gcode")?) } -struct MachineOptions { +struct ProgramOptions { tolerance: f64, feedrate: f64, - tool_on_action: Vec, - tool_off_action: Vec, dpi: f64, } -impl Default for MachineOptions { +impl Default for ProgramOptions { fn default() -> Self { - Self { + ProgramOptions { tolerance: 0.1, feedrate: 3000.0, - tool_on_action: vec![GCode::StopSpindle, GCode::Dwell { p: 1.5 }], - tool_off_action: vec![ - GCode::Dwell { p: 0.1 }, - GCode::StartSpindle { - d: Direction::Clockwise, - s: 40.0, - }, - GCode::Dwell { p: 0.2 }, - ], dpi: 72.0, } } } -fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { - let tool = std::cell::Cell::new(Tool::On); - let tool_on = |p: &mut Program| { - if tool.get() == Tool::Off { - opts.tool_on_action - .iter() - .map(Clone::clone) - .for_each(|x| p.push(x)); - tool.set(Tool::On); - } - }; - - let tool_off = |p: &mut Program| { - if tool.get() == Tool::On { - opts.tool_off_action - .iter() - .map(Clone::clone) - .for_each(|x| p.push(x)); - tool.set(Tool::Off); - } - }; - - let is_absolute = std::cell::Cell::from(true); - let incremental = |p: &mut Program| { - if is_absolute.get() { - p.push(GCode::IncrementalDistanceMode); - is_absolute.set(false); - } - }; - let absolute = |p: &mut Program| { - if !is_absolute.get() { - p.push(GCode::AbsoluteDistanceMode); - is_absolute.set(true); - } - }; - let select_mode = |p: &mut Program, abs: bool| { - if abs { - absolute(p); - } else { - incremental(p); - } - }; - +fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mut mach: Machine) -> Program { let mut current_transform = lyon_geom::euclid::Transform2D::create_scale(1.0, -1.0) .post_translate(math::vector(0.0, 2.0)); let mut transform_stack = vec![]; let mut p = Program::new(); p.push(GCode::UnitsMillimeters); - tool_off(&mut p); + mach.tool_off(&mut p); p.push(GCode::RapidPositioning { x: 0.0.into(), y: 0.0.into(), }); - tool_on(&mut p); + mach.tool_on(&mut p); for edge in doc.root().traverse() { let (node, is_start) = match edge { @@ -198,8 +162,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { for segment in path.iter() { match segment { PathSegment::MoveTo { abs, x, y } => { - tool_off(&mut p); - select_mode(&mut p, *abs); + mach.tool_off(&mut p); + mach.distance(&mut p, *abs); let mut to = math::point(*x, *y); to = current_transform.transform_point(&to); if !*abs { @@ -220,11 +184,11 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { prev_ctrl = curpos; } PathSegment::ClosePath { abs } => { - tool_off(&mut p); + mach.tool_off(&mut p); } PathSegment::LineTo { abs, x, y } => { - tool_on(&mut p); - select_mode(&mut p, *abs); + mach.tool_on(&mut p); + mach.distance(&mut p, *abs); let mut to = math::point(*x, *y); to = current_transform.transform_point(&to); if !*abs { @@ -247,8 +211,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { prev_ctrl = curpos; } PathSegment::HorizontalLineTo { abs, x } => { - tool_on(&mut p); - select_mode(&mut p, *abs); + mach.tool_on(&mut p); + mach.distance(&mut p, *abs); let mut to = if *abs { let inv_transform = current_transform .inverse() @@ -278,8 +242,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { prev_ctrl = curpos; } PathSegment::VerticalLineTo { abs, y } => { - tool_on(&mut p); - select_mode(&mut p, *abs); + mach.tool_on(&mut p); + mach.distance(&mut p, *abs); let mut to = if *abs { let inv_transform = current_transform .inverse() @@ -317,8 +281,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { x, y, } => { - tool_on(&mut p); - absolute(&mut p); + mach.tool_on(&mut p); + mach.absolute(&mut p); let from = curpos; let mut ctrl1 = math::point(*x1, *y1); ctrl1 = current_transform.transform_point(&ctrl1); @@ -351,8 +315,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { prev_ctrl = ctrl1; } PathSegment::SmoothCurveTo { abs, x2, y2, x, y } => { - tool_on(&mut p); - absolute(&mut p); + mach.tool_on(&mut p); + mach.absolute(&mut p); let from = curpos; let mut ctrl1 = prev_ctrl; let mut ctrl2 = math::point(*x2, *y2); @@ -384,8 +348,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { prev_ctrl = ctrl1; } PathSegment::Quadratic { abs, x1, y1, x, y } => { - tool_on(&mut p); - absolute(&mut p); + mach.tool_on(&mut p); + mach.absolute(&mut p); let from = curpos; let mut ctrl = math::point(*x1, *y1); ctrl = current_transform.transform_point(&ctrl); @@ -410,8 +374,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { prev_ctrl = ctrl; } PathSegment::SmoothQuadratic { abs, x, y } => { - tool_on(&mut p); - absolute(&mut p); + mach.tool_on(&mut p); + mach.absolute(&mut p); let from = curpos; let mut ctrl = prev_ctrl; let mut to = math::point(*x, *y); @@ -444,8 +408,8 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { x, y, } => { - tool_on(&mut p); - absolute(&mut p); + mach.tool_on(&mut p); + mach.absolute(&mut p); let from = curpos; let mut to = math::point(*x, *y); to = current_transform.transform_point(&to); @@ -495,12 +459,13 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { } } - tool_off(&mut p); + mach.tool_off(&mut p); + mach.absolute(&mut p); p.push(GCode::RapidPositioning { x: 0.0.into(), y: 0.0.into(), }); - tool_on(&mut p); + mach.tool_on(&mut p); p.push(GCode::ProgramEnd); p