use decorum::{R32, Real};
use num::traits::FloatConst;
use num::{NumCast, One};
use std::cmp;
use geometry::Triplet;
use primitive::generate::{
IndexGenerator, IndexPolygonGenerator, PolygonGenerator, PositionGenerator,
PositionPolygonGenerator, PositionVertexGenerator, VertexGenerator,
};
use primitive::topology::{Polygon, Quad, Triangle};
use primitive::Half;
#[derive(Clone, Copy)]
pub struct Bounds {
radius: R32,
}
impl Bounds {
pub fn unit_radius() -> Self {
Bounds { radius: R32::one() }
}
pub fn unit_width() -> Self {
Bounds {
radius: R32::half(),
}
}
}
impl Default for Bounds {
fn default() -> Self {
Self::unit_radius()
}
}
#[derive(Clone, Copy)]
pub struct UvSphere {
nu: usize, nv: usize, }
impl UvSphere {
pub fn new(nu: usize, nv: usize) -> Self {
UvSphere {
nu: cmp::max(3, nu),
nv: cmp::max(2, nv),
}
}
fn vertex_with_position_from(
&self,
state: &<Self as PositionGenerator>::State,
u: usize,
v: usize,
) -> Triplet<R32> {
let u = (<R32 as NumCast>::from(u).unwrap() / <R32 as NumCast>::from(self.nu).unwrap())
* R32::PI()
* 2.0;
let v = (<R32 as NumCast>::from(v).unwrap() / <R32 as NumCast>::from(self.nv).unwrap())
* R32::PI();
Triplet(
state.radius * u.cos() * v.sin(),
state.radius * u.sin() * v.sin(),
state.radius * v.cos(),
)
}
fn vertex_with_index_from(
&self,
_: &<Self as IndexGenerator>::State,
u: usize,
v: usize,
) -> usize {
if v == 0 {
0
}
else if v == self.nv {
((self.nv - 1) * self.nu) + 1
}
else {
((v - 1) * self.nu) + (u % self.nu) + 1
}
}
fn map_polygon_index(&self, index: usize) -> (usize, usize) {
(index % self.nu, index / self.nu)
}
}
impl Default for UvSphere {
fn default() -> Self {
UvSphere::new(16, 16)
}
}
impl VertexGenerator for UvSphere {
fn vertex_count(&self) -> usize {
(self.nv - 1) * self.nu + 2
}
}
impl PolygonGenerator for UvSphere {
fn polygon_count(&self) -> usize {
self.nu * self.nv
}
}
impl PositionGenerator for UvSphere {
type State = Bounds;
}
impl PositionVertexGenerator for UvSphere {
type Output = Triplet<R32>;
fn vertex_with_position_from(&self, state: &Self::State, index: usize) -> Self::Output {
if index == 0 {
self.vertex_with_position_from(state, 0, 0)
}
else if index == self.vertex_count() - 1 {
self.vertex_with_position_from(state, 0, self.nv)
}
else {
let index = index - 1;
self.vertex_with_position_from(state, index % self.nu, (index / self.nu) + 1)
}
}
}
impl PositionPolygonGenerator for UvSphere {
type Output = Polygon<Triplet<R32>>;
fn polygon_with_position_from(&self, state: &Self::State, index: usize) -> Self::Output {
let (u, v) = self.map_polygon_index(index);
let (p, q) = ((u + 1) % self.nu, v + 1);
let lower = self.vertex_with_position_from(state, u, v);
if v == 0 {
Polygon::Triangle(Triangle::new(
lower,
self.vertex_with_position_from(state, u, q),
self.vertex_with_position_from(state, p, q),
))
}
else if v == self.nv - 1 {
Polygon::Triangle(Triangle::new(
self.vertex_with_position_from(state, 0, self.nv),
self.vertex_with_position_from(state, p, v),
lower,
))
}
else {
Polygon::Quad(Quad::new(
lower,
self.vertex_with_position_from(state, u, q),
self.vertex_with_position_from(state, p, q),
self.vertex_with_position_from(state, p, v),
))
}
}
}
impl IndexGenerator for UvSphere {
type State = ();
}
impl IndexPolygonGenerator for UvSphere {
type Output = Polygon<usize>;
fn polygon_with_index_from(
&self,
state: &Self::State,
index: usize,
) -> <Self as IndexPolygonGenerator>::Output {
let (u, v) = self.map_polygon_index(index);
let (p, q) = (u + 1, v + 1);
let low = self.vertex_with_index_from(state, u, v);
let high = self.vertex_with_index_from(state, p, q);
if v == 0 {
Polygon::Triangle(Triangle::new(
low,
self.vertex_with_index_from(state, u, q),
high,
))
}
else if v == self.nv - 1 {
Polygon::Triangle(Triangle::new(
high,
self.vertex_with_index_from(state, p, v),
low,
))
}
else {
Polygon::Quad(Quad::new(
low,
self.vertex_with_index_from(state, u, q),
high,
self.vertex_with_index_from(state, p, v),
))
}
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use std::iter::FromIterator;
use primitive::sphere::*;
use primitive::*;
#[test]
fn vertex_count() {
assert_eq!(
5,
UvSphere::new(3, 2)
.vertices_with_position() .count()
);
}
#[test]
fn polygon_vertex_count() {
assert_eq!(
18,
UvSphere::new(3, 2)
.polygons_with_position() .vertices()
.count()
);
}
#[test]
fn index_to_vertex_mapping() {
assert_eq!(
5,
BTreeSet::from_iter(UvSphere::new(3, 2)
.polygons_with_index() .vertices())
.len()
)
}
}