#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct RevolutionSurface {
pub verts: Vec<[f32; 3]>,
pub tris: Vec<[u32; 3]>,
pub profile_len: usize,
pub angular_steps: usize,
}
pub fn build_revolution_surface(
profile: &[[f32; 3]],
angular_steps: usize,
angle_rad: f32,
) -> RevolutionSurface {
let n = profile.len();
if n < 2 || angular_steps < 1 {
return RevolutionSurface {
verts: vec![],
tris: vec![],
profile_len: 0,
angular_steps: 0,
};
}
let rings = angular_steps + 1;
let mut verts = Vec::with_capacity(rings * n);
for step in 0..rings {
let theta = angle_rad * step as f32 / angular_steps as f32;
let (sin_t, cos_t) = theta.sin_cos();
for &p in profile {
let r = p[0];
verts.push([r * cos_t, p[1], r * sin_t]);
}
}
let mut tris = Vec::new();
for s in 0..(rings - 1) {
for i in 0..(n - 1) {
let a = (s * n + i) as u32;
let b = (s * n + i + 1) as u32;
let c = ((s + 1) * n + i) as u32;
let d = ((s + 1) * n + i + 1) as u32;
tris.push([a, c, b]);
tris.push([b, c, d]);
}
}
RevolutionSurface {
verts,
tris,
profile_len: n,
angular_steps,
}
}
pub fn revolution_vertex_count(surf: &RevolutionSurface) -> usize {
surf.verts.len()
}
pub fn revolution_tri_count(surf: &RevolutionSurface) -> usize {
surf.tris.len()
}
pub fn validate_revolution_surface(surf: &RevolutionSurface) -> bool {
let n = surf.verts.len() as u32;
surf.tris.iter().all(|t| t[0] < n && t[1] < n && t[2] < n)
}
pub fn revolution_surface_area(surf: &RevolutionSurface) -> f32 {
let mut area = 0.0f32;
for tri in &surf.tris {
let a = surf.verts[tri[0] as usize];
let b = surf.verts[tri[1] as usize];
let c = surf.verts[tri[2] as usize];
let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
let ac = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
let cross = [
ab[1] * ac[2] - ab[2] * ac[1],
ab[2] * ac[0] - ab[0] * ac[2],
ab[0] * ac[1] - ab[1] * ac[0],
];
area += (cross[0].powi(2) + cross[1].powi(2) + cross[2].powi(2)).sqrt() * 0.5;
}
area
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::TAU;
fn cylinder_profile(n: usize) -> Vec<[f32; 3]> {
(0..n).map(|i| [1.0, i as f32, 0.0]).collect()
}
#[test]
fn test_revolution_vertex_count() {
let s = build_revolution_surface(&cylinder_profile(5), 8, TAU);
assert_eq!(revolution_vertex_count(&s), 45);
}
#[test]
fn test_revolution_tri_count() {
let s = build_revolution_surface(&cylinder_profile(5), 8, TAU);
assert_eq!(revolution_tri_count(&s), 64);
}
#[test]
fn test_revolution_empty_on_too_short() {
let s = build_revolution_surface(&[[1.0, 0.0, 0.0]], 8, TAU);
assert_eq!(revolution_vertex_count(&s), 0);
}
#[test]
fn test_revolution_empty_on_zero_steps() {
let s = build_revolution_surface(&cylinder_profile(3), 0, TAU);
assert_eq!(revolution_vertex_count(&s), 0);
}
#[test]
fn test_validate_revolution_surface() {
let s = build_revolution_surface(&cylinder_profile(4), 6, TAU);
assert!(validate_revolution_surface(&s));
}
#[test]
fn test_revolution_surface_area_positive() {
let s = build_revolution_surface(&cylinder_profile(3), 12, TAU);
assert!(revolution_surface_area(&s) > 0.0);
}
#[test]
fn test_revolution_profile_len_stored() {
let s = build_revolution_surface(&cylinder_profile(7), 4, TAU);
assert_eq!(s.profile_len, 7);
assert_eq!(s.angular_steps, 4);
}
#[test]
fn test_revolution_half_turn() {
use std::f32::consts::PI;
let s = build_revolution_surface(&cylinder_profile(3), 4, PI);
assert_eq!(revolution_vertex_count(&s), 15);
}
}