Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions g_code/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ repository.workspace = true
license.workspace = true

[features]
default = ["image"]
serde = ["dep:serde", "g-code/serde", "svg2star/serde"]
image = ["svg2star/image"]

[dependencies]
svg2star = { path = "../star", version = "0.4.0" }
Expand Down
194 changes: 120 additions & 74 deletions g_code/src/turtle.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::{borrow::Cow, fmt::Debug};

use ::g_code::{command, emit::Token};
use lyon_geom::{CubicBezierSegment, Point, QuadraticBezierSegment, SvgArc};
use lyon_geom::{Point, SvgArc};
use rust_decimal::{Decimal, prelude::*};
use svg2star::turtle::{
Turtle,
elements::{ArcOrLineSegment, FlattenWithArcs},
elements::{ArcOrLineSegment, DrawCommand, FillPolygon, FlattenWithArcs, Stroke},
};

use crate::machine::Machine;
Expand Down Expand Up @@ -42,7 +42,7 @@ impl<'input> GCodeTurtle<'input> {
/// Tolerance passed to [`lyon_geom`] calls for line segments.
///
/// Reserves some headroom so that [`Self::round`] won't take
/// the measurement outside of the overall tolernace bounds.
/// the measurement outside of the overall tolerance bounds.
///
/// i.e. e.g. 0.002 & 3 dp returns 0.0015 so we have +/- 0.0005
fn flattening_tolerance(&self) -> f64 {
Expand Down Expand Up @@ -77,6 +77,15 @@ impl<'input> GCodeTurtle<'input> {
}
}

fn line_to(&self, to: Point<f64>) -> Vec<Token<'input>> {
command!(LinearInterpolation {
X: self.round(to.x),
Y: self.round(to.y),
F: self.feedrate,
})
.into_token_vec()
}

