rust_3d/
box_unaligned_3d.rs

1/*
2Copyright 2019 Martin Buck
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation the
7rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the Software
9is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall
12be included all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
20OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23//! Not axis aligned Box in 3D space
24
25use crate::*;
26
27//------------------------------------------------------------------------------
28
29/// Not axis aligned Box in 3D space
30#[derive(Clone)]
31pub struct BoxUnaligned3D {
32    //@todo better name (Box3D vs BoxUnaligned3D vs BoundingBox3D)
33    pub center: Point3D,
34    pub y_dir: Norm3D,
35    pub z_dir: Norm3D,
36    pub size: [Positive; 3],
37}
38
39//------------------------------------------------------------------------------
40
41impl BoxUnaligned3D {
42    pub fn new_from_bb(bb: &BoundingBox3D) -> Self {
43        let center = Point3D::new_from(&bb.center_bb());
44        let y_dir = Norm3D::norm_y();
45        let z_dir = Norm3D::norm_z();
46        let size = [bb.size_x(), bb.size_y(), bb.size_z()];
47
48        Self {
49            center,
50            y_dir,
51            z_dir,
52            size,
53        }
54    }
55    pub fn new_from_z_rotation<P>(center: &P, size: [Positive; 3], rotation: f64) -> Self
56    where
57        P: Is3D,
58    {
59        let mut y_dir = Point3D::new(0.0, 1.0, 0.0);
60        let z_dir = Norm3D::norm_z();
61
62        y_dir = rot2d(&y_dir, rotation);
63        Self {
64            center: Point3D::new_from(center),
65            size,
66            y_dir: Norm3D::new(y_dir).unwrap(), // safe since rotation of valid length
67            z_dir,
68        }
69    }
70    fn d_x(&self) -> Point3D {
71        let d = self.size[0].get();
72        Point3D::new_from(&self.x_dir()) * d
73    }
74    fn d_y(&self) -> Point3D {
75        let d = self.size[1].get();
76        Point3D::new_from(&self.y_dir) * d
77    }
78    fn d_z(&self) -> Point3D {
79        let d = self.size[2].get();
80        Point3D::new_from(&self.z_dir) * d
81    }
82    pub fn x_dir(&self) -> Norm3D {
83        Norm3D::new(cross(&self.z_dir, &Point3D::new_from(&self.y_dir))).unwrap_or(Norm3D::norm_z())
84    }
85}
86
87//------------------------------------------------------------------------------
88
89impl IsMovable3D for BoxUnaligned3D {
90    fn move_by(&mut self, x: f64, y: f64, z: f64) {
91        self.center.move_by(x, y, z);
92    }
93}
94
95//------------------------------------------------------------------------------
96
97impl IsSATObject for BoxUnaligned3D {
98    //@todo also implement for Box3D
99    fn for_each_point<F>(&self, f: &mut F)
100    where
101        F: FnMut(&Point3D),
102    {
103        let c = &self.center;
104        let dx = &Point3D::new_from(&self.d_x());
105        let dy = &Point3D::new_from(&self.d_y());
106        let dz = &Point3D::new_from(&self.d_z());
107
108        f(&(c - dx - (dy - dz)));
109        f(&(c - dx - (dy + dz)));
110        f(&(c - dx + (dy - dz)));
111        f(&(c - dx + (dy + dz)));
112        f(&(c + dx - (dy - dz)));
113        f(&(c + dx - (dy + dz)));
114        f(&(c + dx + (dy - dz)));
115        f(&(c + dx + (dy + dz)));
116    }
117
118    fn for_each_axis<F>(&self, f: &mut F)
119    where
120        F: FnMut(&Norm3D),
121    {
122        f(&self.x_dir());
123        f(&self.y_dir);
124        f(&self.z_dir);
125    }
126}
127
128//------------------------------------------------------------------------------
129
130impl HasBoundingBox3D for BoxUnaligned3D {
131    fn bounding_box(&self) -> BoundingBox3D {
132        let mut max_size = self.size[0];
133        if self.size[1] > max_size {
134            max_size = self.size[1]
135        }
136        if self.size[2] > max_size {
137            max_size = self.size[2]
138        }
139
140        BoundingBox3D::new(
141            &Point3D::new(
142                self.center.x() - 0.5 * max_size.get(),
143                self.center.y() - 0.5 * max_size.get(),
144                self.center.z() - 0.5 * max_size.get(),
145            ),
146            &Point3D::new(
147                self.center.x() + 0.5 * max_size.get(),
148                self.center.y() + 0.5 * max_size.get(),
149                self.center.z() + 0.5 * max_size.get(),
150            ),
151        )
152        .unwrap() //@todo unwrap, see above
153    }
154}
155
156impl HasBoundingBox3DMaybe for BoxUnaligned3D {
157    fn bounding_box_maybe(&self) -> Result<BoundingBox3D> {
158        Ok(self.bounding_box())
159    }
160}
161
162//------------------------------------------------------------------------------
163
164fn rot2d<P>(p: &P, phi: f64) -> P
165where
166    P: IsBuildable3D,
167{
168    let s = (-phi).sin();
169    let c = (-phi).cos();
170
171    P::new(p.x() * c - p.y() * s, p.x() * s + p.y() * c, p.z())
172}