parent
dbf46ef01b
commit
b8b3aa1f5d
@ -1,8 +1,13 @@
|
||||
[package]
|
||||
name = "lsys"
|
||||
version = "0.1.0"
|
||||
authors = ["purisame"]
|
||||
name = "svg2gcode"
|
||||
version = "0.0.1"
|
||||
authors = ["Sameer Puri <purisame@spuri.io>"]
|
||||
edition = "2018"
|
||||
description = "Convert paths in SVG files to GCode for a pen plotter"
|
||||
|
||||
[dependencies]
|
||||
cairo-rs = { version = "^0", features = ["svg"] }
|
||||
svgdom = "^0"
|
||||
lyon_geom = "^0"
|
||||
clap = "^2"
|
||||
log = "^0"
|
||||
env_logger = "^0"
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Sameer Puri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 28 KiB |
@ -1,270 +1,453 @@
|
||||
extern crate cairo;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
extern crate env_logger;
|
||||
extern crate svgdom;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate lyon_geom;
|
||||
|
||||
use cairo::{svg, Context};
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
fn main() {
|
||||
koch();
|
||||
sierpinski();
|
||||
arrowhead();
|
||||
dragon();
|
||||
plant();
|
||||
moore();
|
||||
hilbert();
|
||||
sierpinski_carpet();
|
||||
snowflake();
|
||||
gosper();
|
||||
kolam();
|
||||
crystal();
|
||||
}
|
||||
use lyon_geom::math;
|
||||
use svgdom::{AttributeId, AttributeValue, ElementId, ElementType, FilterSvg, PathSegment};
|
||||
|
||||
fn koch() {
|
||||
let mut out = File::create("out/koch.svg").unwrap();
|
||||
run(
|
||||
"F",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'F' => "F+F-F-F+F".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 2.,
|
||||
4,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
fn main() -> io::Result<()> {
|
||||
env_logger::init();
|
||||
let matches = clap_app!(svg2gcode =>
|
||||
(version: crate_version!())
|
||||
(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")
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
fn sierpinski() {
|
||||
let mut out = File::create("out/sierpinski.svg").unwrap();
|
||||
run(
|
||||
"F-G-G",
|
||||
&['F', 'G'],
|
||||
|c: char| match c {
|
||||
'F' => "F-G+F+G-F".chars().collect(),
|
||||
'G' => "GG".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI * 2. / 3.,
|
||||
4,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let input = match matches.value_of("FILE") {
|
||||
Some(filename) => {
|
||||
let mut f = File::open(filename)?;
|
||||
let len = f.metadata()?.len();
|
||||
let mut input = String::with_capacity(len as usize + 1);
|
||||
f.read_to_string(&mut input)?;
|
||||
input
|
||||
}
|
||||
None => {
|
||||
let mut input = String::new();
|
||||
io::stdin().read_to_string(&mut input)?;
|
||||
input
|
||||
}
|
||||
};
|
||||
|
||||
fn arrowhead() {
|
||||
let mut out = File::create("out/arrowhead.svg").unwrap();
|
||||
run(
|
||||
"A",
|
||||
&['A', 'B'],
|
||||
|c: char| match c {
|
||||
'A' => "B-A-B".chars().collect(),
|
||||
'B' => "A+B+A".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI * 1. / 3.,
|
||||
6,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let tolerance = matches
|
||||
.value_of("tolerance")
|
||||
.and_then(|x| x.parse().ok())
|
||||
.unwrap_or(0.1);
|
||||
let feedrate = matches
|
||||
.value_of("feedrate")
|
||||
.and_then(|x| x.parse().ok())
|
||||
.unwrap_or(3000.0);
|
||||
|
||||
fn dragon() {
|
||||
let mut out = File::create("out/dragon.svg").unwrap();
|
||||
run(
|
||||
"FX",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'X' => "X+YF+".chars().collect(),
|
||||
'Y' => "-FX-Y".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 2.,
|
||||
12,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let doc = svgdom::Document::from_str(&input).expect("Invalid or unsupported SVG file");
|
||||
|
||||
fn plant() {
|
||||
let mut out = File::create("out/plant.svg").unwrap();
|
||||
run(
|
||||
"X",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'X' => "F-[[X]+X]+F[+FX]-X".chars().collect(),
|
||||
'F' => "FF".chars().collect(),
|
||||
other => vec![other],
|
||||
let tool_on_action = vec![MachineCode::StopSpindle, MachineCode::Dwell { p: 1.5 }];
|
||||
let tool_off_action = vec![
|
||||
MachineCode::Dwell { p: 0.1 },
|
||||
MachineCode::StartSpindle {
|
||||
d: Direction::Clockwise,
|
||||
s: 40.0,
|
||||
},
|
||||
std::f64::consts::PI * 25.0 / 180.0,
|
||||
5,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
MachineCode::Dwell { p: 0.2 },
|
||||
];
|
||||
|
||||
fn moore() {
|
||||
let mut out = File::create("out/moore.svg").unwrap();
|
||||
run(
|
||||
"LFL+F+LFL",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'L' => "-RF+LFL+FR-".chars().collect(),
|
||||
'R' => "+LF-RFR-FL+".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI * 90.0 / 180.0,
|
||||
5,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let tool = std::cell::Cell::new(Tool::Off);
|
||||
let tool_on = |p: &mut Program| {
|
||||
if tool.get() == Tool::Off {
|
||||
tool_on_action.iter().for_each(|x| {
|
||||
p.push(x.clone());
|
||||
});
|
||||
tool.set(Tool::On);
|
||||
}
|
||||
};
|
||||
|
||||
fn hilbert() {
|
||||
let mut out = File::create("out/hilbert.svg").unwrap();
|
||||
run(
|
||||
"A",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'A' => "-BF+AFA+FB-".chars().collect(),
|
||||
'B' => "+AF-BFB-FA+".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 2.0,
|
||||
6,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let tool_off = |p: &mut Program| {
|
||||
if tool.get() == Tool::On {
|
||||
tool_off_action.iter().for_each(|x| {
|
||||
p.push(x.clone());
|
||||
});
|
||||
tool.set(Tool::Off);
|
||||
}
|
||||
};
|
||||
|
||||
fn sierpinski_carpet() {
|
||||
let mut out = File::create("out/sierpinski_carpet.svg").unwrap();
|
||||
run(
|
||||
"F+F+F+F",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'F' => "FF+F+F+F+FF".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 2.,
|
||||
4,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let is_absolute = std::cell::Cell::from(true);
|
||||
let incremental = |p: &mut Program| {
|
||||
if is_absolute.get() {
|
||||
p.push(MachineCode::IncrementalDistanceMode);
|
||||
is_absolute.set(false);
|
||||
}
|
||||
};
|
||||
let absolute = |p: &mut Program| {
|
||||
if !is_absolute.get() {
|
||||
p.push(MachineCode::AbsoluteDistanceMode);
|
||||
is_absolute.set(true);
|
||||
}
|
||||
};
|
||||
|
||||
fn snowflake() {
|
||||
let mut out = File::create("out/snowflake.svg").unwrap();
|
||||
run(
|
||||
"F++F++F",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'F' => "F-F++F-F".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 3.,
|
||||
4,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
let mut p = Program::new();
|
||||
p.push(MachineCode::UnitsMillimeters);
|
||||
tool_off(&mut p);
|
||||
p.push(MachineCode::RapidPositioning {
|
||||
x: 0.0.into(),
|
||||
y: 0.0.into(),
|
||||
});
|
||||
tool_on(&mut p);
|
||||
|
||||
fn gosper() {
|
||||
let mut out = File::create("out/gosper.svg").unwrap();
|
||||
run(
|
||||
"XF",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'X' => "X+YF++YF-FX--FXFX-YF+".chars().collect(),
|
||||
'Y' => "-FX+YFYF++YF+FX--FX-Y".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 3.,
|
||||
4,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
for (id, node) in doc.root().descendants().svg() {
|
||||
if node.is_graphic() {
|
||||
match id {
|
||||
ElementId::Path => {
|
||||
let attrs = node.attributes();
|
||||
if let Some(&AttributeValue::Path(ref path)) = attrs.get_value(AttributeId::D) {
|
||||
p.push(MachineCode::Named(Box::new(node.id().to_string())));
|
||||
let mut cx = 0.0;
|
||||
let mut cy = 0.0;
|
||||
for segment in path.iter() {
|
||||
match segment {
|
||||
PathSegment::MoveTo { abs, x, y } => {
|
||||
tool_off(&mut p);
|
||||
if *abs {
|
||||
absolute(&mut p);
|
||||
} else {
|
||||
incremental(&mut p);
|
||||
}
|
||||
p.push(MachineCode::RapidPositioning {
|
||||
x: (*x).into(),
|
||||
y: (*y).into(),
|
||||
});
|
||||
if *abs {
|
||||
cx = *x;
|
||||
cy = *y;
|
||||
} else {
|
||||
cx += *x;
|
||||
cy += *y;
|
||||
}
|
||||
}
|
||||
PathSegment::ClosePath { abs } => {
|
||||
tool_off(&mut p);
|
||||
}
|
||||
PathSegment::LineTo { abs, x, y } => {
|
||||
tool_on(&mut p);
|
||||
if *abs {
|
||||
absolute(&mut p);
|
||||
} else {
|
||||
incremental(&mut p);
|
||||
}
|
||||
p.push(MachineCode::LinearInterpolation {
|
||||
x: (*x).into(),
|
||||
y: (*y).into(),
|
||||
z: None,
|
||||
f: feedrate.into(),
|
||||
});
|
||||
if *abs {
|
||||
cx = *x;
|
||||
cy = *y;
|
||||
} else {
|
||||
cx += *x;
|
||||
cy += *y;
|
||||
}
|
||||
}
|
||||
PathSegment::HorizontalLineTo { abs, x } => {
|
||||
tool_on(&mut p);
|
||||
if *abs {
|
||||
absolute(&mut p);
|
||||
} else {
|
||||
incremental(&mut p);
|
||||
}
|
||||
p.push(MachineCode::LinearInterpolation {
|
||||
x: (*x).into(),
|
||||
y: None,
|
||||
z: None,
|
||||
f: feedrate.into(),
|
||||
});
|
||||
if *abs {
|
||||
cx = *x;
|
||||
} else {
|
||||
cx += *x;
|
||||
}
|
||||
}
|
||||
PathSegment::VerticalLineTo { abs, y } => {
|
||||
tool_on(&mut p);
|
||||
if *abs {
|
||||
absolute(&mut p);
|
||||
} else {
|
||||
incremental(&mut p);
|
||||
}
|
||||
p.push(MachineCode::LinearInterpolation {
|
||||
x: None,
|
||||
y: (*y).into(),
|
||||
z: None,
|
||||
f: feedrate.into(),
|
||||
});
|
||||
if *abs {
|
||||
cy = *y;
|
||||
} else {
|
||||
cy += *y;
|
||||
}
|
||||
}
|
||||
PathSegment::CurveTo {
|
||||
abs,
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
x,
|
||||
y,
|
||||
} => {
|
||||
println!("Curve {:?} starting at ({}, {})", segment, cx, cy);
|
||||
tool_on(&mut p);
|
||||
absolute(&mut p);
|
||||
let from = math::point(cx, cy);
|
||||
let ctrl1 = if *abs {
|
||||
math::point(*x1, *y1)
|
||||
} else {
|
||||
math::point(cx + *x1, cy + *y1)
|
||||
};
|
||||
let ctrl2 = if *abs {
|
||||
math::point(*x2, *y2)
|
||||
} else {
|
||||
math::point(cx + *x2, cy + *y2)
|
||||
};
|
||||
let to = if *abs {
|
||||
math::point(*x, *y)
|
||||
} else {
|
||||
math::point(cx + *x, cy + *y)
|
||||
};
|
||||
let cbs = lyon_geom::CubicBezierSegment {
|
||||
from,
|
||||
ctrl1,
|
||||
ctrl2,
|
||||
to,
|
||||
};
|
||||
let last_point = std::cell::Cell::new(math::point(cx, cy));
|
||||
cbs.flattened(tolerance).for_each(|point| {
|
||||
p.push(MachineCode::LinearInterpolation {
|
||||
x: point.x.into(),
|
||||
y: point.y.into(),
|
||||
z: None,
|
||||
f: feedrate.into(),
|
||||
});
|
||||
last_point.set(point);
|
||||
});
|
||||
cx = last_point.get().x;
|
||||
cy = last_point.get().y;
|
||||
}
|
||||
PathSegment::Quadratic { abs, x1, y1, x, y } => {
|
||||
tool_on(&mut p);
|
||||
absolute(&mut p);
|
||||
let from = math::point(cx, cy);
|
||||
let ctrl = if *abs {
|
||||
math::point(*x1, *y1)
|
||||
} else {
|
||||
math::point(cx + *x1, cy + *y1)
|
||||
};
|
||||
let to = if *abs {
|
||||
math::point(*x, *y)
|
||||
} else {
|
||||
math::point(cx + *x, cy + *y)
|
||||
};
|
||||
let qbs = lyon_geom::QuadraticBezierSegment { from, ctrl, to };
|
||||
let last_point = std::cell::Cell::new(math::point(cx, cy));
|
||||
qbs.flattened(tolerance).for_each(|point| {
|
||||
p.push(MachineCode::LinearInterpolation {
|
||||
x: point.x.into(),
|
||||
y: point.y.into(),
|
||||
z: None,
|
||||
f: feedrate.into(),
|
||||
});
|
||||
last_point.set(point);
|
||||
});
|
||||
cx = last_point.get().x;
|
||||
cy = last_point.get().y;
|
||||
}
|
||||
PathSegment::EllipticalArc {
|
||||
abs,
|
||||
rx,
|
||||
ry,
|
||||
x_axis_rotation,
|
||||
large_arc,
|
||||
sweep,
|
||||
x,
|
||||
y,
|
||||
} => {
|
||||
tool_on(&mut p);
|
||||
absolute(&mut p);
|
||||
let from = math::point(cx, cy);
|
||||
let to = if *abs {
|
||||
math::point(*x, *y)
|
||||
} else {
|
||||
math::point(cx + *x, cy + *y)
|
||||
};
|
||||
let sarc = lyon_geom::SvgArc {
|
||||
from,
|
||||
to,
|
||||
radii: math::vector(*rx, *ry),
|
||||
x_rotation: lyon_geom::euclid::Angle {
|
||||
radians: *x_axis_rotation,
|
||||
},
|
||||
flags: lyon_geom::ArcFlags {
|
||||
large_arc: *large_arc,
|
||||
sweep: *sweep,
|
||||
},
|
||||
};
|
||||
let last_point = std::cell::Cell::new(math::point(cx, cy));
|
||||
sarc.for_each_flattened(
|
||||
tolerance,
|
||||
&mut |point: math::F64Point| {
|
||||
p.push(MachineCode::LinearInterpolation {
|
||||
x: point.x.into(),
|
||||
y: point.y.into(),
|
||||
z: None,
|
||||
f: feedrate.into(),
|
||||
});
|
||||
last_point.set(point);
|
||||
},
|
||||
);
|
||||
cx = last_point.get().x;
|
||||
cy = last_point.get().y;
|
||||
}
|
||||
|
||||
fn kolam() {
|
||||
let mut out = File::create("out/kolam.svg").unwrap();
|
||||
run(
|
||||
"-D--D",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'A' => "F++FFFF--F--FFFF++F++FFFF--F".chars().collect(),
|
||||
'B' => "F--FFFF++F++FFFF--F--FFFF++F".chars().collect(),
|
||||
'C' => "BFA--BFA".chars().collect(),
|
||||
'D' => "CFC--CFC".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 4.0,
|
||||
6,
|
||||
&mut out,
|
||||
);
|
||||
}
|
||||
_ => panic!("Unsupported path segment type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
info!("Other {}", node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn crystal() {
|
||||
let mut out = File::create("out/crystal.svg").unwrap();
|
||||
run(
|
||||
"F+F+F+F",
|
||||
&['F'],
|
||||
|c: char| match c {
|
||||
'F' => "FF+F++F+F".chars().collect(),
|
||||
other => vec![other],
|
||||
},
|
||||
std::f64::consts::PI / 2.,
|
||||
4,
|
||||
&mut out,
|
||||
);
|
||||
tool_off(&mut p);
|
||||
p.push(MachineCode::RapidPositioning {
|
||||
x: 0.0.into(),
|
||||
y: 0.0.into(),
|
||||
});
|
||||
tool_on(&mut p);
|
||||
p.push(MachineCode::ProgramEnd);
|
||||
|
||||
program2gcode(p, File::create("out.gcode")?)
|
||||
}
|
||||
|
||||
fn run<F, W>(
|
||||
axiom: &str,
|
||||
variables_to_draw: &[char],
|
||||
rules: F,
|
||||
angle: f64,
|
||||
iterations: usize,
|
||||
writer: W,
|
||||
) where
|
||||
F: Fn(char) -> Vec<char> + Copy,
|
||||
W: Write,
|
||||
{
|
||||
let surf = svg::Writer::new(1024.0, 1024.0, writer);
|
||||
let ctx = Context::new(&surf);
|
||||
ctx.scale(1024., 1024.);
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
enum Direction {
|
||||
Clockwise,
|
||||
Anticlockwise,
|
||||
}
|
||||
|
||||
ctx.set_line_width(0.001);
|
||||
ctx.set_source_rgb(0., 0., 0.);
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
enum Tool {
|
||||
Off,
|
||||
On,
|
||||
}
|
||||
|
||||
let mut state = axiom.to_string();
|
||||
macro_rules! write_if_some {
|
||||
($w:expr, $s:expr, $v:ident) => {
|
||||
if let Some(v) = $v {
|
||||
write!($w, $s, v)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for _ in 0..iterations {
|
||||
state = state.chars().map(rules).flatten().collect();
|
||||
}
|
||||
#[derive(Clone, PartialEq)]
|
||||
enum MachineCode {
|
||||
RapidPositioning {
|
||||
x: Option<f64>,
|
||||
y: Option<f64>,
|
||||
},
|
||||
LinearInterpolation {
|
||||
x: Option<f64>,
|
||||
y: Option<f64>,
|
||||
z: Option<f64>,
|
||||
f: Option<f64>,
|
||||
},
|
||||
Dwell {
|
||||
p: f64,
|
||||
},
|
||||
UnitsInches,
|
||||
UnitsMillimeters,
|
||||
ProgramEnd,
|
||||
StartSpindle {
|
||||
d: Direction,
|
||||
s: f64,
|
||||
},
|
||||
StopSpindle,
|
||||
AbsoluteDistanceMode,
|
||||
IncrementalDistanceMode,
|
||||
Named(Box<String>),
|
||||
}
|
||||
|
||||
// let segment_count = state.chars().filter(|c| variables.contains(&c)).count();
|
||||
type Program = Vec<MachineCode>;
|
||||
|
||||
ctx.move_to(0.5, 0.5);
|
||||
let mut stack: Vec<((f64, f64), f64)> = vec![];
|
||||
let mut curangle = -std::f64::consts::PI / 2.0;
|
||||
for c in state.chars() {
|
||||
match c {
|
||||
'+' => {
|
||||
// ctx.rotate(angle);
|
||||
curangle += angle;
|
||||
fn program2gcode<W: Write>(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, "")?;
|
||||
}
|
||||
'-' => {
|
||||
// ctx.rotate(-angle);
|
||||
curangle -= angle;
|
||||
Dwell { p } => {
|
||||
writeln!(w, "G4 P{}", p)?;
|
||||
}
|
||||
'|' => {
|
||||
curangle = -curangle;
|
||||
UnitsInches => {
|
||||
writeln!(w, "G20")?;
|
||||
}
|
||||
'[' => {
|
||||
stack.push((ctx.get_current_point(), curangle));
|
||||
UnitsMillimeters => {
|
||||
writeln!(w, "G21")?;
|
||||
}
|
||||
']' => {
|
||||
let state = stack.pop().unwrap();
|
||||
ctx.move_to((state.0).0, (state.0).1);
|
||||
curangle = state.1;
|
||||
ProgramEnd => {
|
||||
writeln!(w, "M20")?;
|
||||
}
|
||||
other => {
|
||||
if variables_to_draw.contains(&other) {
|
||||
ctx.rel_line_to(0.005 * f64::cos(curangle), 0.005 * f64::sin(curangle));
|
||||
}
|
||||
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) => {
|
||||
writeln!(w, "({})", name)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in new issue