1use crate::{Line, Point, Scalar, Vector};
2
3#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
5#[repr(C)]
6pub struct Plane {
7 origin: Point<3>,
8 u: Vector<3>,
9 v: Vector<3>,
10}
11
12impl Plane {
13 pub fn from_parametric(
15 origin: impl Into<Point<3>>,
16 u: impl Into<Vector<3>>,
17 v: impl Into<Vector<3>>,
18 ) -> Self {
19 let origin = origin.into();
20 let u = u.into();
21 let v = v.into();
22
23 Self { origin, u, v }
24 }
25
26 pub fn origin(&self) -> Point<3> {
28 self.origin
29 }
30
31 pub fn u(&self) -> Vector<3> {
33 self.u
34 }
35
36 pub fn v(&self) -> Vector<3> {
38 self.v
39 }
40
41 pub fn normal(&self) -> Vector<3> {
43 self.u().cross(&self.v()).normalize()
44 }
45
46 pub fn three_point_form(&self) -> [Point<3>; 3] {
48 let a = self.origin();
49 let b = self.origin() + self.u();
50 let c = self.origin() + self.v();
51
52 [a, b, c]
53 }
54
55 pub fn constant_normal_form(&self) -> (Scalar, Vector<3>) {
57 let normal = self.normal();
58 let distance = normal.dot(&self.origin().coords);
59
60 (distance, normal)
61 }
62
63 pub fn is_parallel_to_vector(&self, vector: &Vector<3>) -> bool {
65 self.normal().dot(vector) == Scalar::ZERO
66 }
67
68 pub fn project_point(&self, point: impl Into<Point<3>>) -> Point<2> {
70 let origin_to_point = point.into() - self.origin();
71 let coords = self.project_vector(origin_to_point);
72 Point { coords }
73 }
74
75 pub fn project_vector(&self, vector: impl Into<Vector<3>>) -> Vector<2> {
77 let m =
88 nalgebra::Matrix::<_, _, nalgebra::Const<3>, _>::from_columns(&[
89 self.u().to_na(),
90 self.v().to_na(),
91 self.normal().to_na(),
92 ]);
93 let b = vector.into();
94 let x = m
95 .lu()
96 .solve(&b.to_na())
97 .expect("Expected matrix to be invertible");
98
99 Vector::from([x.x, x.y])
100 }
101
102 pub fn project_line(&self, line: &Line<3>) -> Line<2> {
104 let line_origin_in_plane = self.project_point(line.origin());
105 let line_direction_in_plane = self.project_vector(line.direction());
106
107 Line::from_origin_and_direction(
108 line_origin_in_plane,
109 line_direction_in_plane,
110 )
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::{Plane, Point, Vector};
117
118 #[test]
119 fn project_point() {
120 let plane =
121 Plane::from_parametric([1., 1., 1.], [1., 0., 0.], [0., 1., 0.]);
122
123 assert_eq!(plane.project_point([2., 1., 2.]), Point::from([1., 0.]));
124 assert_eq!(plane.project_point([1., 2., 2.]), Point::from([0., 1.]));
125 }
126
127 #[test]
128 fn project_vector() {
129 let plane =
130 Plane::from_parametric([1., 1., 1.], [1., 0., 0.], [0., 1., 0.]);
131
132 assert_eq!(plane.project_vector([1., 0., 1.]), Vector::from([1., 0.]));
133 assert_eq!(plane.project_vector([0., 1., 1.]), Vector::from([0., 1.]));
134
135 let plane =
136 Plane::from_parametric([1., 1., 1.], [1., 0., 0.], [1., 1., 0.]);
137 assert_eq!(plane.project_vector([0., 1., 0.]), Vector::from([-1., 1.]));
138 }
139}