imc_rs/transform/
mod.rs

1extern crate num_traits;
2
3use nalgebra::{DMatrix, Dim, Matrix3, VecStorage, Vector2, Vector3, QR};
4
5#[derive(Debug)]
6/// Describes the direction in which the transform is performed
7pub enum Direction {
8    /// Slide is "fixed" and the transform describes transformation from another space to the slide space
9    ToSlide,
10    /// Slide is "moving" and the transform describes transformation from slide space to another space
11    FromSlide,
12}
13
14/// Create a trait which captures necessary traits for matrix multiplication
15pub trait TransformScalar:
16    nalgebra::Scalar
17    + num_traits::identities::Zero
18    + num_traits::identities::One
19    + nalgebra::ClosedAdd
20    + nalgebra::ClosedMul
21    + nalgebra::ComplexField
22    + Copy
23{
24}
25impl<T> TransformScalar for T where
26    T: nalgebra::Scalar
27        + num_traits::identities::Zero
28        + num_traits::identities::One
29        + nalgebra::ClosedAdd
30        + nalgebra::ClosedMul
31        + nalgebra::ComplexField
32        + Copy
33{
34}
35
36/// AffineTransform describes a mapping from one space to another while preserving parallel lines.
37#[derive(Debug)]
38pub struct AffineTransform<T>
39where
40    T: TransformScalar,
41{
42    direction: Direction,
43
44    matrix: Matrix3<T>,
45    inv_matix: Option<Matrix3<T>>,
46}
47
48fn points_to_dmatrix<T>(points: Vec<Vector2<T>>) -> DMatrix<T>
49where
50    T: TransformScalar,
51{
52    let mut data: Vec<T> = Vec::with_capacity(points.len() * 3);
53
54    for coord in 0..3 {
55        if coord < 2 {
56            for index in 0..points.len() {
57                let point = points.get(index).expect("Point should be present");
58
59                data.push(point[coord]);
60            }
61        } else {
62            for _index in 0..points.len() {
63                data.push(T::one());
64            }
65        }
66    }
67
68    let vec_storage = VecStorage::new(Dim::from_usize(points.len()), Dim::from_usize(3), data);
69    DMatrix::from_data(vec_storage)
70}
71
72impl<T> AffineTransform<T>
73where
74    T: TransformScalar,
75{
76    /// Returns the identity matrix as the transform
77    pub fn identity() -> Self {
78        let mut matrix = Matrix3::zeros();
79        matrix.m11 = T::one();
80        matrix.m22 = T::one();
81        matrix.m33 = T::one();
82
83        let inv_matrix = matrix.try_inverse();
84
85        AffineTransform {
86            direction: Direction::ToSlide,
87            matrix,
88            inv_matix: inv_matrix,
89        }
90    }
91
92    /// Generate a transformation by solving the set of linear equations Ax = y
93    pub fn from_points(moving_points: Vec<Vector2<T>>, fixed_points: Vec<Vector2<T>>) -> Self {
94        let moving = points_to_dmatrix(moving_points);
95        let fixed = points_to_dmatrix(fixed_points);
96
97        let qr = QR::new(fixed);
98        let res = qr.solve(&moving).unwrap();
99        // Probably a better way to do this
100        // Copy data from the solution to linear equations into Matrix4
101        let mut matrix = Matrix3::zeros();
102
103        matrix.m11 = res[(0, 0)];
104        matrix.m21 = res[(0, 1)]; //*res.get((0, 1)).unwrap();
105        matrix.m31 = res[(0, 2)]; //*res.get((0, 2)).unwrap();
106        matrix.m12 = res[(1, 0)]; //*res.get((1, 0)).unwrap();
107        matrix.m22 = res[(1, 1)]; //*res.get((1, 1)).unwrap();
108        matrix.m32 = res[(1, 2)]; //*res.get((1, 2)).unwrap();
109        matrix.m13 = res[(2, 0)]; //*res.get((2, 0)).unwrap();
110        matrix.m23 = res[(2, 1)]; //*res.get((2, 1)).unwrap();
111        matrix.m33 = T::one();
112
113        AffineTransform {
114            direction: Direction::ToSlide,
115            matrix,
116            inv_matix: matrix.try_inverse(),
117        }
118    }
119
120    /// Creates an `AffineTransform` with the
121    pub fn inverse_transform(&self) -> Option<AffineTransform<T>> {
122        // TODO: invert the matrix if necessary
123        let direction = match self.direction {
124            Direction::ToSlide => Direction::FromSlide,
125            Direction::FromSlide => Direction::ToSlide,
126        };
127
128        Some(AffineTransform {
129            direction,
130            matrix: Matrix3::<T>::identity() * self.inv_matix?,
131            inv_matix: Some(Matrix3::<T>::identity() * self.matrix),
132        })
133    }
134
135    /// Returns the transformation matrix in the "towards the slide" direction (from other space, to slide space)
136    pub fn to_slide_matrix(&self) -> Option<&Matrix3<T>> {
137        match self.direction {
138            Direction::ToSlide => Some(&self.matrix),
139            Direction::FromSlide => self.inv_matix.as_ref(),
140        }
141    }
142
143    /// Returns the transformation matrix in the "away the slide" direction (from slide space, to other space)
144    pub fn from_slide_matrix(&self) -> Option<&Matrix3<T>> {
145        match self.direction {
146            Direction::ToSlide => self.inv_matix.as_ref(),
147            Direction::FromSlide => Some(&self.matrix),
148        }
149    }
150
151    /*pub fn from_slide_matrix(&self) -> &Matrix3<T> {
152        match self.direction {
153            Direction::ToSlide => self.get_inv_matrix(),
154            //   Direction::FromSlide => &self.matrix,
155        }
156    }*/
157
158    /// Transforms a point in the external space to the slide space
159    pub fn transform_to_slide(&self, x: T, y: T) -> Option<Vector3<T>> {
160        let point = Vector3::new(x, y, T::one());
161        let point = self.to_slide_matrix()? * point;
162
163        Some(point)
164    }
165
166    /// Transforms a point in slide space to the external space
167    pub fn transform_from_slide(&self, x: T, y: T) -> Option<Vector3<T>> {
168        let point = Vector3::new(x, y, T::one());
169        let point = self.from_slide_matrix()? * point;
170
171        Some(point)
172    }
173
174    //pub fn transform_point(&self,
175}