genmesh/
cylinder.rs

1use super::generators::{IndexedPolygon, SharedVertex};
2use super::{Polygon, Quad, Triangle};
3use std::f32::consts::PI;
4use {Normal, Position, Vertex};
5
6/// Represents a cylinder with radius of 1, height of 2,
7/// and centered at (0, 0, 0) pointing up (to 0, 0, 1).
8#[derive(Clone, Copy)]
9pub struct Cylinder {
10    u: usize,
11    h: isize,
12    sub_u: usize,
13    sub_h: isize,
14}
15
16const TOP: Vertex = Vertex {
17    pos: Position {
18        x: 0.,
19        y: 0.,
20        z: 1.,
21    },
22    normal: Normal {
23        x: 0.,
24        y: 0.,
25        z: 1.,
26    },
27};
28
29const BOT: Vertex = Vertex {
30    pos: Position {
31        x: 0.,
32        y: 0.,
33        z: -1.,
34    },
35    normal: Normal {
36        x: 0.,
37        y: 0.,
38        z: -1.,
39    },
40};
41
42impl Cylinder {
43    /// Create a new cylinder.
44    /// `u` is the number of points across the radius.
45    pub fn new(u: usize) -> Self {
46        assert!(u > 1);
47        Cylinder {
48            u: 0,
49            h: -1,
50            sub_u: u,
51            sub_h: 1,
52        }
53    }
54
55    /// Create a new subdivided cylinder.
56    /// `u` is the number of points across the radius.
57    /// `h` is the number of segments across the height.
58    pub fn subdivide(u: usize, h: usize) -> Self {
59        assert!(u > 1 && h > 0);
60        Cylinder {
61            u: 0,
62            h: -1,
63            sub_u: u,
64            sub_h: h as isize,
65        }
66    }
67
68    fn vert(&self, u: usize, h: isize) -> Vertex {
69        debug_assert!(u <= self.sub_u);
70        let a = (u as f32 / self.sub_u as f32) * PI * 2.;
71        let n = [a.cos(), a.sin(), 0.];
72        let (hc, normal) = if h < 0 {
73            debug_assert_eq!(h, -1);
74            (0, [0., 0., -1.])
75        } else if h > self.sub_h {
76            debug_assert_eq!(h, self.sub_h + 1);
77            (self.sub_h, [0., 0., 1.])
78        } else {
79            (h, n)
80        };
81        let z = (hc as f32 / self.sub_h as f32) * 2. - 1.;
82        Vertex {
83            pos: [n[0], n[1], z].into(),
84            normal: normal.into(),
85        }
86    }
87}
88
89impl Iterator for Cylinder {
90    type Item = Polygon<Vertex>;
91
92    fn next(&mut self) -> Option<Self::Item> {
93        if self.u == self.sub_u {
94            if self.h >= self.sub_h {
95                return None;
96            }
97            self.u = 0;
98            self.h += 1;
99        }
100
101        let u = self.u;
102        self.u += 1;
103        // mathematically, reaching `u + 1 == sub_u` should trivially resolve,
104        // because sin(2pi) == sin(0), but rounding errors go in the way.
105        let u1 = self.u % self.sub_u;
106
107        Some(if self.h < 0 {
108            let x = self.vert(u, self.h);
109            let y = self.vert(u1, self.h);
110            Polygon::PolyTri(Triangle::new(x, BOT, y))
111        } else if self.h == self.sub_h {
112            let x = self.vert(u, self.h + 1);
113            let y = self.vert(u1, self.h + 1);
114            Polygon::PolyTri(Triangle::new(x, y, TOP))
115        } else {
116            let x = self.vert(u, self.h);
117            let y = self.vert(u1, self.h);
118            let z = self.vert(u1, self.h + 1);
119            let w = self.vert(u, self.h + 1);
120            Polygon::PolyQuad(Quad::new(x, y, z, w))
121        })
122    }
123
124    fn size_hint(&self) -> (usize, Option<usize>) {
125        let n = self.sub_u * (1 + self.sub_h - self.h) as usize - self.u;
126        (n, Some(n))
127    }
128}
129
130impl SharedVertex<Vertex> for Cylinder {
131    fn shared_vertex(&self, idx: usize) -> Vertex {
132        if idx == 0 {
133            BOT
134        } else if idx == self.shared_vertex_count() - 1 {
135            TOP
136        } else {
137            // skip the bottom center
138            let idx = idx - 1;
139            let u = idx % self.sub_u;
140            let h = (idx / self.sub_u) as isize - 1;
141            self.vert(u, h)
142        }
143    }
144
145    fn shared_vertex_count(&self) -> usize {
146        (3 + self.sub_h) as usize * self.sub_u + 2
147    }
148}
149
150impl IndexedPolygon<Polygon<usize>> for Cylinder {
151    fn indexed_polygon(&self, idx: usize) -> Polygon<usize> {
152        let u = idx % self.sub_u;
153        let u1 = (idx + 1) % self.sub_u;
154        let h = (idx / self.sub_u) as isize - 1;
155        let base = 1 + idx - u;
156        if h < 0 {
157            let start = 0;
158            Polygon::PolyTri(Triangle::new(base + u, start, base + u1))
159        } else if h == self.sub_h {
160            // We need to to select the next vertex loop over, which
161            // has the correct normals.
162            let base = base + self.sub_u;
163            let end = self.shared_vertex_count() - 1;
164            Polygon::PolyTri(Triangle::new(base + u, base + u1, end))
165        } else {
166            Polygon::PolyQuad(Quad::new(
167                base + u,
168                base + u1,
169                base + u1 + self.sub_u,
170                base + u + self.sub_u,
171            ))
172        }
173    }
174
175    fn indexed_polygon_count(&self) -> usize {
176        (2 + self.sub_h) as usize * self.sub_u
177    }
178}