Skip to main content

implicit3d/
cylinder.rs

1use crate::{BoundingBox, Object, RealField};
2use nalgebra as na;
3use num_traits::Float;
4
5/// A cylinder along the Z-Axis
6#[derive(Clone, Debug, PartialEq)]
7pub struct Cylinder<S: RealField> {
8    radius: S,
9    bbox: BoundingBox<S>,
10}
11
12impl<S: RealField + Float> Cylinder<S> {
13    /// Create a new infinite Cylinder (along the Z-Axis) of radius r.
14    pub fn new(r: S) -> Self {
15        Cylinder {
16            radius: r,
17            bbox: BoundingBox::new(
18                &na::Point3::new(-r, -r, S::neg_infinity()),
19                &na::Point3::new(r, r, S::infinity()),
20            ),
21        }
22    }
23}
24
25impl<S: std::fmt::Debug + RealField + From<f32> + Float> Object<S> for Cylinder<S> {
26    fn approx_value(&self, p: &na::Point3<S>, slack: S) -> S {
27        let approx = self.bbox.distance(p);
28        if approx <= slack {
29            let zero: S = From::from(0f32);
30            let pv = na::Vector3::new(p.x, p.y, zero);
31            pv.norm() - self.radius
32        } else {
33            approx
34        }
35    }
36    fn bbox(&self) -> &BoundingBox<S> {
37        &self.bbox
38    }
39    fn normal(&self, p: &na::Point3<S>) -> na::Vector3<S> {
40        let zero: S = From::from(0f32);
41        let pv = na::Vector3::new(p.x, p.y, zero);
42        pv.normalize()
43    }
44}
45
46/// A cone along the Z-Axis
47#[derive(Clone, Debug, PartialEq)]
48pub struct Cone<S: RealField> {
49    slope: S,
50    distance_multiplier: S,
51    offset: S,            // Offset the singularity from Z-zero
52    normal_multiplier: S, // muliplier for the normal caclulation
53    bbox: BoundingBox<S>,
54}
55
56impl<S: RealField + Float + From<f32>> Cone<S> {
57    /// Create a new infinite Cone (along the Z-Axis) for a given slope and and offset from origin.
58    pub fn new(slope: S, offset: S) -> Self {
59        let one: S = From::from(1f32);
60        Cone {
61            slope,
62            distance_multiplier: one / Float::sqrt(slope * slope + one), // cos(atan(slope))
63            offset,
64            normal_multiplier: slope / Float::sqrt(slope * slope + one), // sin(atan(slope))
65            bbox: BoundingBox::infinity(),
66        }
67    }
68}
69
70impl<S: std::fmt::Debug + RealField + From<f32> + Float> Object<S> for Cone<S> {
71    fn bbox(&self) -> &BoundingBox<S> {
72        &self.bbox
73    }
74    fn set_bbox(&mut self, bbox: &BoundingBox<S>) {
75        self.bbox = bbox.clone();
76    }
77    fn approx_value(&self, p: &na::Point3<S>, _: S) -> S {
78        let radius = Float::abs(self.slope * (p.z + self.offset));
79        let zero: S = From::from(0f32);
80        let pv = na::Vector3::new(p.x, p.y, zero);
81        (pv.norm() - radius) * self.distance_multiplier
82    }
83    fn normal(&self, p: &na::Point3<S>) -> na::Vector3<S> {
84        let s = Float::signum(p.z + self.offset);
85        let zero: S = From::from(0f32);
86        let mut pv = na::Vector3::new(p.x, p.y, zero);
87        pv.normalize_mut();
88        pv *= self.distance_multiplier;
89        pv.z = -s * self.normal_multiplier;
90        pv
91    }
92}
93
94#[cfg(test)]
95mod test {
96    use approx::assert_ulps_eq;
97    use crate::*;
98    use nalgebra as na;
99
100    #[test]
101    fn cylinder() {
102        let cyl = Cylinder::new(1.0);
103        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 0., 0.), 0.), -1.);
104        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(1., 0., 0.), 0.), 0.);
105        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 1., 0.), 0.), 0.);
106        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 10., 0.), 0.), 9.);
107        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 10., 1000.), 0.), 9.);
108    }
109
110    #[test]
111    fn cone() {
112        let c = Cone::new(2., 10.);
113        assert_ulps_eq!(c.approx_value(&na::Point3::new(0., 0., -10.), 0.), 0.);
114        assert_ulps_eq!(c.approx_value(&na::Point3::new(2., 0., -11.), 0.), 0.);
115    }
116}