fn circular_interpolation(&self, svg_arc: SvgArc<f64>) -> Vec<Token<'input>> {
debug_assert!((svg_arc.radii.x.abs() - svg_arc.radii.y.abs()).abs() < f64::EPSILON);
match (svg_arc.flags.large_arc, svg_arc.flags.sweep) {
Expand Down Expand Up @@ -129,90 +138,127 @@ impl<'input> Turtle for GCodeTurtle<'input> {
self.program.extend(self.machine.program_end());
}

fn comment(&mut self, comment: String) {
self.program.push(Token::Comment {
is_inline: false,
inner: Cow::Owned(comment),
});
}
fn stroke(&mut self, stroke: Stroke) {
let start = stroke.start_point();
let mut commands = stroke.into_commands().peekable();

fn move_to(&mut self, to: Point<f64>) {
self.tool_off();

// Comments should be inline after tool_off but before a rapid move.
while matches!(commands.peek(), Some(DrawCommand::Comment(_))) {
if let Some(DrawCommand::Comment(comment)) = commands.next() {
self.program.push(Token::Comment {
is_inline: false,
inner: Cow::Owned(comment),
});
}
}

self.program.append(
&mut command!(RapidPositioning {
X: self.round(to.x),
Y: self.round(to.y),
X: self.round(start.x),
Y: self.round(start.y),
})
.into_token_vec(),
);
}

fn line_to(&mut self, to: Point<f64>) {
self.tool_on();
self.program.append(
&mut command!(LinearInterpolation {
X: self.round(to.x),
Y: self.round(to.y),
F: self.feedrate,
})
.into_token_vec(),
);
}

fn arc(&mut self, svg_arc: SvgArc<f64>) {
if svg_arc.is_straight_line() {
self.line_to(svg_arc.to);
return;
for command in commands {
match command {
DrawCommand::LineTo { from: _, to } => {
self.program.append(
&mut command!(LinearInterpolation {
X: self.round(to.x),
Y: self.round(to.y),
F: self.feedrate,
})
.into_token_vec(),
);
}
DrawCommand::Arc(svg_arc) => {
if self
.machine
.supported_functionality()
.circular_interpolation
{
FlattenWithArcs::flattened(&svg_arc, self.arc_flattening_tolerance())
.into_iter()
.for_each(|segment| match segment {
ArcOrLineSegment::Arc(arc) => {
self.program.append(&mut self.circular_interpolation(arc))
}
ArcOrLineSegment::Line(line) => {
self.line_to(line.to);
}
});
} else {
svg_arc
.to_arc()
.flattened(self.flattening_tolerance())
.for_each(|point| self.program.append(&mut self.line_to(point)));
};
}
DrawCommand::CubicBezier(cbs) => {
if self
.machine
.supported_functionality()
.circular_interpolation
{
FlattenWithArcs::<f64>::flattened(&cbs, self.arc_flattening_tolerance())
.into_iter()
.for_each(|segment| match segment {
ArcOrLineSegment::Arc(arc) => {
self.program.append(&mut self.circular_interpolation(arc))
}
ArcOrLineSegment::Line(line) => {
self.program.append(&mut self.line_to(line.to))
}
});
} else {
cbs.flattened(self.flattening_tolerance())
.for_each(|point| self.program.append(&mut self.line_to(point)));
};
}
DrawCommand::QuadraticBezier(qbs) => {
if self
.machine
.supported_functionality()
.circular_interpolation
{
FlattenWithArcs::<f64>::flattened(
&qbs.to_cubic(),
self.arc_flattening_tolerance(),
)
.into_iter()
.for_each(|segment| match segment {
ArcOrLineSegment::Arc(arc) => {
self.program.append(&mut self.circular_interpolation(arc))
}
ArcOrLineSegment::Line(line) => {
self.program.append(&mut self.line_to(line.to))
}
});
} else {
qbs.flattened(self.flattening_tolerance())
.for_each(|point| self.program.append(&mut self.line_to(point)));
};
}
DrawCommand::Comment(comment) => {
self.program.push(Token::Comment {
is_inline: false,
inner: Cow::Owned(comment),
});
}
}
}

self.tool_on();

if self
.machine
.supported_functionality()
.circular_interpolation
{
FlattenWithArcs::flattened(&svg_arc, self.arc_flattening_tolerance())
.into_iter()
.for_each(|segment| match segment {
ArcOrLineSegment::Arc(arc) => {
self.program.append(&mut self.circular_interpolation(arc))
}
ArcOrLineSegment::Line(line) => {
self.line_to(line.to);
}
});
} else {
svg_arc
.to_arc()
.flattened(self.flattening_tolerance())
.for_each(|point| self.line_to(point));
};
}

fn cubic_bezier(&mut self, cbs: CubicBezierSegment<f64>) {
self.tool_on();

if self
.machine
.supported_functionality()
.circular_interpolation
{
FlattenWithArcs::<f64>::flattened(&cbs, self.arc_flattening_tolerance())
.into_iter()
.for_each(|segment| match segment {
ArcOrLineSegment::Arc(arc) => {
self.program.append(&mut self.circular_interpolation(arc))
}
ArcOrLineSegment::Line(line) => self.line_to(line.to),
});
} else {
cbs.flattened(self.flattening_tolerance())
.for_each(|point| self.line_to(point));
};
#[cfg(feature = "image")]
fn image(&mut self, _image: svg2star::turtle::elements::RasterImage) {
// TODO (?)
}

fn quadratic_bezier(&mut self, qbs: QuadraticBezierSegment<f64>) {
self.cubic_bezier(qbs.to_cubic());
fn fill_polygon(&mut self, _polygon: FillPolygon) {
// TODO
}
}
2 changes: 2 additions & 0 deletions star/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![deny(unused_crate_dependencies)]
#[cfg(test)]
use serde_json as _;

/// Lowers an SVG to an intermediate representation that's easier to work when generating machine code.
pub mod lower;
Expand Down
Loading