From d041febfdc2e2a7d19d3e88dbe808b9ef75bbae2 Mon Sep 17 00:00:00 2001 From: Sameer Puri Date: Thu, 25 Apr 2019 11:49:22 -0500 Subject: [PATCH] Update README.md, cargo fmt, MachineCode -> GCode, separate GCode into code.rs module, fix mirrored images bug --- README.md | 6 +- src/main.rs | 205 ++++++++++++++-------------------------------------- 2 files changed, 60 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index ca609b8..4343099 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ Convert any SVG 1.1 path to gcode for a pen plotter, laser engraver, etc. ## TODO - [x] Support all path variants -- [x] Support transforms +- [x] Support group and path transforms - [ ] Biarc interpolation (G2/G3 instead of many G1) -- [ ] Units +- [x] Px, pc, in to mm +- [x] Configurable DPI for px/pc to mm ## Known bugs & whether fixed - [ ] Smooth curves should not use the control point when the previous curve is not of the same type (quadratic -> smooth cubic, cubic -> smooth quadratic) +- [x] Image coordinates mirrored in the y-axis because SVGs uses upper left corner as (0,0) while GCode uses lower left as (0,0) diff --git a/src/main.rs b/src/main.rs index 0d9d749..2c61fba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,12 +8,19 @@ extern crate lyon_geom; use std::env; use std::fs::File; -use std::io::{self, Read, Write}; +use std::io::{self, Read}; use lyon_geom::math; -use svgdom::{AttributeId, AttributeValue, ElementId, ElementType, FilterSvg, PathSegment}; +use svgdom::{AttributeId, AttributeValue, ElementId, ElementType, PathSegment}; + +mod code; + +use code::*; fn main() -> io::Result<()> { + if let Err(_) = env::var("RUST_LOG") { + env::set_var("RUST_LOG", "svg2gcode=info") + } env_logger::init(); let matches = clap_app!(svg2gcode => (version: crate_version!()) @@ -62,8 +69,8 @@ fn main() -> io::Result<()> { struct MachineOptions { tolerance: f64, feedrate: f64, - tool_on_action: Vec, - tool_off_action: Vec, + tool_on_action: Vec, + tool_off_action: Vec, dpi: f64, } @@ -72,14 +79,14 @@ impl Default for MachineOptions { Self { tolerance: 0.1, feedrate: 3000.0, - tool_on_action: vec![MachineCode::StopSpindle, MachineCode::Dwell { p: 1.5 }], + tool_on_action: vec![GCode::StopSpindle, GCode::Dwell { p: 1.5 }], tool_off_action: vec![ - MachineCode::Dwell { p: 0.1 }, - MachineCode::StartSpindle { + GCode::Dwell { p: 0.1 }, + GCode::StartSpindle { d: Direction::Clockwise, s: 40.0, }, - MachineCode::Dwell { p: 0.2 }, + GCode::Dwell { p: 0.2 }, ], dpi: 72.0, } @@ -111,13 +118,13 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { let is_absolute = std::cell::Cell::from(true); let incremental = |p: &mut Program| { if is_absolute.get() { - p.push(MachineCode::IncrementalDistanceMode); + p.push(GCode::IncrementalDistanceMode); is_absolute.set(false); } }; let absolute = |p: &mut Program| { if !is_absolute.get() { - p.push(MachineCode::AbsoluteDistanceMode); + p.push(GCode::AbsoluteDistanceMode); is_absolute.set(true); } }; @@ -129,13 +136,14 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { } }; - let mut current_transform = lyon_geom::euclid::Transform2D::default(); + 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(MachineCode::UnitsMillimeters); + p.push(GCode::UnitsMillimeters); tool_off(&mut p); - p.push(MachineCode::RapidPositioning { + p.push(GCode::RapidPositioning { x: 0.0.into(), y: 0.0.into(), }); @@ -159,14 +167,15 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { ) { let width_in_mm = length_to_mm(width, opts.dpi); let height_in_mm = length_to_mm(height, opts.dpi); - current_transform = lyon_geom::euclid::Transform2D::create_scale( - width_in_mm / width.num, - height_in_mm / height.num, - ); + current_transform = + current_transform.post_mul(&lyon_geom::euclid::Transform2D::create_scale( + width_in_mm / width.num, + height_in_mm / height.num, + )); } } if let (ElementId::G, true) = (id, is_start) { - p.push(MachineCode::Named(Box::new(node.id().to_string()))); + p.push(GCode::Named(Box::new(node.id().to_string()))); } if let Some(&AttributeValue::Transform(ref t)) = attrs.get_value(AttributeId::Transform) { if is_start { @@ -182,7 +191,7 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { match id { ElementId::Path => { if let Some(&AttributeValue::Path(ref path)) = attrs.get_value(AttributeId::D) { - p.push(MachineCode::Named(Box::new(node.id().to_string()))); + p.push(GCode::Named(Box::new(node.id().to_string()))); let mut curpos = math::point(0.0, 0.0); curpos = current_transform.transform_point(&curpos); let mut prev_ctrl = curpos; @@ -194,9 +203,12 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { let mut to = math::point(*x, *y); to = current_transform.transform_point(&to); if !*abs { - to -= math::vector(current_transform.m31, current_transform.m32); + to -= math::vector( + current_transform.m31, + current_transform.m32, + ); } - p.push(MachineCode::RapidPositioning { + p.push(GCode::RapidPositioning { x: to.x.into(), y: to.y.into(), }); @@ -216,9 +228,12 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { let mut to = math::point(*x, *y); to = current_transform.transform_point(&to); if !*abs { - to -= math::vector(current_transform.m31, current_transform.m32); + to -= math::vector( + current_transform.m31, + current_transform.m32, + ); } - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: to.x.into(), y: to.y.into(), z: None, @@ -244,9 +259,12 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { }; to = current_transform.transform_point(&to); if !*abs { - to -= math::vector(current_transform.m31, current_transform.m32); + to -= math::vector( + current_transform.m31, + current_transform.m32, + ); } - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: to.x.into(), y: to.y.into(), z: None, @@ -272,9 +290,12 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { }; to = current_transform.transform_point(&to); if !*abs { - to -= math::vector(current_transform.m31, current_transform.m32); + to -= math::vector( + current_transform.m31, + current_transform.m32, + ); } - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: to.x.into(), y: to.y.into(), z: None, @@ -318,7 +339,7 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { }; let last_point = std::cell::Cell::new(curpos); cbs.flattened(opts.tolerance).for_each(|point| { - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: point.x.into(), y: point.y.into(), z: None, @@ -351,7 +372,7 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { }; let last_point = std::cell::Cell::new(curpos); cbs.flattened(opts.tolerance).for_each(|point| { - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: point.x.into(), y: point.y.into(), z: None, @@ -377,7 +398,7 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { let qbs = lyon_geom::QuadraticBezierSegment { from, ctrl, to }; let last_point = std::cell::Cell::new(curpos); qbs.flattened(opts.tolerance).for_each(|point| { - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: point.x.into(), y: point.y.into(), z: None, @@ -402,7 +423,7 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { let qbs = lyon_geom::QuadraticBezierSegment { from, ctrl, to }; let last_point = std::cell::Cell::new(curpos); qbs.flattened(opts.tolerance).for_each(|point| { - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: point.x.into(), y: point.y.into(), z: None, @@ -451,7 +472,7 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { sarc.for_each_flattened( opts.tolerance, &mut |point: math::F64Point| { - p.push(MachineCode::LinearInterpolation { + p.push(GCode::LinearInterpolation { x: point.x.into(), y: point.y.into(), z: None, @@ -468,19 +489,19 @@ fn svg2program(doc: &svgdom::Document, opts: MachineOptions) -> Program { } } _ => { - info!("{} node with id {} is unsupported", id, node.id()); + info!("Node <{} id=\"{}\" .../> is not supported", id, node.id()); } } } } tool_off(&mut p); - p.push(MachineCode::RapidPositioning { + p.push(GCode::RapidPositioning { x: 0.0.into(), y: 0.0.into(), }); tool_on(&mut p); - p.push(MachineCode::ProgramEnd); + p.push(GCode::ProgramEnd); p } @@ -498,117 +519,3 @@ fn length_to_mm(l: svgdom::Length, dpi: f64) -> f64 { l.num * scale } - -#[derive(Clone, PartialEq, Eq)] -enum Direction { - Clockwise, - Anticlockwise, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -enum Tool { - Off, - On, -} - -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)] -enum MachineCode { - 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, - AbsoluteDistanceMode, - IncrementalDistanceMode, - Named(Box), -} - -type Program = Vec; - -fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { - use MachineCode::*; - 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")?; - } - AbsoluteDistanceMode => { - writeln!(w, "G90")?; - } - IncrementalDistanceMode => { - writeln!(w, "G91")?; - } - Named(name) => { - if name.len() > 0 { - writeln!(w, "({})", name)?; - } - } - } - } - Ok(()) -}