Skip to main content

implicit3d/
plane.rs

1use crate::{BoundingBox, Object, RealField};
2use nalgebra as na;
3use num_traits::Float;
4
5pub trait Axis: std::fmt::Debug + Clone + Sync + Send {
6    fn value() -> usize;
7    fn inverted() -> bool;
8}
9
10#[derive(Clone, Debug)]
11pub struct AxisX {}
12impl Axis for AxisX {
13    fn value() -> usize {
14        0
15    }
16    fn inverted() -> bool {
17        false
18    }
19}
20
21#[derive(Clone, Debug)]
22pub struct AxisNegX {}
23impl Axis for AxisNegX {
24    fn value() -> usize {
25        0
26    }
27    fn inverted() -> bool {
28        true
29    }
30}
31
32#[derive(Clone, Debug)]
33pub struct AxisY {}
34impl Axis for AxisY {
35    fn value() -> usize {
36        1
37    }
38    fn inverted() -> bool {
39        false
40    }
41}
42
43#[derive(Clone, Debug)]
44pub struct AxisNegY {}
45impl Axis for AxisNegY {
46    fn value() -> usize {
47        1
48    }
49    fn inverted() -> bool {
50        true
51    }
52}
53
54#[derive(Clone, Debug)]
55pub struct AxisZ {}
56impl Axis for AxisZ {
57    fn value() -> usize {
58        2
59    }
60    fn inverted() -> bool {
61        false
62    }
63}
64
65#[derive(Clone, Debug)]
66pub struct AxisNegZ {}
67impl Axis for AxisNegZ {
68    fn value() -> usize {
69        2
70    }
71    fn inverted() -> bool {
72        true
73    }
74}
75
76#[derive(Clone, Debug, PartialEq)]
77pub struct Plane<A: Axis, S: RealField> {
78    distance_from_zero: S,
79    bbox: BoundingBox<S>,
80    normal: na::Vector3<S>,
81    _phantom: std::marker::PhantomData<A>,
82}
83
84impl<A: Axis, S: From<f32> + RealField + Float> Plane<A, S> {
85    pub fn new(distance_from_zero: S) -> Self {
86        let d = distance_from_zero;
87        let mut p_neg = na::Point3::new(S::neg_infinity(), S::neg_infinity(), S::neg_infinity());
88        let mut p_pos = na::Point3::new(S::infinity(), S::infinity(), S::infinity());
89        let zero: S = From::from(0f32);
90        let mut normal = na::Vector3::new(zero, zero, zero);
91
92        if A::inverted() {
93            p_neg[A::value()] = -d;
94            normal[A::value()] = From::from(-1.);
95        } else {
96            p_pos[A::value()] = d;
97            normal[A::value()] = From::from(1.);
98        }
99
100        Plane {
101            distance_from_zero: d,
102            bbox: BoundingBox::new(&p_neg, &p_pos),
103            normal,
104            _phantom: std::marker::PhantomData,
105        }
106    }
107}
108
109impl<A: 'static + Axis, S: Float + From<f32> + RealField> Object<S> for Plane<A, S> {
110    fn approx_value(&self, p: &na::Point3<S>, _: S) -> S {
111        let p: S = p[A::value()];
112        let ap: S = Float::abs(p);
113        if Float::is_sign_positive(p) != A::inverted() {
114            ap - self.distance_from_zero
115        } else {
116            -ap - self.distance_from_zero
117        }
118    }
119    fn bbox(&self) -> &BoundingBox<S> {
120        &self.bbox
121    }
122    fn normal(&self, _: &na::Point3<S>) -> na::Vector3<S> {
123        self.normal
124    }
125}
126
127/// A YZ-Plane.
128pub type PlaneX<S> = Plane<AxisX, S>;
129/// A negative YZ-Plane.
130pub type PlaneNegX<S> = Plane<AxisNegX, S>;
131/// A XZ-Plane
132pub type PlaneY<S> = Plane<AxisY, S>;
133/// A negative XZ-Plane
134pub type PlaneNegY<S> = Plane<AxisNegY, S>;
135/// A XY-Plane
136pub type PlaneZ<S> = Plane<AxisZ, S>;
137/// A negative XZ-Plane
138pub type PlaneNegZ<S> = Plane<AxisNegZ, S>;
139
140/// An arbitrary (not axis aligned) plane.
141#[derive(Clone, Debug, PartialEq)]
142pub struct NormalPlane<S: RealField> {
143    bbox: BoundingBox<S>,
144    normal: na::Vector3<S>,
145    p: S,
146}
147
148impl<S: From<f32> + RealField + Float> NormalPlane<S> {
149    /// Create a plane in hessian form.
150    pub fn from_normal_and_p(normal: na::Vector3<S>, p: S) -> Self {
151        NormalPlane {
152            bbox: BoundingBox::infinity(),
153            normal,
154            p,
155        }
156    }
157    /// Create a plane from 3 points.
158    pub fn from_3_points(a: &na::Point3<S>, b: &na::Point3<S>, c: &na::Point3<S>) -> Self {
159        let v1 = a - c;
160        let v2 = b - c;
161        let normal = v1.cross(&v2).normalize();
162        let p = normal.dot(&a.coords);
163        NormalPlane {
164            bbox: BoundingBox::infinity(),
165            normal,
166            p,
167        }
168    }
169}
170
171impl<S: Float + From<f32> + RealField> Object<S> for NormalPlane<S> {
172    fn approx_value(&self, x0: &na::Point3<S>, _: S) -> S {
173        self.normal.dot(&x0.coords) - self.p
174    }
175    fn bbox(&self) -> &BoundingBox<S> {
176        &self.bbox
177    }
178    fn normal(&self, _: &na::Point3<S>) -> na::Vector3<S> {
179        self.normal
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use approx::assert_ulps_eq;
186    use crate::*;
187    use nalgebra as na;
188
189    #[test]
190    fn simple() {
191        let px = PlaneX::new(10.);
192        assert_ulps_eq!(px.approx_value(&na::Point3::new(0., 0., 0.), 0.), -10.);
193        assert_ulps_eq!(px.approx_value(&na::Point3::new(10., 0., 0.), 0.), 0.);
194        assert_ulps_eq!(px.approx_value(&na::Point3::new(20., 0., 0.), 0.), 10.);
195        assert_ulps_eq!(
196            px.approx_value(&na::Point3::new(20., 1000., 1000.), 0.),
197            10.
198        );
199    }
200
201    #[test]
202    fn hessian_x() {
203        let px = NormalPlane::from_normal_and_p(na::Vector3::new(1., 0., 0.), 0.);
204        assert_ulps_eq!(px.approx_value(&na::Point3::new(0., 0., 0.), 0.), 0.);
205        assert_ulps_eq!(px.approx_value(&na::Point3::new(0., 10., 100.), 0.), 0.);
206        assert_ulps_eq!(px.approx_value(&na::Point3::new(10., 0., 0.), 0.), 10.);
207    }
208
209    #[test]
210    fn hessian_xyz() {
211        let p = NormalPlane::from_normal_and_p(na::Vector3::new(1., 1., 1.).normalize(), 1.);
212        assert_ulps_eq!(p.approx_value(&na::Point3::new(0., 0., 0.), 0.), -1.);
213        assert_ulps_eq!(
214            p.approx_value(&na::Point3::new(1., 1., 1.), 0.),
215            Float::sqrt(3.) - 1.0
216        );
217        assert_ulps_eq!(
218            p.approx_value(&na::Point3::new(2., 2., 2.), 0.),
219            Float::sqrt(12.) - 1.0
220        );
221    }
222
223    #[test]
224    fn hessian_3points_x() {
225        let p = NormalPlane::from_3_points(
226            &na::Point3::new(10., 0., 0.),
227            &na::Point3::new(10., 1., 0.),
228            &na::Point3::new(10., 0., 1.),
229        );
230        assert_ulps_eq!(p.approx_value(&na::Point3::new(0., 0., 0.), 0.), -10.);
231        assert_ulps_eq!(p.approx_value(&na::Point3::new(1., 1., 1.), 0.), -9.0);
232        assert_ulps_eq!(p.approx_value(&na::Point3::new(100., 100., 100.), 0.), 90.);
233    }
234
235    #[test]
236    fn hessian_3points() {
237        let p = NormalPlane::from_3_points(
238            &na::Point3::new(0., 1., 1.),
239            &na::Point3::new(1., 0., 1.),
240            &na::Point3::new(1., 1., 0.),
241        );
242        assert_ulps_eq!(
243            p.approx_value(&na::Point3::new(0., 0., 0.), 0.),
244            -Float::sqrt(4. / 3.)
245        );
246        assert_ulps_eq!(
247            p.approx_value(&na::Point3::new(1., 1., 1.), 0.),
248            Float::sqrt(3.) - Float::sqrt(4. / 3.)
249        );
250        assert_ulps_eq!(
251            p.approx_value(&na::Point3::new(2., 2., 2.), 0.),
252            Float::sqrt(12.) - Float::sqrt(4. / 3.)
253        );
254    }
255}