vox_geometry_rust/
sphere3.rs

1/*
2 * // Copyright (c) 2021 Feng Yang
3 * //
4 * // I am making my contributions/submissions to this project solely in my
5 * // personal capacity and am not conveying any rights to any intellectual
6 * // property of any third parties.
7 */
8
9use crate::surface3::*;
10use crate::vector3::Vector3D;
11use crate::transform3::Transform3;
12use crate::bounding_box3::BoundingBox3D;
13use crate::ray3::Ray3D;
14use std::sync::{RwLock, Arc};
15
16///
17/// # 3-D sphere geometry.
18///
19/// This class represents 3-D sphere geometry which extends Surface3 by
20/// overriding surface-related queries.
21///
22pub struct Sphere3 {
23    /// Center of the sphere.
24    pub center: Vector3D,
25
26    /// Radius of the sphere.
27    pub radius: f64,
28
29    /// data from surface3
30    pub surface_data: Surface3Data,
31}
32
33impl Sphere3 {
34    /// Constructs a sphere with center at (0, 0) and radius of 1.
35    pub fn new_default(transform: Option<Transform3>,
36                       is_normal_flipped: Option<bool>) -> Sphere3 {
37        return Sphere3 {
38            center: Vector3D::new_default(),
39            radius: 0.0,
40            surface_data: Surface3Data::new(transform, is_normal_flipped),
41        };
42    }
43
44    /// Constructs a sphere with \p center and \p radius.
45    pub fn new(center: Vector3D,
46               radius: f64,
47               transform: Option<Transform3>,
48               is_normal_flipped: Option<bool>) -> Sphere3 {
49        return Sphere3 {
50            center,
51            radius,
52            surface_data: Surface3Data::new(transform, is_normal_flipped),
53        };
54    }
55
56    /// Returns builder fox Sphere3.
57    pub fn builder() -> Builder {
58        return Builder::new();
59    }
60}
61
62impl Surface3 for Sphere3 {
63    fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
64        return self.closest_normal_local(other_point) * self.radius + self.center;
65    }
66
67    fn bounding_box_local(&self) -> BoundingBox3D {
68        let r = Vector3D::new(self.radius, self.radius, self.radius);
69        return BoundingBox3D::new(self.center - r, self.center + r);
70    }
71
72    fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
73        let mut intersection = SurfaceRayIntersection3::new();
74        let r = ray.origin - self.center;
75        let b = ray.direction.dot(&r);
76        let c = r.length_squared() - crate::math_utils::square(self.radius);
77        let mut d = b * b - c;
78
79        if d > 0.0 {
80            d = f64::sqrt(d);
81            let mut t_min = -b - d;
82            let t_max = -b + d;
83
84            if t_min < 0.0 {
85                t_min = t_max;
86            }
87
88            if t_min < 0.0 {
89                intersection.is_intersecting = false;
90            } else {
91                intersection.is_intersecting = true;
92                intersection.distance = t_min;
93                intersection.point = ray.origin + ray.direction * t_min;
94                intersection.normal = (intersection.point - self.center).normalized();
95            }
96        } else {
97            intersection.is_intersecting = false;
98        }
99
100        return intersection;
101    }
102
103    fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
104        return if self.center.is_similar(&other_point, None) {
105            Vector3D::new(1.0, 0.0, 0.0)
106        } else {
107            (*other_point - self.center).normalized()
108        };
109    }
110
111    fn intersects_local(&self, ray_local: &Ray3D) -> bool {
112        let r = ray_local.origin - self.center;
113        let b = ray_local.direction.dot(&r);
114        let c = r.length_squared() - crate::math_utils::square(self.radius);
115        let mut d = b * b - c;
116
117        if d > 0.0 {
118            d = f64::sqrt(d);
119            let mut t_min = -b - d;
120            let t_max = -b + d;
121
122            if t_min < 0.0 {
123                t_min = t_max;
124            }
125
126            if t_min >= 0.0 {
127                return true;
128            }
129        }
130
131        return false;
132    }
133
134    fn closest_distance_local(&self, other_point_local: &Vector3D) -> f64 {
135        return f64::abs(self.center.distance_to(*other_point_local) - self.radius);
136    }
137
138    fn view(&self) -> &Surface3Data {
139        return &self.surface_data;
140    }
141}
142
143/// Shared pointer for the Sphere3 type.
144pub type Sphere3Ptr = Arc<RwLock<Sphere3>>;
145
146//--------------------------------------------------------------------------------------------------
147///
148/// # Front-end to create Sphere3 objects step by step.
149///
150pub struct Builder {
151    _center: Vector3D,
152    _radius: f64,
153
154    _surface_data: Surface3Data,
155}
156
157impl Builder {
158    /// Returns builder with sphere center.
159    pub fn with_center(&mut self, center: Vector3D) -> &mut Self {
160        self._center = center;
161        return self;
162    }
163
164    /// Returns builder with sphere radius.
165    pub fn with_radius(&mut self, radius: f64) -> &mut Self {
166        self._radius = radius;
167        return self;
168    }
169
170    /// Builds Sphere3.
171    pub fn build(&mut self) -> Sphere3 {
172        return Sphere3::new(self._center,
173                            self._radius,
174                            Some(self._surface_data.transform.clone()),
175                            Some(self._surface_data.is_normal_flipped),
176        );
177    }
178
179    /// Builds shared pointer of Sphere3 instance.
180    pub fn make_shared(&mut self) -> Sphere3Ptr {
181        return Sphere3Ptr::new(RwLock::new(self.build()));
182    }
183
184    /// constructor
185    pub fn new() -> Builder {
186        return Builder {
187            _center: Vector3D::new_default(),
188            _radius: 0.0,
189            _surface_data: Surface3Data::new(None, None),
190        };
191    }
192}
193
194impl SurfaceBuilderBase3 for Builder {
195    fn view(&mut self) -> &mut Surface3Data {
196        return &mut self._surface_data;
197    }
198}