summaryrefslogtreecommitdiff
path: root/physics/src/algebra/subspace.rs
blob: 9fb944e2047008e1137f046a5f8fa5e98de69978 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use nalgebra::SMatrix;

use super::{Scalar, N, Point, Vector};


type ProjectionMatrix = SMatrix<Scalar, N, N>;

pub struct Subspace<const DIM: usize> {
    point: Point,
    vectors: [Vector; DIM],
    projection_matrix: ProjectionMatrix,
}

pub type Line = Subspace<1>;
pub type Plane = Subspace<2>;

impl<const DIM: usize> Subspace<DIM> {
    pub fn new(point: Point, mut vectors: [Vector; DIM]) -> Self {
        for vector in &mut vectors {
            vector.normalize_mut();
        }

        Self {
            point,
            vectors,
            projection_matrix: {
                let v = SMatrix::<Scalar, N, DIM>::from_columns(&vectors);
                let transpose = v.transpose();
                let inverse = (transpose * v).try_inverse().unwrap();
                v * inverse * transpose
            }
        }
    }

    pub fn project_point(&self, point: Point) -> Point {
        self.projection_matrix * (point - self.point.coords) + self.point.coords
    }

    pub fn distance(&self, point: Point) -> Scalar {
        let projected = self.project_point(point);
        (projected - point).norm()
    }
}

#[cfg(test)]
mod tests {
    use crate::algebra::{subspace::Line, Point, Vector};

    #[test]
    fn test_projection_onto_line() {
        let line = Line::new(Point::new(1.0, 0.0), [Vector::new(3.0, 1.0)]);
        let point = Point::new(3.0, 4.0);

        let matrix = line.projection_matrix;
        {
            let diff = matrix * matrix - matrix;
            assert!(diff.norm() < 0.001, "Projection matrix squared should be equal to itself: {}, {}", matrix, matrix* matrix);
        }
        {
            let diff = matrix - matrix.transpose();
            assert!(diff.norm() < 0.001, "Projection matrix transpose should be equal to itself: {}, {}", matrix, matrix.transpose());
        }

        {
            let projected = line.project_point(point);
            let diff = projected - Point::new(4.0, 1.0);
            assert!(diff.norm() < 0.001, "Point projected incorrectly");
        }
    }
}