Skip to main content

rscad_csg_bsp/primitives/
torus.rs

1use crate::*;
2use nalgebra::Point;
3
4/// Create a torus centered at the origin, lying in the XZ plane (tube wraps around the Y axis).
5///
6/// - `major_radius`: distance from the center of the torus to the center of the tube
7/// - `minor_radius`: radius of the tube
8/// - `major_segments`: divisions around the ring (around Y axis)
9/// - `minor_segments`: divisions around the tube cross-section
10pub fn torus<T: Number>(
11    major_radius: T,
12    minor_radius: T,
13    major_segments: usize,
14    minor_segments: usize,
15) -> Csg<T> {
16    let major_segments = major_segments.max(3);
17    let minor_segments = minor_segments.max(3);
18    let two_pi = T::PI() + T::PI();
19    let maj_t = T::from(major_segments).expect("BUG: segment count is representable as T");
20    let min_t = T::from(minor_segments).expect("BUG: segment count is representable as T");
21
22    let vertex = |i: usize, j: usize| -> Point<T, 3> {
23        let theta = two_pi * T::from(i).expect("BUG: loop index is representable as T") / maj_t;
24        let phi = two_pi * T::from(j).expect("BUG: loop index is representable as T") / min_t;
25        let r = major_radius + minor_radius * num_traits::real::Real::cos(phi);
26        Point::from([
27            r * num_traits::real::Real::cos(theta),
28            minor_radius * num_traits::real::Real::sin(phi),
29            r * num_traits::real::Real::sin(theta),
30        ])
31    };
32
33    let mut polygons = Vec::with_capacity(major_segments * minor_segments);
34
35    for i in 0..major_segments {
36        let in_ = (i + 1) % major_segments;
37        for j in 0..minor_segments {
38            let jn = (j + 1) % minor_segments;
39
40            let v00 = vertex(i, j);
41            let v01 = vertex(i, jn);
42            let v10 = vertex(in_, j);
43            let v11 = vertex(in_, jn);
44
45            if let Ok(p) = Polygon::from_points(vec![v00, v01, v11, v10]) {
46                polygons.push(p);
47            }
48        }
49    }
50
51    Csg::from_polygons(polygons)
52}