diff --git a/src/geometry/isometry.rs b/src/geometry/isometry.rs index 6aa7a3c38..5f09fd155 100644 --- a/src/geometry/isometry.rs +++ b/src/geometry/isometry.rs @@ -42,7 +42,7 @@ use rkyv::bytecheck; /// this page. /// /// # Construction -/// * [From a 2D vector and/or an angle `new`, `translation`, `rotation`…](#construction-from-a-2d-vector-andor-a-rotation-angle) +/// * [From a 2D vector and/or an angle `new`, `translation`, `rotation`, `face_towards`…](#construction-from-a-2d-vector-andor-a-rotation-angle) /// * [From a 3D vector and/or an axis-angle `new`, `translation`, `rotation`…](#construction-from-a-3d-vector-andor-an-axis-angle) /// * [From a 3D eye position and target point `look_at`, `look_at_lh`, `face_towards`…](#construction-from-a-3d-eye-position-and-target-point) /// * [From the translation and rotation parts `from_parts`…](#from-the-translation-and-rotation-parts) diff --git a/src/geometry/isometry_construction.rs b/src/geometry/isometry_construction.rs index b466ac7dc..fb415f19d 100644 --- a/src/geometry/isometry_construction.rs +++ b/src/geometry/isometry_construction.rs @@ -17,8 +17,8 @@ use crate::base::{Vector2, Vector3}; use crate::{ AbstractRotation, Isometry, Isometry2, Isometry3, IsometryMatrix2, IsometryMatrix3, Point, - Point3, Rotation, Rotation3, Scalar, Translation, Translation2, Translation3, UnitComplex, - UnitQuaternion, + Point2, Point3, Rotation, Rotation3, Scalar, Translation, Translation2, Translation3, + UnitComplex, UnitQuaternion, }; impl, const D: usize> Default for Isometry @@ -155,6 +155,44 @@ where Self::new(Vector2::zeros(), angle) } + /// Creates an isometry that corresponds to the local frame of an observer standing at the + /// point `eye` and looking toward `target`. + /// + /// It maps the `x` axis to the view direction `target - eye` and the origin to the `eye`. + /// + /// # Arguments + /// * eye - The observer position. + /// * target - The target position. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{IsometryMatrix2, Point2, Vector2}; + /// let eye = Point2::new(1.0, 2.0); + /// let target = Point2::new(2.0, 2.0); + /// + /// let iso = IsometryMatrix2::face_towards(&eye, &target); + /// assert_eq!(iso * Point2::origin(), eye); + /// assert_relative_eq!(iso * Vector2::x(), Vector2::x()); + /// ``` + #[inline] + pub fn face_towards(eye: &Point2, target: &Point2) -> Self { + let dir = target - eye; + let angle = dir.y.clone().simd_atan2(dir.x.clone()); + + Self::from_parts( + Translation::from(eye.coords.clone()), + Rotation::::new(angle), + ) + } + + /// Deprecated: Use [`IsometryMatrix2::face_towards`] instead. + #[deprecated(note = "renamed to `face_towards`")] + pub fn new_observer_frame(eye: &Point2, target: &Point2) -> Self { + Self::face_towards(eye, target) + } + /// Cast the components of `self` to another type. /// /// # Example @@ -209,6 +247,44 @@ where Self::new(Vector2::zeros(), angle) } + /// Creates an isometry that corresponds to the local frame of an observer standing at the + /// point `eye` and looking toward `target`. + /// + /// It maps the `x` axis to the view direction `target - eye` and the origin to the `eye`. + /// + /// # Arguments + /// * eye - The observer position. + /// * target - The target position. + /// + /// # Example + /// + /// ``` + /// # #[macro_use] extern crate approx; + /// # use nalgebra::{Isometry2, Point2, Vector2}; + /// let eye = Point2::new(1.0, 2.0); + /// let target = Point2::new(2.0, 2.0); + /// + /// let iso = Isometry2::face_towards(&eye, &target); + /// assert_eq!(iso * Point2::origin(), eye); + /// assert_relative_eq!(iso * Vector2::x(), Vector2::x()); + /// ``` + #[inline] + pub fn face_towards(eye: &Point2, target: &Point2) -> Self { + let dir = target - eye; + let angle = dir.y.clone().simd_atan2(dir.x.clone()); + + Self::from_parts( + Translation::from(eye.coords.clone()), + UnitComplex::from_angle(angle), + ) + } + + /// Deprecated: Use [`Isometry2::face_towards`] instead. + #[deprecated(note = "renamed to `face_towards`")] + pub fn new_observer_frame(eye: &Point2, target: &Point2) -> Self { + Self::face_towards(eye, target) + } + /// Cast the components of `self` to another type. /// /// # Example diff --git a/tests/geometry/isometry.rs b/tests/geometry/isometry.rs index cfacaffd4..1183ab5c7 100644 --- a/tests/geometry/isometry.rs +++ b/tests/geometry/isometry.rs @@ -1,10 +1,10 @@ #![cfg(feature = "proptest-support")] #![allow(non_snake_case)] -use na::{Isometry3, Point3, Vector3}; +use na::{Isometry2, Isometry3, IsometryMatrix2, Point2, Point3, Vector2, Vector3}; use crate::proptest::*; -use proptest::{prop_assert, prop_assert_eq, proptest}; +use proptest::{prop_assert, prop_assert_eq, prop_assume, proptest}; proptest!( #[test] @@ -48,6 +48,40 @@ proptest!( )) } + #[test] + fn observer_frame_2(eye in point2(), target in point2()) { + let dir = target - eye; + prop_assume!(dir.norm_squared() > 1.0e-14); + + let observer = Isometry2::face_towards(&eye, &target); + let matrix_observer = IsometryMatrix2::face_towards(&eye, &target); + let origin = Point2::origin(); + let direction = dir.normalize(); + + #[allow(deprecated)] + { + prop_assert!(relative_eq!( + Isometry2::new_observer_frame(&eye, &target), + observer, + epsilon = 1.0e-7 + )); + prop_assert!(relative_eq!( + IsometryMatrix2::new_observer_frame(&eye, &target), + matrix_observer, + epsilon = 1.0e-7 + )); + } + + prop_assert!(relative_eq!(observer * origin, eye, epsilon = 1.0e-7) + && relative_eq!(observer * Vector2::x(), direction, epsilon = 1.0e-7) + && relative_eq!(matrix_observer * origin, eye, epsilon = 1.0e-7) + && relative_eq!( + matrix_observer * Vector2::x(), + direction, + epsilon = 1.0e-7 + )) + } + #[test] fn inverse_is_identity(i in isometry3(), p in point3(), v in vector3()) { let ii = i.inverse();