491: Fix Matrix2::look_at, add look_at_stable r=kvark a=blargg

## Changes

1. Fixes `Matrix2::look_at`
2. Adds tests for `look_at`
3. Adds a new function, `look_at_stable`

## Notes
I added a new function for 2d look at rotation. `look_at` is a bit weird in practice for 2d. For example, if you are making a basis matrix to orient a 2d character to look at a point, `look_at` will flip the character as they rotate past `up` or `-up` vectors. This is the best match for what look_at is supposed to do, I think.

`look_at_stable` will not flip based on orientation, you just pass in which way to flip. This is a bit easier to use to rotate 2d characters.

`look_at_stable` could have a better name. I think we can also consider removing the flip param, and just let the user flip the matrix with a transform later.

Co-authored-by: blargg <tomjankauski@gmail.com>
This commit is contained in:
bors[bot] 2019-09-03 22:13:25 +00:00 committed by GitHub
commit f69e781b8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 4 deletions

View file

@ -99,11 +99,23 @@ impl<S> Matrix2<S> {
} }
impl<S: BaseFloat> Matrix2<S> { impl<S: BaseFloat> Matrix2<S> {
/// Create a transformation matrix that will cause a vector to point at /// Create a transformation matrix that will cause `unit_x()` to point at
/// `dir`, using `up` for orientation. /// `dir`. `unit_y()` will be perpendicular to `dir`, and the closest to `up`.
pub fn look_at(dir: Vector2<S>, up: Vector2<S>) -> Matrix2<S> { pub fn look_at(dir: Vector2<S>, up: Vector2<S>) -> Matrix2<S> {
//TODO: verify look_at 2D Matrix2::look_at_stable(dir, up.x * dir.y >= up.y * dir.x)
Matrix2::from_cols(up, dir).transpose() }
/// 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<S>, flip: bool) -> Matrix2<S> {
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] #[inline]

View file

@ -146,6 +146,14 @@ pub struct Basis2<S> {
mat: Matrix2<S>, mat: Matrix2<S>,
} }
impl<S: BaseFloat> Basis2<S> {
pub fn look_at_stable(dir: Vector2<S>, flip: bool) -> Basis2<S> {
Basis2 {
mat: Matrix2::look_at_stable(dir, flip),
}
}
}
impl<S: BaseFloat> AsRef<Matrix2<S>> for Basis2<S> { impl<S: BaseFloat> AsRef<Matrix2<S>> for Basis2<S> {
#[inline] #[inline]
fn as_ref(&self) -> &Matrix2<S> { fn as_ref(&self) -> &Matrix2<S> {

View file

@ -175,6 +175,27 @@ pub mod matrix2 {
let rot3: Matrix2<f64> = Matrix2::from_angle(Rad(f64::consts::PI)); let rot3: Matrix2<f64> = Matrix2::from_angle(Rad(f64::consts::PI));
assert_ulps_eq!(rot3 * Vector2::new(1.0, 1.0), &Vector2::new(-1.0, -1.0)); 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 { pub mod matrix3 {