constructive/
plane.rs

1use glam::{Vec3, Vec4, Vec4Swizzles};
2
3use crate::{
4    brush::{Face, FaceIntersect},
5    util::TOLERANCE,
6};
7
8#[derive(Debug, Copy, Clone)]
9pub struct Plane {
10    pub normal: Vec3,
11    pub distance: f32,
12}
13
14impl Plane {
15    pub fn new(normal: Vec3, distance: f32) -> Self {
16        Self { normal, distance }
17    }
18
19    pub fn from_face(face: Face) -> Self {
20        let normal = face.normal();
21        assert!(normal.is_finite());
22        let distance = face.p1.dot(normal);
23
24        Self { normal, distance }
25    }
26
27    pub fn distance_to_point(&self, point: Vec3) -> f32 {
28        point.dot(self.normal) - self.distance
29    }
30
31    pub fn intersect_ray(&self, ray_origin: Vec3, ray_direction: Vec3) -> Option<f32> {
32        let denom = self.normal.dot(ray_direction);
33        if denom.abs() > f32::EPSILON {
34            let t = (self.normal * self.distance - ray_origin).dot(self.normal) / denom;
35            if t >= 0.0 {
36                return Some(t);
37            }
38        }
39
40        None
41    }
42
43    pub fn classify_face(&self, face: Face) -> FaceIntersect {
44        let d1 = self.distance_to_point(face.p1);
45        let d2 = self.distance_to_point(face.p2);
46        let d3 = self.distance_to_point(face.p3);
47
48        if d1.abs() <= TOLERANCE && d2.abs() <= TOLERANCE && d3.abs() <= TOLERANCE {
49            if face.normal().dot(self.normal) > 0.0 {
50                return FaceIntersect::CoplanarFront;
51            } else {
52                return FaceIntersect::CoplanarBack;
53            }
54        }
55
56        if d1 >= -TOLERANCE && d2 >= -TOLERANCE && d3 >= -TOLERANCE {
57            FaceIntersect::Front
58        } else if d1 <= TOLERANCE && d2 <= TOLERANCE && d3 <= TOLERANCE {
59            FaceIntersect::Back
60        } else {
61            FaceIntersect::Intersect
62        }
63    }
64
65    pub fn split_face(
66        &self,
67        face: Face,
68        front_result: &mut Vec<Face>,
69        back_result: &mut Vec<Face>,
70    ) {
71        let mut front_count = 0;
72        let mut back_count = 0;
73        let mut coplanar_count = 0;
74
75        let mut front = [Vec4::NAN; 3];
76        let mut back = [Vec4::NAN; 3];
77        let mut coplanar = [Vec3::NAN; 3];
78
79        for p in face.points() {
80            let distance = self.distance_to_point(p);
81            if distance >= TOLERANCE {
82                front[front_count] = p.extend(distance);
83                front_count += 1;
84            } else if distance <= -TOLERANCE {
85                back[back_count] = p.extend(distance);
86                back_count += 1;
87            } else {
88                coplanar[coplanar_count] = p;
89                coplanar_count += 1;
90            }
91        }
92
93        let normal = face.normal();
94        let orient = |face: Face| {
95            if face.normal().dot(normal) < 0.0 {
96                Face::new(face.p3, face.p2, face.p1)
97            } else {
98                face
99            }
100        };
101
102        if coplanar_count == 1 {
103            assert_eq!(back_count, 1);
104            assert_eq!(front_count, 1);
105            let back = back[0];
106            let front = front[0];
107            let coplanar = coplanar[0];
108
109            let i1 = back.xyz().lerp(front.xyz(), back.w / (back.w - front.w));
110
111            front_result.push(orient(Face::new(coplanar, front.xyz(), i1)));
112            back_result.push(orient(Face::new(coplanar, i1, back.xyz())));
113        } else if front_count == 1 && back_count == 2 {
114            // One point in front, two in back
115            let f = front[0].xyz();
116
117            let back1 = back[0].xyz();
118            let back2 = back[1].xyz();
119
120            let i1 = f.lerp(back1, front[0].w / (front[0].w - back[0].w));
121            let i2 = f.lerp(back2, front[0].w / (front[0].w - back[1].w));
122
123            front_result.push(orient(Face::new(f, i1, i2)));
124            back_result.push(orient(Face::new(back1, back2, i1)));
125            back_result.push(orient(Face::new(i1, back2, i2)));
126        } else if front_count == 2 && back_count == 1 {
127            let b = back[0].xyz();
128            let front1 = front[0].xyz();
129            let front2 = front[1].xyz();
130
131            // Two points in front, one in back
132            let t1 = back[0].w / (back[0].w - front[0].w);
133            let t2 = back[0].w / (back[0].w - front[1].w);
134
135            let i1 = b.lerp(front1, t1);
136            let i2 = b.lerp(front2, t2);
137
138            back_result.push(orient(Face::new(b, i1, i2)));
139            front_result.push(orient(Face::new(front1, front2, i1)));
140            front_result.push(orient(Face::new(i1, front2, i2)));
141        }
142    }
143
144    pub(crate) fn invert(&self) -> Self {
145        Self {
146            normal: -self.normal,
147            distance: -self.distance,
148        }
149    }
150}