polyscope_render/
reflection.rs1use glam::{Mat4, Vec3, Vec4};
4
5#[must_use]
10pub fn reflection_matrix(plane_point: Vec3, plane_normal: Vec3) -> Mat4 {
11 let n = plane_normal.normalize();
12 let d = -plane_point.dot(n);
13
14 Mat4::from_cols(
21 Vec4::new(
22 1.0 - 2.0 * n.x * n.x,
23 -2.0 * n.x * n.y,
24 -2.0 * n.x * n.z,
25 0.0,
26 ),
27 Vec4::new(
28 -2.0 * n.x * n.y,
29 1.0 - 2.0 * n.y * n.y,
30 -2.0 * n.y * n.z,
31 0.0,
32 ),
33 Vec4::new(
34 -2.0 * n.x * n.z,
35 -2.0 * n.y * n.z,
36 1.0 - 2.0 * n.z * n.z,
37 0.0,
38 ),
39 Vec4::new(-2.0 * n.x * d, -2.0 * n.y * d, -2.0 * n.z * d, 1.0),
40 )
41}
42
43#[must_use]
47pub fn ground_reflection_matrix(height: f32) -> Mat4 {
48 reflection_matrix(Vec3::new(0.0, height, 0.0), Vec3::Y)
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn test_reflection_matrix_identity_at_origin() {
57 let mat = reflection_matrix(Vec3::ZERO, Vec3::Y);
58
59 let point = Vec3::new(1.0, 2.0, 3.0);
61 let reflected = mat.transform_point3(point);
62
63 assert!((reflected.x - point.x).abs() < 0.001);
64 assert!((reflected.y - (-point.y)).abs() < 0.001);
65 assert!((reflected.z - point.z).abs() < 0.001);
66 }
67
68 #[test]
69 fn test_ground_reflection_at_height() {
70 let height = 1.0;
71 let mat = ground_reflection_matrix(height);
72
73 let point = Vec3::new(0.0, 3.0, 0.0);
75 let reflected = mat.transform_point3(point);
76
77 assert!((reflected.y - (-1.0)).abs() < 0.001);
79 }
80
81 #[test]
82 fn test_reflection_is_involution() {
83 let mat = reflection_matrix(Vec3::new(0.0, 1.0, 0.0), Vec3::Y);
84 let double = mat * mat;
85
86 for i in 0..4 {
88 for j in 0..4 {
89 let expected = if i == j { 1.0 } else { 0.0 };
90 assert!((double.col(j)[i] - expected).abs() < 0.001);
91 }
92 }
93 }
94}