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();