vox_geometry_rust/
box2.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::surface2::*;
10use crate::vector2::Vector2D;
11use crate::transform2::Transform2;
12use crate::bounding_box2::BoundingBox2D;
13use crate::ray2::Ray2D;
14use crate::plane2::Plane2;
15use std::sync::{RwLock, Arc};
16
17///
18/// # 2-D box geometry.
19///
20/// This class represents 2-D box geometry which extends Surface2 by overriding
21/// surface-related queries. This box implementation is an axis-aligned box
22/// that wraps lower-level primitive type, BoundingBox2D.
23///
24pub struct Box2 {
25    /// Bounding box of this box.
26    bound: BoundingBox2D,
27
28    /// data from surface2
29    pub surface_data: Surface2Data,
30}
31
32impl Box2 {
33    /// Constructs (0, 0) x (1, 1) box.
34    pub fn new_default(transform: Option<Transform2>,
35                       is_normal_flipped: Option<bool>) -> Box2 {
36        return Box2 {
37            bound: BoundingBox2D::new(Vector2D::new_default(), Vector2D::new(1.0, 1.0)),
38            surface_data: Surface2Data::new(transform, is_normal_flipped),
39        };
40    }
41
42    /// Constructs a box with given \p lower_corner and \p upper_corner.
43    pub fn new(lower_corner: Vector2D,
44               upper_corner: Vector2D,
45               transform: Option<Transform2>,
46               is_normal_flipped: Option<bool>) -> Box2 {
47        return Box2 {
48            bound: BoundingBox2D::new(lower_corner, upper_corner),
49            surface_data: Surface2Data::new(transform, is_normal_flipped),
50        };
51    }
52    /// Constructs a box with BoundingBox2D instance.
53    pub fn new_aabb(bounding_box: BoundingBox2D,
54                    transform: Option<Transform2>,
55                    is_normal_flipped: Option<bool>) -> Box2 {
56        return Box2 {
57            bound: bounding_box,
58            surface_data: Surface2Data::new(transform, is_normal_flipped),
59        };
60    }
61
62    /// Returns builder fox Box2.
63    pub fn builder() -> Builder {
64        return Builder::new();
65    }
66}
67
68impl Surface2 for Box2 {
69    fn closest_point_local(&self, other_point: &Vector2D) -> Vector2D {
70        return if self.bound.contains(&other_point) {
71            let planes = [Plane2::new(Vector2D::new(1.0, 0.0), self.bound.upper_corner, None, None),
72                Plane2::new(Vector2D::new(0.0, 1.0), self.bound.upper_corner, None, None),
73                Plane2::new(Vector2D::new(-1.0, 0.0), self.bound.lower_corner, None, None),
74                Plane2::new(Vector2D::new(0.0, -1.0), self.bound.lower_corner, None, None)];
75
76            let mut result = planes[0].closest_point(other_point);
77            let mut distance_squared = result.distance_squared_to(*other_point);
78
79            for i in 1..4 {
80                let local_result = planes[i].closest_point(other_point);
81                let local_distance_squared =
82                    local_result.distance_squared_to(*other_point);
83
84                if local_distance_squared < distance_squared {
85                    result = local_result;
86                    distance_squared = local_distance_squared;
87                }
88            }
89
90            result
91        } else {
92            crate::vector2::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner)
93        };
94    }
95
96    fn bounding_box_local(&self) -> BoundingBox2D {
97        return self.bound.clone();
98    }
99
100    fn closest_intersection_local(&self, ray: &Ray2D) -> SurfaceRayIntersection2 {
101        let mut intersection = SurfaceRayIntersection2::new();
102        let bb_ray_intersection =
103            self.bound.closest_intersection(&ray);
104        intersection.is_intersecting = bb_ray_intersection.is_intersecting;
105        if intersection.is_intersecting {
106            intersection.distance = bb_ray_intersection.t_near;
107            intersection.point = ray.point_at(bb_ray_intersection.t_near);
108            intersection.normal = self.closest_normal_local(&intersection.point);
109        }
110        return intersection;
111    }
112
113    fn closest_normal_local(&self, other_point: &Vector2D) -> Vector2D {
114        let planes = [Plane2::new(Vector2D::new(1.0, 0.0), self.bound.upper_corner, None, None),
115            Plane2::new(Vector2D::new(0.0, 1.0), self.bound.upper_corner, None, None),
116            Plane2::new(Vector2D::new(-1.0, 0.0), self.bound.lower_corner, None, None),
117            Plane2::new(Vector2D::new(0.0, -1.0), self.bound.lower_corner, None, None)];
118
119        return if self.bound.contains(other_point) {
120            let mut closest_normal = planes[0].normal;
121            let closest_point = planes[0].closest_point(other_point);
122            let mut min_distance_squared = (closest_point - *other_point).length_squared();
123
124            for i in 1..4 {
125                let local_closest_point = planes[i].closest_point(other_point);
126                let local_distance_squared =
127                    (local_closest_point - *other_point).length_squared();
128
129                if local_distance_squared < min_distance_squared {
130                    closest_normal = planes[i].normal;
131                    min_distance_squared = local_distance_squared;
132                }
133            }
134
135            closest_normal
136        } else {
137            let closest_point =
138                crate::vector2::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner);
139            let closest_point_to_input_point = *other_point - closest_point;
140            let mut closest_normal = planes[0].normal;
141            let mut max_cosine_angle = closest_normal.dot(&closest_point_to_input_point);
142
143            for i in 1..4 {
144                let cosine_angle = planes[i].normal.dot(&closest_point_to_input_point);
145
146                if cosine_angle > max_cosine_angle {
147                    closest_normal = planes[i].normal;
148                    max_cosine_angle = cosine_angle;
149                }
150            }
151
152            closest_normal
153        };
154    }
155
156    fn intersects_local(&self, ray_local: &Ray2D) -> bool {
157        return self.bound.intersects(ray_local);
158    }
159
160    fn view(&self) -> &Surface2Data {
161        return &self.surface_data;
162    }
163}
164
165/// Shared pointer type for the Box2.
166pub type Box2Ptr = Arc<RwLock<Box2>>;
167
168///
169/// # Front-end to create Box2 objects step by step.
170///
171pub struct Builder {
172    _lower_corner: Vector2D,
173    _upper_corner: Vector2D,
174
175    _surface_data: Surface2Data,
176}
177
178impl Builder {
179    /// Returns builder with lower corner set.
180    pub fn with_lower_corner(&mut self, pt: Vector2D) -> &mut Self {
181        self._lower_corner = pt;
182        return self;
183    }
184
185    /// Returns builder with upper corner set.
186    pub fn with_upper_corner(&mut self, pt: Vector2D) -> &mut Self {
187        self._upper_corner = pt;
188        return self;
189    }
190
191    /// Returns builder with bounding box.
192    pub fn with_bounding_box(&mut self, bbox: BoundingBox2D) -> &mut Self {
193        self._lower_corner = bbox.lower_corner;
194        self._upper_corner = bbox.upper_corner;
195        return self;
196    }
197
198    /// Builds Box2.
199    pub fn build(&mut self) -> Box2 {
200        return Box2::new_aabb(
201            BoundingBox2D::new(self._lower_corner, self._upper_corner),
202            Some(self._surface_data.transform.clone()),
203            Some(self._surface_data.is_normal_flipped),
204        );
205    }
206
207    /// Builds shared pointer of Box2 instance.
208    pub fn make_shared(&mut self) -> Box2Ptr {
209        return Box2Ptr::new(RwLock::new(self.build()));
210    }
211
212    /// constructor
213    pub fn new() -> Builder {
214        return Builder {
215            _lower_corner: Vector2D::new_default(),
216            _upper_corner: Vector2D::new(1.0, 1.0),
217            _surface_data: Surface2Data::new(None, None),
218        };
219    }
220}
221
222impl SurfaceBuilderBase2 for Builder {
223    fn view(&mut self) -> &mut Surface2Data {
224        return &mut self._surface_data;
225    }
226}