@ -40,42 +40,64 @@ fn main() -> io::Result<()> {
}
}
} ;
} ;
let tolerance = matches
let mut opts = MachineOptions ::default ( ) ;
. value_of ( "tolerance" )
. and_then ( | x | x . parse ( ) . ok ( ) )
if let Some ( tolerance ) = matches . value_of ( "tolerance" ) . and_then ( | x | x . parse ( ) . ok ( ) ) {
. unwrap_or ( 0.1 ) ;
opts . tolerance = tolerance ;
let feedrate = matches
}
. value_of ( "feedrate" )
if let Some ( feedrate ) = matches . value_of ( "feedrate" ) . and_then ( | x | x . parse ( ) . ok ( ) ) {
. and_then ( | x | x . parse ( ) . ok ( ) )
opts . feedrate = feedrate ;
. unwrap_or ( 3000.0 ) ;
}
let doc = svgdom ::Document ::from_str ( & input ) . expect ( "Invalid or unsupported SVG file" ) ;
let doc = svgdom ::Document ::from_str ( & input ) . expect ( "Invalid or unsupported SVG file" ) ;
let tool_on_action = vec! [ MachineCode ::StopSpindle , MachineCode ::Dwell { p : 1.5 } ] ;
let prog = svg2program ( & doc , opts ) ;
let tool_off_action = vec! [
program2gcode ( & prog , File ::create ( "out.gcode" ) ? )
MachineCode ::Dwell { p : 0.1 } ,
}
MachineCode ::StartSpindle {
d : Direction ::Clockwise ,
struct MachineOptions {
s : 40.0 ,
tolerance : f64 ,
} ,
feedrate : f64 ,
MachineCode ::Dwell { p : 0.2 } ,
tool_on_action : Vec < MachineCode > ,
] ;
tool_off_action : Vec < MachineCode > ,
}
impl Default for MachineOptions {
fn default ( ) -> Self {
Self {
tolerance : 0.1 ,
feedrate : 3000.0 ,
tool_on_action : vec ! [ MachineCode ::StopSpindle , MachineCode ::Dwell { p : 1.5 } ] ,
tool_off_action : vec ! [
MachineCode ::Dwell { p : 0.1 } ,
MachineCode ::StartSpindle {
d : Direction ::Clockwise ,
s : 40.0 ,
} ,
MachineCode ::Dwell { p : 0.2 } ,
] ,
}
}
}
let tool = std ::cell ::Cell ::new ( Tool ::Off ) ;
fn svg2program ( doc : & svgdom ::Document , opts : MachineOptions ) -> Program {
let tool = std ::cell ::Cell ::new ( Tool ::On ) ;
let tool_on = | p : & mut Program | {
let tool_on = | p : & mut Program | {
if tool . get ( ) = = Tool ::Off {
if tool . get ( ) = = Tool ::Off {
tool_on_action . iter ( ) . for_each ( | x | {
opts . tool_on_action
p . push ( x . clone ( ) ) ;
. iter ( )
} ) ;
. map ( Clone ::clone )
. for_each ( | x | p . push ( x ) ) ;
tool . set ( Tool ::On ) ;
tool . set ( Tool ::On ) ;
}
}
} ;
} ;
let tool_off = | p : & mut Program | {
let tool_off = | p : & mut Program | {
if tool . get ( ) = = Tool ::On {
if tool . get ( ) = = Tool ::On {
tool_off_action . iter ( ) . for_each ( | x | {
opts . tool_off_action
p . push ( x . clone ( ) ) ;
. iter ( )
} ) ;
. map ( Clone ::clone )
. for_each ( | x | p . push ( x ) ) ;
tool . set ( Tool ::Off ) ;
tool . set ( Tool ::Off ) ;
}
}
} ;
} ;
@ -93,6 +115,16 @@ fn main() -> io::Result<()> {
is_absolute . set ( true ) ;
is_absolute . set ( true ) ;
}
}
} ;
} ;
let select_mode = | p : & mut Program , abs : bool | {
if abs {
absolute ( p ) ;
} else {
incremental ( p ) ;
}
} ;
let mut current_transform = lyon_geom ::euclid ::Transform2D ::default ( ) ;
let mut transform_stack = vec! [ ] ;
let mut p = Program ::new ( ) ;
let mut p = Program ::new ( ) ;
p . push ( MachineCode ::UnitsMillimeters ) ;
p . push ( MachineCode ::UnitsMillimeters ) ;
@ -103,97 +135,114 @@ fn main() -> io::Result<()> {
} ) ;
} ) ;
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
for ( id , node ) in doc . root ( ) . descendants ( ) . svg ( ) {
for edge in doc . root ( ) . traverse ( ) {
if node . is_graphic ( ) {
let ( node , is_start ) = match edge {
svgdom ::NodeEdge ::Start ( node ) = > ( node , true ) ,
svgdom ::NodeEdge ::End ( node ) = > ( node , false ) ,
} ;
let id = if let svgdom ::QName ::Id ( id ) = * node . tag_name ( ) {
id
} else {
continue ;
} ;
if let ( ElementId ::G , true ) = ( id , is_start ) {
p . push ( MachineCode ::Named ( Box ::new ( node . id ( ) . to_string ( ) ) ) ) ;
}
if let Some ( & AttributeValue ::Transform ( ref t ) ) =
node . attributes ( ) . get_value ( AttributeId ::Transform )
{
if is_start {
transform_stack . push ( current_transform ) ;
current_transform = current_transform . post_mul ( & lyon_geom ::euclid ::Transform2D ::row_major ( t . a , t . b , t . c , t . d , t . e , t . f ) ) ;
} else {
current_transform = transform_stack . pop ( ) . unwrap ( ) ;
}
}
if node . is_graphic ( ) & & is_start {
match id {
match id {
ElementId ::Path = > {
ElementId ::Path = > {
let attrs = node . attributes ( ) ;
let attrs = node . attributes ( ) ;
if let Some ( & AttributeValue ::Path ( ref path ) ) = attrs . get_value ( AttributeId ::D ) {
if let Some ( & AttributeValue ::Path ( ref path ) ) = attrs . get_value ( AttributeId ::D ) {
p . push ( MachineCode ::Named ( Box ::new ( node . id ( ) . to_string ( ) ) ) ) ;
p . push ( MachineCode ::Named ( Box ::new ( node . id ( ) . to_string ( ) ) ) ) ;
let mut cx = 0.0 ;
let mut curpos = math ::point ( 0.0 , 0.0 ) ;
let mut cy = 0.0 ;
curpos = current_transform . transform_point ( & curpos ) ;
let mut prev_ctrl = curpos ;
for segment in path . iter ( ) {
for segment in path . iter ( ) {
match segment {
match segment {
PathSegment ::MoveTo { abs , x , y } = > {
PathSegment ::MoveTo { abs , x , y } = > {
tool_off ( & mut p ) ;
tool_off ( & mut p ) ;
if * abs {
select_mode ( & mut p , * abs ) ;
absolute ( & mut p ) ;
let mut to = math ::point ( * x , * y ) ;
} else {
to = current_transform . transform_point ( & to ) ;
incremental ( & mut p ) ;
}
p . push ( MachineCode ::RapidPositioning {
p . push ( MachineCode ::RapidPositioning {
x : ( * x ) . into ( ) ,
x : to . x . into ( ) ,
y : ( * y ) . into ( ) ,
y : to . y . into ( ) ,
} ) ;
} ) ;
if * abs {
if * abs {
cx = * x ;
curpos = to ;
cy = * y ;
} else {
} else {
cx + = * x ;
curpos + = to . to_vector ( ) ;
cy + = * y ;
}
}
prev_ctrl = curpos ;
}
}
PathSegment ::ClosePath { abs } = > {
PathSegment ::ClosePath { abs } = > {
tool_off ( & mut p ) ;
tool_off ( & mut p ) ;
}
}
PathSegment ::LineTo { abs , x , y } = > {
PathSegment ::LineTo { abs , x , y } = > {
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
if * abs {
select_mode ( & mut p , * abs ) ;
absolute ( & mut p ) ;
let mut to = math ::point ( * x , * y ) ;
} else {
to = current_transform . transform_point ( & to ) ;
incremental ( & mut p ) ;
}
p . push ( MachineCode ::LinearInterpolation {
p . push ( MachineCode ::LinearInterpolation {
x : ( * x ) . into ( ) ,
x : to . x . into ( ) ,
y : ( * y ) . into ( ) ,
y : to . y . into ( ) ,
z : None ,
z : None ,
f : feedrate. into ( ) ,
f : opts. feedrate. into ( ) ,
} ) ;
} ) ;
if * abs {
if * abs {
cx = * x ;
curpos = to ;
cy = * y ;
} else {
} else {
cx + = * x ;
curpos + = to . to_vector ( ) ;
cy + = * y ;
}
}
prev_ctrl = curpos ;
}
}
PathSegment ::HorizontalLineTo { abs , x } = > {
PathSegment ::HorizontalLineTo { abs , x } = > {
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
if * abs {
select_mode ( & mut p , * abs ) ;
absolute ( & mut p ) ;
let inv_transform = current_transform . inverse ( ) . expect ( "could not invert transform" ) ;
} else {
let mut to = math ::point ( * x , inv_transform . transform_point ( & curpos ) . y ) ;
incremental ( & mut p ) ;
to = current_transform . transform_point ( & to ) ;
}
p . push ( MachineCode ::LinearInterpolation {
p . push ( MachineCode ::LinearInterpolation {
x : ( * x ) . into ( ) ,
x : to . x . into ( ) ,
y : None ,
y : to . y . into ( ) ,
z : None ,
z : None ,
f : feedrate. into ( ) ,
f : opts. feedrate. into ( ) ,
} ) ;
} ) ;
if * abs {
if * abs {
c x = * x ;
c urpos = to ;
} else {
} else {
c x + = * x ;
c urpos + = to . to_vector ( ) ;
}
}
prev_ctrl = curpos ;
}
}
PathSegment ::VerticalLineTo { abs , y } = > {
PathSegment ::VerticalLineTo { abs , y } = > {
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
if * abs {
select_mode ( & mut p , * abs ) ;
absolute ( & mut p ) ;
let inv_transform = current_transform . inverse ( ) . expect ( "could not invert transform" ) ;
} else {
let mut to = math ::point ( inv_transform . transform_point ( & curpos ) . x , * y ) ;
incremental ( & mut p ) ;
to = current_transform . transform_point ( & to ) ;
}
p . push ( MachineCode ::LinearInterpolation {
p . push ( MachineCode ::LinearInterpolation {
x : None ,
x : to . x . into ( ) ,
y : ( * y ) . into ( ) ,
y : to . y . into ( ) ,
z : None ,
z : None ,
f : feedrate. into ( ) ,
f : opts. feedrate. into ( ) ,
} ) ;
} ) ;
if * abs {
if * abs {
c y = * y ;
c urpos = to ;
} else {
} else {
c y + = * y ;
c urpos + = to . to_vector ( ) ;
}
}
prev_ctrl = curpos ;
}
}
PathSegment ::CurveTo {
PathSegment ::CurveTo {
abs ,
abs ,
@ -204,71 +253,122 @@ fn main() -> io::Result<()> {
x ,
x ,
y ,
y ,
} = > {
} = > {
println! ( "Curve {:?} starting at ({}, {})" , segment , cx , cy ) ;
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
absolute ( & mut p ) ;
absolute ( & mut p ) ;
let from = math ::point ( cx , cy ) ;
let from = curpos ;
let ctrl1 = if * abs {
let mut ctrl1 = math ::point ( * x1 , * y1 ) ;
math ::point ( * x1 , * y1 )
ctrl1 = current_transform . transform_point ( & ctrl1 ) ;
} else {
let mut ctrl2 = math ::point ( * x2 , * y2 ) ;
math ::point ( cx + * x1 , cy + * y1 )
ctrl2 = current_transform . transform_point ( & ctrl2 ) ;
} ;
let mut to = math ::point ( * x , * y ) ;
let ctrl2 = if * abs {
to = current_transform . transform_point ( & to ) ;
math ::point ( * x2 , * y2 )
if ! * abs {
} else {
ctrl1 + = curpos . to_vector ( ) ;
math ::point ( cx + * x2 , cy + * y2 )
ctrl2 + = curpos . to_vector ( ) ;
} ;
to + = curpos . to_vector ( ) ;
let to = if * abs {
}
math ::point ( * x , * y )
let cbs = lyon_geom ::CubicBezierSegment {
} else {
from ,
math ::point ( cx + * x , cy + * y )
ctrl1 ,
ctrl2 ,
to ,
} ;
} ;
let last_point = std ::cell ::Cell ::new ( curpos ) ;
cbs . flattened ( opts . tolerance ) . for_each ( | point | {
p . push ( MachineCode ::LinearInterpolation {
x : point . x . into ( ) ,
y : point . y . into ( ) ,
z : None ,
f : opts . feedrate . into ( ) ,
} ) ;
last_point . set ( point ) ;
} ) ;
curpos = last_point . get ( ) ;
prev_ctrl = ctrl1 ;
}
PathSegment ::SmoothCurveTo { abs , x2 , y2 , x , y } = > {
tool_on ( & mut p ) ;
absolute ( & mut p ) ;
let from = curpos ;
let mut ctrl1 = prev_ctrl ;
let mut ctrl2 = math ::point ( * x2 , * y2 ) ;
ctrl2 = current_transform . transform_point ( & ctrl2 ) ;
let mut to = math ::point ( * x , * y ) ;
to = current_transform . transform_point ( & to ) ;
if ! * abs {
ctrl1 + = curpos . to_vector ( ) ;
ctrl2 + = curpos . to_vector ( ) ;
to + = curpos . to_vector ( ) ;
}
let cbs = lyon_geom ::CubicBezierSegment {
let cbs = lyon_geom ::CubicBezierSegment {
from ,
from ,
ctrl1 ,
ctrl1 ,
ctrl2 ,
ctrl2 ,
to ,
to ,
} ;
} ;
let last_point = std ::cell ::Cell ::new ( math ::point ( cx , cy ) ) ;
let last_point = std ::cell ::Cell ::new ( curpos ) ;
cbs . flattened ( tolerance ) . for_each ( | point | {
cbs . flattened ( opts. tolerance) . for_each ( | point | {
p . push ( MachineCode ::LinearInterpolation {
p . push ( MachineCode ::LinearInterpolation {
x : point . x . into ( ) ,
x : point . x . into ( ) ,
y : point . y . into ( ) ,
y : point . y . into ( ) ,
z : None ,
z : None ,
f : feedrate . into ( ) ,
f : opts. feedrate. into ( ) ,
} ) ;
} ) ;
last_point . set ( point ) ;
last_point . set ( point ) ;
} ) ;
} ) ;
cx = last_point . get ( ) . x ;
c urpos = last_point . get ( ) ;
cy = last_point . get ( ) . y ;
prev_ctrl = ctrl1 ;
}
}
PathSegment ::Quadratic { abs , x1 , y1 , x , y } = > {
PathSegment ::Quadratic { abs , x1 , y1 , x , y } = > {
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
absolute ( & mut p ) ;
absolute ( & mut p ) ;
let from = math ::point ( cx , cy ) ;
let from = curpos ;
let ctrl = if * abs {
let mut ctrl = math ::point ( * x1 , * y1 ) ;
math ::point ( * x1 , * y1 )
ctrl = current_transform . transform_point ( & ctrl ) ;
} else {
let mut to = math ::point ( * x , * y ) ;
math ::point ( cx + * x1 , cy + * y1 )
to = current_transform . transform_point ( & to ) ;
} ;
if ! * abs {
let to = if * abs {
ctrl + = curpos . to_vector ( ) ;
math ::point ( * x , * y )
to + = curpos . to_vector ( ) ;
} else {
}
math ::point ( cx + * x , cy + * y )
} ;
let qbs = lyon_geom ::QuadraticBezierSegment { from , ctrl , to } ;
let qbs = lyon_geom ::QuadraticBezierSegment { from , ctrl , to } ;
let last_point = std ::cell ::Cell ::new ( math::point ( cx , cy ) ) ;
let last_point = std ::cell ::Cell ::new ( curpos ) ;
qbs . flattened ( tolerance) . for_each ( | point | {
qbs . flattened ( opts. tolerance) . for_each ( | point | {
p . push ( MachineCode ::LinearInterpolation {
p . push ( MachineCode ::LinearInterpolation {
x : point . x . into ( ) ,
x : point . x . into ( ) ,
y : point . y . into ( ) ,
y : point . y . into ( ) ,
z : None ,
z : None ,
f : feedrate. into ( ) ,
f : opts. feedrate. into ( ) ,
} ) ;
} ) ;
last_point . set ( point ) ;
last_point . set ( point ) ;
} ) ;
} ) ;
cx = last_point . get ( ) . x ;
curpos = last_point . get ( ) ;
cy = last_point . get ( ) . y ;
prev_ctrl = ctrl ;
}
PathSegment ::SmoothQuadratic { abs , x , y } = > {
tool_on ( & mut p ) ;
absolute ( & mut p ) ;
let from = curpos ;
let mut ctrl = prev_ctrl ;
let mut to = math ::point ( * x , * y ) ;
to = current_transform . transform_point ( & to ) ;
if ! * abs {
ctrl + = curpos . to_vector ( ) ;
to + = curpos . to_vector ( ) ;
}
let qbs = lyon_geom ::QuadraticBezierSegment { from , ctrl , to } ;
let last_point = std ::cell ::Cell ::new ( curpos ) ;
qbs . flattened ( opts . tolerance ) . for_each ( | point | {
p . push ( MachineCode ::LinearInterpolation {
x : point . x . into ( ) ,
y : point . y . into ( ) ,
z : None ,
f : opts . feedrate . into ( ) ,
} ) ;
last_point . set ( point ) ;
} ) ;
curpos = last_point . get ( ) ;
prev_ctrl = ctrl ;
}
}
PathSegment ::EllipticalArc {
PathSegment ::EllipticalArc {
abs ,
abs ,
@ -282,16 +382,20 @@ fn main() -> io::Result<()> {
} = > {
} = > {
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
absolute ( & mut p ) ;
absolute ( & mut p ) ;
let from = math ::point ( cx , cy ) ;
let from = curpos ;
let to = if * abs {
let mut to = math ::point ( * x , * y ) ;
math ::point ( * x , * y )
to = current_transform . transform_point ( & to ) ;
} else {
if ! * abs {
math ::point ( cx + * x , cy + * y )
to + = curpos . to_vector ( ) ;
} ;
}
let mut radii = math ::vector ( * rx , * ry ) ;
radii = current_transform . transform_vector ( & radii ) ;
let sarc = lyon_geom ::SvgArc {
let sarc = lyon_geom ::SvgArc {
from ,
from ,
to ,
to ,
radii : math ::vector ( * rx , * ry ) ,
radii ,
x_rotation : lyon_geom ::euclid ::Angle {
x_rotation : lyon_geom ::euclid ::Angle {
radians : * x_axis_rotation ,
radians : * x_axis_rotation ,
} ,
} ,
@ -300,30 +404,28 @@ fn main() -> io::Result<()> {
sweep : * sweep ,
sweep : * sweep ,
} ,
} ,
} ;
} ;
let last_point = std ::cell ::Cell ::new ( math::point ( cx , cy ) ) ;
let last_point = std ::cell ::Cell ::new ( curpos ) ;
sarc . for_each_flattened (
sarc . for_each_flattened (
tolerance,
opts. tolerance,
& mut | point : math ::F64Point | {
& mut | point : math ::F64Point | {
p . push ( MachineCode ::LinearInterpolation {
p . push ( MachineCode ::LinearInterpolation {
x : point . x . into ( ) ,
x : point . x . into ( ) ,
y : point . y . into ( ) ,
y : point . y . into ( ) ,
z : None ,
z : None ,
f : feedrate. into ( ) ,
f : opts. feedrate. into ( ) ,
} ) ;
} ) ;
last_point . set ( point ) ;
last_point . set ( point ) ;
} ,
} ,
) ;
) ;
c x = last_point . get ( ) . x ;
c urpos = last_point . get ( ) ;
cy = last_point . get ( ) . y ;
prev_ctrl = curpos ;
}
}
_ = > panic! ( "Unsupported path segment type" ) ,
}
}
}
}
}
}
}
}
_ = > {
_ = > {
info ! ( " Other {}", node ) ;
info ! ( " {} node with id {} is unsupported", id , node . id ( ) ) ;
}
}
}
}
}
}
@ -337,7 +439,7 @@ fn main() -> io::Result<()> {
tool_on ( & mut p ) ;
tool_on ( & mut p ) ;
p . push ( MachineCode ::ProgramEnd ) ;
p . push ( MachineCode ::ProgramEnd ) ;
p rogram2gcode( p , File ::create ( "out.gcode" ) ? )
p
}
}
#[ derive(Clone, PartialEq, Eq) ]
#[ derive(Clone, PartialEq, Eq) ]
@ -392,7 +494,7 @@ enum MachineCode {
type Program = Vec < MachineCode > ;
type Program = Vec < MachineCode > ;
fn program2gcode < W : Write > ( p : Program , mut w : W ) -> io ::Result < ( ) > {
fn program2gcode < W : Write > ( p : & Program , mut w : W ) -> io ::Result < ( ) > {
use MachineCode ::* ;
use MachineCode ::* ;
for code in p . iter ( ) {
for code in p . iter ( ) {
match code {
match code {
@ -445,7 +547,9 @@ fn program2gcode<W: Write>(p: Program, mut w: W) -> io::Result<()> {
writeln! ( w , "G91" ) ? ;
writeln! ( w , "G91" ) ? ;
}
}
Named ( name ) = > {
Named ( name ) = > {
writeln! ( w , "({})" , name ) ? ;
if name . len ( ) > 0 {
writeln! ( w , "({})" , name ) ? ;
}
}
}
}
}
}
}