1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use alga::general::Real;
use na;
use num_traits::Float;
use {BoundingBox, Object};

/// A cylinder along the Z-Axis
#[derive(Clone, Debug, PartialEq)]
pub struct Cylinder<S: Real> {
    radius: S,
    bbox: BoundingBox<S>,
}

impl<S: Real + Float> Cylinder<S> {
    /// Create a new infinite Cylinder (along the Z-Axis) of radius r.
    pub fn new(r: S) -> Box<Cylinder<S>> {
        Box::new(Cylinder {
            radius: r,
            bbox: BoundingBox::new(
                &na::Point3::new(-r, -r, S::neg_infinity()),
                &na::Point3::new(r, r, S::infinity()),
            ),
        })
    }
}

impl<S: ::std::fmt::Debug + Real + From<f32> + Float> Object<S> for Cylinder<S> {
    fn approx_value(&self, p: &na::Point3<S>, slack: S) -> S {
        let approx = self.bbox.distance(p);
        if approx <= slack {
            let zero: S = From::from(0f32);
            let pv = na::Vector3::new(p.x, p.y, zero);
            pv.norm() - self.radius
        } else {
            approx
        }
    }
    fn bbox(&self) -> &BoundingBox<S> {
        &self.bbox
    }
    fn normal(&self, p: &na::Point3<S>) -> na::Vector3<S> {
        let zero: S = From::from(0f32);
        let pv = na::Vector3::new(p.x, p.y, zero);
        pv.normalize()
    }
}

/// A cone along the Z-Axis
#[derive(Clone, Debug, PartialEq)]
pub struct Cone<S: Real> {
    slope: S,
    distance_multiplier: S,
    offset: S,            // Offset the singularity from Z-zero
    normal_multiplier: S, // muliplier for the normal caclulation
    bbox: BoundingBox<S>,
}

impl<S: Real + Float + From<f32>> Cone<S> {
    /// Create a new infinite Cone (along the Z-Axis) for a given slope and and offset from origin.
    pub fn new(slope: S, offset: S) -> Box<Cone<S>> {
        let one: S = From::from(1f32);
        Box::new(Cone {
            slope,
            distance_multiplier: one / Float::sqrt(slope * slope + one), // cos(atan(slope))
            offset,
            normal_multiplier: slope / Float::sqrt(slope * slope + one), // sin(atan(slope))
            bbox: BoundingBox::infinity(),
        })
    }
}

impl<S: ::std::fmt::Debug + Real + From<f32> + Float> Object<S> for Cone<S> {
    fn bbox(&self) -> &BoundingBox<S> {
        &self.bbox
    }
    fn set_bbox(&mut self, bbox: &BoundingBox<S>) {
        self.bbox = bbox.clone();
    }
    fn approx_value(&self, p: &na::Point3<S>, _: S) -> S {
        let radius = Float::abs(self.slope * (p.z + self.offset));
        let zero: S = From::from(0f32);
        let pv = na::Vector3::new(p.x, p.y, zero);
        (pv.norm() - radius) * self.distance_multiplier
    }
    fn normal(&self, p: &na::Point3<S>) -> na::Vector3<S> {
        let s = Float::signum(p.z + self.offset);
        let zero: S = From::from(0f32);
        let mut pv = na::Vector3::new(p.x, p.y, zero);
        pv.normalize_mut();
        pv *= self.distance_multiplier;
        pv.z = -s * self.normal_multiplier;
        pv
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn cylinder() {
        let cyl = Cylinder::new(1.0);
        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 0., 0.), 0.), -1.);
        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(1., 0., 0.), 0.), 0.);
        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 1., 0.), 0.), 0.);
        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 10., 0.), 0.), 9.);
        assert_ulps_eq!(cyl.approx_value(&na::Point3::new(0., 10., 1000.), 0.), 9.);
    }

    #[test]
    fn cone() {
        let c = Cone::new(2., 10.);
        assert_ulps_eq!(c.approx_value(&na::Point3::new(0., 0., -10.), 0.), 0.);
        assert_ulps_eq!(c.approx_value(&na::Point3::new(2., 0., -11.), 0.), 0.);
    }
}