diff --git a/README.md b/README.md index 4ca700c..c279986 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,29 @@ Convert any SVG 1.1 path to gcode for a pen plotter, laser engraver, etc. - [ ] Biarc interpolation (G2/G3 instead of many G1) - [x] Px, pc, in to mm - [x] Configurable DPI for px/pc to mm +- [ ] Sort paths by distance to reduce G0 distances ## 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) - [x] Close path command connects back to (0.0, 0.0) instead of the last move - [ ] Ellipse paths are dubious -- large_arc, sweep may need to be inverted + + +## Demonstration + +### Input + +```bash +cargo run --release -- examples/Vanderbilt_Commodores_logo.svg +``` + +![Vanderbilt Commodores Logo](examples/Vanderbilt_Commodores_logo.svg) + +### Output, rendered at [https://ncviewer.com]() + +```bash +cat output.gcode +``` + +![Vanderbilt Commodores Logo Gcode](examples/Vanderbilt_Commodores_logo_gcode.png) diff --git a/examples/Vanderbilt_Commodores_logo.svg b/examples/Vanderbilt_Commodores_logo.svg new file mode 100644 index 0000000..971bf2b --- /dev/null +++ b/examples/Vanderbilt_Commodores_logo.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/examples/Vanderbilt_Commodores_logo_gcode.png b/examples/Vanderbilt_Commodores_logo_gcode.png new file mode 100644 index 0000000..84de8c8 Binary files /dev/null and b/examples/Vanderbilt_Commodores_logo_gcode.png differ diff --git a/src/main.rs b/src/main.rs index 67304bc..8c8383f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -261,7 +261,7 @@ fn svg2program(doc: &svgdom::Document, opts: ProgramOptions, mach: Machine) -> P } } _ => { - info!("Node <{} id=\"{}\" .../> is not supported", id, node.id()); + warn!("Node <{} id=\"{}\" .../> is not supported", id, node.id()); } } } diff --git a/src/turtle.rs b/src/turtle.rs index 59df6f4..1b392ae 100644 --- a/src/turtle.rs +++ b/src/turtle.rs @@ -1,9 +1,12 @@ -use crate::code::{GCode}; +use crate::code::GCode; use crate::machine::Machine; use lyon_geom::euclid::{Angle, Transform2D}; 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 pub struct Turtle { curpos: F64Point, initpos: F64Point, @@ -11,7 +14,7 @@ pub struct Turtle { scaling: Option>, transtack: Vec>, pub mach: Machine, - prev_ctrl: F64Point, + prev_ctrl: Option, } impl Default for Turtle { @@ -23,7 +26,7 @@ impl Default for Turtle { scaling: None, transtack: vec![], mach: Machine::default(), - prev_ctrl: point(0.0, 0.0), + prev_ctrl: None, } } } @@ -49,7 +52,7 @@ impl Turtle { to = self.curtran.transform_point(&to); self.curpos = to; self.initpos = to; - self.prev_ctrl = to; + self.prev_ctrl = None; vec![ self.mach.tool_off(), @@ -69,6 +72,14 @@ impl Turtle { Z: Into>, F: Into>, { + // See https://www.w3.org/TR/SVG/paths.html#Segment-CompletingClosePath which could result in a G91 G1 X0 Y0 + if (self.curpos - self.initpos) + .abs() + .lower_than(&vector(std::f64::EPSILON, std::f64::EPSILON)) + .all() + { + return vec![]; + } self.curpos = self.initpos; vec![ self.mach.tool_on(), @@ -106,7 +117,7 @@ impl Turtle { let mut to = point(x, y); to = self.curtran.transform_point(&to); self.curpos = to; - self.prev_ctrl = self.curpos; + self.prev_ctrl = None; vec![ self.mach.tool_on(), @@ -144,7 +155,12 @@ impl Turtle { last_point.set(point); }); self.curpos = last_point.get(); - self.prev_ctrl = cbs.ctrl1; + // See https://www.w3.org/TR/SVG/paths.html#ReflectedControlPoints + self.prev_ctrl = point( + 2.0 * self.curpos.x - cbs.ctrl2.x, + 2.0 * self.curpos.y - cbs.ctrl2.y, + ) + .into(); vec![self.mach.tool_on(), self.mach.absolute(), cubic] .drain(..) @@ -209,7 +225,7 @@ impl Turtle { F: Into>, { let from = self.curpos; - let ctrl1 = self.prev_ctrl; + let ctrl1 = self.prev_ctrl.unwrap_or(self.curpos); let mut ctrl2 = point(x2, y2); let mut to = point(x, y); if !abs { @@ -244,7 +260,7 @@ impl Turtle { F: Into>, { let from = self.curpos; - let ctrl = self.prev_ctrl; + let ctrl = self.prev_ctrl.unwrap_or(self.curpos); let mut to = point(x, y); if !abs { let invtran = self.curtran.inverse().unwrap(); @@ -345,7 +361,7 @@ impl Turtle { last_point.set(point); }); self.curpos = last_point.get(); - self.prev_ctrl = self.curpos; + self.prev_ctrl = None; vec![self.mach.tool_on(), self.mach.absolute(), ellipse] .drain(..) @@ -385,7 +401,7 @@ impl Turtle { pub fn reset(&mut self) { self.curpos = point(0.0, 0.0); self.curpos = self.curtran.transform_point(&self.curpos); - self.prev_ctrl = self.curpos; + self.prev_ctrl = None; self.initpos = self.curpos; } }