From f7e037cfdd41b12d4fc1075b0da50c444047e6ad Mon Sep 17 00:00:00 2001 From: Sameer Puri Date: Tue, 31 Mar 2020 15:22:54 -0400 Subject: [PATCH] Finish pass of macro conversion, continue docs --- Cargo.lock | 250 ++++++++++++++------------ src/code.rs | 469 ++++++++++++++++++++++++++++++------------------- src/machine.rs | 60 ++----- src/main.rs | 73 ++++---- src/turtle.rs | 300 ++++++++++++++----------------- 5 files changed, 600 insertions(+), 552 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a51842..3ca3d47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [[package]] name = "aho-corasick" -version = "0.7.10" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -13,37 +13,39 @@ name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "arrayvec" -version = "0.5.1" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "atty" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "1.0.0" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -52,63 +54,55 @@ version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "euclid" -version = "0.20.8" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "float-cmp" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "hermit-abi" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "humantime" -version = "1.3.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.68" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -116,30 +110,35 @@ name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lyon_geom" -version = "0.15.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.20.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memchr" -version = "2.3.3" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "nodrop" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num-traits" -version = "0.2.11" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -148,7 +147,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "paste-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -156,15 +155,31 @@ name = "paste-impl" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro-hack" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -177,7 +192,7 @@ dependencies = [ [[package]] name = "quick-error" -version = "1.2.3" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -190,18 +205,18 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.6" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.17" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -214,11 +229,8 @@ dependencies = [ [[package]] name = "simplecss" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "siphasher" @@ -240,36 +252,34 @@ name = "svg2gcode" version = "0.0.1" dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lyon_geom 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lyon_geom 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "svgdom 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uom 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "svgdom 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "uom 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "svgdom" -version = "0.18.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "simplecss 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "svgtypes 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "xmlwriter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "svgtypes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "svgtypes" -version = "0.5.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "float-cmp 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "float-cmp 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -284,10 +294,10 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -295,25 +305,25 @@ name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thread_local" -version = "1.0.1" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "typenum" -version = "1.11.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -323,11 +333,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "uom" -version = "0.27.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -337,7 +347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -351,10 +361,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -363,62 +373,68 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "xmlparser" -version = "0.9.0" +name = "wincolor" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "xmlwriter" -version = "0.1.0" +name = "xmlparser" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" -"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -"checksum euclid 0.20.8 (registry+https://github.com/rust-lang/crates.io-index)" = "57d9ae79e19d69ffb5b47b0a744c4215c115dfed4039314c9ca1b8ddc6c333be" -"checksum float-cmp 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" -"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" -"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +"checksum euclid 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89c879a4e57d6a2785d517b0771ea6857916173debef0102bf81142d36ca9254" +"checksum float-cmp 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef4eee449a2818084dad09f4fcd6e6e8932c482d8d94298493226782bb45b5e" +"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.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum lyon_geom 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76579b1f0a1ab9c3565c48faf951152d772d67ea35292fa9d36a991916b19a21" -"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum lyon_geom 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69589b8844c0b3745cc031a35b62bc33b0fb9e5ba7613756d802c52861dcdb4c" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum paste 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "092d791bf7847f70bbd49085489fba25fc2c193571752bff9e36e74e72403932" "checksum paste-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "406c23fb4c45cc6f68a9bbabb8ec7bd6f8cfcbd17e9e8f72c2460282f8325729" -"checksum proc-macro-hack 0.5.14 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420" +"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-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" "checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" -"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" -"checksum regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" -"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +"checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26" +"checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" "checksum roxmltree 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "330d8f80a274bc3cb608908ee345970e7e24b96907f1ad69615a498bec57871c" -"checksum simplecss 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "596554e63596d556a0dbd681416342ca61c75f1a45203201e7e77d3fa2fa9014" +"checksum simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum svgdom 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ae7d6df72b3a36c0f003f1f8919c96be43d20d7c015f8dcf2f82531a4fc33b" -"checksum svgtypes 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff" +"checksum svgdom 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ddce601e49ed213b0126ff4172cd9f5f8dba5f1df2277ecbe0e298f9865baba" +"checksum svgtypes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "444c882c28925ae0585df228a90f9951569588646ceca4753560de93cdd02258" "checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" -"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum typenum 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9af116643fb2603767cc7850b2f70b571ae451a2abe795acbf01747dfd33aa70" +"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum uom 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51fc04fb44bcb7806da71885872cb15d123b681e459a476ef8a0bab287bee0cd" +"checksum uom 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3198c29f199fa8a23d732f4aa21ddc4f4d0a257cb0c2a44afea30145ce2575c1" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" "checksum xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" -"checksum xmlwriter 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" diff --git a/src/code.rs b/src/code.rs index 1631c75..ef5ff93 100644 --- a/src/code.rs +++ b/src/code.rs @@ -1,236 +1,343 @@ +use core::convert::TryFrom; use std::io::{self, Write}; -/// Fields are the basic unit of GCode. -trait Field { - /// An uppercase letter - const LETTER: char; - /// A number if the field has a fixed number. - const NUMBER: Option; - /// A fraction if the field has a fixed fraction following a fixed number. - const FRACTION: Option; - - fn from_arguments<'a>(arguments: &[Argument<'a>]) -> Self; - fn into_arguments<'a>(&'a self) -> Vec>; +/// Collapses GCode words into higher-level commands +/// Relies on the first word being a command. +pub struct CommandVec { + pub inner: Vec, } -/// Arguments, described by a letter and a value, belong to a field. -pub struct Argument<'a> { - letter: char, - value: &'a str, +impl Default for CommandVec { + fn default() -> Self { + Self { + inner: vec![] + } + } +} + +impl IntoIterator for CommandVec { + type Item = Command; + type IntoIter = CommandVecIntoIterator; + fn into_iter(self) -> Self::IntoIter { + CommandVecIntoIterator { + vec: self, + index: 0, + } + } +} + +pub struct CommandVecIntoIterator { + vec: CommandVec, + index: usize, +} + +impl Iterator for CommandVecIntoIterator { + type Item = Command; + fn next(&mut self) -> Option { + if self.vec.inner.len() == self.index { + return None; + } + + let mut i = self.index + 1; + while i < self.vec.inner.len() { + if CommandWord::is_command(&self.vec.inner[i]) { + break; + } + i += 1; + } + Command::try_from(&self.vec.inner[self.index..i]).ok() + } +} + +/// Fundamental unit of GCode: a value preceded by a descriptive letter. +/// A float is used here to encompass all the possible variations of a value. +/// Some flavors of GCode may allow strings, but that is currently not supported. +#[derive(Clone, PartialEq, Debug)] +pub struct Word { + pub letter: char, + pub value: f64, +} + +#[macro_export] +macro_rules! command { + ($commandWord: expr, {$($argument: ident : $value: expr,)*}) => { + paste::expr! (Command::new($commandWord, vec![$(Word { + letter: stringify!([<$argument:upper>]).chars().next().unwrap(), + value: $value as f64, + },)*])) + }; } -macro_rules! field { - ($(#[$outer:meta])* $fieldName: ident {$letter: pat, $number: pat, $fraction: pat, {$($(#[$inner:meta])* $argument: ident : $type: ty), *} }) => { - $(#[$outer])* - struct $fieldName { +macro_rules! commands { + ($($(#[$outer:meta])* $commandName: ident {$letter: pat, $number: pat, $fraction: pat, {$($(#[$inner:meta])* $argument: ident), *} },)*) => { + + /// Commands are the operational unit of GCode + /// They consist of an identifying word followed by arguments + #[derive(Clone, PartialEq)] + pub struct Command { + command_word: CommandWord, + arguments: Vec + } + + paste::item! { + impl Command { + pub fn new(command_word: CommandWord, mut arguments: Vec) -> Self { + Self { + command_word: command_word.clone(), + arguments: arguments.drain(..).filter(|w| { + match command_word { + $(CommandWord::$commandName => match w.letter.to_lowercase() { + $($argument => true,)* + _ => false + },)* + _ => false + } + }).collect() + } + } + + pub fn push(&mut self, argument: Word) { + match self.command_word { + $(CommandWord::$commandName => match argument.letter.to_lowercase() { + $($argument => { + self.arguments.push(argument); + })* + _ => {} + },)* + _ => {} + } + } + } + } + + paste::item! { + impl Into> for Command { + fn into(self) -> Vec { + let mut args = self.arguments; + args.insert(0, self.command_word.into()); + args + } + } + } + + paste::item! { + impl TryFrom<&[Word]> for Command { + type Error = (); + fn try_from(words: &[Word]) -> Result { + let command_word = CommandWord::try_from(&words[0])?; + let mut arguments = Vec::with_capacity(words.len() - 1); + for i in 1..words.len() { + match command_word { + $(CommandWord::$commandName => match words[i].letter.to_lowercase() { + $($argument => { + arguments.push(words[i].clone()); + })* + _ => {} + },)* + _ => {} + } + } + Ok(Self { + command_word, + arguments + }) + } + } + } + + #[derive(Clone, PartialEq, Eq)] + pub enum CommandWord { $( - $(#[$inner])* - $argument: Option<$type>, + $(#[$outer])* + $commandName, )* + /// A comment is a special command: it is a semicolon followed by text until the end of the line + Comment(Box), + /// Letter N followed by an integer (with no sign) between 0 and 99999 written with no more than five digits + LineNumber(u16), + /// Byte-sized checksums are used by some GCode generators at the end of each line + Checksum(u8), } paste::item! { - impl Field for $fieldName { - const LETTER: char = $letter; - const NUMBER: Option = $number; - const FRACTION: Option = $fraction; - fn from_arguments<'a>(arguments: &[Argument<'a>]) -> Self { - let mut field = Self { - $($argument: None,)* + impl CommandWord { + pub fn is_command(word: &Word) -> bool { + let number = word.value as u16; + let fraction_numeric = + f64::from_bits(word.value.fract().to_bits() & 0x00_00_FF_FF_FF_FF_FF_FF) as u16; + let fraction = if fraction_numeric == 0 { + None + } else { + Some(fraction_numeric) }; - for arg in arguments.iter() { - $(if arg.letter == stringify!([<$argument:upper>]).chars().next().unwrap() { - field.$argument = Some(arg.value.parse().unwrap()); - })* + match (word.letter, number, fraction) { + $(($letter, $number, $fraction) => true,)* + ('*', _checksum, None) => true, + ('N', _line_number, None) => true, + (_, _, _) => false } - field } - fn into_arguments<'a>(&'a self) -> Vec> { - let mut args = vec![]; - $( - if let Some(value) = self.$argument { - args.push(Argument { - letter: stringify!([<$argument:upper>]).chars().next().unwrap(), - value: &value.to_string() - }); + } + } + paste::item! { + impl TryFrom<&Word> for CommandWord { + type Error = (); + fn try_from(word: &Word) -> Result { + let number = word.value as u16; + let fraction_numeric = + f64::from_bits(word.value.fract().to_bits() & 0x00_00_FF_FF_FF_FF_FF_FF) as u16; + let fraction = if fraction_numeric == 0 { + None + } else { + Some(fraction_numeric) + }; + match (word.letter, number, fraction) { + $(($letter, $number, $fraction) => Ok(Self::$commandName),)* + ('*', checksum, None) => Ok(Self::Checksum(checksum as u8)), + ('N', line_number, None) => Ok(Self::LineNumber(line_number)), + (_, _, _) => Err(()) + } + } + } + } + paste::item!{ + impl Into for CommandWord { + fn into(self) -> Word { + match self { + $( + Self::$commandName {} => Word { + letter: $letter, + // TODO: fix fraction + value: $number as f64 + ($fraction.unwrap_or(0) as f64) + }, + )* + Self::Checksum(value) => Word { + letter: '*', + value: value as f64 + }, + Self::LineNumber(value) => Word { + letter: 'N', + value: value as f64 + }, + Self::Comment(_string) => Word { + letter: ';', + value: 0.0 } - )* - args + } } } } }; } -field!( - /// Moves the head at the fastest possible speed to the desired speed. - /// Never enter a cut with rapid positioning. - /// Some older machines may "dog leg" rapid positioning, moving one axis at a time. +commands!( + /// Moves the head at the fastest possible speed to the desired speed + /// Never enter a cut with rapid positioning + /// Some older machines may "dog leg" rapid positioning, moving one axis at a time RapidPositioning { - 'G', Some(0), None, { - x: f64, - y: f64, - z: f64, - e: f64, - f: f64, - h: f64, - r: f64, - s: f64, - a: f64, - b: f64, - c: f64 - } -}); - -field!( + 'G', 0, None, { + x, + y, + z, + e, + f, + h, + r, + s, + a, + b, + c + } + }, /// Typically used for "cutting" motion LinearInterpolation { - 'G', Some(1), None, { - x: f64, - y: f64, - z: f64, - e: f64, - f: f64, - h: f64, - r: f64, - s: f64, - a: f64, - b: f64, - c: f64 - } -}); - -field!( - /// This will keep the axes unmoving for the period of time in seconds specified by the P number. + 'G', 1, None, { + x, + y, + z, + e, + f, + h, + r, + s, + a, + b, + c + } + }, + /// This will keep the axes unmoving for the period of time in seconds specified by the P number Dwell { - 'G', Some(4), None, { - /// Time in seconds - p: f64 - } -}); - -field!( + 'G', 4, None, { + /// Time in seconds + p + } + }, /// Use inches for length units UnitsInches { - 'G', Some(20), None, {} -}); - -field!( + 'G', 20, None, {} + }, /// Use millimeters for length units UnitsMillimeters { - 'G', Some(21), None, {} -}); - -field!( + 'G', 21, None, {} + }, /// In absolute distance mode, axis numbers usually represent positions in terms of the currently active coordinate system. AbsoluteDistanceMode { - 'G', Some(90), None, {} -}); - -field!( - /// In incremental distance mode, axis numbers usually represent increments from the current values of the numbers. - IncrementalDistanceMode { - 'G', Some(91), None, {} -}); - -field!( + 'G', 90, None, {} + }, + /// In relative distance mode, axis numbers usually represent increments from the current values of the numbers + RelativeDistanceMode { + 'G', 91, None, {} + }, /// Start spinning the spindle clockwise with speed `p` StartSpindleClockwise { - 'M', Some(3), None, { + 'M', 3, None, { /// Speed - p: f64 + p } - } -); - -field!( + }, /// Start spinning the spindle counterclockwise with speed `p` StartSpindleCounterclockwise { - 'M', Some(4), None, { + 'M', 4, None, { /// Speed - p: f64 + p } - } -); - -field!( + }, /// Stop spinning the spindle StopSpindle { - 'M', Some(5), None, {} - } -); - -field!( + 'M', 5, None, {} + }, /// Signals the end of a program ProgramEnd { - 'M', Some(20), None, {} - } + 'M', 20, None, {} + }, ); -/// Checksums are used by some GCode generators at the end of each line -struct Checksum { - /// Checksum value - value: u8, -} - -impl Field for Checksum { - const LETTER: char = '*'; - const NUMBER: Option = None; - const FRACTION: Option = None; - fn from_arguments<'a>(arguments: &[Argument<'a>]) -> Self { - Self { value: 0 } - } - fn into_arguments(&self) -> Vec { - vec![] - } -} - -/// A line number is the letter N followed by an integer (with no sign) between 0 and 99999 written with no more than five digits -struct LineNumber { - /// Line number - value: u16, -} - -impl Field for LineNumber { - const LETTER: char = 'N'; - const NUMBER: Option = None; - const FRACTION: Option = None; - fn from_arguments<'a>(arguments: &[Argument<'a>]) -> Self { - Self { value: 0 } - } - fn into_arguments(&self) -> Vec { - vec![] - } -} - -/// Rudimentary regular expression GCode validator. +/// 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)) } -// TODO: Documentation -// TODO: This function is too large -pub fn program2gcode(p: &Program, mut w: W) -> io::Result<()> { - - let mut last_feedrate: Option = None; - let letter = '*'; - let number = Some(0); - let fraction = None; - - macro_rules! match_field { - ($($fieldName: ident)*) => { - match (letter, number, fraction) { - $(($fieldName::LETTER, $fieldName::NUMBER, $fieldName::FRACTION) => { - Some($fieldName::from_arguments(arguments)) - },)* - _ => {None} +/// Writes a GCode program (or sequence) to a Writer +pub fn program2gcode(program: Vec, mut w: W) -> io::Result<()> { + for command in program.into_iter() { + match &command.command_word { + CommandWord::Comment(string) => { + writeln!(w, ";{}", string)?; + }, + _other => { + let words: Vec = command.into(); + let mut it = words.iter(); + if let Some(command_word) = it.next() { + write!(w, "{}{}", command_word.letter, command_word.value)?; + for word in it { + write!(w, " {}{} ", word.letter, word.value)?; + } + writeln!(w, "")?; } - }; - } - for code in p.iter() { - match_field!(LineNumber); + } + } } Ok(()) } diff --git a/src/machine.rs b/src/machine.rs index b6daf32..c9b4ea9 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1,3 +1,4 @@ +#[macro_use] use crate::code::*; //// Direction of the machine spindle @@ -22,44 +23,29 @@ pub enum Distance { } /// Generic machine state simulation, assuming nothing is known about the machine when initialized. +/// This is used to reduce output GCode verbosity and run repetitive actions. pub struct Machine { tool_state: Option, distance_mode: Option, - pub tool_on_action: Vec, - pub tool_off_action: Vec, + tool_on_action: Vec, + tool_off_action: Vec, } -/// Assigns reasonable default settings that apply to most gcode applications. impl Machine { - fn default() -> Self { + /// Create a generic machine, given a tool on/off GCode sequence. + pub fn new(tool_on_action: CommandVec, tool_off_action: CommandVec) -> Self { Self { tool_state: None, distance_mode: None, - 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 }, - ], + tool_on_action: tool_on_action.into_iter().collect(), + tool_off_action: tool_off_action.into_iter().collect() } } } -// TODO: Documentation -// Implements the state machine functions to export Gcode. impl Machine { - // Outputs gcode to turn the tool on. - pub fn tool_on(&mut self) -> Vec { + /// Output gcode to turn the tool on. + 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() @@ -68,8 +54,8 @@ impl Machine { } } - // Outputs gcode to turn the tool off. - pub fn tool_off(&mut self) -> Vec { + /// Output gcode to turn the tool off. + 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() @@ -77,31 +63,21 @@ impl Machine { vec![] } } - - // Outputs gcode for how distance should be measured: relative or absolute. - pub fn distance(&mut self, is_absolute: bool) -> Vec { - if is_absolute { - self.absolute() - } else { - self.relative() - } - } - - // Outputs gcode command to use absolute motion - pub fn absolute(&mut self) -> Vec { + /// Output relative distance field if mode was absolute or unknown. + pub fn absolute(&mut self) -> Vec { if self.distance_mode == Some(Distance::Relative) || self.distance_mode == None { self.distance_mode = Some(Distance::Absolute); - vec![GCode::DistanceMode(Distance::Absolute)] + vec![command!(CommandWord::AbsoluteDistanceMode, {})] } else { vec![] } } - /// Set the distance mode to relative - pub fn relative(&mut self) -> Vec { + /// Output absolute distance field if mode was relative or unknown. + pub fn relative(&mut self) -> Vec { if self.distance_mode == Some(Distance::Absolute) || self.distance_mode == None { self.distance_mode = Some(Distance::Relative); - vec![GCode::DistanceMode(Distance::Relative)] + vec![command!(CommandWord::RelativeDistanceMode, {})] } else { vec![] } diff --git a/src/main.rs b/src/main.rs index 6f0f187..ae0a7f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use std::io::{self, Read}; use lyon_geom::{euclid, math}; use svgdom::{AttributeId, AttributeValue, ElementId, ElementType, PathSegment}; +#[macro_use] mod code; mod machine; mod turtle; @@ -60,7 +61,7 @@ fn main() -> io::Result<()> { }; let mut opts = ProgramOptions::default(); - let mut mach = Machine::default(); + let mut mach = Machine::new(CommandVec::default(), CommandVec::default()); if let Some(tolerance) = matches.value_of("tolerance").and_then(|x| x.parse().ok()) { opts.tolerance = tolerance; @@ -72,17 +73,17 @@ fn main() -> io::Result<()> { opts.dpi = dpi; } - 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 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()))]; - } + // 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 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"); let prog = svg2program(&doc, opts, mach); - program2gcode(&prog, File::create("out.gcode")?) + program2gcode(prog, File::create("out.gcode")?) } // TODO: Documentation @@ -105,15 +106,16 @@ impl Default for ProgramOptions { // TODO: Documentation // TODO: This function is much too large -fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> Program { - let mut p = Program::default(); - let mut t = Turtle::from(mach); +fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> Vec { + let mut p = vec![]; + let mut t = Turtle::new(mach); let mut namestack: Vec = vec![]; - p.push(GCode::UnitsMillimeters); - p += t.mach.tool_off().into(); - p += t.move_to(true, 0.0, 0.0).into(); + p.push(command!(CommandWord::UnitsMillimeters, {})); + p.append(&mut t.mach.tool_off()); + p.append(&mut + t.move_to(true, 0.0, 0.0)); for edge in doc.root().traverse() { let (node, is_start) = match edge { @@ -149,7 +151,7 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P } if let ElementId::G = id { if is_start { - namestack.push(node.id().to_string()); + namestack.push(format!("{}#{}", node.tag_name(), node.id().to_string())); } else { namestack.pop(); } @@ -180,13 +182,16 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P let prefix: String = namestack.iter().fold(String::new(), |mut acc, name| { acc += name; - acc += "-->"; + acc += " => "; acc }); - p.push(GCode::Comment(Box::new(prefix + &node.id()))); + p.push(command!( + CommandWord::Comment(Box::new(prefix + &node.id())), + {} + )); t.reset(); for segment in path.iter() { - let segment_gcode = match segment { + p.append(&mut 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 @@ -273,8 +278,7 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P opts.feedrate, opts.tolerance, ), - }; - p += segment_gcode.into(); + }); } } } @@ -285,13 +289,13 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P } } - p += t.mach.tool_off().into(); - p += t.mach.absolute().into(); - p.push(GCode::RapidPositioning { - x: 0.0.into(), - y: 0.0.into(), - }); - p.push(GCode::ProgramEnd); + p.append(&mut t.mach.tool_off()); + p.append(&mut t.mach.absolute()); + p.push(command!(CommandWord::RapidPositioning, { + x: 0.0, + y: 0.0, + })); + p.push(command!(CommandWord::ProgramEnd, {})); p } @@ -313,18 +317,3 @@ fn length_to_mm(l: svgdom::Length, dpi: f64) -> f64 { length.get::() } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_svg2program() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_length_to_mm() { - panic!("TODO: basic passing test"); - } -} diff --git a/src/turtle.rs b/src/turtle.rs index 5f75c97..ef851bd 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -1,14 +1,12 @@ /// TODO: Documentation - -use crate::code::GCode; +use crate::code::*; use crate::machine::Machine; -use lyon_geom::euclid::{Angle, default::Transform2D}; +use lyon_geom::euclid::{default::Transform2D, Angle}; use lyon_geom::math::{point, vector, F64Point}; use lyon_geom::{ArcFlags, CubicBezierSegment, QuadraticBezierSegment, SvgArc}; -/// Turtle graphics simulator for paths that outputs the GCode enum -/// representation for each operation. Handles trasforms, scaling, position -/// offsets, etc. See https://www.w3.org/TR/SVG/paths.html +/// Turtle graphics simulator for paths that outputs the gcode representation for each operation. +/// Handles trasforms, scaling, position, offsets, etc. See https://www.w3.org/TR/SVG/paths.html pub struct Turtle { curpos: F64Point, initpos: F64Point, @@ -19,33 +17,25 @@ pub struct Turtle { prev_ctrl: Option, } -// TODO: Documentation -impl Default for Turtle { - fn default() -> Self { +impl Turtle { + /// Create a turtle at the origin with no scaling or transform + pub fn new(machine: Machine) -> Self { Self { curpos: point(0.0, 0.0), initpos: point(0.0, 0.0), curtran: Transform2D::identity(), scaling: None, transtack: vec![], - mach: Machine::default(), + mach: machine, prev_ctrl: None, } } } -// TODO: Documentation -impl From for Turtle { - fn from(m: Machine) -> Self { - let mut t = Self::default(); - t.mach = m; - t - } -} - -// TODO: Documentation impl Turtle { - pub fn move_to(&mut self, abs: bool, x: X, y: Y) -> Vec + /// Move the turtle to the given absolute/relative coordinates in the current transform + /// https://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands + pub fn move_to(&mut self, abs: bool, x: X, y: Y) -> Vec where X: Into>, Y: Into>, @@ -67,21 +57,41 @@ impl Turtle { self.initpos = to; self.prev_ctrl = None; - vec![ - self.mach.tool_off(), - self.mach.absolute(), - vec![GCode::RapidPositioning { - x: to.x.into(), - y: to.y.into(), - }], - ] - .drain(..) - .flatten() - .collect() + self.mach + .tool_off() + .iter() + .chain(self.mach.absolute().iter()) + .chain(std::iter::once(&command!(CommandWord::RapidPositioning, { + x : to.x as f64, + y : to.y as f64, + }))) + .map(Clone::clone) + .collect() + } + + fn linear_interpolation(x: f64, y: f64, z: Option, f: Option) -> Command { + let mut linear_interpolation = command!(CommandWord::LinearInterpolation, { + x: x, + y: y, + }); + if let Some(z) = z { + linear_interpolation.push(Word { + letter: 'Z', + value: z, + }); + } + if let Some(f) = f { + linear_interpolation.push(Word { + letter: 'F', + value: f, + }); + } + linear_interpolation } - // TODO: Documentation - pub fn close(&mut self, z: Z, f: F) -> Vec + /// Close an SVG path, cutting back to its initial position + /// https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand + pub fn close(&mut self, z: Z, f: F) -> Vec where Z: Into>, F: Into>, @@ -96,23 +106,24 @@ impl Turtle { return vec![]; } self.curpos = self.initpos; - vec![ - self.mach.tool_on(), - self.mach.absolute(), - vec![GCode::LinearInterpolation { - x: self.initpos.x.into(), - y: self.initpos.y.into(), - z: z.into(), - f: f.into(), - }], - ] - .drain(..) - .flatten() - .collect() + + self.mach + .tool_on() + .iter() + .chain(self.mach.absolute().iter()) + .chain(std::iter::once(&Self::linear_interpolation( + self.initpos.x.into(), + self.initpos.y.into(), + z.into(), + f.into(), + ))) + .map(Clone::clone) + .collect() } - // TODO: Documentation - pub fn line(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Vec + /// Draw a line from the current position in the current transform to the specified position + /// https://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands + pub fn line(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Vec where X: Into>, Y: Into>, @@ -135,41 +146,45 @@ impl Turtle { self.curpos = to; self.prev_ctrl = None; - vec![ - self.mach.tool_on(), - self.mach.absolute(), - vec![GCode::LinearInterpolation { - x: to.x.into(), - y: to.y.into(), - z: z.into(), - f: f.into(), - }], - ] - .drain(..) - .flatten() - .collect() + self.mach + .tool_on() + .iter() + .chain(self.mach.absolute().iter()) + .chain(std::iter::once(&Self::linear_interpolation( + to.x.into(), + to.y.into(), + z.into(), + f.into(), + ))) + .map(Clone::clone) + .collect() } + /// Draw a cubic bezier curve segment + /// The public bezier functions call this command after converting to a cubic bezier segment + /// https://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands fn bezier>, F: Into>>( &mut self, cbs: CubicBezierSegment, tolerance: f64, z: Z, f: F, - ) -> Vec { + ) -> Vec { let z = z.into(); let f = f.into(); let last_point = std::cell::Cell::new(self.curpos); - let mut cubic = vec![]; - cbs.flattened(tolerance).for_each(|point| { - cubic.push(GCode::LinearInterpolation { - x: point.x.into(), - y: point.y.into(), - z, - f, - }); - last_point.set(point); - }); + let mut cubic: Vec = cbs + .flattened(tolerance) + .map(|point| { + last_point.set(point); + Self::linear_interpolation( + point.x.into(), + point.y.into(), + z.into(), + f.into(), + ) + }) + .collect(); self.curpos = last_point.get(); // See https://www.w3.org/TR/SVG/paths.html#ReflectedControlPoints self.prev_ctrl = point( @@ -178,14 +193,17 @@ impl Turtle { ) .into(); - vec![self.mach.tool_on(), self.mach.absolute(), cubic] - .drain(..) - .flatten() + self.mach + .tool_on() + .iter() + .chain(self.mach.absolute().iter()) + .chain(cubic.iter()) + .map(Clone::clone) .collect() } - // TODO: Documentation - // TODO: Function too long + /// Draw a cubic curve from the current point to (x, y) with specified control points (x1, y1) and (x2, y2) + /// https://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands pub fn cubic_bezier( &mut self, abs: bool, @@ -198,7 +216,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Vec + ) -> Vec where Z: Into>, F: Into>, @@ -227,7 +245,8 @@ impl Turtle { self.bezier(cbs, tolerance, z, f) } - // TODO: Documentation + /// Draw a shorthand/smooth cubic bezier segment, where the first control point was already given + /// https://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands pub fn smooth_cubic_bezier( &mut self, abs: bool, @@ -238,7 +257,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Vec + ) -> Vec where Z: Into>, F: Into>, @@ -265,7 +284,8 @@ impl Turtle { self.bezier(cbs, tolerance, z, f) } - // TODO: Documentation + /// Draw a shorthand/smooth cubic bezier segment, where the control point was already given + /// https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands pub fn smooth_quadratic_bezier( &mut self, abs: bool, @@ -274,7 +294,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Vec + ) -> Vec where Z: Into>, F: Into>, @@ -293,7 +313,8 @@ impl Turtle { self.bezier(qbs.to_cubic(), tolerance, z, f) } - // TODO: Documentation + /// Draw a quadratic bezier segment + /// https://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands pub fn quadratic_bezier( &mut self, abs: bool, @@ -304,7 +325,7 @@ impl Turtle { tolerance: f64, z: Z, f: F, - ) -> Vec + ) -> Vec where Z: Into>, F: Into>, @@ -325,7 +346,8 @@ impl Turtle { self.bezier(qbs.to_cubic(), tolerance, z, f) } - // TODO: Documentation + /// Draw an elliptical arc curve + /// https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands pub fn elliptical( &mut self, abs: bool, @@ -339,7 +361,7 @@ impl Turtle { z: Z, f: F, tolerance: f64, - ) -> Vec + ) -> Vec where Z: Into>, F: Into>, @@ -374,24 +396,29 @@ impl Turtle { let mut ellipse = vec![]; sarc.for_each_flattened(tolerance, &mut |point: F64Point| { - ellipse.push(GCode::LinearInterpolation { - x: point.x.into(), - y: point.y.into(), - z, - f, - }); + ellipse.push(Self::linear_interpolation( + point.x.into(), + point.y.into(), + z.into(), + f.into(), + )); last_point.set(point); }); self.curpos = last_point.get(); self.prev_ctrl = None; - vec![self.mach.tool_on(), self.mach.absolute(), ellipse] - .drain(..) - .flatten() + self.mach + .tool_on() + .iter() + .chain(self.mach.absolute().iter()) + .chain(ellipse.iter()) + .map(Clone::clone) .collect() } - // TODO: Documentation + /// Push a new scaling-only transform onto the stack + /// This is useful for handling things like the viewBox + /// https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute pub fn stack_scaling(&mut self, scaling: Transform2D) { self.curtran = self.curtran.post_transform(&scaling); if let Some(ref current_scaling) = self.scaling { @@ -401,7 +428,9 @@ impl Turtle { } } - // TODO: Documentation + /// Push a generic transform onto the stack + /// Could be any valid CSS transform https://drafts.csswg.org/css-transforms-1/#typedef-transform-function + /// https://www.w3.org/TR/SVG/coords.html#InterfaceSVGTransform pub fn push_transform(&mut self, trans: Transform2D) { self.transtack.push(self.curtran); if let Some(ref scaling) = self.scaling { @@ -415,7 +444,8 @@ impl Turtle { } } - // TODO: Documentation + /// Pop a generic transform off the stack, returning to the previous transform state + /// This means that most recent transform went out of scope pub fn pop_transform(&mut self) { self.curtran = self .transtack @@ -423,7 +453,7 @@ impl Turtle { .expect("popped when no transforms left"); } - // TODO: Documentation + /// Reset the position of the turtle to the origin in the current transform stack pub fn reset(&mut self) { self.curpos = point(0.0, 0.0); self.curpos = self.curtran.transform_point(self.curpos); @@ -431,73 +461,3 @@ impl Turtle { self.initpos = self.curpos; } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_move_to() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_close() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_line() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_bezier() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_cubic_bezier() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_smooth_cubic_bezier() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_smooth_quadratic_bezier() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_quadratic_bezier() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_elliptical() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_stack_scaling() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_push_transform() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_pop_transform() { - panic!("TODO: basic passing test"); - } - - #[test] - fn test_reset() { - panic!("TODO: basic passing test"); - } -}