diff --git a/src/matrix.rs b/src/matrix.rs index abaf861..7ecbb5b 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -99,11 +99,23 @@ impl Matrix2 { } impl Matrix2 { - /// Create a transformation matrix that will cause a vector to point at - /// `dir`, using `up` for orientation. + /// Create a transformation matrix that will cause `unit_x()` to point at + /// `dir`. `unit_y()` will be perpendicular to `dir`, and the closest to `up`. pub fn look_at(dir: Vector2, up: Vector2) -> Matrix2 { - //TODO: verify look_at 2D - Matrix2::from_cols(up, dir).transpose() + Matrix2::look_at_stable(dir, up.x * dir.y >= up.y * dir.x) + } + + /// Crate a transformation that will cause `unit_x()` to point at + /// `dir`. This is similar to `look_at`, but does not take an `up` vector. + /// This will not cause `unit_y()` to flip when `dir` crosses over the `up` vector. + pub fn look_at_stable(dir: Vector2, flip: bool) -> Matrix2 { + let basis1 = dir.normalize(); + let basis2 = if flip { + Vector2::new(basis1.y, -basis1.x) + } else { + Vector2::new(-basis1.y, basis1.x) + }; + Matrix2::from_cols(basis1, basis2) } #[inline] diff --git a/src/rotation.rs b/src/rotation.rs index 1949e1d..a8e28ca 100644 --- a/src/rotation.rs +++ b/src/rotation.rs @@ -146,6 +146,14 @@ pub struct Basis2 { mat: Matrix2, } +impl Basis2 { + pub fn look_at_stable(dir: Vector2, flip: bool) -> Basis2 { + Basis2 { + mat: Matrix2::look_at_stable(dir, flip), + } + } +} + impl AsRef> for Basis2 { #[inline] fn as_ref(&self) -> &Matrix2 { diff --git a/tests/matrix.rs b/tests/matrix.rs index 236a2f1..4af2419 100644 --- a/tests/matrix.rs +++ b/tests/matrix.rs @@ -175,6 +175,27 @@ pub mod matrix2 { let rot3: Matrix2 = Matrix2::from_angle(Rad(f64::consts::PI)); assert_ulps_eq!(rot3 * Vector2::new(1.0, 1.0), &Vector2::new(-1.0, -1.0)); } + + #[test] + fn test_look_at() { + // rot should rotate unit_x() to look at the input vector + let rot = Matrix2::look_at(V, Vector2::unit_y()); + assert_eq!(rot * Vector2::unit_x(), + V.normalize()); + let new_up = Vector2::new(-V.y, V.x).normalize(); + assert_eq!(rot * Vector2::unit_y(), + new_up); + + let rot_down = Matrix2::look_at(V, -1.0 * Vector2::unit_y()); + assert_eq!(rot_down * Vector2::unit_x(), + V.normalize()); + assert_eq!(rot_down * Vector2::unit_y(), + -1.0 * new_up); + + let rot2 = Matrix2::look_at(-V, Vector2::unit_y()); + assert_eq!(rot2 * Vector2::unit_x(), + (-V).normalize()); + } } pub mod matrix3 {