Skip to main content

rscad_csg_bsp/primitives/
cylinder.rs

1use crate::*;
2use nalgebra::{Point, Vector3};
3
4/// Create a cylinder with base at the origin, axis along Y.
5///
6/// - `radius`: cylinder radius
7/// - `height`: total height (base at y=0, top at y=height)
8/// - `segments`: number of sides around the circumference
9pub fn cylinder<T: Number>(radius: T, height: T, segments: usize) -> Csg<T> {
10    let segments = segments.max(3);
11    let two = T::one() + T::one();
12    let half_h = height / two;
13    let pi = T::PI();
14    let two_pi = pi + pi;
15    let seg_t = T::from(segments).expect("BUG: segment count is representable as T");
16
17    let circle_point = |j: usize, y: T| -> Point<T, 3> {
18        let angle = two_pi * T::from(j).expect("BUG: loop index is representable as T") / seg_t;
19        Point::from([
20            radius * num_traits::real::Real::cos(angle),
21            y,
22            radius * num_traits::real::Real::sin(angle),
23        ])
24    };
25
26    let top: Vec<Point<T, 3>> = (0..segments).map(|j| circle_point(j, half_h)).collect();
27    let bot: Vec<Point<T, 3>> = (0..segments).map(|j| circle_point(j, -half_h)).collect();
28
29    let mut polygons = Vec::new();
30
31    // Top cap (+Y)
32    if let Ok(p) = Polygon::from_points_and_plane(top.clone(), Plane::new(Vector3::y(), half_h)) {
33        polygons.push(p);
34    }
35
36    // Bottom cap (-Y), reversed winding
37    let mut bot_cap = bot.clone();
38    bot_cap.reverse();
39    if let Ok(p) = Polygon::from_points_and_plane(bot_cap, Plane::new(-Vector3::y(), half_h)) {
40        polygons.push(p);
41    }
42
43    // Side quads
44    for j in 0..segments {
45        let jn = (j + 1) % segments;
46
47        // Outward-pointing side plane
48        let mid_angle =
49            (two * T::from(j).expect("BUG: loop index is representable as T") + T::one()) * pi
50                / seg_t;
51        let normal = Vector3::new(
52            num_traits::real::Real::cos(mid_angle),
53            T::zero(),
54            num_traits::real::Real::sin(mid_angle),
55        );
56        let dist = radius * num_traits::real::Real::cos(pi / seg_t);
57
58        let verts = [top[j], top[jn], bot[jn], bot[j]];
59        if let Ok(p) = Polygon::from_points_and_plane(verts, Plane::new(normal, dist)) {
60            polygons.push(p);
61        }
62    }
63
64    Csg::from_polygons(polygons).translate(Vector3::new(T::zero(), half_h, T::zero()))
65}