#![allow(dead_code)]
use std::f32::consts::PI;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CapType {
Flat,
Dome,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CylinderCap {
pub positions: Vec<[f32; 3]>,
pub indices: Vec<u32>,
pub cap_type: CapType,
}
#[allow(dead_code)]
pub fn flat_cap(radius: f32, segments: usize, y: f32, flip: bool) -> CylinderCap {
let segs = segments.max(3);
let mut positions = Vec::with_capacity(segs + 1);
let mut indices = Vec::with_capacity(segs * 3);
positions.push([0.0, y, 0.0]);
for i in 0..segs {
let theta = 2.0 * PI * (i as f32) / (segs as f32);
positions.push([radius * theta.cos(), y, radius * theta.sin()]);
}
for i in 0..segs {
let a = (i + 1) as u32;
let b = ((i + 1) % segs + 1) as u32;
if flip {
indices.extend_from_slice(&[0, b, a]);
} else {
indices.extend_from_slice(&[0, a, b]);
}
}
CylinderCap {
positions,
indices,
cap_type: CapType::Flat,
}
}
#[allow(dead_code)]
pub fn dome_cap(
radius: f32,
segments: usize,
rings: usize,
y_base: f32,
flip: bool,
) -> CylinderCap {
let segs = segments.max(3);
let rings = rings.max(1);
let mut positions = Vec::new();
let mut indices = Vec::new();
for r in 0..=rings {
let phi = PI * 0.5 * (r as f32) / (rings as f32);
let yr = y_base + radius * phi.sin();
let xr = radius * phi.cos();
for s in 0..segs {
let theta = 2.0 * PI * (s as f32) / (segs as f32);
positions.push([xr * theta.cos(), yr, xr * theta.sin()]);
}
}
for r in 0..rings {
for s in 0..segs {
let a = (r * segs + s) as u32;
let b = (r * segs + (s + 1) % segs) as u32;
let c = ((r + 1) * segs + s) as u32;
let d = ((r + 1) * segs + (s + 1) % segs) as u32;
if flip {
indices.extend_from_slice(&[a, c, b, b, c, d]);
} else {
indices.extend_from_slice(&[a, b, c, b, d, c]);
}
}
}
CylinderCap {
positions,
indices,
cap_type: CapType::Dome,
}
}
#[allow(dead_code)]
pub fn cap_vertex_count(cap: &CylinderCap) -> usize {
cap.positions.len()
}
#[allow(dead_code)]
pub fn cap_triangle_count(cap: &CylinderCap) -> usize {
cap.indices.len() / 3
}
#[allow(dead_code)]
pub fn is_flat_cap(cap: &CylinderCap) -> bool {
cap.cap_type == CapType::Flat
}
#[allow(dead_code)]
pub fn cap_to_json(cap: &CylinderCap) -> String {
format!(
"{{\"vertices\":{},\"triangles\":{}}}",
cap_vertex_count(cap),
cap_triangle_count(cap)
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flat_cap_vertex_count() {
let cap = flat_cap(1.0, 8, 0.0, false);
assert_eq!(cap_vertex_count(&cap), 9); }
#[test]
fn flat_cap_triangle_count() {
let cap = flat_cap(1.0, 8, 0.0, false);
assert_eq!(cap_triangle_count(&cap), 8);
}
#[test]
fn flat_cap_is_flat() {
let cap = flat_cap(1.0, 6, 0.0, false);
assert!(is_flat_cap(&cap));
}
#[test]
fn dome_cap_not_flat() {
let cap = dome_cap(1.0, 8, 4, 0.0, false);
assert!(!is_flat_cap(&cap));
}
#[test]
fn dome_cap_has_vertices() {
let cap = dome_cap(1.0, 8, 4, 0.0, false);
assert!(cap_vertex_count(&cap) > 0);
}
#[test]
fn flat_cap_center_at_y() {
let cap = flat_cap(1.0, 6, 2.5, false);
assert!((cap.positions[0][1] - 2.5).abs() < 1e-5);
}
#[test]
fn flat_cap_flipped_has_same_count() {
let a = flat_cap(1.0, 8, 0.0, false);
let b = flat_cap(1.0, 8, 0.0, true);
assert_eq!(cap_vertex_count(&a), cap_vertex_count(&b));
assert_eq!(cap_triangle_count(&a), cap_triangle_count(&b));
}
#[test]
fn json_contains_vertices() {
let cap = flat_cap(1.0, 6, 0.0, false);
let j = cap_to_json(&cap);
assert!(j.contains("vertices"));
}
#[test]
fn pi_from_consts() {
let circumference = 2.0 * PI * 1.0_f32;
assert!(circumference > 6.0);
}
#[test]
fn contains_range() {
let v = 0.5_f32;
assert!((0.0..=1.0).contains(&v));
}
}