use na::{Pnt2, Pnt3, Vec3, Iso3};
use na;
use polyline::Polyline;
use trimesh::{TriMesh, IndexBuffer};
use utils;
use path::{StrokePattern, CurveSampler, PathSample};
use math::Scalar;
pub struct PolylinePattern<N: Scalar, C1, C2> {
pattern: Polyline<Pnt3<N>>,
closed: bool,
last_start_id: u32,
start_cap: C1,
end_cap: C2,
}
pub trait PolylineCompatibleCap<N: Scalar> {
fn gen_start_cap(&self,
attach_id: u32,
pattern: &Polyline<Pnt3<N>>,
pt: &Pnt3<N>,
dir: &Vec3<N>,
closed: bool,
coords: &mut Vec<Pnt3<N>>,
indices: &mut Vec<Pnt3<u32>>);
fn gen_end_cap(&self,
attach_id: u32,
pattern: &Polyline<Pnt3<N>>,
pt: &Pnt3<N>,
dir: &Vec3<N>,
closed: bool,
coords: &mut Vec<Pnt3<N>>,
indices: &mut Vec<Pnt3<u32>>);
}
impl<N, C1, C2> PolylinePattern<N, C1, C2>
where N: Scalar,
C1: PolylineCompatibleCap<N>,
C2: PolylineCompatibleCap<N> {
pub fn new(pattern: &Polyline<Pnt2<N>>,
closed: bool,
start_cap: C1,
end_cap: C2)
-> PolylinePattern<N, C1, C2> {
let mut coords3d = Vec::with_capacity(pattern.coords.len());
for v in pattern.coords.iter() {
coords3d.push(Pnt3::new(v.x.clone(), v.y.clone(), na::zero()));
}
PolylinePattern {
pattern: Polyline::new(coords3d, None),
closed: closed,
last_start_id: 0,
start_cap: start_cap,
end_cap: end_cap
}
}
}
impl<N, C1, C2> StrokePattern<Pnt3<N>> for PolylinePattern<N, C1, C2>
where N: Scalar,
C1: PolylineCompatibleCap<N>,
C2: PolylineCompatibleCap<N>{
fn stroke<C>(&mut self, sampler: &mut C) -> TriMesh<Pnt3<N>>
where C: CurveSampler<Pnt3<N>> {
let mut vertices = Vec::new();
let mut indices = Vec::new();
let npts = self.pattern.coords.len() as u32;
loop {
let next = sampler.next();
match next {
PathSample::StartPoint(ref pt, ref dir) |
PathSample::InnerPoint(ref pt, ref dir) |
PathSample::EndPoint(ref pt, ref dir) => {
let mut new_polyline = self.pattern.clone();
let transform;
if dir.x.is_zero() && dir.z.is_zero() { transform = Iso3::new_observer_frame(pt, &(*pt + *dir), &Vec3::x());
}
else {
transform = Iso3::new_observer_frame(pt, &(*pt + *dir), &Vec3::y());
}
new_polyline.transform_by(&transform);
let new_start_id = vertices.len() as u32;
vertices.extend(new_polyline.coords.into_iter());
if new_start_id != 0 {
if self.closed {
utils::push_ring_indices(new_start_id, self.last_start_id, npts, &mut indices);
}
else {
utils::push_open_ring_indices(new_start_id, self.last_start_id, npts, &mut indices);
}
self.last_start_id = new_start_id;
}
},
PathSample::EndOfSample =>
return TriMesh::new(vertices, None, None, Some(IndexBuffer::Unified(indices)))
}
match next {
PathSample::StartPoint(ref pt, ref dir) => {
self.start_cap.gen_start_cap(0, &self.pattern,
pt, dir, self.closed, &mut vertices, &mut indices);
},
PathSample::EndPoint(ref pt, ref dir) => {
self.end_cap.gen_end_cap(vertices.len() as u32 - npts, &self.pattern,
pt, dir, self.closed, &mut vertices, &mut indices);
},
_ => { }
}
}
}
}