Skip to content

Commit

Permalink
Add to_scale_angle_translation (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-blackbird authored Sep 17, 2023
1 parent 2db9a7f commit f3832dc
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
27 changes: 27 additions & 0 deletions codegen/templates/affine.rs.tera
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ impl {{ self_t }} {
}
{% endif %}

/// Extracts `scale`, `angle` and `translation` from `self`.
///
/// The transform is expected to be non-degenerate and without shearing, or the output
/// will be invalid.
///
/// # Panics
///
/// Will panic if the determinant `self.matrix2` is zero or if the resulting scale
/// vector contains any zero elements when `glam_assert` is enabled.
#[inline]
pub fn to_scale_angle_translation(self) -> ({{ vec2_t }}, {{ scalar_t }}, {{ vec2_t }}) {
use crate::{{ scalar_t }}::math;
let det = self.matrix2.determinant();
glam_assert!(det != 0.0);

let scale = {{ col_t }}::new(
self.matrix2.x_axis.length() * math::signum(det),
self.matrix2.y_axis.length(),
);

glam_assert!(scale.cmpne({{ col_t }}::ZERO).all());

let angle = math::atan2(-self.matrix2.y_axis.x, self.matrix2.y_axis.y);

(scale, angle, self.translation)
}

/// Transforms the given 2D point, applying shear, scale, rotation and translation.
#[inline]
pub fn transform_point2(&self, rhs: {{ vec2_t }}) -> {{ vec2_t }} {
Expand Down
27 changes: 27 additions & 0 deletions src/f32/affine2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,33 @@ impl Affine2 {
}
}

/// Extracts `scale`, `angle` and `translation` from `self`.
///
/// The transform is expected to be non-degenerate and without shearing, or the output
/// will be invalid.
///
/// # Panics
///
/// Will panic if the determinant `self.matrix2` is zero or if the resulting scale
/// vector contains any zero elements when `glam_assert` is enabled.
#[inline]
pub fn to_scale_angle_translation(self) -> (Vec2, f32, Vec2) {
use crate::f32::math;
let det = self.matrix2.determinant();
glam_assert!(det != 0.0);

let scale = Vec2::new(
self.matrix2.x_axis.length() * math::signum(det),
self.matrix2.y_axis.length(),
);

glam_assert!(scale.cmpne(Vec2::ZERO).all());

let angle = math::atan2(-self.matrix2.y_axis.x, self.matrix2.y_axis.y);

(scale, angle, self.translation)
}

/// Transforms the given 2D point, applying shear, scale, rotation and translation.
#[inline]
pub fn transform_point2(&self, rhs: Vec2) -> Vec2 {
Expand Down
27 changes: 27 additions & 0 deletions src/f64/daffine2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,33 @@ impl DAffine2 {
}
}

/// Extracts `scale`, `angle` and `translation` from `self`.
///
/// The transform is expected to be non-degenerate and without shearing, or the output
/// will be invalid.
///
/// # Panics
///
/// Will panic if the determinant `self.matrix2` is zero or if the resulting scale
/// vector contains any zero elements when `glam_assert` is enabled.
#[inline]
pub fn to_scale_angle_translation(self) -> (DVec2, f64, DVec2) {
use crate::f64::math;
let det = self.matrix2.determinant();
glam_assert!(det != 0.0);

let scale = DVec2::new(
self.matrix2.x_axis.length() * math::signum(det),
self.matrix2.y_axis.length(),
);

glam_assert!(scale.cmpne(DVec2::ZERO).all());

let angle = math::atan2(-self.matrix2.y_axis.x, self.matrix2.y_axis.y);

(scale, angle, self.translation)
}

/// Transforms the given 2D point, applying shear, scale, rotation and translation.
#[inline]
pub fn transform_point2(&self, rhs: DVec2) -> DVec2 {
Expand Down
68 changes: 68 additions & 0 deletions tests/affine2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,74 @@ macro_rules! impl_affine2_tests {
should_glam_assert!({ $affine2::ZERO.inverse() });
});

glam_test!(test_affine2_decompose, {
// identity
let (out_scale, out_rotation, out_translation) =
$affine2::IDENTITY.to_scale_angle_translation();
assert_approx_eq!($vec2::ONE, out_scale);
assert_eq!(out_rotation, 0.0);
assert_approx_eq!($vec2::ZERO, out_translation);

// no scale
let in_scale = $vec2::ONE;
let in_translation = $vec2::new(-2.0, 4.0);
let in_rotation = $t::to_radians(-45.0);
let in_mat =
$affine2::from_scale_angle_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_angle_translation();
assert_approx_eq!(in_scale, out_scale, 1e-6);
assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
$affine2::from_scale_angle_translation(out_scale, out_rotation, out_translation),
1e-6
);

// positive scale
let in_scale = $vec2::new(1.0, 2.0);
let in_mat =
$affine2::from_scale_angle_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_angle_translation();
assert_approx_eq!(in_scale, out_scale, 1e-6);
assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
$affine2::from_scale_angle_translation(out_scale, out_rotation, out_translation),
1e-5
);

// negative scale
let in_scale = $vec2::new(-4.0, 1.0);
let in_mat =
$affine2::from_scale_angle_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_angle_translation();
assert_approx_eq!(in_scale, out_scale, 1e-6);
assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
$affine2::from_scale_angle_translation(out_scale, out_rotation, out_translation),
1e-5
);

// negative scale
let in_scale = $vec2::new(4.0, -1.0);
let in_mat =
$affine2::from_scale_angle_translation(in_scale, in_rotation, in_translation);
let (out_scale, out_rotation, out_translation) = in_mat.to_scale_angle_translation();
// out_scale and out_rotation are different but they produce the same matrix
// assert_approx_eq!(in_scale, out_scale, 1e-6);
// assert_approx_eq!(in_rotation, out_rotation);
assert_approx_eq!(in_translation, out_translation);
assert_approx_eq!(
in_mat,
$affine2::from_scale_angle_translation(out_scale, out_rotation, out_translation),
1e-6
);
});

glam_test!(test_affine2_ops, {
let m0 = $affine2::from_cols_array_2d(&MATRIX2D);
assert_approx_eq!(m0, m0 * $affine2::IDENTITY);
Expand Down

0 comments on commit f3832dc

Please sign in to comment.