use super::generators::{IndexedPolygon, SharedVertex};
use super::{Polygon, Quad, Triangle};
use std::f32::consts::PI;
use {Normal, Position, Vertex};
#[derive(Clone, Copy)]
pub struct Cylinder {
u: usize,
h: isize,
sub_u: usize,
sub_h: isize,
}
const TOP: Vertex = Vertex {
pos: Position {
x: 0.,
y: 0.,
z: 1.,
},
normal: Normal {
x: 0.,
y: 0.,
z: 1.,
},
};
const BOT: Vertex = Vertex {
pos: Position {
x: 0.,
y: 0.,
z: -1.,
},
normal: Normal {
x: 0.,
y: 0.,
z: -1.,
},
};
impl Cylinder {
pub fn new(u: usize) -> Self {
assert!(u > 1);
Cylinder {
u: 0,
h: -1,
sub_u: u,
sub_h: 1,
}
}
pub fn subdivide(u: usize, h: usize) -> Self {
assert!(u > 1 && h > 0);
Cylinder {
u: 0,
h: -1,
sub_u: u,
sub_h: h as isize,
}
}
fn vert(&self, u: usize, h: isize) -> Vertex {
debug_assert!(u <= self.sub_u);
let a = (u as f32 / self.sub_u as f32) * PI * 2.;
let n = [a.cos(), a.sin(), 0.];
let (hc, normal) = if h < 0 {
debug_assert_eq!(h, -1);
(0, [0., 0., -1.])
} else if h > self.sub_h {
debug_assert_eq!(h, self.sub_h + 1);
(self.sub_h, [0., 0., 1.])
} else {
(h, n)
};
let z = (hc as f32 / self.sub_h as f32) * 2. - 1.;
Vertex {
pos: [n[0], n[1], z].into(),
normal: normal.into(),
}
}
}
impl Iterator for Cylinder {
type Item = Polygon<Vertex>;
fn next(&mut self) -> Option<Self::Item> {
if self.u == self.sub_u {
if self.h >= self.sub_h {
return None;
}
self.u = 0;
self.h += 1;
}
let u = self.u;
self.u += 1;
let u1 = self.u % self.sub_u;
Some(if self.h < 0 {
let x = self.vert(u, self.h);
let y = self.vert(u1, self.h);
Polygon::PolyTri(Triangle::new(x, BOT, y))
} else if self.h == self.sub_h {
let x = self.vert(u, self.h + 1);
let y = self.vert(u1, self.h + 1);
Polygon::PolyTri(Triangle::new(x, y, TOP))
} else {
let x = self.vert(u, self.h);
let y = self.vert(u1, self.h);
let z = self.vert(u1, self.h + 1);
let w = self.vert(u, self.h + 1);
Polygon::PolyQuad(Quad::new(x, y, z, w))
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.sub_u * (1 + self.sub_h - self.h) as usize - self.u;
(n, Some(n))
}
}
impl SharedVertex<Vertex> for Cylinder {
fn shared_vertex(&self, idx: usize) -> Vertex {
if idx == 0 {
BOT
} else if idx == self.shared_vertex_count() - 1 {
TOP
} else {
let idx = idx - 1;
let u = idx % self.sub_u;
let h = (idx / self.sub_u) as isize - 1;
self.vert(u, h)
}
}
fn shared_vertex_count(&self) -> usize {
(3 + self.sub_h) as usize * self.sub_u + 2
}
}
impl IndexedPolygon<Polygon<usize>> for Cylinder {
fn indexed_polygon(&self, idx: usize) -> Polygon<usize> {
let u = idx % self.sub_u;
let u1 = (idx + 1) % self.sub_u;
let h = (idx / self.sub_u) as isize - 1;
let base = 1 + idx - u;
if h < 0 {
let start = 0;
Polygon::PolyTri(Triangle::new(base + u, start, base + u1))
} else if h == self.sub_h {
let base = base + self.sub_u;
let end = self.shared_vertex_count() - 1;
Polygon::PolyTri(Triangle::new(base + u, base + u1, end))
} else {
Polygon::PolyQuad(Quad::new(
base + u,
base + u1,
base + u1 + self.sub_u,
base + u + self.sub_u,
))
}
}
fn indexed_polygon_count(&self) -> usize {
(2 + self.sub_h) as usize * self.sub_u
}
}