From c04ba6b625037463a94da557b7c3f0596bcca2bb Mon Sep 17 00:00:00 2001 From: Sameer Puri Date: Wed, 1 May 2019 23:31:30 -0500 Subject: [PATCH] Support custom tool on/off gcode, use uom for unit conversions, rename Named=>Comment after reviewing NIST spec, update dependencies --- Cargo.lock | 42 ++++++++++++++++++++++++---------- Cargo.toml | 4 +++- README.md | 1 + src/code.rs | 19 +++++++++++----- src/machine.rs | 18 +++++++++++++-- src/main.rs | 61 +++++++++++++++++++++++--------------------------- src/turtle.rs | 8 +++++++ 7 files changed, 100 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6bdc5f..b9d7418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,7 +29,7 @@ name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -84,9 +84,9 @@ name = "euclid_macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.29 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.33 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -112,7 +112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.51" +version = "0.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -171,7 +171,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -187,7 +187,7 @@ name = "quote" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -259,7 +259,9 @@ dependencies = [ "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lyon_geom 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "svgdom 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uom 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -285,10 +287,10 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.32" +version = "0.15.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.29 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -306,7 +308,7 @@ name = "termion" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -328,6 +330,11 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "typenum" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ucd-util" version = "0.1.3" @@ -343,6 +350,15 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "uom" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "utf8-ranges" version = "1.0.2" @@ -408,7 +424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)" = "ec350a9417dfd244dc9a6c4a71e13895a4db6b92f0b106f07ebbc3f3bc580cee" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lyon_geom 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0ea0ba5f8d2d91d6d895aca54d1ec0d84ddfa4826f33fbfe8abb39f08f9e4153" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" @@ -417,7 +433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "ba92c84f814b3f9a44c5cfca7d2ad77fa10710867d2bbb1b3d175ab5f47daa12" +"checksum proc-macro2 0.4.29 (registry+https://github.com/rust-lang/crates.io-index)" = "64c827cea7a7ab30ce4593e5e04d7a11617ad6ece2fa230605a78b00ff965316" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" @@ -431,14 +447,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum svgdom 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ffe3c59d84b307fc361bbf0baff2aa6f6d7c946fdd4f1fbbcbb7efcd04b0334" "checksum svgtypes 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "06280cc80ac6d6be32d497f068c40a25552cc837519265edab70d9040e64c52b" -"checksum syn 0.15.32 (registry+https://github.com/rust-lang/crates.io-index)" = "846620ec526c1599c070eff393bfeeeb88a93afa2513fc3b49f1fea84cf7b0ed" +"checksum syn 0.15.33 (registry+https://github.com/rust-lang/crates.io-index)" = "ec52cd796e5f01d0067225a5392e70084acc4c0013fa71d55166d38a8b307836" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum uom 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5cbf1d58ad58167ac4d22381cf4e2361f321712f7f876c8252ba5757989de53e" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" diff --git a/Cargo.toml b/Cargo.toml index 6790898..3fe59aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,6 @@ svgdom = "^0" lyon_geom = "^0" clap = "^2" log = "^0" -env_logger = "^0" \ No newline at end of file +env_logger = "^0" +uom = "^0" +regex = "^1" diff --git a/README.md b/README.md index bd6b42a..1a75a2b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Convert any SVG 1.1 path to gcode for a pen plotter, laser engraver, etc. - [x] Px, pc, in to mm - [x] Configurable DPI for px/pc to mm - [ ] Sort paths by distance to reduce G0 distances +- [ ] Configurable start/end sequence ## 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) diff --git a/src/code.rs b/src/code.rs index aab5ba8..dcf81ce 100644 --- a/src/code.rs +++ b/src/code.rs @@ -58,6 +58,13 @@ macro_rules! write_if_some { }; } +/// Rudimentary regular expression GCode validator. +pub fn validate_gcode(gcode: &&str) -> bool { + use regex::Regex; + let re = Regex::new(r##"^(?:(?:%|\(.*\)|(?:[A-Z^E^U][+-]?\d+(?:\.\d*)?))\h*)*$"##).unwrap(); + gcode.lines().all(|line| re.is_match(line)) +} + #[derive(Clone, PartialEq)] pub enum GCode { RapidPositioning { @@ -82,7 +89,8 @@ pub enum GCode { }, StopSpindle, DistanceMode(Distance), - Named(Box), + Comment(Box), + Raw(Box), } pub fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { @@ -164,10 +172,11 @@ pub fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { } )?; } - Named(name) => { - if name.len() > 0 { - writeln!(w, "({})", name)?; - } + Comment(name) => { + writeln!(w, "({})", name)?; + } + Raw(raw) => { + writeln!(w, "{}", raw)?; } } } diff --git a/src/machine.rs b/src/machine.rs index 687dbfc..5e03a40 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -13,8 +13,22 @@ impl Default for Machine { Self { tool_state: None, distance_mode: None, - tool_on_action: vec![], - tool_off_action: vec![], + tool_on_action: vec![ + GCode::Dwell { p: 0.1 }, + GCode::StartSpindle { + d: Direction::Clockwise, + s: 70.0, + }, + GCode::Dwell { p: 0.1 }, + ], + tool_off_action: vec![ + GCode::Dwell { p: 0.1 }, + GCode::StartSpindle { + d: Direction::Clockwise, + s: 50.0, + }, + GCode::Dwell { p: 0.1 }, + ], } } } diff --git a/src/main.rs b/src/main.rs index 8c8383f..dbac391 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ #[macro_use] extern crate clap; extern crate env_logger; -extern crate svgdom; #[macro_use] extern crate log; extern crate lyon_geom; +extern crate regex; +extern crate svgdom; +extern crate uom; use std::env; use std::fs::File; @@ -31,9 +33,11 @@ fn main() -> io::Result<()> { (author: crate_authors!()) (about: crate_description!()) (@arg FILE: "Selects the input SVG file to use, else reading from stdin") - (@arg tolerance: "Sets the interpolation tolerance for curves") - (@arg feedrate: "Sets the machine feed rate") - (@arg dpi: "Sets the DPI for SVGs with units in pt, pc, etc.") + (@arg tolerance: --tolerance "Sets the interpolation tolerance for curves") + (@arg feedrate: --feedrate "Sets the machine feed rate") + (@arg dpi: --dpi "Sets the DPI for SVGs with units in pt, pc, etc.") + (@arg tool_on_action: --tool_on_action "Sets the tool on GCode sequence") + (@arg tool_off_action: --tool_off_action "Sets the tool off GCode sequence") ) .get_matches(); @@ -65,24 +69,11 @@ fn main() -> io::Result<()> { opts.dpi = dpi; } - if true { - mach.tool_on_action = vec![ - GCode::StartSpindle { - d: Direction::Clockwise, - s: 70.0, - }, - GCode::Dwell { p: 0.1 }, - ]; + if let Some(tool_on_action) = matches.value_of("tool_on_action").filter(validate_gcode) { + mach.tool_on_action = vec![GCode::Raw(Box::new(tool_on_action.to_string()))]; } - if true { - mach.tool_off_action = vec![ - GCode::Dwell { p: 0.1 }, - GCode::StartSpindle { - d: Direction::Clockwise, - s: 50.0, - }, - GCode::Dwell { p: 0.1 }, - ]; + if let Some(tool_off_action) = matches.value_of("tool_off_action").filter(validate_gcode) { + mach.tool_off_action = vec![GCode::Raw(Box::new(tool_off_action.to_string()))]; } let doc = svgdom::Document::from_str(&input).expect("Invalid or unsupported SVG file"); @@ -109,8 +100,7 @@ impl Default for ProgramOptions { fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> Program { let mut p = Program::default(); - let mut t = Turtle::default(); - t.mach = mach; + let mut t = Turtle::from(mach); p.push(GCode::UnitsMillimeters); p += t.mach.tool_off().into(); @@ -121,11 +111,13 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P svgdom::NodeEdge::Start(node) => (node, true), svgdom::NodeEdge::End(node) => (node, false), }; + let id = if let svgdom::QName::Id(id) = *node.tag_name() { id } else { continue; }; + let attrs = node.attributes(); if let (ElementId::Svg, true) = (id, is_start) { if let Some(&AttributeValue::ViewBox(vbox)) = attrs.get_value(AttributeId::ViewBox) { @@ -148,7 +140,7 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P } if let ElementId::G = id { if is_start { - p.push(GCode::Named(Box::new(node.id().to_string()))); + p.push(GCode::Comment(Box::new(node.id().to_string()))); } } if let Some(&AttributeValue::Transform(ref trans)) = attrs.get_value(AttributeId::Transform) @@ -165,7 +157,7 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P match id { ElementId::Path => { if let Some(&AttributeValue::Path(ref path)) = attrs.get_value(AttributeId::D) { - p.push(GCode::Named(Box::new(node.id().to_string()))); + p.push(GCode::Comment(Box::new(node.id().to_string()))); t.reset(); for segment in path.iter() { let segment_gcode = match segment { @@ -280,14 +272,17 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P fn length_to_mm(l: svgdom::Length, dpi: f64) -> f64 { use svgdom::LengthUnit::*; - let scale = match l.unit { - Cm => 0.1, - Mm => 1.0, - In => 25.4, - Pt => 25.4 / dpi, - Pc => 25.4 / (6.0 * (dpi / 72.0)), - _ => 1.0, + use uom::si::f64::Length; + use uom::si::length::*; + + let length = match l.unit { + Cm => Length::new::(l.num), + Mm => Length::new::(l.num), + In => Length::new::(l.num), + Pt => Length::new::(l.num) * dpi / 72.0, // See https://github.com/iliekturtles/uom/blob/5cad47d4e67c902304c4c2b7feeb9c3d34fdffba/src/si/length.rs#L61 + Pc => Length::new::(l.num) * dpi / 72.0, // See https://github.com/iliekturtles/uom/blob/5cad47d4e67c902304c4c2b7feeb9c3d34fdffba/src/si/length.rs#L58 + _ => Length::new::(l.num), }; - l.num * scale + length.get::() } diff --git a/src/turtle.rs b/src/turtle.rs index 1b392ae..888759a 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -31,6 +31,14 @@ impl Default for Turtle { } } +impl From for Turtle { + fn from(m: Machine) -> Self { + let mut t = Self::default(); + t.mach = m; + t + } +} + impl Turtle { pub fn move_to(&mut self, abs: bool, x: X, y: Y) -> Vec where