Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased

- Fix `Rot2::slerp` (and therefore `Pose2::lerp`) taking the long way around whenever the arc
crosses the `-pi`/`pi` cut of `Rot2::angle`. It now interpolates along the shortest arc.

# v0.3.0

- Update to `glam 0.33`, `simba 0.10`, and `nalgebra 0.35`. ([#5](https://github.com/dimforge/glamx/pull/5))
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ isize = ["glam/isize"]
usize = ["glam/usize"]

[dependencies]
glam = { version = "0.33", default-features = false }
glam = { version = "0.33.7", default-features = false }
simba = { version = "0.10", default-features = false }
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
approx = { version = "0.5", default-features = false, optional = true }
Expand Down
10 changes: 10 additions & 0 deletions src/pose2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,16 @@ mod tests {
assert_relative_eq!(p.translation.y, p2.translation.y, epsilon = 1e-6);
}

#[test]
fn test_pose2_lerp_across_branch_cut() {
let p1 = Pose2::new(glam::Vec2::new(0.0, 0.0), 3.13);
let p2 = Pose2::new(glam::Vec2::new(2.0, 4.0), 3.15);
let mid = p1.lerp(&p2, 0.5);
assert_relative_eq!(mid.rotation.angle(), 3.14, epsilon = 1e-6);
assert_relative_eq!(mid.translation.x, 1.0, epsilon = 1e-6);
assert_relative_eq!(mid.translation.y, 2.0, epsilon = 1e-6);
}

#[cfg(feature = "f64")]
#[test]
fn test_dpose2_new() {
Expand Down
22 changes: 16 additions & 6 deletions src/pose3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use core::ops::{Mul, MulAssign};

/// Macro to generate a 3D pose type for a specific scalar type.
macro_rules! impl_pose3 {
($Pose3:ident, $Rot3:ident, $Real:ty, $Vec3:ty, $Mat4: ty $(, $Padding: ty, $bytemuck: ident)?) => {
($Pose3:ident, $Rot3:ident, $Real:ty, $Vec3:ty, $Mat4: ty, $camera: ident $(, $Padding: ty, $bytemuck: ident)?) => {
#[doc = concat!("A 3D pose (rotation + translation), representing a rigid body transformation (", stringify!($Real), " precision).")]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -225,7 +225,7 @@ macro_rules! impl_pose3 {
/// requirement of this parameter is to not be collinear to target - eye.
#[inline]
pub fn look_at_rh(eye: $Vec3, target: $Vec3, up: $Vec3) -> $Pose3 {
let rotation = <$Rot3>::look_at_rh(eye.into(), target.into(), up.into());
let rotation = glam::$camera::rh::view::look_at_quat(eye.into(), target.into(), up.into());
let translation = rotation * (-eye);

$Pose3 {
Expand All @@ -248,7 +248,8 @@ macro_rules! impl_pose3 {
#[inline]
pub fn face_towards(eye: $Vec3, target: $Vec3, up: $Vec3) -> $Pose3 {
$Pose3 {
rotation: <$Rot3>::look_at_lh(eye.into(), target.into(), up.into()).inverse(),
rotation: glam::$camera::lh::view::look_at_quat(eye.into(), target.into(), up.into())
.inverse(),
translation: eye,
$(padding: 0 as $Padding,)*
}
Expand Down Expand Up @@ -413,10 +414,19 @@ macro_rules! impl_pose3 {
};
}

impl_pose3!(Pose3, Rot3, f32, glam::Vec3, glam::Mat4, u32, bytemuck);
impl_pose3!(Pose3A, Rot3, f32, glam::Vec3A, glam::Mat4);
impl_pose3!(
Pose3,
Rot3,
f32,
glam::Vec3,
glam::Mat4,
camera,
u32,
bytemuck
);
impl_pose3!(Pose3A, Rot3, f32, glam::Vec3A, glam::Mat4, camera);
#[cfg(feature = "f64")]
impl_pose3!(DPose3, DRot3, f64, glam::DVec3, glam::DMat4);
impl_pose3!(DPose3, DRot3, f64, glam::DVec3, glam::DMat4, dcamera);

// f32 <-> f64 conversions
#[cfg(feature = "f64")]
Expand Down
35 changes: 33 additions & 2 deletions src/rot2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,14 @@ macro_rules! impl_rot2 {
}

/// Spherical linear interpolation between two rotations.
///
/// Interpolates along the shortest arc: the result is `self` at `t == 0` and `other`
/// at `t == 1`, whichever branch of `angle` the two rotations fall on. Interpolating
/// their angles instead would take the long way around whenever the arc crosses the
/// `-pi`/`pi` cut.
#[inline]
pub fn slerp(&self, other: &Self, t: $Real) -> Self {
let angle_diff = other.angle() - self.angle();
Self::new(self.angle() + t * angle_diff)
*self * Self::new(self.angle_between(other) * t)
}

/// Raises this rotation to a power.
Expand Down Expand Up @@ -504,6 +508,33 @@ mod tests {
assert_relative_eq!(r1.angle_between(&r2), PI / 2.0, epsilon = 1e-6);
}

#[test]
fn test_rot2_slerp() {
let r1 = Rot2::new(0.2);
let r2 = Rot2::new(1.0);
assert_relative_eq!(r1.slerp(&r2, 0.0).angle(), 0.2, epsilon = 1e-6);
assert_relative_eq!(r1.slerp(&r2, 1.0).angle(), 1.0, epsilon = 1e-6);
assert_relative_eq!(r1.slerp(&r2, 0.5).angle(), 0.6, epsilon = 1e-6);
assert!(r1.slerp(&r2, 0.5).is_normalized());
}

#[test]
fn test_rot2_slerp_shortest_arc() {
// The interpolation from 0 to 3pi/2 goes backwards through -3pi/4, not forwards.
let r1 = Rot2::new(0.0);
let r2 = Rot2::new(3.0 * PI / 2.0);
assert_relative_eq!(r1.slerp(&r2, 0.5).angle(), -PI / 4.0, epsilon = 1e-6);
}

#[test]
fn test_rot2_slerp_across_branch_cut() {
// `angle` wraps to (-pi, pi], so these two are 0.02 rad apart, not 6.26.
let r1 = Rot2::new(3.13);
let r2 = Rot2::new(3.15);
assert_relative_eq!(r1.slerp(&r2, 0.5).angle(), 3.14, epsilon = 1e-6);
assert_relative_eq!(r1.slerp(&r2, 1.0).angle(), r2.angle(), epsilon = 1e-6);
}

#[cfg(feature = "f64")]
#[test]
fn test_drot2_new() {
Expand Down
Loading