diff --git a/src/geom/mod.rs b/src/geom/mod.rs index 692897c..27f97a1 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -213,3 +213,46 @@ impl Display for CylinderZ { write!(f, "d({:.1e}, {:.1e}) <= {:.1e}", self.center.x(), self.center.y(), self.radius) } } + +#[derive(Copy, Clone, Serialize, Deserialize)] +pub struct Torus { + center: Meters, + /// Unit-length vector describing the axis of the torus + normal: Meters, + /// Distance from origin to the "center" of the solid part of the torus + major_rad: Real, + /// Distance from a center-point of the torus to its surface + minor_rad: Real, +} + +impl Torus { + pub fn new(center: Meters, normal: Meters, major_rad: Flt, minor_rad: Flt) -> Self { + Self { + center, + normal, + major_rad: Real::from_inner(major_rad), + minor_rad: Real::from_inner(minor_rad), + } + } + pub fn new_xy(center: Meters, major_rad: Flt, minor_rad: Flt) -> Self { + Self::new(center, Meters(Vec3::new(0.0, 0.0, 1.0)), major_rad, minor_rad) + } + pub fn new_xz(center: Meters, major_rad: Flt, minor_rad: Flt) -> Self { + Self::new(center, Meters(Vec3::new(0.0, 1.0, 0.0)), major_rad, minor_rad) + } +} + +impl Region for Torus { + fn contains(&self, p: Meters) -> bool { + // a torus is the set of all points < distance `r` from the circle of radius `R`, + // where `r`= minor_rad, `R` = major_rad, and `center` and `normal` define the center and + // plane of the circle. + // 1. Project `p` onto the plane of the circle. + // 2. Find the point `q` on the circle which is nearest to `p`. + // 3. Consider the distance from `p` to `q`. + let p_on_plane = *p - self.normal.with_mag(self.normal.dot(*p)); + let q = (p_on_plane - *self.center).with_mag(self.major_rad.into_inner()); + let distance_to_circle = (*p - q).mag(); + distance_to_circle < self.minor_rad.into_inner() + } +} diff --git a/src/geom/vec.rs b/src/geom/vec.rs index dff80a3..b41d018 100644 --- a/src/geom/vec.rs +++ b/src/geom/vec.rs @@ -179,6 +179,16 @@ impl Vec3 { z: self.z / other.z, } } + + pub fn with_mag(&self, new_mag: Flt) -> Vec3 { + if new_mag == 0.0 { + // avoid div-by-zero if self.mag() == 0 and new_mag == 0 + Self::zero() + } else { + let scale = Real::from_inner(new_mag) / self.mag(); + self.clone() * scale.into_inner() + } + } } impl Into<(Flt, Flt, Flt)> for Vec3 {