use rin_math::Swizzles2;
use rin_math::{
Pnt2, scalar::SubsetOf, Deg, Angle, convert, Float, NumCast, cast, zero,
RealField, FloatPnt, NumPnt, ToPnt
};
use crate::Polyline;
use super::{mesh, Mesh, bezier, quad_bezier, arc_vertices, star_vertices, catmull_rom_vertices};
use color::{ToRgba,Rgba};
use color::consts::WHITE;
use std::iter::FromIterator;
use std::ops::Neg;
#[derive(Copy, Clone)]
pub enum Command<Point>
where
Point: NumPnt + Copy,
<Point as NumPnt>::Field: Copy
{
Move{ to: Point },
Line{ to: Point },
Bezier{ cp1: Point, cp2: Point, to: Point },
QuadBezier{ cp1: Point, to: Point },
CatmullRom{ to: Point },
Arc{
center: Point,
init_angle: Deg<<Point as NumPnt>::Field>,
angle: Deg<<Point as NumPnt>::Field>,
w: <Point as NumPnt>::Field,
h: <Point as NumPnt>::Field
},
Close
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum LineCap{
Square,
Triangular,
}
#[derive(Clone)]
pub struct Path2D<Point = Pnt2>
where
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
commands: Vec<Command<Point>>,
line_width: f32,
color_line: Rgba<f32>,
color_fill: Rgba<f32>,
line_cap: LineCap,
}
impl<Point> Path2D<Point>
where
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
pub fn new() -> Path2D<Point>{
Path2D {
commands: Vec::new(),
line_width: 1.0,
color_line: WHITE.to_rgba(),
color_fill: WHITE.to_rgba(),
line_cap: LineCap::Square,
}
}
pub fn set_line_width(&mut self, w: f32){
self.line_width = w;
}
pub fn line_width(&self) -> f32{
self.line_width
}
pub fn set_line_cap(&mut self, cap: LineCap){
self.line_cap = cap;
}
pub fn line_cap(&self) -> LineCap{
self.line_cap
}
pub fn set_line_color<C: ToRgba>(&mut self, c: &C){
self.color_line = c.to_rgba().to_standard();
}
pub fn line_color(&self) -> Rgba<f32>{
self.color_line
}
pub fn set_fill_color<C: ToRgba>(&mut self, c: &C){
self.color_fill = c.to_rgba().to_standard();
}
pub fn fill_color(&self) -> Rgba<f32>{
self.color_fill
}
pub fn move_to(&mut self, to: Point){
self.commands.push(Command::Move{to: to});
}
pub fn line_to(&mut self, to: Point){
self.commands.push(Command::Line{to: to});
}
pub fn bezier_to(&mut self, cp1: Point, cp2: Point, to: Point){
self.commands.push(Command::Bezier{cp1: cp1, cp2: cp2, to:to});
}
pub fn quad_bezier_to(&mut self, cp1: Point, to: Point){
self.commands.push(Command::QuadBezier{cp1: cp1, to:to});
}
pub fn catmull_rom_to(&mut self, to: Point){
self.commands.push(Command::CatmullRom{to: to});
}
pub fn arc(&mut self,
center: Point,
w: <Point as NumPnt>::Field,
h: <Point as NumPnt>::Field,
init_angle: Deg<<Point as NumPnt>::Field>,
angle: Deg<<Point as NumPnt>::Field>)
{
self.commands.push(Command::Arc{center: center, init_angle: init_angle, angle: angle, w: w, h: h});
}
pub fn close(&mut self){
self.commands.push(Command::Close);
}
pub fn append(&mut self, path: Path2D<Point>){
self.commands.extend(path.commands.into_iter());
}
pub fn clear(&mut self){
self.commands.truncate(0);
}
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
pub fn push_command(&mut self, command: Command<Point>) {
self.commands.push(command)
}
pub fn to_lines<F>(&self, resolution: u32, mut f: F)
where F: FnMut(Point, Point, bool),
Point: Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates> + Copy + ToPnt<Point>,
<Point as NumPnt>::Field: RealField + Float + NumCast
{
let mut prev: Point = Point::origin();
let mut first: Point = Point::origin();
let mut just_closed: bool = true;
let mut curve_points: Vec<Point> = Vec::new();
for c in self.commands.iter(){
match *c{
Command::Move{to} => {
curve_points.clear();
prev = to;
first = to;
just_closed = false;
},
Command::Line{to} => {
curve_points.truncate(0);
f(prev,to,false);
prev = to;
if just_closed { first = to; just_closed = false}
},
Command::Bezier{cp1, cp2, to} => {
curve_points.clear();
bezier(prev, cp1, cp2, to, resolution, |p|{
f(prev,p,false);
prev = p;
});
},
Command::QuadBezier{cp1, to} => {
curve_points.clear();
quad_bezier(prev, cp1, to, resolution, |p|{
f(prev,p,false);
prev=p;
})
},
Command::CatmullRom{to} => {
curve_points.push(to);
if curve_points.len() == 4{
catmull_rom_vertices(&curve_points[0],&curve_points[1],&curve_points[2],&curve_points[3], cast(0.5).unwrap(), resolution, |p|{
f(prev,p,false);
prev = p;
});
curve_points.remove(0);
}
},
Command::Arc{center, w, h, init_angle, angle} => {
curve_points.clear();
let mut first_p = true;
arc_vertices(center, w*cast(0.5).unwrap(), h*cast(0.5).unwrap(), init_angle, angle, resolution, |p|{
if !first_p {
f(prev,p,false);
}else{
first_p = false;
if just_closed { first = p; just_closed = false}
}
prev = p;
});
},
Command::Close => {
curve_points.clear();
f(prev,first,true);
just_closed = true;
},
}
}
}
pub fn to_line_strips<F>(&self, resolution: u32, mut f: F)
where F: FnMut(Point, bool),
Point: Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates> + Copy + ToPnt<Point>,
<Point as NumPnt>::Field: Float + NumCast
{
let mut prev: Point = Point::origin();
let mut first: Point = Point::origin();
let mut just_closed: bool = true;
let mut curve_points: Vec<Point> = Vec::new();
for c in self.commands.iter(){
match *c{
Command::Move{to} => {
curve_points.clear();
f(to,true);
prev = to;
first = to;
just_closed = false;
},
Command::Line{to} => {
curve_points.clear();
f(to,false);
prev = to;
if just_closed { first = to; just_closed = false;}
},
Command::Bezier{cp1, cp2, to} => {
curve_points.clear();
bezier(prev, cp1, cp2, to, resolution, |p|{
f(p,false);
});
prev = to;
},
Command::QuadBezier{cp1, to} => {
curve_points.clear();
quad_bezier(prev, cp1, to, resolution, |p|{
f(p,false);
});
prev = to;
},
Command::CatmullRom{to} => {
curve_points.push(to);
if curve_points.len() == 4{
catmull_rom_vertices(&curve_points[0],&curve_points[1],&curve_points[2],&curve_points[3], cast(0.5).unwrap(), resolution, |p|{
f(p,false);
prev = p;
});
curve_points.remove(0);
}
},
Command::Arc{center, init_angle, angle, w, h} => {
curve_points.clear();
f(center,true);
let mut first_p = true;
arc_vertices(center, w*cast(0.5).unwrap(), h*cast(0.5).unwrap(), init_angle, angle, resolution, |p|{
if first_p{
if just_closed { first = p; just_closed = false;}
first_p = false;
}
f(p, false);
prev = p;
});
f(center,false);
},
Command::Close => {
curve_points.clear();
f(first,false);
just_closed = true;
},
}
}
}
pub fn to_polylines(&self, resolution: u32) -> Vec<Polyline<<Point as NumPnt>::Field>>
where
Point: Swizzles2<<Point as NumPnt>::Field, Swizzle2 = Pnt2<<Point as NumPnt>::Field>> + Copy,
<Point as NumPnt>::Coordinates: Copy + ToPnt<Point>,
<Point as NumPnt>::Field: NumCast + RealField + Float,
{
let mut polylines = vec![Polyline::new()];
let mut prev: Point = Point::origin();
let mut first: Point = Point::origin();
let mut just_closed: bool = true;
let mut curve_points: Vec<Point> = Vec::new();
for c in self.commands.iter(){
match *c{
Command::Move{to} => {
curve_points.clear();
let polyline = if polylines.last().unwrap().is_empty(){
polylines.last_mut().unwrap()
}else{
polylines.push(Polyline::new());
polylines.last_mut().unwrap()
};
polyline.push(to.xy());
prev = to;
first = to;
just_closed = false;
},
Command::Line{to} => {
curve_points.clear();
polylines.last_mut().unwrap().push(to.xy());
prev = to;
if just_closed { first = to; just_closed = false;}
},
Command::Bezier{cp1, cp2, to} => {
curve_points.clear();
bezier(prev, cp1, cp2, to, resolution, |p|{
polylines.last_mut().unwrap().push(p.xy());
});
prev = to;
},
Command::QuadBezier{cp1, to} => {
curve_points.clear();
quad_bezier(prev, cp1, to, resolution, |p|{
polylines.last_mut().unwrap().push(p.xy());
});
prev = to;
},
Command::CatmullRom{to} => {
curve_points.push(to);
if curve_points.len() == 4{
catmull_rom_vertices(&curve_points[0],&curve_points[1],&curve_points[2],&curve_points[3], cast(0.5).unwrap(), resolution, |p|{
polylines.last_mut().unwrap().push(p.xy());
prev = p;
});
curve_points.remove(0);
}
},
Command::Arc{center, init_angle, angle, w, h} => {
curve_points.clear();
let mut first_p = true;
arc_vertices(center, w*cast(0.5).unwrap(), h*cast(0.5).unwrap(), init_angle, angle, resolution, |p|{
if first_p{
if just_closed { first = p; just_closed = false;}
first_p = false;
}
polylines.last_mut().unwrap().push(p.xy());
prev = p;
});
},
Command::Close => {
curve_points.clear();
polylines.last_mut().unwrap().close();
just_closed = true;
},
}
}
polylines
}
pub fn to_outline_mesh(&self, curve_resolution: u32) -> Mesh<<Point as NumPnt>::Coordinates>
where <Point as NumPnt>::Field: Float + NumCast,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates> + Copy + ToPnt<Point>,
Point: Copy,
{
let mut mesh = Mesh::default();
mesh.set_primitive_type(mesh::PrimitiveType::Lines);
self.to_lines(curve_resolution, |from, to, _close|{
mesh.push(from.coordinates());
mesh.push(to.coordinates());
});
mesh
}
pub fn to_fill_mesh(&self, curve_resolution: u32) -> Mesh<<Point as NumPnt>::Coordinates>
where <Point as NumPnt>::Field: Float + NumCast,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates> + Copy + ToPnt<Point>,
Point: Copy,
{
let mut mesh = Mesh::default();
mesh.set_primitive_type(mesh::PrimitiveType::TriangleFan);
self.to_line_strips(curve_resolution, |to, _new_shape|{
mesh.push(to.coordinates());
});
mesh
}
}
impl<Point> FromIterator<Command<Point>> for Path2D<Point>
where
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
fn from_iter<T>(commands: T) -> Self where T: IntoIterator<Item = Command<Point>> {
Path2D {
commands: commands.into_iter().collect(),
line_width: 1.0,
color_line: WHITE.to_rgba(),
color_fill: WHITE.to_rgba(),
line_cap: LineCap::Square,
}
}
}
pub fn star<Point>(
center: Point,
r: <Point as NumPnt>::Field,
ri: <Point as NumPnt>::Field,
points: u32) -> Path2D<Point>
where
u32: SubsetOf<<Point as NumPnt>::Field>,
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Float + NumCast + Neg,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
let mut path = Path2D::new();
let mut first = true;
star_vertices(center,r,ri,points,|p|{
if first {path.move_to(p);}
else {path.line_to(p);}
first = false;
});
path.close();
path
}
pub fn circle<Point>(center: Point, r: <Point as NumPnt>::Field) -> Path2D<Point>
where Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Float + NumCast + Neg + Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
let mut path = Path2D::new();
let two = cast(2.0).unwrap();
path.arc(center, r*two, r*two, zero(), Deg::two_pi());
path
}
pub fn regular_poly<Point>(
center: Point,
r: <Point as NumPnt>::Field,
sides: u32) -> Path2D<Point>
where
u32: SubsetOf<<Point as NumPnt>::Field>,
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Float + NumCast + Neg,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
let mut path = Path2D::new();
let mut first = true;
let from = -Deg::half_pi();
let two_pi: Deg<<Point as NumPnt>::Field> = Deg::two_pi();
let fsides: <Point as NumPnt>::Field = convert(sides);
let fsides_1: <Point as NumPnt>::Field = convert(sides - 1);
let to = two_pi * fsides_1 / fsides;
arc_vertices(center, r, r, from, to, sides, |p|{
if first {path.move_to(p); }
else {path.line_to(p); }
first = false;
});
path.close();
path
}
pub fn ellipse<Point>(
center: Point,
w: <Point as NumPnt>::Field,
h: <Point as NumPnt>::Field) -> Path2D<Point>
where
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Float + NumCast + Neg + Copy,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
let mut path = Path2D::new();
path.arc(center,w,h,zero(),Deg::two_pi());
path
}
pub fn rect<Point>(
pos: Point,
w: <Point as NumPnt>::Field,
h: <Point as NumPnt>::Field) -> Path2D<Point>
where
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Float + NumCast + Neg,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
let mut path = Path2D::new();
path.move_to(pos);
let mut t = Point::origin().coordinates();
t[0] = w;
path.line_to(pos + t);
let mut t = Point::origin().coordinates();
t[0] = w;
t[1] = h;
path.line_to(pos + t);
let mut t = Point::origin().coordinates();
t[1] = h;
path.line_to(pos + t);
path.close();
path
}
pub fn triangle<Point>(p0: Point, p1: Point, p2: Point) -> Path2D<Point>
where
Point: FloatPnt + Copy,
<Point as NumPnt>::Field: RealField + Float + NumCast + Neg,
<Point as NumPnt>::Coordinates: Neg<Output = <Point as NumPnt>::Coordinates>
{
let mut path = Path2D::new();
path.move_to(p0);
path.line_to(p1);
path.line_to(p2);
path.close();
path
}