#![allow(dead_code)]
use std::f32::consts::TAU;
#[allow(dead_code)]
pub struct TubeGen2 {
pub radius: f32,
pub segments: usize,
pub rings: usize,
pub length: f32,
}
#[allow(dead_code)]
pub fn gen_tube2(radius: f32, length: f32, rings: usize, segments: usize) -> (Vec<[f32;3]>, Vec<u32>) {
let rings = rings.max(2);
let segs = segments.max(3);
let mut positions = Vec::new();
let mut indices = Vec::new();
for r in 0..=rings {
let z = (r as f32 / rings as f32) * length - length * 0.5;
for s in 0..segs {
let angle = (s as f32 / segs as f32) * TAU;
positions.push([radius * angle.cos(), radius * angle.sin(), z]);
}
}
for r in 0..rings {
for s in 0..segs {
let next_s = (s + 1) % segs;
let i00 = (r * segs + s) as u32;
let i10 = (r * segs + next_s) as u32;
let i01 = ((r+1) * segs + s) as u32;
let i11 = ((r+1) * segs + next_s) as u32;
indices.extend_from_slice(&[i00, i10, i01, i10, i11, i01]);
}
}
(positions, indices)
}
#[allow(dead_code)]
pub fn gen_tube2_from_path(path: &[[f32;3]], radius: f32, segments: usize) -> (Vec<[f32;3]>, Vec<u32>) {
if path.len() < 2 { return (Vec::new(), Vec::new()); }
let segs = segments.max(3);
let mut positions = Vec::new();
let mut indices = Vec::new();
for (ri, ¢er) in path.iter().enumerate() {
for s in 0..segs {
let angle = (s as f32 / segs as f32) * TAU;
positions.push([center[0] + radius * angle.cos(), center[1] + radius * angle.sin(), center[2]]);
}
if ri + 1 < path.len() {
for s in 0..segs {
let next_s = (s + 1) % segs;
let i00 = (ri * segs + s) as u32;
let i10 = (ri * segs + next_s) as u32;
let i01 = ((ri+1) * segs + s) as u32;
let i11 = ((ri+1) * segs + next_s) as u32;
indices.extend_from_slice(&[i00, i10, i01, i10, i11, i01]);
}
}
}
(positions, indices)
}
#[allow(dead_code)]
pub fn tube2_vertex_count(rings: usize, segments: usize) -> usize {
(rings + 1) * segments
}
#[allow(dead_code)]
pub fn tube2_cap_flat(
positions: &mut Vec<[f32;3]>,
indices: &mut Vec<u32>,
rings: usize,
segments: usize,
) {
let segs = segments.max(3);
let bottom_center: u32 = positions.len() as u32;
let first_bottom = positions[0];
let cx0 = positions[0..segs].iter().map(|p| p[0]).sum::<f32>() / segs as f32;
let cy0 = positions[0..segs].iter().map(|p| p[1]).sum::<f32>() / segs as f32;
let cz0 = first_bottom[2];
positions.push([cx0, cy0, cz0]);
for s in 0..segs {
let next_s = (s + 1) % segs;
indices.extend_from_slice(&[bottom_center, s as u32, next_s as u32]);
}
let top_start = rings * segs;
let top_center: u32 = positions.len() as u32;
let cx1 = positions[top_start..top_start+segs].iter().map(|p| p[0]).sum::<f32>() / segs as f32;
let cy1 = positions[top_start..top_start+segs].iter().map(|p| p[1]).sum::<f32>() / segs as f32;
let cz1 = positions[top_start][2];
positions.push([cx1, cy1, cz1]);
for s in 0..segs {
let next_s = (s + 1) % segs;
indices.extend_from_slice(&[top_center, (top_start + next_s) as u32, (top_start + s) as u32]);
}
}
#[allow(dead_code)]
pub fn tube2_radius_vary(radii: &[f32], length: f32, segments: usize) -> (Vec<[f32;3]>, Vec<u32>) {
if radii.is_empty() { return (Vec::new(), Vec::new()); }
let segs = segments.max(3);
let rings = radii.len() - 1;
let mut positions = Vec::new();
let mut indices = Vec::new();
for (ri, &r) in radii.iter().enumerate() {
let z = if rings == 0 { 0.0 } else { (ri as f32 / rings as f32) * length - length * 0.5 };
for s in 0..segs {
let angle = (s as f32 / segs as f32) * TAU;
positions.push([r * angle.cos(), r * angle.sin(), z]);
}
}
for r in 0..rings {
for s in 0..segs {
let next_s = (s + 1) % segs;
let i00 = (r * segs + s) as u32;
let i10 = (r * segs + next_s) as u32;
let i01 = ((r+1) * segs + s) as u32;
let i11 = ((r+1) * segs + next_s) as u32;
indices.extend_from_slice(&[i00, i10, i01, i10, i11, i01]);
}
}
(positions, indices)
}
#[allow(dead_code)]
pub fn tube2_uvs(rings: usize, segments: usize) -> Vec<[f32;2]> {
let mut uvs = Vec::new();
for r in 0..=rings {
let v = r as f32 / rings as f32;
for s in 0..segments {
let u = s as f32 / segments as f32;
uvs.push([u, v]);
}
}
uvs
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gen_tube2_vertex_count() {
let (pos, _) = gen_tube2(1.0, 2.0, 4, 8);
assert_eq!(pos.len(), tube2_vertex_count(4, 8));
}
#[test]
fn test_gen_tube2_index_count() {
let (_, idx) = gen_tube2(1.0, 2.0, 2, 6);
assert_eq!(idx.len(), 2 * 2 * 6 * 3);
}
#[test]
fn test_gen_tube2_from_path_empty() {
let (pos, idx) = gen_tube2_from_path(&[[0.0,0.0,0.0]], 0.5, 6);
assert!(pos.is_empty() && idx.is_empty());
}
#[test]
fn test_gen_tube2_from_path_two_rings() {
let path = vec![[0.0f32,0.0,0.0],[0.0,0.0,1.0]];
let (pos, _) = gen_tube2_from_path(&path, 0.5, 6);
assert_eq!(pos.len(), 12);
}
#[test]
fn test_tube2_vertex_count() {
assert_eq!(tube2_vertex_count(3, 8), 32);
}
#[test]
fn test_tube2_uvs_count() {
let uvs = tube2_uvs(4, 8);
assert_eq!(uvs.len(), 5 * 8);
}
#[test]
fn test_tube2_radius_vary() {
let radii = vec![0.5f32, 1.0, 0.5];
let (pos, idx) = tube2_radius_vary(&radii, 2.0, 6);
assert_eq!(pos.len(), 3 * 6);
assert!(!idx.is_empty());
}
#[test]
fn test_gen_tube2_config() {
let cfg = TubeGen2 { radius: 1.0, segments: 8, rings: 4, length: 2.0 };
assert!((cfg.radius - 1.0).abs() < 1e-5);
}
#[test]
fn test_tube2_uvs_range() {
let uvs = tube2_uvs(2, 4);
for uv in &uvs {
assert!((0.0..=1.0).contains(&uv[0]));
assert!((0.0..=1.0).contains(&uv[1]));
}
}
}