use alga::general::Real; use na::{Isometry3, Point2, Point3, Vector3}; use na; use polyline::Polyline; use trimesh::{IndexBuffer, TriMesh}; use utils; use path::{CurveSampler, PathSample, StrokePattern}; /// A pattern composed of polyline and two caps. pub struct PolylinePattern { pattern: Polyline>, closed: bool, last_start_id: u32, start_cap: C1, end_cap: C2, } /// Trait to be implemented by caps compatible with a `PolylinePattern`. pub trait PolylineCompatibleCap { /// Generates the mesh for the cap at the beginning of a path. fn gen_start_cap( &self, attach_id: u32, pattern: &Polyline>, pt: &Point3, dir: &Vector3, closed: bool, coords: &mut Vec>, indices: &mut Vec>, ); /// Generates the mesh for the cap at the end of a path. fn gen_end_cap( &self, attach_id: u32, pattern: &Polyline>, pt: &Point3, dir: &Vector3, closed: bool, coords: &mut Vec>, indices: &mut Vec>, ); } impl PolylinePattern where N: Real, C1: PolylineCompatibleCap, C2: PolylineCompatibleCap, { /// Creates a new polyline pattern. pub fn new( pattern: &Polyline>, closed: bool, start_cap: C1, end_cap: C2, ) -> PolylinePattern { let mut coords3d = Vec::with_capacity(pattern.coords().len()); for v in pattern.coords().iter() { coords3d.push(Point3::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 StrokePattern> for PolylinePattern where N: Real, C1: PolylineCompatibleCap, C2: PolylineCompatibleCap, { fn stroke(&mut self, sampler: &mut C) -> TriMesh> where C: CurveSampler>, { let mut vertices = Vec::new(); let mut indices = Vec::new(); let npts = self.pattern.coords().len() as u32; // FIXME: collect the normals too. // let mut normals = Vec::new(); loop { let next = sampler.next(); // second match to add the inner triangles. 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() { // FIXME: this might not be enough to avoid singularities. transform = Isometry3::new_observer_frame(pt, &(*pt + *dir), &Vector3::x()); } else { transform = Isometry3::new_observer_frame(pt, &(*pt + *dir), &Vector3::y()); } new_polyline.transform_by(&transform); let new_start_id = vertices.len() as u32; vertices.extend(new_polyline.unwrap().0.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))) } } // third match to add the end cap // FIXME: this will fail with patterns having multiple starting and end points! 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, ); } _ => {} } } } }