use crate::{
SymmetricMat2,
ops::{self, FloatPow},
};
use glam::{Mat2, Vec2, Vec2Swizzles};
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SymmetricEigen2 {
pub eigenvalues: Vec2,
pub eigenvectors: Mat2,
}
impl SymmetricEigen2 {
pub fn new(mat: SymmetricMat2) -> Self {
let eigenvalues = Self::eigenvalues(mat);
let eigenvector1 = Self::eigenvector(mat, eigenvalues.x);
let eigenvector2 = Self::eigenvector(mat, eigenvalues.y);
Self {
eigenvalues,
eigenvectors: Mat2::from_cols(eigenvector1, eigenvector2),
}
}
pub fn reverse(&self) -> Self {
Self {
eigenvalues: self.eigenvalues.yx(),
eigenvectors: Mat2::from_cols(self.eigenvectors.y_axis, self.eigenvectors.x_axis),
}
}
pub fn eigenvalues(mat: SymmetricMat2) -> Vec2 {
let [a, b, c] = [
1.0,
-(mat.m00 + mat.m11),
mat.m00 * mat.m11 - mat.m01 * mat.m01,
];
let sqrt_part = ops::sqrt(b.squared() - 4.0 * a * c);
let eigen1 = (-b + sqrt_part) / (2.0 * a);
let eigen2 = (-b - sqrt_part) / (2.0 * a);
Vec2::new(eigen1, eigen2)
}
pub fn eigenvector(mat: SymmetricMat2, eigenvalue: f32) -> Vec2 {
Vec2::new(1.0, (eigenvalue - mat.m00) / mat.m01).normalize()
}
}
#[cfg(test)]
mod test {
use approx::assert_relative_eq;
use glam::{Mat2, Vec2};
use crate::SymmetricMat2;
use super::SymmetricEigen2;
#[test]
fn eigen_2x2() {
let mat = SymmetricMat2::new(6.0, 3.0, 4.0);
let eigen = SymmetricEigen2::new(mat);
assert_relative_eq!(
eigen.eigenvalues,
Vec2::new(8.16228, 1.83772),
epsilon = 0.001
);
assert_relative_eq!(
Mat2::from_cols(eigen.eigenvectors.x_axis, eigen.eigenvectors.y_axis,),
Mat2::from_cols(Vec2::new(0.811242, 0.58471), Vec2::new(0.58471, -0.811242),),
epsilon = 0.001
);
}
}