ncollide3d/procedural/path/
polyline_pattern.rs

1use crate::procedural::path::{CurveSampler, PathSample, StrokePattern};
2use crate::procedural::trimesh::{IndexBuffer, TriMesh};
3use crate::procedural::utils;
4use na::{self, Isometry3, Point2, Point3, RealField, Vector3};
5
6/// A pattern composed of polyline and two caps.
7pub struct PolylinePattern<N: RealField + Copy, C1, C2> {
8    pattern: Vec<Point3<N>>,
9    closed: bool,
10    last_start_id: u32,
11    start_cap: C1,
12    end_cap: C2,
13}
14
15/// Trait to be implemented by caps compatible with a `PolylinePattern`.
16pub trait PolylineCompatibleCap<N: RealField + Copy> {
17    /// Generates the mesh for the cap at the beginning of a path.
18    fn gen_start_cap(
19        &self,
20        attach_id: u32,
21        pattern: &[Point3<N>],
22        pt: &Point3<N>,
23        dir: &Vector3<N>,
24        closed: bool,
25        coords: &mut Vec<Point3<N>>,
26        indices: &mut Vec<Point3<u32>>,
27    );
28
29    /// Generates the mesh for the cap at the end of a path.
30    fn gen_end_cap(
31        &self,
32        attach_id: u32,
33        pattern: &[Point3<N>],
34        pt: &Point3<N>,
35        dir: &Vector3<N>,
36        closed: bool,
37        coords: &mut Vec<Point3<N>>,
38        indices: &mut Vec<Point3<u32>>,
39    );
40}
41
42impl<N, C1, C2> PolylinePattern<N, C1, C2>
43where
44    N: RealField + Copy,
45    C1: PolylineCompatibleCap<N>,
46    C2: PolylineCompatibleCap<N>,
47{
48    /// Creates a new polyline pattern.
49    pub fn new(
50        pattern: &[Point2<N>],
51        closed: bool,
52        start_cap: C1,
53        end_cap: C2,
54    ) -> PolylinePattern<N, C1, C2> {
55        let mut coords3d = Vec::with_capacity(pattern.len());
56
57        for v in pattern.iter() {
58            coords3d.push(Point3::new(v.x.clone(), v.y.clone(), na::zero()));
59        }
60
61        PolylinePattern {
62            pattern: coords3d,
63            closed: closed,
64            last_start_id: 0,
65            start_cap: start_cap,
66            end_cap: end_cap,
67        }
68    }
69}
70
71impl<N, C1, C2> StrokePattern<N> for PolylinePattern<N, C1, C2>
72where
73    N: RealField + Copy,
74    C1: PolylineCompatibleCap<N>,
75    C2: PolylineCompatibleCap<N>,
76{
77    fn stroke<C: CurveSampler<N>>(&mut self, sampler: &mut C) -> TriMesh<N> {
78        let mut vertices = Vec::new();
79        let mut indices = Vec::new();
80        let npts = self.pattern.len() as u32;
81        // FIXME: collect the normals too.
82        // let mut normals  = Vec::new();
83
84        loop {
85            let next = sampler.next();
86
87            // second match to add the inner triangles.
88            match next {
89                PathSample::StartPoint(ref pt, ref dir)
90                | PathSample::InnerPoint(ref pt, ref dir)
91                | PathSample::EndPoint(ref pt, ref dir) => {
92                    let mut new_polyline = self.pattern.clone();
93                    let transform;
94
95                    if dir.x.is_zero() && dir.z.is_zero() {
96                        // FIXME: this might not be enough to avoid singularities.
97                        transform = Isometry3::face_towards(pt, &(*pt + *dir), &Vector3::x());
98                    } else {
99                        transform = Isometry3::face_towards(pt, &(*pt + *dir), &Vector3::y());
100                    }
101
102                    for p in &mut new_polyline {
103                        *p = transform * &*p;
104                    }
105
106                    let new_start_id = vertices.len() as u32;
107
108                    vertices.extend(new_polyline.into_iter());
109
110                    if new_start_id != 0 {
111                        if self.closed {
112                            utils::push_ring_indices(
113                                new_start_id,
114                                self.last_start_id,
115                                npts,
116                                &mut indices,
117                            );
118                        } else {
119                            utils::push_open_ring_indices(
120                                new_start_id,
121                                self.last_start_id,
122                                npts,
123                                &mut indices,
124                            );
125                        }
126
127                        self.last_start_id = new_start_id;
128                    }
129                }
130                PathSample::EndOfSample => {
131                    return TriMesh::new(vertices, None, None, Some(IndexBuffer::Unified(indices)))
132                }
133            }
134
135            // third match to add the end cap
136            // FIXME: this will fail with patterns having multiple starting and end points!
137            match next {
138                PathSample::StartPoint(ref pt, ref dir) => {
139                    self.start_cap.gen_start_cap(
140                        0,
141                        &self.pattern,
142                        pt,
143                        dir,
144                        self.closed,
145                        &mut vertices,
146                        &mut indices,
147                    );
148                }
149                PathSample::EndPoint(ref pt, ref dir) => {
150                    self.end_cap.gen_end_cap(
151                        vertices.len() as u32 - npts,
152                        &self.pattern,
153                        pt,
154                        dir,
155                        self.closed,
156                        &mut vertices,
157                        &mut indices,
158                    );
159                }
160                _ => {}
161            }
162        }
163    }
164}