reduce string allocations to minimum possible w/ g-code v0.1.1

master
Sameer Puri 4 years ago
parent 126217a21f
commit 178448be7b

9
Cargo.lock generated

@ -133,13 +133,12 @@ checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e"
[[package]]
name = "g-code"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60958e3fe54d293ae76fb1e0c433161595a8dbe9c1c74663b24f76b9f98b962c"
checksum = "a4b0866fca554553ea6d676c69ce31f6260ceb5680d0e8bb3bf6711f89799a38"
dependencies = [
"codespan",
"codespan-reporting",
"lazy_static",
"num",
"num-rational",
"paste",
@ -178,9 +177,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.93"
version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "log"

@ -6,7 +6,7 @@ edition = "2018"
description = "Convert paths in SVG files to GCode for a pen plotter, laser engraver, or other machine."
[dependencies]
g-code = "0.1.0"
g-code = "0.1.1"
lyon_geom = "0"
euclid = "0.22"
structopt = "0.3"

@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::str::FromStr;
use g_code::{command, emit::Token};
@ -10,7 +11,6 @@ use svgtypes::{
LengthListParser, PathParser, PathSegment, TransformListParser, TransformListToken, ViewBox,
};
use crate::machine::*;
use crate::turtle::*;
/// High-level output options
@ -34,9 +34,11 @@ impl Default for ProgramOptions {
}
}
pub fn svg2program(doc: &Document, options: ProgramOptions, mach: Machine) -> Vec<Token> {
let mut turtle = Turtle::new(mach);
pub fn svg2program<'input>(
doc: &Document,
options: ProgramOptions,
turtle: &'input mut Turtle<'input>,
) -> Vec<Token<'input>> {
let mut program = command!(UnitsMillimeters {})
.into_token_vec()
.drain(..)
@ -123,9 +125,9 @@ pub fn svg2program(doc: &Document, options: ProgramOptions, mach: Machine) -> Ve
comment += &node_name(&node);
program.push(Token::Comment {
is_inline: false,
inner: comment,
inner: Cow::Owned(comment),
});
program.extend(apply_path(&mut turtle, &options, d));
program.extend(apply_path(turtle, &options, d));
} else {
warn!("There is a path node containing no actual path: {:?}", node);
}
@ -188,7 +190,11 @@ fn width_and_height_into_transform(
}
}
fn apply_path(turtle: &mut Turtle, options: &ProgramOptions, path: &str) -> Vec<Token> {
fn apply_path<'a, 'input>(
turtle: &'a mut Turtle<'input>,
options: &ProgramOptions,
path: &str,
) -> Vec<Token<'input>> {
use PathSegment::*;
PathParser::from(path)
.map(|segment| segment.expect("could not parse path segment"))

@ -1,8 +1,4 @@
use g_code::{
command,
emit::Token,
parse::{ast::Snippet, token::Field},
};
use g_code::{command, emit::Token, parse::ast::Snippet};
/// Whether the tool is active (i.e. cutting)
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -52,13 +48,13 @@ pub struct Machine<'input> {
impl<'input> Machine<'input> {
/// Output gcode to turn the tool on.
pub fn tool_on<'a>(&'a mut self) -> Vec<Token> {
pub fn tool_on(&mut self) -> Vec<Token<'input>> {
if self.tool_state == Some(Tool::Off) || self.tool_state == None {
self.tool_state = Some(Tool::On);
self.tool_on_action
.iter()
.flat_map(|s| s.iter_fields())
.map(|f: &Field| Token::from(f))
.map(Token::from)
.collect()
} else {
vec![]
@ -66,36 +62,36 @@ impl<'input> Machine<'input> {
}
/// Output gcode to turn the tool off.
pub fn tool_off<'a>(&'a mut self) -> Vec<Token> {
pub fn tool_off(&mut self) -> Vec<Token<'input>> {
if self.tool_state == Some(Tool::On) || self.tool_state == None {
self.tool_state = Some(Tool::Off);
self.tool_off_action
.iter()
.flat_map(|s| s.iter_fields())
.map(|f: &Field| Token::from(f))
.map(Token::from)
.collect()
} else {
vec![]
}
}
pub fn program_begin<'a>(&'a self) -> Vec<Token> {
pub fn program_begin(&self) -> Vec<Token<'input>> {
self.program_begin_sequence
.iter()
.flat_map(|s| s.iter_fields())
.map(|f: &Field| Token::from(f))
.map(Token::from)
.collect()
}
pub fn program_end<'a>(&'a self) -> Vec<Token> {
pub fn program_end(&self) -> Vec<Token<'input>> {
self.program_end_sequence
.iter()
.flat_map(|s| s.iter_fields())
.map(|f: &Field| Token::from(f))
.map(Token::from)
.collect()
}
/// Output absolute distance field if mode was relative or unknown.
pub fn absolute(&mut self) -> Vec<Token> {
pub fn absolute(&mut self) -> Vec<Token<'input>> {
if self.distance_mode == Some(Distance::Relative) || self.distance_mode == None {
self.distance_mode = Some(Distance::Absolute);
command!(AbsoluteDistanceMode {}).into_token_vec()
@ -105,7 +101,7 @@ impl<'input> Machine<'input> {
}
/// Output relative distance field if mode was absolute or unknown.
pub fn relative(&mut self) -> Vec<Token> {
pub fn relative(&mut self) -> Vec<Token<'input>> {
if self.distance_mode == Some(Distance::Absolute) || self.distance_mode == None {
self.distance_mode = Some(Distance::Relative);
command!(RelativeDistanceMode {}).into_token_vec()

@ -6,7 +6,7 @@ use std::fs::File;
use std::io::{self, Read};
use std::path::PathBuf;
use g_code::parse::{ast::Snippet, ParseError, snippet_parser};
use g_code::parse::{ast::Snippet, snippet_parser, ParseError};
use structopt::StructOpt;
/// Converts an SVG to GCode in an internal representation
@ -22,6 +22,7 @@ mod turtle;
use converter::ProgramOptions;
use machine::Machine;
use turtle::Turtle;
#[derive(Debug, StructOpt)]
#[structopt(name = "svg2gcode", author, about)]
@ -81,10 +82,11 @@ fn main() -> io::Result<()> {
}
};
let mut options = ProgramOptions::default();
options.tolerance = opt.tolerance;
options.feedrate = opt.feedrate;
options.dpi = opt.dpi;
let options = ProgramOptions {
tolerance: opt.tolerance,
feedrate: opt.feedrate,
dpi: opt.dpi,
};
let snippets = [
opt.tool_on_sequence.as_ref().map(parse_snippet).transpose(),
@ -139,7 +141,8 @@ fn main() -> io::Result<()> {
let document = roxmltree::Document::parse(&input).expect("Invalid or unsupported SVG file");
let mut program = converter::svg2program(&document, options, machine);
let mut turtle = Turtle::new(machine);
let mut program = converter::svg2program(&document, options, &mut turtle);
let origin = opt
.origin
@ -155,12 +158,12 @@ fn main() -> io::Result<()> {
}
}
fn parse_snippet<'input>(gcode: &'input String) -> Result<Snippet<'input>, ParseError> {
fn parse_snippet(gcode: &'_ String) -> Result<Snippet<'_>, ParseError> {
snippet_parser(gcode)
}
fn tokens_into_gcode<W: std::io::Write>(
program: Vec<g_code::emit::Token>,
program: Vec<g_code::emit::Token<'_>>,
mut w: W,
) -> io::Result<()> {
use g_code::emit::Token::*;
@ -169,8 +172,8 @@ fn tokens_into_gcode<W: std::io::Write>(
match token {
Field(f) => {
if !preceded_by_newline {
if matches!(f.letters.as_str(), "G" | "M") {
writeln!(w, "")?;
if matches!(f.letters.as_ref(), "G" | "M") {
writeln!(w)?;
} else {
write!(w, " ")?;
}
@ -197,7 +200,7 @@ fn tokens_into_gcode<W: std::io::Write>(
}
// Ensure presence of trailing newline
if !preceded_by_newline {
writeln!(w, "")?;
writeln!(w)?;
}
Ok(())
}
@ -205,6 +208,7 @@ fn tokens_into_gcode<W: std::io::Write>(
#[cfg(test)]
mod test {
use super::*;
use crate::turtle::Turtle;
use pretty_assertions::assert_eq;
fn get_actual(input: &str) -> String {
@ -219,7 +223,8 @@ mod test {
};
let document = roxmltree::Document::parse(input).unwrap();
let mut program = converter::svg2program(&document, options, machine);
let mut turtle = Turtle::new(machine);
let mut program = converter::svg2program(&document, options, &mut turtle);
postprocess::set_origin(&mut program, lyon_geom::point(0., 0.));
let mut actual = vec![];

@ -7,15 +7,15 @@ use lyon_geom::{point, vector, Point};
type F64Point = Point<f64>;
/// Moves all the commands so that they are beyond a specified position
pub fn set_origin(tokens: &mut [Token], origin: F64Point) {
pub fn set_origin(tokens: &mut [Token<'_>], origin: F64Point) {
let offset = -get_bounding_box(tokens.iter()).min.to_vector() + origin.to_vector();
let mut is_relative = false;
let mut current_position = point(0f64, 0f64);
let x = "X".to_string();
let y = "Y".to_string();
let abs_tok = Token::Field(ABSOLUTE_DISTANCE_MODE_FIELD.clone());
let rel_tok = Token::Field(RELATIVE_DISTANCE_MODE_FIELD.clone());
let x = "X";
let y = "Y";
let abs_tok = Token::Field(ABSOLUTE_DISTANCE_MODE_FIELD);
let rel_tok = Token::Field(RELATIVE_DISTANCE_MODE_FIELD);
for token in tokens {
match token {
abs if *abs == abs_tok => is_relative = false,
@ -45,14 +45,14 @@ pub fn set_origin(tokens: &mut [Token], origin: F64Point) {
}
}
fn get_bounding_box<'a, I: Iterator<Item = &'a Token>>(tokens: I) -> Box2D<f64> {
fn get_bounding_box<'a, I: Iterator<Item = &'a Token<'a>>>(tokens: I) -> Box2D<f64> {
let (mut minimum, mut maximum) = (point(0f64, 0f64), point(0f64, 0f64));
let mut is_relative = false;
let mut current_position = point(0f64, 0f64);
let x = "X".to_string();
let y = "Y".to_string();
let abs_tok = Token::Field(ABSOLUTE_DISTANCE_MODE_FIELD.clone());
let rel_tok = Token::Field(RELATIVE_DISTANCE_MODE_FIELD.clone());
let x = "X";
let y = "Y";
let abs_tok = Token::Field(ABSOLUTE_DISTANCE_MODE_FIELD);
let rel_tok = Token::Field(RELATIVE_DISTANCE_MODE_FIELD);
for token in tokens {
match token {
abs if *abs == abs_tok => is_relative = false,

@ -6,6 +6,7 @@ use g_code::{
use lyon_geom::euclid::{default::Transform2D, Angle};
use lyon_geom::{point, vector, Point};
use lyon_geom::{ArcFlags, CubicBezierSegment, QuadraticBezierSegment, SvgArc};
use std::borrow::Cow;
type F64Point = Point<f64>;
@ -36,7 +37,7 @@ impl<'input> Turtle<'input> {
/// 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<X, Y>(&mut self, abs: bool, x: X, y: Y) -> Vec<Token>
pub fn move_to<X, Y>(&mut self, abs: bool, x: X, y: Y) -> Vec<Token<'input>>
where
X: Into<Option<f64>>,
Y: Into<Option<f64>>,
@ -84,17 +85,17 @@ impl<'input> Turtle<'input> {
.collect()
}
fn linear_interpolation(x: f64, y: f64, z: Option<f64>, f: Option<f64>) -> Vec<Token> {
fn linear_interpolation(x: f64, y: f64, z: Option<f64>, f: Option<f64>) -> Vec<Token<'static>> {
let mut linear_interpolation = command! {LinearInterpolation { X: x, Y: y, }};
if let Some(z) = z {
linear_interpolation.push(Field {
letters: "Z".to_string(),
letters: Cow::Borrowed("Z"),
value: Value::Float(z),
});
}
if let Some(f) = f {
linear_interpolation.push(Field {
letters: "F".into(),
letters: Cow::Borrowed("F"),
value: Value::Float(f),
});
}
@ -103,7 +104,7 @@ impl<'input> Turtle<'input> {
/// Close an SVG path, cutting back to its initial position
/// https://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
pub fn close<Z, F>(&mut self, z: Z, f: F) -> Vec<Token>
pub fn close<Z, F>(&mut self, z: Z, f: F) -> Vec<Token<'input>>
where
Z: Into<Option<f64>>,
F: Into<Option<f64>>,
@ -134,7 +135,7 @@ impl<'input> Turtle<'input> {
/// 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<X, Y, Z, F>(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Vec<Token>
pub fn line<X, Y, Z, F>(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Vec<Token<'input>>
where
X: Into<Option<f64>>,
Y: Into<Option<f64>>,
@ -186,7 +187,7 @@ impl<'input> Turtle<'input> {
tolerance: f64,
z: Z,
f: F,
) -> Vec<Token> {
) -> Vec<Token<'input>> {
let z = z.into();
let f = f.into();
let last_point = std::cell::Cell::new(self.current_position);
@ -227,7 +228,7 @@ impl<'input> Turtle<'input> {
tolerance: f64,
z: Z,
f: F,
) -> Vec<Token>
) -> Vec<Token<'input>>
where
Z: Into<Option<f64>>,
F: Into<Option<f64>>,
@ -269,7 +270,7 @@ impl<'input> Turtle<'input> {
tolerance: f64,
z: Z,
f: F,
) -> Vec<Token>
) -> Vec<Token<'input>>
where
Z: Into<Option<f64>>,
F: Into<Option<f64>>,
@ -307,7 +308,7 @@ impl<'input> Turtle<'input> {
tolerance: f64,
z: Z,
f: F,
) -> Vec<Token>
) -> Vec<Token<'input>>
where
Z: Into<Option<f64>>,
F: Into<Option<f64>>,
@ -339,7 +340,7 @@ impl<'input> Turtle<'input> {
tolerance: f64,
z: Z,
f: F,
) -> Vec<Token>
) -> Vec<Token<'input>>
where
Z: Into<Option<f64>>,
F: Into<Option<f64>>,
@ -376,7 +377,7 @@ impl<'input> Turtle<'input> {
z: Z,
f: F,
tolerance: f64,
) -> Vec<Token>
) -> Vec<Token<'input>>
where
Z: Into<Option<f64>>,
F: Into<Option<f64>>,

Loading…
Cancel
Save