csci5607/assignment-1b/src/utils.rs

92 lines
2.6 KiB
Rust
Raw Normal View History

2023-02-06 03:52:42 +00:00
use anyhow::Result;
use nalgebra::{Matrix3, Vector3};
use ordered_float::NotNan;
/// Finds the minimum of an iterator of f64s, ignoring any NaN values
pub fn min_f64<I>(i: I) -> Option<f64>
where
I: Iterator<Item = f64>,
{
i.filter_map(|i| NotNan::new(i).ok())
.min()
.map(|i| i.into_inner())
}
/// Finds the minimum of an iterator of f64s using the given predicate, ignoring
/// any NaN values
pub fn min_f64_by_key<I, F>(i: I, f: F) -> Option<f64>
where
I: Iterator<Item = f64>,
F: FnMut(&NotNan<f64>),
{
i.filter_map(|i| NotNan::new(i).ok())
.min_by_key(f)
.map(|i| i.into_inner())
}
2023-02-16 01:22:17 +00:00
/// Dot-product between two 3D vectors.
#[inline]
pub fn dot(a: Vector3<f64>, b: Vector3<f64>) -> f64 {
a.x * b.x + a.y * b.y + a.z * b.z
}
/// Cross-product between two 3D vectors.
#[inline]
pub fn cross(a: Vector3<f64>, b: Vector3<f64>) -> Vector3<f64> {
let x = a.y * b.z - a.z * b.y;
let y = a.z * b.x - a.x * b.z;
let z = a.x * b.y - a.y * b.x;
Vector3::new(x, y, z)
}
2023-02-06 03:52:42 +00:00
/// Calculate the rotation matrix between the 2 given vectors
2023-02-13 05:46:54 +00:00
///
/// Based on the method given [here][1].
///
/// [1]: https://math.stackexchange.com/a/897677
2023-02-06 03:52:42 +00:00
pub fn compute_rotation_matrix(
a: Vector3<f64>,
b: Vector3<f64>,
) -> Result<Matrix3<f64>> {
2023-02-15 23:13:22 +00:00
// Special case: if a and b are in the same direction, just return the
// identity matrix.
2023-02-06 03:52:42 +00:00
if a.normalize() == b.normalize() {
2023-02-15 23:13:22 +00:00
return Ok(Matrix3::identity());
2023-02-06 03:52:42 +00:00
}
2023-02-16 01:22:17 +00:00
let cos_t = dot(a, b);
let sin_t = cross(a, b).norm();
2023-02-06 03:52:42 +00:00
let g = Matrix3::new(cos_t, -sin_t, 0.0, sin_t, cos_t, 0.0, 0.0, 0.0, 1.0);
// New basis vectors
let u = a;
2023-02-16 01:22:17 +00:00
let v = (b - cos_t * a).normalize();
let w = cross(b, a);
2023-02-06 03:52:42 +00:00
// Not sure if this is required to be invertible?
let f_inverse = Matrix3::from_columns(&[u, v, w]);
let f = match f_inverse.try_inverse() {
Some(v) => v,
None => {
// So I ran into this case trying to compute the rotation matrix where one
// of the vector endpoints was (0, 0, 0). I'm pretty sure this case makes
2023-02-16 01:22:17 +00:00
// no sense in reality, which means if I ever encounter this case, I
// probably made a mistake somewhere before. So going to just error
// out here and screw recovering.
2023-02-06 03:52:42 +00:00
//
// println!("Failed to compute inverse matrix.");
// println!("- Initial: a = {a}, b = {b}");
// println!("- cos(t) = {cos_t}, sin(t) = {sin_t}");
// println!("- Basis: u = {u}, v = {v}, w = {w}");
bail!("Failed to compute inverse matrix of {f_inverse}\na = {a}\nb = {b}")
}
};
// if (f_inverse * g * f).norm() != 1.0 {
// bail!("WTF {}", (f_inverse * g * f).norm());
// }
Ok(f_inverse * g * f)
}