parent
125d1facf7
commit
f10c20a1e1
@ -0,0 +1,366 @@
|
||||
use crate::code::{GCode, Program};
|
||||
use crate::machine::Machine;
|
||||
use lyon_geom::euclid::{Angle, Transform2D};
|
||||
use lyon_geom::math::{point, vector, F64Point};
|
||||
use lyon_geom::{ArcFlags, CubicBezierSegment, QuadraticBezierSegment, SvgArc};
|
||||
|
||||
pub struct Turtle {
|
||||
curpos: F64Point,
|
||||
curtran: Transform2D<f64>,
|
||||
scaling: Option<Transform2D<f64>>,
|
||||
transtack: Vec<Transform2D<f64>>,
|
||||
pub mach: Machine,
|
||||
prev_ctrl: F64Point,
|
||||
}
|
||||
|
||||
impl Default for Turtle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
curpos: point(0.0, 0.0),
|
||||
curtran: Transform2D::identity(),
|
||||
scaling: None,
|
||||
transtack: vec![],
|
||||
mach: Machine::default(),
|
||||
prev_ctrl: point(0.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Turtle {
|
||||
pub fn move_to<X, Y>(&mut self, abs: bool, x: X, y: Y) -> Program
|
||||
where
|
||||
X: Into<Option<f64>>,
|
||||
Y: Into<Option<f64>>,
|
||||
{
|
||||
let invtran = self.curtran.inverse().unwrap();
|
||||
let origcurpos = invtran.transform_point(&self.curpos);
|
||||
let x = x
|
||||
.into()
|
||||
.map(|x| if abs { x } else { origcurpos.x + x })
|
||||
.unwrap_or(origcurpos.x);
|
||||
let y = y
|
||||
.into()
|
||||
.map(|y| if abs { y } else { origcurpos.y + y })
|
||||
.unwrap_or(origcurpos.y);
|
||||
|
||||
let mut to = point(x, y);
|
||||
to = self.curtran.transform_point(&to);
|
||||
self.curpos = to;
|
||||
self.prev_ctrl = self.curpos;
|
||||
|
||||
vec![
|
||||
self.mach.tool_off(),
|
||||
self.mach.absolute(),
|
||||
vec![GCode::RapidPositioning {
|
||||
x: to.x.into(),
|
||||
y: to.y.into(),
|
||||
}],
|
||||
]
|
||||
.drain(..)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn line<X, Y, Z, F>(&mut self, abs: bool, x: X, y: Y, z: Z, f: F) -> Program
|
||||
where
|
||||
X: Into<Option<f64>>,
|
||||
Y: Into<Option<f64>>,
|
||||
Z: Into<Option<f64>>,
|
||||
F: Into<Option<f64>>,
|
||||
{
|
||||
let invtran = self.curtran.inverse().unwrap();
|
||||
let origcurpos = invtran.transform_point(&self.curpos);
|
||||
let x = x
|
||||
.into()
|
||||
.map(|x| if abs { x } else { origcurpos.x + x })
|
||||
.unwrap_or(origcurpos.x);
|
||||
let y = y
|
||||
.into()
|
||||
.map(|y| if abs { y } else { origcurpos.y + y })
|
||||
.unwrap_or(origcurpos.y);
|
||||
|
||||
let mut to = point(x, y);
|
||||
to = self.curtran.transform_point(&to);
|
||||
self.curpos = to;
|
||||
self.prev_ctrl = self.curpos;
|
||||
|
||||
vec![
|
||||
self.mach.tool_on(),
|
||||
self.mach.absolute(),
|
||||
vec![GCode::LinearInterpolation {
|
||||
x: to.x.into(),
|
||||
y: to.y.into(),
|
||||
z: z.into(),
|
||||
f: f.into(),
|
||||
}],
|
||||
]
|
||||
.drain(..)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn bezier<Z: Into<Option<f64>>, F: Into<Option<f64>>>(
|
||||
&mut self,
|
||||
cbs: CubicBezierSegment<f64>,
|
||||
tolerance: f64,
|
||||
z: Z,
|
||||
f: F,
|
||||
) -> Program {
|
||||
let z = z.into();
|
||||
let f = f.into();
|
||||
let last_point = std::cell::Cell::new(self.curpos);
|
||||
let mut cubic = vec![];
|
||||
cbs.flattened(tolerance).for_each(|point| {
|
||||
cubic.push(GCode::LinearInterpolation {
|
||||
x: point.x.into(),
|
||||
y: point.y.into(),
|
||||
z,
|
||||
f,
|
||||
});
|
||||
last_point.set(point);
|
||||
});
|
||||
self.curpos = last_point.get();
|
||||
self.prev_ctrl = cbs.ctrl1;
|
||||
|
||||
vec![self.mach.tool_on(), self.mach.absolute(), cubic]
|
||||
.drain(..)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn cubic_bezier<Z, F>(
|
||||
&mut self,
|
||||
abs: bool,
|
||||
x1: f64,
|
||||
y1: f64,
|
||||
x2: f64,
|
||||
y2: f64,
|
||||
x: f64,
|
||||
y: f64,
|
||||
tolerance: f64,
|
||||
z: Z,
|
||||
f: F,
|
||||
) -> Program
|
||||
where
|
||||
Z: Into<Option<f64>>,
|
||||
F: Into<Option<f64>>,
|
||||
{
|
||||
let from = self.curpos;
|
||||
let mut ctrl1 = point(x1, y1);
|
||||
let mut ctrl2 = point(x2, y2);
|
||||
let mut to = point(x, y);
|
||||
if !abs {
|
||||
let invtran = self.curtran.inverse().unwrap();
|
||||
let origcurpos = invtran.transform_point(&self.curpos);
|
||||
ctrl1 += origcurpos.to_vector();
|
||||
ctrl2 += origcurpos.to_vector();
|
||||
to += origcurpos.to_vector();
|
||||
}
|
||||
ctrl1 = self.curtran.transform_point(&ctrl1);
|
||||
ctrl2 = self.curtran.transform_point(&ctrl2);
|
||||
to = self.curtran.transform_point(&to);
|
||||
let cbs = lyon_geom::CubicBezierSegment {
|
||||
from,
|
||||
ctrl1,
|
||||
ctrl2,
|
||||
to,
|
||||
};
|
||||
|
||||
self.bezier(cbs, tolerance, z, f)
|
||||
}
|
||||
|
||||
pub fn smooth_cubic_bezier<Z, F>(
|
||||
&mut self,
|
||||
abs: bool,
|
||||
x2: f64,
|
||||
y2: f64,
|
||||
x: f64,
|
||||
y: f64,
|
||||
tolerance: f64,
|
||||
z: Z,
|
||||
f: F,
|
||||
) -> Program
|
||||
where
|
||||
Z: Into<Option<f64>>,
|
||||
F: Into<Option<f64>>,
|
||||
{
|
||||
let from = self.curpos;
|
||||
let ctrl1 = self.prev_ctrl;
|
||||
let mut ctrl2 = point(x2, y2);
|
||||
let mut to = point(x, y);
|
||||
if !abs {
|
||||
let invtran = self.curtran.inverse().unwrap();
|
||||
let origcurpos = invtran.transform_point(&self.curpos);
|
||||
ctrl2 += origcurpos.to_vector();
|
||||
to += origcurpos.to_vector();
|
||||
}
|
||||
ctrl2 = self.curtran.transform_point(&ctrl2);
|
||||
to = self.curtran.transform_point(&to);
|
||||
let cbs = lyon_geom::CubicBezierSegment {
|
||||
from,
|
||||
ctrl1,
|
||||
ctrl2,
|
||||
to,
|
||||
};
|
||||
|
||||
self.bezier(cbs, tolerance, z, f)
|
||||
}
|
||||
|
||||
pub fn smooth_quadratic_bezier<Z, F>(
|
||||
&mut self,
|
||||
abs: bool,
|
||||
x: f64,
|
||||
y: f64,
|
||||
tolerance: f64,
|
||||
z: Z,
|
||||
f: F,
|
||||
) -> Program
|
||||
where
|
||||
Z: Into<Option<f64>>,
|
||||
F: Into<Option<f64>>,
|
||||
{
|
||||
let from = self.curpos;
|
||||
let ctrl = self.prev_ctrl;
|
||||
let mut to = point(x, y);
|
||||
if !abs {
|
||||
let invtran = self.curtran.inverse().unwrap();
|
||||
let origcurpos = invtran.transform_point(&self.curpos);
|
||||
to += origcurpos.to_vector();
|
||||
}
|
||||
to = self.curtran.transform_point(&to);
|
||||
let qbs = QuadraticBezierSegment { from, ctrl, to };
|
||||
|
||||
self.bezier(qbs.to_cubic(), tolerance, z, f)
|
||||
}
|
||||
|
||||
pub fn quadratic_bezier<Z, F>(
|
||||
&mut self,
|
||||
abs: bool,
|
||||
x1: f64,
|
||||
y1: f64,
|
||||
x: f64,
|
||||
y: f64,
|
||||
tolerance: f64,
|
||||
z: Z,
|
||||
f: F,
|
||||
) -> Program
|
||||
where
|
||||
Z: Into<Option<f64>>,
|
||||
F: Into<Option<f64>>,
|
||||
{
|
||||
let from = self.curpos;
|
||||
let mut ctrl = point(x1, y1);
|
||||
let mut to = point(x, y);
|
||||
if !abs {
|
||||
let invtran = self.curtran.inverse().unwrap();
|
||||
let origcurpos = invtran.transform_point(&self.curpos);
|
||||
to += origcurpos.to_vector();
|
||||
ctrl += origcurpos.to_vector();
|
||||
}
|
||||
ctrl = self.curtran.transform_point(&ctrl);
|
||||
to = self.curtran.transform_point(&to);
|
||||
let qbs = QuadraticBezierSegment { from, ctrl, to };
|
||||
|
||||
self.bezier(qbs.to_cubic(), tolerance, z, f)
|
||||
}
|
||||
|
||||
pub fn elliptical<Z, F>(
|
||||
&mut self,
|
||||
abs: bool,
|
||||
rx: f64,
|
||||
ry: f64,
|
||||
x_axis_rotation: f64,
|
||||
large_arc: bool,
|
||||
sweep: bool,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: Z,
|
||||
f: F,
|
||||
tolerance: f64,
|
||||
) -> Program
|
||||
where
|
||||
Z: Into<Option<f64>>,
|
||||
F: Into<Option<f64>>,
|
||||
{
|
||||
let z = z.into();
|
||||
let f = f.into();
|
||||
|
||||
let from = self.curpos;
|
||||
let mut to = point(x, y);
|
||||
to = self.curtran.transform_point(&to);
|
||||
if !abs {
|
||||
to -= vector(self.curtran.m31, self.curtran.m32);
|
||||
to += self.curpos.to_vector();
|
||||
}
|
||||
|
||||
let mut radii = vector(rx, ry);
|
||||
radii = self.curtran.transform_vector(&radii);
|
||||
|
||||
let sarc = SvgArc {
|
||||
from,
|
||||
to,
|
||||
radii,
|
||||
x_rotation: Angle {
|
||||
radians: x_axis_rotation,
|
||||
},
|
||||
flags: ArcFlags {
|
||||
large_arc: large_arc,
|
||||
sweep: sweep,
|
||||
},
|
||||
};
|
||||
let last_point = std::cell::Cell::new(self.curpos);
|
||||
|
||||
let mut ellipse = vec![];
|
||||
sarc.for_each_flattened(tolerance, &mut |point: F64Point| {
|
||||
ellipse.push(GCode::LinearInterpolation {
|
||||
x: point.x.into(),
|
||||
y: point.y.into(),
|
||||
z,
|
||||
f,
|
||||
});
|
||||
last_point.set(point);
|
||||
});
|
||||
self.curpos = last_point.get();
|
||||
self.prev_ctrl = self.curpos;
|
||||
|
||||
vec![self.mach.tool_on(), self.mach.absolute(), ellipse]
|
||||
.drain(..)
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn set_scaling(&mut self, scaling: Transform2D<f64>) {
|
||||
if let Some(ref scaling) = self.scaling {
|
||||
self.curtran = self.curtran.post_mul(&scaling.inverse().unwrap());
|
||||
}
|
||||
self.scaling = Some(scaling);
|
||||
self.curtran = self
|
||||
.curtran
|
||||
.post_mul(&scaling);
|
||||
}
|
||||
|
||||
pub fn push_transform(&mut self, trans: Transform2D<f64>) {
|
||||
self.transtack.push(self.curtran);
|
||||
if let Some(ref scaling) = self.scaling {
|
||||
println!("{:?}", trans);
|
||||
self.curtran = self
|
||||
.curtran
|
||||
.post_mul(&scaling.inverse().unwrap())
|
||||
.post_mul(&trans)
|
||||
.post_mul(&scaling);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_transform(&mut self) {
|
||||
self.curtran = self
|
||||
.transtack
|
||||
.pop()
|
||||
.expect("popped when no transforms left");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue