1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
extern crate cgmath;
extern crate genmesh;
use std::collections::HashSet;
use cgmath::InnerSpace;
use genmesh::{generators, EmitLines, Line, Lines, MapToVertices, Vertex};
#[derive(Debug)]
struct Edge {
dir: cgmath::Vector3<f32>,
mid: cgmath::Vector3<f32>,
nor: cgmath::Vector3<f32>,
}
impl Edge {
fn new(line: Line<Vertex>) -> Self {
let Line {
x: Vertex { pos: x, normal: nx },
y: Vertex { pos: y, normal: ny },
} = line;
Edge {
dir: cgmath::vec3(y.x - x.x, y.y - x.y, y.z - x.z),
mid: cgmath::vec3(y.x + x.x, y.y + x.y, y.z + x.z) * 0.5,
nor: cgmath::vec3(nx.x + ny.x, nx.y + ny.y, nx.z + ny.z),
}
}
/// Check that the corner `(self, e)` has outward winding order
/// (thus, it's normal is in the same hemisphere as it's offset).
fn check_to(&self, e: &Edge) {
let normal = self.dir.cross(e.dir);
let mid = (self.mid + e.mid) * 0.5;
assert!(normal.dot(mid) > 0.0 && e.nor.dot(mid) > 0.0);
}
}
/// Make sure that all the polygons in the `poly_iter` have the outward
/// winding order relative to the origin of the coordinate system.
/// This is a simplified (and incomplete) convex shape test.
fn test_outward<P, I>(poly_iter: I)
where
P: EmitLines<Vertex = Vertex> + ::std::fmt::Debug,
I: Iterator<Item = P>,
{
let mut edges = Vec::new();
for poly in poly_iter {
edges.clear();
poly.emit_lines(|l| edges.push(Edge::new(l)));
// check last-first corner first, since it wraps
edges.last().unwrap().check_to(&edges[0]);
// check all the non-wrapping corners
for (a, b) in edges.iter().zip(edges[1..].iter()) {
a.check_to(b);
}
}
}
// this will check that a primitive is closed, and that every edge of
// every polygon making up the primitive has a paired neighbor. This
// does not test that a polygon has the correcting winding direction
// just that the winding directions are consistent for the polygon.
//
// This is based on ftp://ftp.sgi.com/opengl/contrib/blythe/advanced99/notes/node16.html
//
fn test_closed<P, I>(poly_iter: I)
where
P: EmitLines<Vertex = Vertex> + ::std::fmt::Debug,
I: Iterator<Item = P>,
{
// convert the vertex to something that we can use to find approximate
// polygons. This is mostly to get past the fact that f32 is a cursed
// type in rust and can not be used as a key.
fn to_checkable(vertex: Vertex) -> [i32; 3] {
[
(vertex.pos.x * 1000000.) as i32,
(vertex.pos.y * 1000000.) as i32,
(vertex.pos.z * 1000000.) as i32,
]
}
let mut lines = HashSet::new();
for line in poly_iter.lines().vertex(to_checkable) {
// if the line was in the set we found the matching pair
// which is one less pair that we are looking for
if !lines.remove(&line) {
// if didn't find the pair, we flip the line around and put it into
// the search table.
lines.insert(Line {
x: line.y,
y: line.x,
});
}
}
// if we found all the pairs, we should be valid and a closed geometry
// this means that there is no polygon who's neighbor switches winding
// direction, but it does not mean that the polygon is correct. They
// all could be backwards. So this still requires a secondary inspection.
assert!(lines.len() == 0);
}
#[test]
fn wind_plane() {
// the plane is not closed, so no point in testing for a closed
// shape.
test_outward(generators::Plane::new().vertex(|mut v| {
v.pos.z = 1.;
v
}));
test_outward(generators::Plane::subdivide(3, 4).vertex(|mut v| {
v.pos.z = 1.;
v
}));
}
#[test]
fn gen_cube() {
test_outward(generators::Cube::new());
test_closed(generators::Cube::new());
}
#[test]
fn gen_cylinder() {
test_outward(generators::Cylinder::new(5));
test_closed(generators::Cylinder::new(5));
test_outward(generators::Cylinder::subdivide(3, 4));
test_closed(generators::Cylinder::subdivide(3, 4));
}
#[test]
fn gen_sphere_uv() {
test_outward(generators::SphereUv::new(4, 3));
test_closed(generators::SphereUv::new(4, 3));
}
#[test]
fn gen_ico_sphere() {
test_outward(generators::IcoSphere::new());
test_closed(generators::IcoSphere::new());
test_outward(generators::IcoSphere::subdivide(3));
test_closed(generators::IcoSphere::subdivide(3));
}
#[test]
fn gen_cone() {
test_outward(generators::Cone::new(8));
test_closed(generators::Cone::new(8));
}
#[test]
fn gen_torus() {
// we don't do an outward test because the primitive is not
// convex and will fail this test.
test_closed(generators::Torus::new(10.0, 5.0, 8, 8));
}
|