{% import "macros.rs.tera" as macros %}
{% import "coresimd.rs.tera" as coresimd %}
{% import "neon.rs.tera" as neon %}
{% import "sse2.rs.tera" as sse2 %}
{% import "wasm32.rs.tera" as wasm32 %}
// Generated from {{template_path}} template. Edit the template, not the generated file.
{% if scalar_t == "f32" %}
{% set vecn_t = "Vec" ~ dim %}
{% if dim == 3 and is_align %}
{% set self_t = "Mat3A" %}
{% set col_t = "Vec3A" %}
{% else %}
{% set self_t = "Mat" ~ dim %}
{% set col_t = vecn_t %}
{% endif %}
{% set quat_t = "Quat" %}
{% set affine2_t = "Affine2" %}
{% set affine3_t = "Affine3A" %}
{% set vec2_t = "Vec2" %}
{% set vec3_t = "Vec3" %}
{% set vec4_t = "Vec4" %}
{% set mat2_t = "Mat2" %}
{% set mat3_t = "Mat3" %}
{% set mat4_t = "Mat4" %}
{% elif scalar_t == "f64" %}
{% set vecn_t = "DVec" ~ dim %}
{% set self_t = "DMat" ~ dim %}
{% set col_t = vecn_t %}
{% set quat_t = "DQuat" %}
{% set affine2_t = "DAffine2" %}
{% set affine3_t = "DAffine3" %}
{% set vec2_t = "DVec2" %}
{% set vec3_t = "DVec3" %}
{% set vec4_t = "DVec4" %}
{% set mat2_t = "DMat2" %}
{% set mat3_t = "DMat3" %}
{% set mat4_t = "DMat4" %}
{% endif %}
{% if self_t == "Mat2" %}
{% if not is_scalar %}
{% set is_simd = true %}
{% if is_sse2 %}
{% set simd_t = "__m128" %}
{% elif is_wasm32 %}
{% set simd_t = "v128" %}
{% elif is_coresimd %}
{% set simd_t = "f32x4" %}
{% elif is_neon %}
{% set simd_t = "float32x4_t" %}
{% endif %}
{% endif %}
{% endif %}
{% set size = dim * dim %}
{% set nxn = dim ~ "x" ~ dim %}
{% set components = ["x", "y", "z", "w"] | slice(end = dim) %}
{% set axes = ["x_axis", "y_axis", "z_axis", "w_axis"] | slice(end = dim) %}
{% set dimension_in_full = ["zero", "one", "two", "three", "four"] | nth(n = dim) %}
use crate::{
{% if scalar_t == "f32" %}
DMat{{ dim }},
{% elif scalar_t == "f64" %}
Mat{{ dim }},
{% endif %}
{% if dim == 2 %}
{{ mat3_t }}, {{ vec2_t }},
{% if scalar_t == "f32" %}
Mat3A,
{% endif %}
{% elif dim == 3 %}
EulerRot, euler::{FromEuler, ToEuler},
{{ mat2_t }}, {{ mat4_t }}, {{ quat_t }}, {{ vec2_t }}, {{ col_t }},
{% if is_align %}
{{ mat3_t }}, {{ vec3_t }},
{% elif scalar_t == "f32" %}
Mat3A, Vec3A,
{% endif %}
{% elif dim == 4 %}
EulerRot, euler::{FromEuler, ToEuler},
{{ mat3_t }}, {{ quat_t }}, {{ vec3_t }}, {{ col_t }},
{% if scalar_t == "f32" %}
Mat3A, Vec3A,
{% endif %}
{% endif %}
{% if self_t == "Mat4" %}
{% if is_sse2 %}
sse2::*,
{% elif is_wasm32 %}
wasm32::*,
{% elif is_coresimd %}
coresimd::*,
{% elif is_neon %}
neon::*,
{% endif %}
{% endif %}
{{ scalar_t }}::math,
swizzles::*,
};
use core::fmt;
use core::iter::{Product, Sum};
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
{% if is_sse2 %}
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
{% elif is_wasm32 %}
use core::arch::wasm32::*;
{% elif is_coresimd %}
use core::simd::*;
{% elif is_neon %}
use core::arch::aarch64::*;
{% endif %}
{% set is_sse2_or_neon = is_sse2 or is_neon %}
{% if self_t == "Mat2" and is_sse2_or_neon %}
#[repr(C)]
union UnionCast {
a: [f32; 4],
v: {{ self_t }}
}
{% endif %}
/// Creates a {{ nxn }} matrix from {{ dimension_in_full }} column vectors.
#[inline(always)]
#[must_use]
pub const fn {{ self_t | lower }}(
{% for axis in axes %}
{{ axis }}: {{ col_t }},
{% endfor %}
) -> {{ self_t }} {
{{ self_t }}::from_cols({{ axes | join(sep=",") }})
}
/// A {{ nxn }} column major matrix.
{%- if dim == 3 %}
///
/// This 3x3 matrix type features convenience methods for creating and using linear and
/// affine transformations. If you are primarily dealing with 2D affine transformations the
/// [`{{ affine2_t }}`](crate::{{ affine2_t }}) type is much faster and more space efficient than
/// using a 3x3 matrix.
///
/// Linear transformations including 3D rotation and scale can be created using methods
/// such as [`Self::from_diagonal()`], [`Self::from_quat()`], [`Self::from_axis_angle()`],
/// [`Self::from_rotation_x()`], [`Self::from_rotation_y()`], or
/// [`Self::from_rotation_z()`].
///
/// The resulting matrices can be use to transform 3D vectors using regular vector
/// multiplication.
///
/// Affine transformations including 2D translation, rotation and scale can be created
/// using methods such as [`Self::from_translation()`], [`Self::from_angle()`],
/// [`Self::from_scale()`] and [`Self::from_scale_angle_translation()`].
///
/// The [`Self::transform_point2()`] and [`Self::transform_vector2()`] convenience methods
/// are provided for performing affine transforms on 2D vectors and points. These multiply
/// 2D inputs as 3D vectors with an implicit `z` value of `1` for points and `0` for
/// vectors respectively. These methods assume that `Self` contains a valid affine
/// transform.
{%- elif dim == 4 %}
///
/// This 4x4 matrix type features convenience methods for creating and using affine transforms and
/// perspective projections. If you are primarily dealing with 3D affine transformations
/// considering using [`{{affine3_t}}`](crate::{{ affine3_t }}) which is faster than a 4x4 matrix
/// for some affine operations.
///
/// Affine transformations including 3D translation, rotation and scale can be created
/// using methods such as [`Self::from_translation()`], [`Self::from_quat()`],
/// [`Self::from_scale()`] and [`Self::from_scale_rotation_translation()`].
///
/// Orthographic projections can be created using the methods [`Self::orthographic_lh()`] for
/// left-handed coordinate systems and [`Self::orthographic_rh()`] for right-handed
/// systems. The resulting matrix is also an affine transformation.
///
/// The [`Self::transform_point3()`] and [`Self::transform_vector3()`] convenience methods
/// are provided for performing affine transformations on 3D vectors and points. These
/// multiply 3D inputs as 4D vectors with an implicit `w` value of `1` for points and `0`
/// for vectors respectively. These methods assume that `Self` contains a valid affine
/// transform.
///
/// Perspective projections can be created using methods such as
/// [`Self::perspective_lh()`], [`Self::perspective_infinite_lh()`] and
/// [`Self::perspective_infinite_reverse_lh()`] for left-handed co-ordinate systems and
/// [`Self::perspective_rh()`], [`Self::perspective_infinite_rh()`] and
/// [`Self::perspective_infinite_reverse_rh()`] for right-handed co-ordinate systems.
///
/// The resulting perspective project can be use to transform 3D vectors as points with
/// perspective correction using the [`Self::project_point3()`] convenience method.
{%- endif %}
{%- if is_simd %}
///
/// SIMD vector types are used for storage on supported platforms.
///
/// This type is 16 byte aligned.
{%- endif %}
#[derive(Clone, Copy)]
{%- if self_t == "Mat4" and is_scalar %}
#[cfg_attr(
any(
not(any(feature = "scalar-math", target_arch = "spirv")),
feature = "cuda"),
repr(align(16))
)]
{%- elif self_t == "Mat2" and is_scalar %}
#[cfg_attr(not(any(feature = "scalar-math", target_arch = "spirv")), repr(align(16)))]
#[cfg_attr(feature = "cuda", repr(align(8)))]
{%- elif self_t == "DMat2" or self_t == "DMat4" %}
#[cfg_attr(feature = "cuda", repr(align(16)))]
{%- endif %}
{%- if self_t == "Mat2" and not is_scalar %}
#[repr(transparent)]
pub struct {{ self_t }}(pub(crate) {{ simd_t }});
{%- else %}
#[repr(C)]
pub struct {{ self_t }}
{
{% for axis in axes %}
pub {{ axis }}: {{ col_t }},
{%- endfor %}
}
{% endif %}
impl {{ self_t }} {
/// A {{ nxn }} matrix with all elements set to `0.0`.
pub const ZERO: Self = Self::from_cols(
{% for axis in axes %}
{{ col_t }}::ZERO,
{%- endfor %}
);
/// A {{ nxn }} identity matrix, where all diagonal elements are `1`, and all off-diagonal elements are `0`.
pub const IDENTITY: Self = Self::from_cols(
{% for i in range(end = dim) %}
{{ col_t }}::{{ components[i] | upper }},
{%- endfor %}
);
/// All NAN:s.
pub const NAN: Self = Self::from_cols(
{% for axis in axes %}
{{ col_t }}::NAN,
{%- endfor %}
);
#[allow(clippy::too_many_arguments)]
#[inline(always)]
#[must_use]
const fn new(
{% for i in range(end = dim) %}
{%- for j in range(end = dim) %}
m{{ i }}{{ j }}: {{ scalar_t }},
{%- endfor %}
{%- endfor %}
) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
unsafe { UnionCast { a: [m00, m01, m10, m11] }.v }
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4(m00, m01, m10, m11))
{% elif self_t == "Mat2" and is_coresimd %}
Self(f32x4::from_array([m00, m01, m10, m11]))
{% elif self_t == "Mat2" and is_neon %}
unsafe { UnionCast { a: [m00, m01, m10, m11] }.v }
{% else %}
Self {
{% for i in range(end = dim) %}
{{ axes[i] }}: {{ col_t}}::new(
{% for j in range(end = dim) %}
m{{ i }}{{ j }},
{% endfor %}
),
{%- endfor %}
}
{% endif %}
}
/// Creates a {{ nxn }} matrix from {{ dimension_in_full }} column vectors.
#[inline(always)]
#[must_use]
pub const fn from_cols(
{% for axis in axes %}
{{ axis }}: {{ col_t }},
{% endfor %}
) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
unsafe { UnionCast { a: [x_axis.x, x_axis.y, y_axis.x, y_axis.y] }.v }
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4(x_axis.x, x_axis.y, y_axis.x, y_axis.y))
{% elif self_t == "Mat2" and is_coresimd %}
Self(f32x4::from_array([x_axis.x, x_axis.y, y_axis.x, y_axis.y]))
{% elif self_t == "Mat2" and is_neon %}
unsafe { UnionCast { a: [x_axis.x, x_axis.y, y_axis.x, y_axis.y] }.v }
{% else %}
Self {
{% for axis in axes %}
{{ axis }},
{%- endfor %}
}
{% endif %}
}
/// Creates a {{ nxn }} matrix from a `[{{ scalar_t }}; {{ size }}]` array stored in column major order.
/// If your data is stored in row major you will need to `transpose` the returned
/// matrix.
#[inline]
#[must_use]
pub const fn from_cols_array(m: &[{{ scalar_t }}; {{ size }}]) -> Self {
{% if self_t == "Mat2" and is_coresimd %}
Self(f32x4::from_array(*m))
{% else %}
Self::new(
{% for i in range(end = size) %}
m[{{ i }}],
{%- endfor %}
)
{% endif %}
}
/// Creates a `[{{ scalar_t }}; {{ size }}]` array storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
#[must_use]
pub const fn to_cols_array(&self) -> [{{ scalar_t }}; {{ size }}] {
{% if self_t == "Mat2" and not is_scalar %}
unsafe { *(self as *const Self as *const [f32; 4]) }
{% else %}
{% if dim >= 2 and not is_scalar %}
{% for axis in axes %}
let [{% for c in components %} {{ axis }}_{{ c }}, {% endfor %}] = self.{{ axis }}.to_array();
{%- endfor %}
{% endif %}
[
{% for axis in axes %}
{% for c in components %}
{% if dim >= 2 and not is_scalar %}
{{ axis }}_{{ c }},
{% else %}
self.{{ axis }}.{{ c }},
{% endif %}
{%- endfor %}
{%- endfor %}
]
{% endif %}
}
/// Creates a {{ nxn }} matrix from a `[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]` {{ dim }}D array stored in column major order.
/// If your data is in row major order you will need to `transpose` the returned
/// matrix.
#[inline]
#[must_use]
pub const fn from_cols_array_2d(m: &[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]) -> Self {
Self::from_cols(
{% for i in range(end = dim) %}
{{ col_t }}::from_array(m[{{ i }}]),
{%- endfor %}
)
}
/// Creates a `[[{{ scalar_t }}; {{ dim }}]; {{ dim }}]` {{ dim }}D array storing data in column major order.
/// If you require data in row major order `transpose` the matrix first.
#[inline]
#[must_use]
pub const fn to_cols_array_2d(&self) -> [[{{ scalar_t }}; {{ dim }}]; {{ dim }}] {
{% if self_t == "Mat2" and not is_scalar %}
unsafe { *(self as *const Self as *const [[f32; 2]; 2]) }
{% else %}
[
{% for axis in axes %}
self.{{ axis }}.to_array(),
{%- endfor %}
]
{% endif %}
}
/// Creates a {{ nxn }} matrix with its diagonal set to `diagonal` and all other entries set to 0.
#[doc(alias = "scale")]
#[inline]
#[must_use]
pub const fn from_diagonal(diagonal: {{ vecn_t }}) -> Self {
{% if self_t == "Mat4" and not is_scalar %}
// diagonal.x, diagonal.y etc can't be done in a const-context
let [x, y, z, w] = diagonal.to_array();
Self::new(
{% for i in range(end = dim) %}
{% for j in range(end = dim) %}
{% if i == j %}
{{ components[i] }},
{% else %}
0.0,
{% endif %}
{%- endfor %}
{%- endfor %}
)
{% else %}
Self::new(
{% for i in range(end = dim) %}
{% for j in range(end = dim) %}
{% if i == j %}
diagonal.{{ components[i] }},
{% else %}
0.0,
{% endif %}
{%- endfor %}
{%- endfor %}
)
{% endif %}
}
{% if dim == 2 %}
/// Creates a {{ nxn }} matrix containing the combining non-uniform `scale` and rotation of
/// `angle` (in radians).
#[inline]
#[must_use]
pub fn from_scale_angle(scale: {{ col_t }}, angle: {{ scalar_t }}) -> Self {
let (sin, cos) = math::sin_cos(angle);
Self::new(cos * scale.x, sin * scale.x, -sin * scale.y, cos * scale.y)
}
/// Creates a {{ nxn }} matrix containing a rotation of `angle` (in radians).
#[inline]
#[must_use]
pub fn from_angle(angle: {{ scalar_t }}) -> Self {
let (sin, cos) = math::sin_cos(angle);
Self::new(cos, sin, -sin, cos)
}
/// Creates a {{ nxn }} matrix from a 3x3 matrix, discarding the 2nd row and column.
#[inline]
#[must_use]
pub fn from_mat3(m: {{ mat3_t }}) -> Self {
Self::from_cols(m.x_axis.xy(), m.y_axis.xy())
}
{{ macros::impl_mat3_minor(mat3_t=mat3_t) }}
{% if scalar_t == "f32" %}
/// Creates a {{ nxn }} matrix from a 3x3 matrix, discarding the 2nd row and column.
#[inline]
#[must_use]
pub fn from_mat3a(m: Mat3A) -> Self {
{# TODO: SIMD optimise #}
Self::from_cols(m.x_axis.xy(), m.y_axis.xy())
}
{{ macros::impl_mat3_minor(mat3_t="Mat3A", align="a") }}
{% endif %}
{% elif dim == 3 %}
/// Creates a 3x3 matrix from a 4x4 matrix, discarding the 4th row and column.
#[inline]
#[must_use]
pub fn from_mat4(m: {{ mat4_t }}) -> Self {
Self::from_cols(
{{ col_t }}::from_vec4(m.x_axis),
{{ col_t }}::from_vec4(m.y_axis),
{{ col_t }}::from_vec4(m.z_axis),
)
}
{{ macros::impl_mat4_minor(mat4_t=mat4_t, vec3_t=col_t) }}
/// Creates a 3D rotation matrix from the given quaternion.
///
/// # Panics
///
/// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_quat(rotation: {{ quat_t }}) -> Self {
glam_assert!(rotation.is_normalized());
let x2 = rotation.x + rotation.x;
let y2 = rotation.y + rotation.y;
let z2 = rotation.z + rotation.z;
let xx = rotation.x * x2;
let xy = rotation.x * y2;
let xz = rotation.x * z2;
let yy = rotation.y * y2;
let yz = rotation.y * z2;
let zz = rotation.z * z2;
let wx = rotation.w * x2;
let wy = rotation.w * y2;
let wz = rotation.w * z2;
Self::from_cols(
{{ col_t }}::new(1.0 - (yy + zz), xy + wz, xz - wy),
{{ col_t }}::new(xy - wz, 1.0 - (xx + zz), yz + wx),
{{ col_t }}::new(xz + wy, yz - wx, 1.0 - (xx + yy)),
)
}
/// Creates a 3D rotation matrix from a normalized rotation `axis` and `angle` (in
/// radians).
///
/// # Panics
///
/// Will panic if `axis` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_axis_angle(axis: {{ vec3_t }}, angle: {{ scalar_t }}) -> Self {
{# TODO: make common with dim == 4 #}
glam_assert!(axis.is_normalized());
let (sin, cos) = math::sin_cos(angle);
let (xsin, ysin, zsin) = axis.mul(sin).into();
let (x, y, z) = axis.into();
let (x2, y2, z2) = axis.mul(axis).into();
let omc = 1.0 - cos;
let xyomc = x * y * omc;
let xzomc = x * z * omc;
let yzomc = y * z * omc;
Self::from_cols(
{{ col_t }}::new(x2 * omc + cos, xyomc + zsin, xzomc - ysin),
{{ col_t }}::new(xyomc - zsin, y2 * omc + cos, yzomc + xsin),
{{ col_t }}::new(xzomc + ysin, yzomc - xsin, z2 * omc + cos),
)
}
/// Creates a 3D rotation matrix from the given euler rotation sequence and the angles (in
/// radians).
#[inline]
#[must_use]
pub fn from_euler(order: EulerRot, a: {{ scalar_t }}, b: {{ scalar_t }}, c: {{ scalar_t }}) -> Self {
Self::from_euler_angles(order, a, b, c)
}
/// Extract Euler angles with the given Euler rotation order.
///
/// Note if the input matrix contains scales, shears, or other non-rotation transformations then
/// the resulting Euler angles will be ill-defined.
///
/// # Panics
///
/// Will panic if any input matrix column is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn to_euler(&self, order: EulerRot) -> ({{ scalar_t }}, {{ scalar_t }}, {{ scalar_t }}) {
glam_assert!(self.x_axis.is_normalized()
&& self.y_axis.is_normalized()
&& self.z_axis.is_normalized());
self.to_euler_angles(order)
}
/// Creates a 3D rotation matrix from `angle` (in radians) around the x axis.
#[inline]
#[must_use]
pub fn from_rotation_x(angle: {{ scalar_t }}) -> Self {
let (sina, cosa) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::X,
{{ col_t }}::new(0.0, cosa, sina),
{{ col_t }}::new(0.0, -sina, cosa),
)
}
/// Creates a 3D rotation matrix from `angle` (in radians) around the y axis.
#[inline]
#[must_use]
pub fn from_rotation_y(angle: {{ scalar_t }}) -> Self {
let (sina, cosa) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::new(cosa, 0.0, -sina),
{{ col_t }}::Y,
{{ col_t }}::new(sina, 0.0, cosa),
)
}
/// Creates a 3D rotation matrix from `angle` (in radians) around the z axis.
#[inline]
#[must_use]
pub fn from_rotation_z(angle: {{ scalar_t }}) -> Self {
let (sina, cosa) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::new(cosa, sina, 0.0),
{{ col_t }}::new(-sina, cosa, 0.0),
{{ col_t }}::Z,
)
}
/// Creates an affine transformation matrix from the given 2D `translation`.
///
/// The resulting matrix can be used to transform 2D points and vectors. See
/// [`Self::transform_point2()`] and [`Self::transform_vector2()`].
#[inline]
#[must_use]
pub fn from_translation(translation: {{ vec2_t }}) -> Self {
Self::from_cols(
{{ col_t }}::X,
{{ col_t }}::Y,
{{ col_t }}::new(translation.x, translation.y, 1.0))
}
/// Creates an affine transformation matrix from the given 2D rotation `angle` (in
/// radians).
///
/// The resulting matrix can be used to transform 2D points and vectors. See
/// [`Self::transform_point2()`] and [`Self::transform_vector2()`].
#[inline]
#[must_use]
pub fn from_angle(angle: {{ scalar_t }}) -> Self {
let (sin, cos) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::new(cos, sin, 0.0),
{{ col_t }}::new(-sin, cos, 0.0),
{{ col_t }}::Z,
)
}
/// Creates an affine transformation matrix from the given 2D `scale`, rotation `angle` (in
/// radians) and `translation`.
///
/// The resulting matrix can be used to transform 2D points and vectors. See
/// [`Self::transform_point2()`] and [`Self::transform_vector2()`].
#[inline]
#[must_use]
pub fn from_scale_angle_translation(scale: {{ vec2_t }}, angle: {{ scalar_t }}, translation: {{ vec2_t }}) -> Self {
let (sin, cos) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::new(cos * scale.x, sin * scale.x, 0.0),
{{ col_t }}::new(-sin * scale.y, cos * scale.y, 0.0),
{{ col_t }}::new(translation.x, translation.y, 1.0),
)
}
/// Creates an affine transformation matrix from the given non-uniform 2D `scale`.
///
/// The resulting matrix can be used to transform 2D points and vectors. See
/// [`Self::transform_point2()`] and [`Self::transform_vector2()`].
///
/// # Panics
///
/// Will panic if all elements of `scale` are zero when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_scale(scale: {{ vec2_t }}) -> Self {
// Do not panic as long as any component is non-zero
glam_assert!(scale.cmpne({{ vec2_t }}::ZERO).any());
Self::from_cols(
{{ col_t }}::new(scale.x, 0.0, 0.0),
{{ col_t }}::new(0.0, scale.y, 0.0),
{{ col_t }}::Z,
)
}
/// Creates an affine transformation matrix from the given 2x2 matrix.
///
/// The resulting matrix can be used to transform 2D points and vectors. See
/// [`Self::transform_point2()`] and [`Self::transform_vector2()`].
#[inline]
pub fn from_mat2(m: {{ mat2_t }}) -> Self {
Self::from_cols((m.x_axis, 0.0).into(), (m.y_axis, 0.0).into(), {{ col_t }}::Z)
}
{% elif dim == 4 %}
#[inline]
#[must_use]
fn quat_to_axes(rotation: {{ quat_t }}) -> ({{ col_t }}, {{ col_t }}, {{ col_t }}) {
glam_assert!(rotation.is_normalized());
let (x, y, z, w) = rotation.into();
let x2 = x + x;
let y2 = y + y;
let z2 = z + z;
let xx = x * x2;
let xy = x * y2;
let xz = x * z2;
let yy = y * y2;
let yz = y * z2;
let zz = z * z2;
let wx = w * x2;
let wy = w * y2;
let wz = w * z2;
let x_axis = {{ col_t }}::new(1.0 - (yy + zz), xy + wz, xz - wy, 0.0);
let y_axis = {{ col_t }}::new(xy - wz, 1.0 - (xx + zz), yz + wx, 0.0);
let z_axis = {{ col_t }}::new(xz + wy, yz - wx, 1.0 - (xx + yy), 0.0);
(x_axis, y_axis, z_axis)
}
/// Creates an affine transformation matrix from the given 3D `scale`, `rotation` and
/// `translation`.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
///
/// # Panics
///
/// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_scale_rotation_translation(
scale: {{ vec3_t }},
rotation: {{ quat_t }},
translation: {{ vec3_t }},
) -> Self {
let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation);
Self::from_cols(
x_axis.mul(scale.x),
y_axis.mul(scale.y),
z_axis.mul(scale.z),
{{ col_t }}::from((translation, 1.0)),
)
}
/// Creates an affine transformation matrix from the given 3D `translation`.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
///
/// # Panics
///
/// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_rotation_translation(rotation: {{ quat_t }}, translation: {{ vec3_t }}) -> Self {
let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation);
Self::from_cols(x_axis, y_axis, z_axis, {{ col_t }}::from((translation, 1.0)))
}
/// Extracts `scale`, `rotation` and `translation` from `self`. The input matrix is
/// expected to be a 3D affine transformation matrix otherwise the output will be invalid.
///
/// # Panics
///
/// Will panic if the determinant of `self` is zero or if the resulting scale vector
/// contains any zero elements when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn to_scale_rotation_translation(&self) -> ({{ vec3_t }}, {{ quat_t }}, {{ vec3_t }}) {
let det = self.determinant();
glam_assert!(det != 0.0);
let scale = {{ vec3_t }}::new(
self.x_axis.length() * math::signum(det),
self.y_axis.length(),
self.z_axis.length(),
);
glam_assert!(scale.cmpne({{ vec3_t }}::ZERO).all());
let inv_scale = scale.recip();
let rotation = {{ quat_t }}::from_rotation_axes(
self.x_axis.mul(inv_scale.x).xyz(),
self.y_axis.mul(inv_scale.y).xyz(),
self.z_axis.mul(inv_scale.z).xyz(),
);
let translation = self.w_axis.xyz();
(scale, rotation, translation)
}
/// Creates an affine transformation matrix from the given `rotation` quaternion.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
///
/// # Panics
///
/// Will panic if `rotation` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_quat(rotation: {{ quat_t }}) -> Self {
let (x_axis, y_axis, z_axis) = Self::quat_to_axes(rotation);
Self::from_cols(x_axis, y_axis, z_axis, {{ col_t }}::W)
}
/// Creates an affine transformation matrix from the given 3x3 linear transformation
/// matrix.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_mat3(m: {{ mat3_t }}) -> Self {
Self::from_cols(
{{ col_t }}::from((m.x_axis, 0.0)),
{{ col_t }}::from((m.y_axis, 0.0)),
{{ col_t }}::from((m.z_axis, 0.0)),
{{ col_t }}::W,
)
}
/// Creates an affine transformation matrics from a 3x3 matrix (expressing scale, shear and
/// rotation) and a translation vector.
///
/// Equivalent to `{{ self_t }}::from_translation(translation) * {{ self_t }}::from_mat3(mat3)`
#[inline]
#[must_use]
pub fn from_mat3_translation(mat3: {{ mat3_t }}, translation: {{ vec3_t }}) -> Self {
Self::from_cols(
{{ col_t }}::from((mat3.x_axis, 0.0)),
{{ col_t }}::from((mat3.y_axis, 0.0)),
{{ col_t }}::from((mat3.z_axis, 0.0)),
{{ col_t }}::from((translation, 1.0)),
)
}
{% if scalar_t == "f32" %}
/// Creates an affine transformation matrix from the given 3x3 linear transformation
/// matrix.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_mat3a(m: Mat3A) -> Self {
Self::from_cols(
Vec4::from((m.x_axis, 0.0)),
Vec4::from((m.y_axis, 0.0)),
Vec4::from((m.z_axis, 0.0)),
Vec4::W,
)
}
{% endif %}
/// Creates an affine transformation matrix from the given 3D `translation`.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_translation(translation: {{ vec3_t }}) -> Self {
Self::from_cols(
{{ col_t }}::X,
{{ col_t }}::Y,
{{ col_t }}::Z,
{{ col_t }}::new(translation.x, translation.y, translation.z, 1.0),
)
}
/// Creates an affine transformation matrix containing a 3D rotation around a normalized
/// rotation `axis` of `angle` (in radians).
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
///
/// # Panics
///
/// Will panic if `axis` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_axis_angle(axis: {{ vec3_t }}, angle: {{ scalar_t }}) -> Self {
{# TODO: make common with dim == 3 #}
glam_assert!(axis.is_normalized());
let (sin, cos) = math::sin_cos(angle);
let axis_sin = axis.mul(sin);
let axis_sq = axis.mul(axis);
let omc = 1.0 - cos;
let xyomc = axis.x * axis.y * omc;
let xzomc = axis.x * axis.z * omc;
let yzomc = axis.y * axis.z * omc;
Self::from_cols(
{{ col_t }}::new(
axis_sq.x * omc + cos,
xyomc + axis_sin.z,
xzomc - axis_sin.y,
0.0,
),
{{ col_t }}::new(
xyomc - axis_sin.z,
axis_sq.y * omc + cos,
yzomc + axis_sin.x,
0.0,
),
{{ col_t }}::new(
xzomc + axis_sin.y,
yzomc - axis_sin.x,
axis_sq.z * omc + cos,
0.0,
),
{{ col_t }}::W,
)
}
/// Creates a affine transformation matrix containing a rotation from the given euler
/// rotation sequence and angles (in radians).
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_euler(order: EulerRot, a: {{ scalar_t }}, b: {{ scalar_t }}, c: {{ scalar_t }}) -> Self {
Self::from_euler_angles(order, a, b, c)
}
/// Extract Euler angles with the given Euler rotation order.
///
/// Note if the upper 3x3 matrix contain scales, shears, or other non-rotation transformations
/// then the resulting Euler angles will be ill-defined.
///
/// # Panics
///
/// Will panic if any column of the upper 3x3 rotation matrix is not normalized when
/// `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn to_euler(&self, order: EulerRot) -> ({{ scalar_t }}, {{ scalar_t }}, {{ scalar_t }}) {
glam_assert!(self.x_axis.xyz().is_normalized()
&& self.y_axis.xyz().is_normalized()
&& self.z_axis.xyz().is_normalized());
self.to_euler_angles(order)
}
/// Creates an affine transformation matrix containing a 3D rotation around the x axis of
/// `angle` (in radians).
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_rotation_x(angle: {{ scalar_t }}) -> Self {
let (sina, cosa) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::X,
{{ col_t }}::new(0.0, cosa, sina, 0.0),
{{ col_t }}::new(0.0, -sina, cosa, 0.0),
{{ col_t }}::W,
)
}
/// Creates an affine transformation matrix containing a 3D rotation around the y axis of
/// `angle` (in radians).
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_rotation_y(angle: {{ scalar_t }}) -> Self {
let (sina, cosa) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::new(cosa, 0.0, -sina, 0.0),
{{ col_t }}::Y,
{{ col_t }}::new(sina, 0.0, cosa, 0.0),
{{ col_t }}::W,
)
}
/// Creates an affine transformation matrix containing a 3D rotation around the z axis of
/// `angle` (in radians).
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
#[inline]
#[must_use]
pub fn from_rotation_z(angle: {{ scalar_t }}) -> Self {
let (sina, cosa) = math::sin_cos(angle);
Self::from_cols(
{{ col_t }}::new(cosa, sina, 0.0, 0.0),
{{ col_t }}::new(-sina, cosa, 0.0, 0.0),
{{ col_t }}::Z,
{{ col_t }}::W,
)
}
/// Creates an affine transformation matrix containing the given 3D non-uniform `scale`.
///
/// The resulting matrix can be used to transform 3D points and vectors. See
/// [`Self::transform_point3()`] and [`Self::transform_vector3()`].
///
/// # Panics
///
/// Will panic if all elements of `scale` are zero when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn from_scale(scale: {{ vec3_t }}) -> Self {
// Do not panic as long as any component is non-zero
glam_assert!(scale.cmpne({{ vec3_t }}::ZERO).any());
Self::from_cols(
{{ col_t }}::new(scale.x, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, scale.y, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, scale.z, 0.0),
{{ col_t }}::W,
)
}
{% endif %}
/// Creates a {{ nxn }} matrix from the first {{ size }} values in `slice`.
///
/// # Panics
///
/// Panics if `slice` is less than {{ size }} elements long.
#[inline]
#[must_use]
pub const fn from_cols_slice(slice: &[{{ scalar_t }}]) -> Self {
Self::new(
{% for i in range(end = size) %}
slice[{{ i }}],
{%- endfor %}
)
}
/// Writes the columns of `self` to the first {{ size }} elements in `slice`.
///
/// # Panics
///
/// Panics if `slice` is less than {{ size }} elements long.
#[inline]
pub fn write_cols_to_slice(self, slice: &mut [{{ scalar_t }}]) {
{% for i in range(end = dim) %}
{%- for j in range(end = dim) %}
slice[{{ i * dim + j }}] = self.{{ axes[i] }}.{{ components[j] }};
{%- endfor %}
{%- endfor %}
}
/// Returns the matrix column for the given `index`.
///
/// # Panics
///
/// Panics if `index` is greater than {{ dim - 1 }}.
#[inline]
#[must_use]
pub fn col(&self, index: usize) -> {{ col_t }} {
match index {
{% for axis in axes %}
{{ loop.index0 }} => self.{{ axis }},
{%- endfor %}
_ => panic!("index out of bounds"),
}
}
/// Returns a mutable reference to the matrix column for the given `index`.
///
/// # Panics
///
/// Panics if `index` is greater than {{ dim - 1 }}.
#[inline]
pub fn col_mut(&mut self, index: usize) -> &mut {{ col_t }} {
match index {
{% for axis in axes %}
{{ loop.index0 }} => &mut self.{{ axis }},
{%- endfor %}
_ => panic!("index out of bounds"),
}
}
/// Returns the matrix row for the given `index`.
///
/// # Panics
///
/// Panics if `index` is greater than {{ dim - 1 }}.
#[inline]
#[must_use]
pub fn row(&self, index: usize) -> {{ col_t }} {
match index {
{% for i in range(end=dim) %}
{{ i }} => {{ col_t }}::new(
{% for axis in axes %}
self.{{ axis }}.{{ components[i] }},
{%- endfor %}
),
{%- endfor %}
_ => panic!("index out of bounds"),
}
}
/// Returns `true` if, and only if, all elements are finite.
/// If any element is either `NaN`, positive or negative infinity, this will return `false`.
#[inline]
#[must_use]
pub fn is_finite(&self) -> bool {
{% for axis in axes %}
self.{{ axis }}.is_finite() {% if not loop.last %} && {% endif %}
{% endfor %}
}
/// Returns `true` if any elements are `NaN`.
#[inline]
#[must_use]
pub fn is_nan(&self) -> bool {
{% for axis in axes %}
self.{{ axis }}.is_nan() {% if not loop.last %} || {% endif %}
{% endfor %}
}
/// Returns the transpose of `self`.
#[inline]
#[must_use]
pub fn transpose(&self) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
Self(unsafe { _mm_shuffle_ps(self.0, self.0, 0b11_01_10_00) })
{% elif self_t == "Mat2" and is_wasm32 %}
Self(i32x4_shuffle::<0, 2, 5, 7>(self.0, self.0))
{% elif self_t == "Mat2" and is_coresimd %}
Self(simd_swizzle!(self.0, [0, 2, 1, 3]))
{% elif self_t == "Mat2" and is_neon %}
Self(unsafe {
vsetq_lane_f32(
vgetq_lane_f32(self.0, 2),
vsetq_lane_f32(vgetq_lane_f32(self.0, 1), self.0, 2),
1,
)
})
{% elif self_t == "Mat3A" and is_sse2 %}
unsafe {
let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
Self {
x_axis: Vec3A(_mm_shuffle_ps(tmp0, self.z_axis.0, 0b00_00_10_00)),
y_axis: Vec3A(_mm_shuffle_ps(tmp0, self.z_axis.0, 0b01_01_11_01)),
z_axis: Vec3A(_mm_shuffle_ps(tmp1, self.z_axis.0, 0b10_10_10_00)),
}
}
{% elif self_t == "Mat3A" and is_wasm32 %}
let tmp0 = i32x4_shuffle::<0, 1, 4, 5>(self.x_axis.0, self.y_axis.0);
let tmp1 = i32x4_shuffle::<2, 3, 6, 7>(self.x_axis.0, self.y_axis.0);
Self {
x_axis: Vec3A(i32x4_shuffle::<0, 2, 4, 4>(tmp0, self.z_axis.0)),
y_axis: Vec3A(i32x4_shuffle::<1, 3, 5, 5>(tmp0, self.z_axis.0)),
z_axis: Vec3A(i32x4_shuffle::<0, 2, 6, 6>(tmp1, self.z_axis.0)),
}
{% elif self_t == "Mat3A" and is_coresimd %}
let tmp0 = simd_swizzle!(
self.x_axis.0,
self.y_axis.0,
[0, 1, 4, 5]
);
let tmp1 = simd_swizzle!(
self.x_axis.0,
self.y_axis.0,
[2, 3, 6, 7]
);
Self {
x_axis: Vec3A(simd_swizzle!(
tmp0,
self.z_axis.0,
[0, 2, 4, 4]
)),
y_axis: Vec3A(simd_swizzle!(
tmp0,
self.z_axis.0,
[1, 3, 5, 5]
)),
z_axis: Vec3A(simd_swizzle!(
tmp1,
self.z_axis.0,
[0, 2, 6, 6]
)),
}
{% elif self_t == "Mat3A" and is_neon %}
let x = self.x_axis.0;
let y = self.y_axis.0;
let z = self.z_axis.0;
unsafe {
let tmp0 = vreinterpretq_f32_u64(vsetq_lane_u64(
vgetq_lane_u64(vreinterpretq_u64_f32(y), 0),
vreinterpretq_u64_f32(x),
1,
));
let tmp1 = vreinterpretq_f32_u64(vzip2q_u64(
vreinterpretq_u64_f32(x),
vreinterpretq_u64_f32(y),
));
Mat3A::from_cols(
Vec3A::from(vsetq_lane_f32(vgetq_lane_f32(z, 0), vuzp1q_f32(tmp0, z), 3)),
Vec3A::from(vuzp2q_f32(tmp0, vdupq_laneq_f32(z, 1))),
Vec3A::from(vsetq_lane_f32(vgetq_lane_f32(z, 2), vuzp1q_f32(tmp1, z), 2)),
)
}
{% elif self_t == "Mat4" and is_sse2 %}
unsafe {
// Based on https://github.com/microsoft/DirectXMath `XMMatrixTranspose`
let tmp0 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b01_00_01_00);
let tmp1 = _mm_shuffle_ps(self.x_axis.0, self.y_axis.0, 0b11_10_11_10);
let tmp2 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b01_00_01_00);
let tmp3 = _mm_shuffle_ps(self.z_axis.0, self.w_axis.0, 0b11_10_11_10);
Self {
x_axis: Vec4(_mm_shuffle_ps(tmp0, tmp2, 0b10_00_10_00)),
y_axis: Vec4(_mm_shuffle_ps(tmp0, tmp2, 0b11_01_11_01)),
z_axis: Vec4(_mm_shuffle_ps(tmp1, tmp3, 0b10_00_10_00)),
w_axis: Vec4(_mm_shuffle_ps(tmp1, tmp3, 0b11_01_11_01)),
}
}
{% elif self_t == "Mat4" and is_wasm32 %}
// Based on https://github.com/microsoft/DirectXMath `XMMatrixTranspose`
let tmp0 = i32x4_shuffle::<0, 1, 4, 5>(self.x_axis.0, self.y_axis.0);
let tmp1 = i32x4_shuffle::<2, 3, 6, 7>(self.x_axis.0, self.y_axis.0);
let tmp2 = i32x4_shuffle::<0, 1, 4, 5>(self.z_axis.0, self.w_axis.0);
let tmp3 = i32x4_shuffle::<2, 3, 6, 7>(self.z_axis.0, self.w_axis.0);
Self {
x_axis: Vec4(i32x4_shuffle::<0, 2, 4, 6>(tmp0, tmp2)),
y_axis: Vec4(i32x4_shuffle::<1, 3, 5, 7>(tmp0, tmp2)),
z_axis: Vec4(i32x4_shuffle::<0, 2, 4, 6>(tmp1, tmp3)),
w_axis: Vec4(i32x4_shuffle::<1, 3, 5, 7>(tmp1, tmp3)),
}
{% elif self_t == "Mat4" and is_coresimd %}
// Based on https://github.com/microsoft/DirectXMath `XMMatrixTranspose`
let tmp0 = simd_swizzle!(
self.x_axis.0,
self.y_axis.0,
[0, 1, 4, 5]
);
let tmp1 = simd_swizzle!(
self.x_axis.0,
self.y_axis.0,
[2, 3, 6, 7]
);
let tmp2 = simd_swizzle!(
self.z_axis.0,
self.w_axis.0,
[0, 1, 4, 5]
);
let tmp3 = simd_swizzle!(
self.z_axis.0,
self.w_axis.0,
[2, 3, 6, 7]
);
Self {
x_axis: Vec4(simd_swizzle!(tmp0, tmp2, [0, 2, 4, 6])),
y_axis: Vec4(simd_swizzle!(tmp0, tmp2, [1, 3, 5, 7])),
z_axis: Vec4(simd_swizzle!(tmp1, tmp3, [0, 2, 4, 6])),
w_axis: Vec4(simd_swizzle!(tmp1, tmp3, [1, 3, 5, 7])),
}
{% else %}
Self {
{% for i in range(end = dim) %}
{{ axes[i] }}: {{ col_t }}::new(
{% for j in range(end = dim) %}
self.{{ axes[j] }}.{{ components[i] }},
{% endfor %}
),
{%- endfor %}
}
{% endif %}
}
/// Returns the determinant of `self`.
{%- if dim < 4 %}
#[inline]
{%- endif %}
#[must_use]
pub fn determinant(&self) -> {{ scalar_t }} {
{% if self_t == "Mat2" and is_sse2 %}
unsafe {
let abcd = self.0;
let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
let prod = _mm_mul_ps(abcd, dcba);
let det = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
_mm_cvtss_f32(det)
}
{% elif self_t == "Mat2" and is_wasm32 %}
let abcd = self.0;
let dcba = i32x4_shuffle::<3, 2, 5, 4>(abcd, abcd);
let prod = f32x4_mul(abcd, dcba);
let det = f32x4_sub(prod, i32x4_shuffle::<1, 1, 5, 5>(prod, prod));
f32x4_extract_lane::<0>(det)
{% elif self_t == "Mat2" and is_coresimd %}
let abcd = self.0;
let dcba = simd_swizzle!(abcd, [3, 2, 1, 0]);
let prod = abcd * dcba;
let det = prod - simd_swizzle!(prod, [1, 1, 1, 1]);
det[0]
{% elif self_t == "Mat2" and is_neon %}
unsafe {
let abcd = self.0;
let badc = vrev64q_f32(abcd);
let dcba = vextq_f32(badc, badc, 2);
let prod = vmulq_f32(abcd, dcba);
let det = vsubq_f32(prod, vdupq_laneq_f32(prod, 1));
vgetq_lane_f32(det, 0)
}
{% elif self_t == "Mat4" and is_sse2 %}
unsafe {
// Based on https://github.com/g-truc/glm `glm_mat4_determinant_lowp`
let swp2a = _mm_shuffle_ps(self.z_axis.0, self.z_axis.0, 0b00_01_01_10);
let swp3a = _mm_shuffle_ps(self.w_axis.0, self.w_axis.0, 0b11_10_11_11);
let swp2b = _mm_shuffle_ps(self.z_axis.0, self.z_axis.0, 0b11_10_11_11);
let swp3b = _mm_shuffle_ps(self.w_axis.0, self.w_axis.0, 0b00_01_01_10);
let swp2c = _mm_shuffle_ps(self.z_axis.0, self.z_axis.0, 0b00_00_01_10);
let swp3c = _mm_shuffle_ps(self.w_axis.0, self.w_axis.0, 0b01_10_00_00);
let mula = _mm_mul_ps(swp2a, swp3a);
let mulb = _mm_mul_ps(swp2b, swp3b);
let mulc = _mm_mul_ps(swp2c, swp3c);
let sube = _mm_sub_ps(mula, mulb);
let subf = _mm_sub_ps(_mm_movehl_ps(mulc, mulc), mulc);
let subfaca = _mm_shuffle_ps(sube, sube, 0b10_01_00_00);
let swpfaca = _mm_shuffle_ps(self.y_axis.0, self.y_axis.0, 0b00_00_00_01);
let mulfaca = _mm_mul_ps(swpfaca, subfaca);
let subtmpb = _mm_shuffle_ps(sube, subf, 0b00_00_11_01);
let subfacb = _mm_shuffle_ps(subtmpb, subtmpb, 0b11_01_01_00);
let swpfacb = _mm_shuffle_ps(self.y_axis.0, self.y_axis.0, 0b01_01_10_10);
let mulfacb = _mm_mul_ps(swpfacb, subfacb);
let subres = _mm_sub_ps(mulfaca, mulfacb);
let subtmpc = _mm_shuffle_ps(sube, subf, 0b01_00_10_10);
let subfacc = _mm_shuffle_ps(subtmpc, subtmpc, 0b11_11_10_00);
let swpfacc = _mm_shuffle_ps(self.y_axis.0, self.y_axis.0, 0b10_11_11_11);
let mulfacc = _mm_mul_ps(swpfacc, subfacc);
let addres = _mm_add_ps(subres, mulfacc);
let detcof = _mm_mul_ps(addres, _mm_setr_ps(1.0, -1.0, 1.0, -1.0));
dot4(self.x_axis.0, detcof)
}
{% elif self_t == "Mat4" and is_wasm32 %}
// Based on https://github.com/g-truc/glm `glm_mat4_determinant`
let swp2a = i32x4_shuffle::<2, 1, 1, 0>(self.z_axis.0, self.z_axis.0);
let swp3a = i32x4_shuffle::<3, 3, 2, 3>(self.w_axis.0, self.w_axis.0);
let swp2b = i32x4_shuffle::<3, 3, 2, 3>(self.z_axis.0, self.z_axis.0);
let swp3b = i32x4_shuffle::<2, 1, 1, 0>(self.w_axis.0, self.w_axis.0);
let swp2c = i32x4_shuffle::<2, 1, 0, 0>(self.z_axis.0, self.z_axis.0);
let swp3c = i32x4_shuffle::<0, 0, 2, 1>(self.w_axis.0, self.w_axis.0);
let mula = f32x4_mul(swp2a, swp3a);
let mulb = f32x4_mul(swp2b, swp3b);
let mulc = f32x4_mul(swp2c, swp3c);
let sube = f32x4_sub(mula, mulb);
let subf = f32x4_sub(i32x4_shuffle::<6, 7, 2, 3>(mulc, mulc), mulc);
let subfaca = i32x4_shuffle::<0, 0, 1, 2>(sube, sube);
let swpfaca = i32x4_shuffle::<1, 0, 0, 0>(self.y_axis.0, self.y_axis.0);
let mulfaca = f32x4_mul(swpfaca, subfaca);
let subtmpb = i32x4_shuffle::<1, 3, 4, 4>(sube, subf);
let subfacb = i32x4_shuffle::<0, 1, 1, 3>(subtmpb, subtmpb);
let swpfacb = i32x4_shuffle::<2, 2, 1, 1>(self.y_axis.0, self.y_axis.0);
let mulfacb = f32x4_mul(swpfacb, subfacb);
let subres = f32x4_sub(mulfaca, mulfacb);
let subtmpc = i32x4_shuffle::<2, 2, 4, 5>(sube, subf);
let subfacc = i32x4_shuffle::<0, 2, 3, 3>(subtmpc, subtmpc);
let swpfacc = i32x4_shuffle::<3, 3, 3, 2>(self.y_axis.0, self.y_axis.0);
let mulfacc = f32x4_mul(swpfacc, subfacc);
let addres = f32x4_add(subres, mulfacc);
let detcof = f32x4_mul(addres, f32x4(1.0, -1.0, 1.0, -1.0));
dot4(self.x_axis.0, detcof)
{% elif self_t == "Mat4" and is_coresimd %}
// Based on https://github.com/g-truc/glm `glm_mat4_determinant`
let swp2a = simd_swizzle!(self.z_axis.0, [2, 1, 1, 0]);
let swp3a = simd_swizzle!(self.w_axis.0, [3, 3, 2, 3]);
let swp2b = simd_swizzle!(self.z_axis.0, [3, 3, 2, 3]);
let swp3b = simd_swizzle!(self.w_axis.0, [2, 1, 1, 0]);
let swp2c = simd_swizzle!(self.z_axis.0, [2, 1, 0, 0]);
let swp3c = simd_swizzle!(self.w_axis.0, [0, 0, 2, 1]);
let mula = swp2a * swp3a;
let mulb = swp2b * swp3b;
let mulc = swp2c * swp3c;
let sube = mula - mulb;
let subf = simd_swizzle!(mulc, [2, 3, 2, 3]) - mulc;
let subfaca = simd_swizzle!(sube, [0, 0, 1, 2]);
let swpfaca = simd_swizzle!(self.y_axis.0, [1, 0, 0, 0]);
let mulfaca = swpfaca * subfaca;
let subtmpb = simd_swizzle!(sube, subf, [1, 3, 4, 4]);
let subfacb = simd_swizzle!(subtmpb, [0, 1, 1, 3]);
let swpfacb = simd_swizzle!(self.y_axis.0, [2, 2, 1, 1]);
let mulfacb = swpfacb * subfacb;
let subres = mulfaca - mulfacb;
let subtmpc = simd_swizzle!(sube, subf, [2, 2, 4, 5]);
let subfacc = simd_swizzle!(subtmpc, [0, 2, 3, 3]);
let swpfacc = simd_swizzle!(self.y_axis.0, [3, 3, 3, 2]);
let mulfacc = swpfacc * subfacc;
let addres = subres + mulfacc;
let detcof = addres * f32x4::from_array([1.0, -1.0, 1.0, -1.0]);
dot4(self.x_axis.0, detcof)
{#
// neon implementation is slower than scalar
// {% elif self_t == "Mat4" and is_neon %}
// unsafe {
// let swizz2110 = |x| {
// let x = vuzp1q_f32(x, vdupq_laneq_f32(x, 1));
// vextq_f32(x, x, 1)
// };
// let swizz3323 = |x| {
// let xy = vgetq_lane_f32(x, 3);
// vsetq_lane_f32(xy, vsetq_lane_f32(xy, x, 0), 1)
// };
// let swizz2100 = |x| {
// let y = vuzp1q_f32(x, x);
// vuzp1q_f32(vextq_f32(x, y, 3), y)
// };
// let swizz0021 = |x| vtrn1q_f32(x, vzip1q_f32(x, x));
// // let swizz6723 = |x, y| {
// // vsetq_lane_f64(vgetq_lane_f64(y, 1), 0)
// // };
// let swizz2323 = |x| vreinterpretq_f32_f64(vdupq_laneq_f64(vreinterpretq_f64_f32(x), 1));
// let swizz0012 = |x| vzip1q_f32(x, vuzp1q_f32(x, x));
// let swizz1000 = |x| vsetq_lane_f32(vgetq_lane_f32(x, 1), vdupq_laneq_f32(x, 0), 0);
// let swizz1344 = |x, y| vuzp2q_f32(x, vdupq_laneq_f32(y, 0));
// let swizz0113 = |x| vsetq_lane_f32(vgetq_lane_f32(x, 1), x, 2);
// let swizz2211 = |x| {
// let x = vsetq_lane_f32(vgetq_lane_f32(x, 1), x, 3);
// vzip2q_f32(x, x)
// };
// let swizz2245 = |x, y| vextq_f32(vtrn1q_f32(x, x), y, 1);
// let swizz0233 = |x| vuzp1q_f32(x, vdupq_laneq_f32(x, 3));
// let swizz3332 = |x| vsetq_lane_f32(vgetq_lane_f32(x, 2), vdupq_laneq_f32(x, 3), 3);
// // Based on https://github.com/g-truc/glm `glm_mat4_determinant`
// let swp2a = swizz2110(self.z_axis.0);
// let swp3a = swizz3323(self.w_axis.0);
// let swp2b = swizz3323(self.z_axis.0);
// let swp3b = swizz2110(self.w_axis.0);
// let swp2c = swizz2100(self.z_axis.0);
// let swp3c = swizz0021(self.w_axis.0);
// let mula = vmulq_f32(swp2a, swp3a);
// let mulb = vmulq_f32(swp2b, swp3b);
// let mulc = vmulq_f32(swp2c, swp3c);
// let sube = vsubq_f32(mula, mulb);
// let subf = vsubq_f32(swizz2323(mulc), mulc);
// let subfaca = swizz0012(sube);
// let swpfaca = swizz1000(self.y_axis.0);
// let mulfaca = vmulq_f32(swpfaca, subfaca);
// let subtmpb = swizz1344(sube, subf);
// let subfacb = swizz0113(subtmpb);
// let swpfacb = swizz2211(self.y_axis.0);
// let mulfacb = vmulq_f32(swpfacb, subfacb);
// let subres = vsubq_f32(mulfaca, mulfacb);
// let subtmpc = swizz2245(sube, subf);
// let subfacc = swizz0233(subtmpc);
// let swpfacc = swizz3332(self.y_axis.0);
// let mulfacc = vmulq_f32(swpfacc, subfacc);
// let addres = vaddq_f32(subres, mulfacc);
// const COF: float32x4_t = Vec4::new(1.0, -1.0, 1.0, -1.0).0;
// let detcof = vmulq_f32(addres, COF);
// dot4(self.x_axis.0, detcof)
// }
#}
{% elif dim == 2 %}
self.x_axis.x * self.y_axis.y - self.x_axis.y * self.y_axis.x
{% elif dim == 3 %}
self.z_axis.dot(self.x_axis.cross(self.y_axis))
{% elif dim == 4 %}
let (m00, m01, m02, m03) = self.x_axis.into();
let (m10, m11, m12, m13) = self.y_axis.into();
let (m20, m21, m22, m23) = self.z_axis.into();
let (m30, m31, m32, m33) = self.w_axis.into();
let a2323 = m22 * m33 - m23 * m32;
let a1323 = m21 * m33 - m23 * m31;
let a1223 = m21 * m32 - m22 * m31;
let a0323 = m20 * m33 - m23 * m30;
let a0223 = m20 * m32 - m22 * m30;
let a0123 = m20 * m31 - m21 * m30;
m00 * (m11 * a2323 - m12 * a1323 + m13 * a1223)
- m01 * (m10 * a2323 - m12 * a0323 + m13 * a0223)
+ m02 * (m10 * a1323 - m11 * a0323 + m13 * a0123)
- m03 * (m10 * a1223 - m11 * a0223 + m12 * a0123)
{% endif %}
}
/// Returns the inverse of `self`.
///
/// If the matrix is not invertible the returned matrix will be invalid.
///
/// # Panics
///
/// Will panic if the determinant of `self` is zero when `glam_assert` is enabled.
{%- if dim < 4 %}
#[inline]
{%- endif %}
#[must_use]
pub fn inverse(&self) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
unsafe {
const SIGN: __m128 = crate::sse2::m128_from_f32x4([1.0, -1.0, -1.0, 1.0]);
let abcd = self.0;
let dcba = _mm_shuffle_ps(abcd, abcd, 0b00_01_10_11);
let prod = _mm_mul_ps(abcd, dcba);
let sub = _mm_sub_ps(prod, _mm_shuffle_ps(prod, prod, 0b01_01_01_01));
let det = _mm_shuffle_ps(sub, sub, 0b00_00_00_00);
let tmp = _mm_div_ps(SIGN, det);
glam_assert!(Mat2(tmp).is_finite());
let dbca = _mm_shuffle_ps(abcd, abcd, 0b00_10_01_11);
Self(_mm_mul_ps(dbca, tmp))
}
{% elif self_t == "Mat2" and is_wasm32 %}
const SIGN: v128 = crate::wasm32::v128_from_f32x4([1.0, -1.0, -1.0, 1.0]);
let abcd = self.0;
let dcba = i32x4_shuffle::<3, 2, 5, 4>(abcd, abcd);
let prod = f32x4_mul(abcd, dcba);
let sub = f32x4_sub(prod, i32x4_shuffle::<1, 1, 5, 5>(prod, prod));
let det = i32x4_shuffle::<0, 0, 4, 4>(sub, sub);
let tmp = f32x4_div(SIGN, det);
glam_assert!(Mat2(tmp).is_finite());
let dbca = i32x4_shuffle::<3, 1, 6, 4>(abcd, abcd);
Self(f32x4_mul(dbca, tmp))
{% elif self_t == "Mat2" and is_coresimd %}
const SIGN: f32x4 = f32x4::from_array([1.0, -1.0, -1.0, 1.0]);
let abcd = self.0;
let dcba = simd_swizzle!(abcd, [3, 2, 1, 0]);
let prod = abcd * dcba;
let sub = prod - simd_swizzle!(prod, [1, 1, 1, 1]);
let det = simd_swizzle!(sub, [0, 0, 0, 0]);
let tmp = SIGN / det;
glam_assert!(Mat2(tmp).is_finite());
let dbca = simd_swizzle!(abcd, [3, 1, 2, 0]);
Self(dbca.mul(tmp))
{% elif self_t == "Mat2" and is_neon %}
unsafe {
const SIGN: float32x4_t = crate::neon::f32x4_from_array([1.0, -1.0, -1.0, 1.0]);
let abcd = self.0;
let badc = vrev64q_f32(abcd);
let dcba = vextq_f32(badc, badc, 2);
let prod = vmulq_f32(abcd, dcba);
let sub = vsubq_f32(prod, vdupq_laneq_f32(prod, 1));
let det = vdupq_laneq_f32(sub, 0);
let tmp = vdivq_f32(SIGN, det);
glam_assert!(Mat2(tmp).is_finite());
//let dbca = simd_swizzle!(abcd, [3, 1, 2, 0]);
let dbca = vsetq_lane_f32(
vgetq_lane_f32(abcd, 0),
vsetq_lane_f32(vgetq_lane_f32(abcd, 3), abcd, 0),
3,
);
Self(vmulq_f32(dbca, tmp))
}
{% elif self_t == "Mat4" and is_sse2 %}
{{ sse2::impl_mat4_inverse() }}
{% elif self_t == "Mat4" and is_wasm32 %}
{{ wasm32::impl_mat4_inverse() }}
{% elif self_t == "Mat4" and is_coresimd %}
{{ coresimd::impl_mat4_inverse() }}
{% elif self_t == "Mat4" and is_neon %}
{{ neon::impl_mat4_inverse() }}
{% elif dim == 2 %}
let inv_det = {
let det = self.determinant();
glam_assert!(det != 0.0);
det.recip()
};
Self::new(
self.y_axis.y * inv_det,
self.x_axis.y * -inv_det,
self.y_axis.x * -inv_det,
self.x_axis.x * inv_det,
)
{% elif dim == 3 %}
let tmp0 = self.y_axis.cross(self.z_axis);
let tmp1 = self.z_axis.cross(self.x_axis);
let tmp2 = self.x_axis.cross(self.y_axis);
let det = self.z_axis.dot(tmp2);
glam_assert!(det != 0.0);
let inv_det = {{ col_t }}::splat(det.recip());
Self::from_cols(tmp0.mul(inv_det), tmp1.mul(inv_det), tmp2.mul(inv_det)).transpose()
{% elif dim == 4 %}
let (m00, m01, m02, m03) = self.x_axis.into();
let (m10, m11, m12, m13) = self.y_axis.into();
let (m20, m21, m22, m23) = self.z_axis.into();
let (m30, m31, m32, m33) = self.w_axis.into();
let coef00 = m22 * m33 - m32 * m23;
let coef02 = m12 * m33 - m32 * m13;
let coef03 = m12 * m23 - m22 * m13;
let coef04 = m21 * m33 - m31 * m23;
let coef06 = m11 * m33 - m31 * m13;
let coef07 = m11 * m23 - m21 * m13;
let coef08 = m21 * m32 - m31 * m22;
let coef10 = m11 * m32 - m31 * m12;
let coef11 = m11 * m22 - m21 * m12;
let coef12 = m20 * m33 - m30 * m23;
let coef14 = m10 * m33 - m30 * m13;
let coef15 = m10 * m23 - m20 * m13;
let coef16 = m20 * m32 - m30 * m22;
let coef18 = m10 * m32 - m30 * m12;
let coef19 = m10 * m22 - m20 * m12;
let coef20 = m20 * m31 - m30 * m21;
let coef22 = m10 * m31 - m30 * m11;
let coef23 = m10 * m21 - m20 * m11;
let fac0 = {{ col_t }}::new(coef00, coef00, coef02, coef03);
let fac1 = {{ col_t }}::new(coef04, coef04, coef06, coef07);
let fac2 = {{ col_t }}::new(coef08, coef08, coef10, coef11);
let fac3 = {{ col_t }}::new(coef12, coef12, coef14, coef15);
let fac4 = {{ col_t }}::new(coef16, coef16, coef18, coef19);
let fac5 = {{ col_t }}::new(coef20, coef20, coef22, coef23);
let vec0 = {{ col_t }}::new(m10, m00, m00, m00);
let vec1 = {{ col_t }}::new(m11, m01, m01, m01);
let vec2 = {{ col_t }}::new(m12, m02, m02, m02);
let vec3 = {{ col_t }}::new(m13, m03, m03, m03);
let inv0 = vec1.mul(fac0).sub(vec2.mul(fac1)).add(vec3.mul(fac2));
let inv1 = vec0.mul(fac0).sub(vec2.mul(fac3)).add(vec3.mul(fac4));
let inv2 = vec0.mul(fac1).sub(vec1.mul(fac3)).add(vec3.mul(fac5));
let inv3 = vec0.mul(fac2).sub(vec1.mul(fac4)).add(vec2.mul(fac5));
let sign_a = {{ col_t }}::new(1.0, -1.0, 1.0, -1.0);
let sign_b = {{ col_t }}::new(-1.0, 1.0, -1.0, 1.0);
let inverse = Self::from_cols(
inv0.mul(sign_a),
inv1.mul(sign_b),
inv2.mul(sign_a),
inv3.mul(sign_b),
);
let col0 = {{ col_t }}::new(
inverse.x_axis.x,
inverse.y_axis.x,
inverse.z_axis.x,
inverse.w_axis.x,
);
let dot0 = self.x_axis.mul(col0);
let dot1 = dot0.x + dot0.y + dot0.z + dot0.w;
glam_assert!(dot1 != 0.0);
let rcp_det = dot1.recip();
inverse.mul(rcp_det)
{% endif %}
}
{% if dim == 3 %}
/// Transforms the given 2D vector as a point.
///
/// This is the equivalent of multiplying `rhs` as a 3D vector where `z` is `1`.
///
/// This method assumes that `self` contains a valid affine transform.
///
/// # Panics
///
/// Will panic if the 2nd row of `self` is not `(0, 0, 1)` when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn transform_point2(&self, rhs: {{ vec2_t }}) -> {{ vec2_t }} {
glam_assert!(self.row(2).abs_diff_eq({{ col_t }}::Z, 1e-6));
{{ mat2_t }}::from_cols(self.x_axis.xy(), self.y_axis.xy()) * rhs + self.z_axis.xy()
}
/// Rotates the given 2D vector.
///
/// This is the equivalent of multiplying `rhs` as a 3D vector where `z` is `0`.
///
/// This method assumes that `self` contains a valid affine transform.
///
/// # Panics
///
/// Will panic if the 2nd row of `self` is not `(0, 0, 1)` when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn transform_vector2(&self, rhs: {{ vec2_t }}) -> {{ vec2_t }} {
glam_assert!(self.row(2).abs_diff_eq({{ col_t }}::Z, 1e-6));
{{ mat2_t }}::from_cols(self.x_axis.xy(), self.y_axis.xy()) * rhs
}
/// Creates a left-handed view matrix using a facing direction and an up direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
///
/// # Panics
///
/// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn look_to_lh(dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
Self::look_to_rh(-dir, up)
}
/// Creates a right-handed view matrix using a facing direction and an up direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
///
/// # Panics
///
/// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn look_to_rh(dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
glam_assert!(dir.is_normalized());
glam_assert!(up.is_normalized());
let f = dir;
let s = f.cross(up).normalize();
let u = s.cross(f);
Self::from_cols(
{{ col_t }}::new(s.x, u.x, -f.x),
{{ col_t }}::new(s.y, u.y, -f.y),
{{ col_t }}::new(s.z, u.z, -f.z),
)
}
/// Creates a left-handed view matrix using a camera position, a focal point and an up
/// direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
///
/// # Panics
///
/// Will panic if `up` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn look_at_lh(eye: {{ vec3_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
Self::look_to_lh(center.sub(eye).normalize(), up)
}
/// Creates a right-handed view matrix using a camera position, a focal point and an up
/// direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
///
/// # Panics
///
/// Will panic if `up` is not normalized when `glam_assert` is enabled.
#[inline]
pub fn look_at_rh(eye: {{ vec3_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
Self::look_to_rh(center.sub(eye).normalize(), up)
}
{% elif dim == 4 %}
/// Creates a left-handed view matrix using a camera position, a facing direction and an up
/// direction
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
///
/// # Panics
///
/// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn look_to_lh(eye: {{ vec3_t }}, dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
Self::look_to_rh(eye, -dir, up)
}
/// Creates a right-handed view matrix using a camera position, a facing direction, and an up
/// direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
///
/// # Panics
///
/// Will panic if `dir` or `up` are not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn look_to_rh(eye: {{ vec3_t }}, dir: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
glam_assert!(dir.is_normalized());
glam_assert!(up.is_normalized());
let f = dir;
let s = f.cross(up).normalize();
let u = s.cross(f);
Self::from_cols(
{{ col_t }}::new(s.x, u.x, -f.x, 0.0),
{{ col_t }}::new(s.y, u.y, -f.y, 0.0),
{{ col_t }}::new(s.z, u.z, -f.z, 0.0),
{{ col_t }}::new(-eye.dot(s), -eye.dot(u), eye.dot(f), 1.0),
)
}
/// Creates a left-handed view matrix using a camera position, a focal points and an up
/// direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=forward`.
///
/// # Panics
///
/// Will panic if `up` is not normalized when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn look_at_lh(eye: {{ vec3_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
Self::look_to_lh(eye, center.sub(eye).normalize(), up)
}
/// Creates a right-handed view matrix using a camera position, a focal point, and an up
/// direction.
///
/// For a view coordinate system with `+X=right`, `+Y=up` and `+Z=back`.
///
/// # Panics
///
/// Will panic if `up` is not normalized when `glam_assert` is enabled.
#[inline]
pub fn look_at_rh(eye: {{ vec3_t }}, center: {{ vec3_t }}, up: {{ vec3_t }}) -> Self {
Self::look_to_rh(eye, center.sub(eye).normalize(), up)
}
/// Creates a right-handed perspective projection matrix with `[-1,1]` depth range.
///
/// Useful to map the standard right-handed coordinate system into what OpenGL expects.
///
/// This is the same as the OpenGL `gluPerspective` function.
/// See <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml>
#[inline]
#[must_use]
pub fn perspective_rh_gl(
fov_y_radians: {{ scalar_t }},
aspect_ratio: {{ scalar_t }},
z_near: {{ scalar_t }},
z_far: {{ scalar_t }},
) -> Self {
let inv_length = 1.0 / (z_near - z_far);
let f = 1.0 / math::tan(0.5 * fov_y_radians);
let a = f / aspect_ratio;
let b = (z_near + z_far) * inv_length;
let c = (2.0 * z_near * z_far) * inv_length;
Self::from_cols(
{{ col_t }}::new(a, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, f, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, b, -1.0),
{{ col_t }}::new(0.0, 0.0, c, 0.0),
)
}
/// Creates a left-handed perspective projection matrix with `[0,1]` depth range.
///
/// Useful to map the standard left-handed coordinate system into what WebGPU/Metal/Direct3D expect.
///
/// # Panics
///
/// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
/// enabled.
#[inline]
#[must_use]
pub fn perspective_lh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}) -> Self {
glam_assert!(z_near > 0.0 && z_far > 0.0);
let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
let r = z_far / (z_far - z_near);
Self::from_cols(
{{ col_t }}::new(w, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, h, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, r, 1.0),
{{ col_t }}::new(0.0, 0.0, -r * z_near, 0.0),
)
}
/// Creates a right-handed perspective projection matrix with `[0,1]` depth range.
///
/// Useful to map the standard right-handed coordinate system into what WebGPU/Metal/Direct3D expect.
///
/// # Panics
///
/// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
/// enabled.
#[inline]
#[must_use]
pub fn perspective_rh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}, z_far: {{ scalar_t }}) -> Self {
glam_assert!(z_near > 0.0 && z_far > 0.0);
let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
let r = z_far / (z_near - z_far);
Self::from_cols(
{{ col_t }}::new(w, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, h, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, r, -1.0),
{{ col_t }}::new(0.0, 0.0, r * z_near, 0.0),
)
}
/// Creates an infinite left-handed perspective projection matrix with `[0,1]` depth range.
///
/// Like `perspective_lh`, but with an infinite value for `z_far`.
/// The result is that points near `z_near` are mapped to depth `0`, and as they move towards infinity the depth approaches `1`.
///
/// # Panics
///
/// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
/// enabled.
#[inline]
#[must_use]
pub fn perspective_infinite_lh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}) -> Self {
glam_assert!(z_near > 0.0);
let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
Self::from_cols(
{{ col_t }}::new(w, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, h, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, 1.0, 1.0),
{{ col_t }}::new(0.0, 0.0, -z_near, 0.0),
)
}
/// Creates an infinite reverse left-handed perspective projection matrix with `[0,1]` depth range.
///
/// Similar to `perspective_infinite_lh`, but maps `Z = z_near` to a depth of `1` and `Z = infinity` to a depth of `0`.
///
/// # Panics
///
/// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn perspective_infinite_reverse_lh(
fov_y_radians: {{ scalar_t }},
aspect_ratio: {{ scalar_t }},
z_near: {{ scalar_t }},
) -> Self {
glam_assert!(z_near > 0.0);
let (sin_fov, cos_fov) = math::sin_cos(0.5 * fov_y_radians);
let h = cos_fov / sin_fov;
let w = h / aspect_ratio;
Self::from_cols(
{{ col_t }}::new(w, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, h, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, 0.0, 1.0),
{{ col_t }}::new(0.0, 0.0, z_near, 0.0),
)
}
/// Creates an infinite right-handed perspective projection matrix with `[0,1]` depth range.
///
/// Like `perspective_rh`, but with an infinite value for `z_far`.
/// The result is that points near `z_near` are mapped to depth `0`, and as they move towards infinity the depth approaches `1`.
///
/// # Panics
///
/// Will panic if `z_near` or `z_far` are less than or equal to zero when `glam_assert` is
/// enabled.
#[inline]
#[must_use]
pub fn perspective_infinite_rh(fov_y_radians: {{ scalar_t }}, aspect_ratio: {{ scalar_t }}, z_near: {{ scalar_t }}) -> Self {
glam_assert!(z_near > 0.0);
let f = 1.0 / math::tan(0.5 * fov_y_radians);
Self::from_cols(
{{ col_t }}::new(f / aspect_ratio, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, f, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, -1.0, -1.0),
{{ col_t }}::new(0.0, 0.0, -z_near, 0.0),
)
}
/// Creates an infinite reverse right-handed perspective projection matrix with `[0,1]` depth range.
///
/// Similar to `perspective_infinite_rh`, but maps `Z = z_near` to a depth of `1` and `Z = infinity` to a depth of `0`.
///
/// # Panics
///
/// Will panic if `z_near` is less than or equal to zero when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn perspective_infinite_reverse_rh(
fov_y_radians: {{ scalar_t }},
aspect_ratio: {{ scalar_t }},
z_near: {{ scalar_t }},
) -> Self {
glam_assert!(z_near > 0.0);
let f = 1.0 / math::tan(0.5 * fov_y_radians);
Self::from_cols(
{{ col_t }}::new(f / aspect_ratio, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, f, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, 0.0, -1.0),
{{ col_t }}::new(0.0, 0.0, z_near, 0.0),
)
}
/// Creates a right-handed orthographic projection matrix with `[-1,1]` depth
/// range. This is the same as the OpenGL `glOrtho` function in OpenGL.
/// See
/// <https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml>
///
/// Useful to map a right-handed coordinate system to the normalized device coordinates that OpenGL expects.
#[inline]
#[must_use]
pub fn orthographic_rh_gl(
left: {{ scalar_t }},
right: {{ scalar_t }},
bottom: {{ scalar_t }},
top: {{ scalar_t }},
near: {{ scalar_t }},
far: {{ scalar_t }},
) -> Self {
let a = 2.0 / (right - left);
let b = 2.0 / (top - bottom);
let c = -2.0 / (far - near);
let tx = -(right + left) / (right - left);
let ty = -(top + bottom) / (top - bottom);
let tz = -(far + near) / (far - near);
Self::from_cols(
{{ col_t }}::new(a, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, b, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, c, 0.0),
{{ col_t }}::new(tx, ty, tz, 1.0),
)
}
/// Creates a left-handed orthographic projection matrix with `[0,1]` depth range.
///
/// Useful to map a left-handed coordinate system to the normalized device coordinates that WebGPU/Direct3D/Metal expect.
#[inline]
#[must_use]
pub fn orthographic_lh(
left: {{ scalar_t }},
right: {{ scalar_t }},
bottom: {{ scalar_t }},
top: {{ scalar_t }},
near: {{ scalar_t }},
far: {{ scalar_t }},
) -> Self {
let rcp_width = 1.0 / (right - left);
let rcp_height = 1.0 / (top - bottom);
let r = 1.0 / (far - near);
Self::from_cols(
{{ col_t }}::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, r, 0.0),
{{ col_t }}::new(
-(left + right) * rcp_width,
-(top + bottom) * rcp_height,
-r * near,
1.0,
),
)
}
/// Creates a right-handed orthographic projection matrix with `[0,1]` depth range.
///
/// Useful to map a right-handed coordinate system to the normalized device coordinates that WebGPU/Direct3D/Metal expect.
#[inline]
#[must_use]
pub fn orthographic_rh(
left: {{ scalar_t }},
right: {{ scalar_t }},
bottom: {{ scalar_t }},
top: {{ scalar_t }},
near: {{ scalar_t }},
far: {{ scalar_t }},
) -> Self {
let rcp_width = 1.0 / (right - left);
let rcp_height = 1.0 / (top - bottom);
let r = 1.0 / (near - far);
Self::from_cols(
{{ col_t }}::new(rcp_width + rcp_width, 0.0, 0.0, 0.0),
{{ col_t }}::new(0.0, rcp_height + rcp_height, 0.0, 0.0),
{{ col_t }}::new(0.0, 0.0, r, 0.0),
{{ col_t }}::new(
-(left + right) * rcp_width,
-(top + bottom) * rcp_height,
r * near,
1.0,
),
)
}
/// Transforms the given 3D vector as a point, applying perspective correction.
///
/// This is the equivalent of multiplying the 3D vector as a 4D vector where `w` is `1.0`.
/// The perspective divide is performed meaning the resulting 3D vector is divided by `w`.
///
/// This method assumes that `self` contains a projective transform.
#[inline]
#[must_use]
pub fn project_point3(&self, rhs: {{ vec3_t }}) -> {{ vec3_t }} {
let mut res = self.x_axis.mul(rhs.x);
res = self.y_axis.mul(rhs.y).add(res);
res = self.z_axis.mul(rhs.z).add(res);
res = self.w_axis.add(res);
res = res.div(res.w);
res.xyz()
}
/// Transforms the given 3D vector as a point.
///
/// This is the equivalent of multiplying the 3D vector as a 4D vector where `w` is
/// `1.0`.
///
/// This method assumes that `self` contains a valid affine transform. It does not perform
/// a perspective divide, if `self` contains a perspective transform, or if you are unsure,
/// the [`Self::project_point3()`] method should be used instead.
///
/// # Panics
///
/// Will panic if the 3rd row of `self` is not `(0, 0, 0, 1)` when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn transform_point3(&self, rhs: {{ vec3_t }}) -> {{ vec3_t }} {
glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::W, 1e-6));
let mut res = self.x_axis.mul(rhs.x);
res = self.y_axis.mul(rhs.y).add(res);
res = self.z_axis.mul(rhs.z).add(res);
res = self.w_axis.add(res);
res.xyz()
}
/// Transforms the give 3D vector as a direction.
///
/// This is the equivalent of multiplying the 3D vector as a 4D vector where `w` is
/// `0.0`.
///
/// This method assumes that `self` contains a valid affine transform.
///
/// # Panics
///
/// Will panic if the 3rd row of `self` is not `(0, 0, 0, 1)` when `glam_assert` is enabled.
#[inline]
#[must_use]
pub fn transform_vector3(&self, rhs: {{ vec3_t }}) -> {{ vec3_t }} {
glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::W, 1e-6));
let mut res = self.x_axis.mul(rhs.x);
res = self.y_axis.mul(rhs.y).add(res);
res = self.z_axis.mul(rhs.z).add(res);
res.xyz()
}
{% endif %}
{% if self_t == "Mat4" %}
/// Transforms the given [`Vec3A`] as a 3D point, applying perspective correction.
///
/// This is the equivalent of multiplying the [`Vec3A`] as a 4D vector where `w` is `1.0`.
/// The perspective divide is performed meaning the resulting 3D vector is divided by `w`.
///
/// This method assumes that `self` contains a projective transform.
#[inline]
#[must_use]
pub fn project_point3a(&self, rhs: Vec3A) -> Vec3A {
{% if is_scalar %}
self.project_point3(rhs.into()).into()
{% else %}
let mut res = self.x_axis.mul(rhs.xxxx());
res = self.y_axis.mul(rhs.yyyy()).add(res);
res = self.z_axis.mul(rhs.zzzz()).add(res);
res = self.w_axis.add(res);
res = res.div(res.wwww());
Vec3A::from_vec4(res)
{% endif %}
}
/// Transforms the given [`Vec3A`] as 3D point.
///
/// This is the equivalent of multiplying the [`Vec3A`] as a 4D vector where `w` is `1.0`.
#[inline]
#[must_use]
pub fn transform_point3a(&self, rhs: Vec3A) -> Vec3A {
{% if is_scalar %}
self.transform_point3(rhs.into()).into()
{% else %}
glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::W, 1e-6));
let mut res = self.x_axis.mul(rhs.xxxx());
res = self.y_axis.mul(rhs.yyyy()).add(res);
res = self.z_axis.mul(rhs.zzzz()).add(res);
res = self.w_axis.add(res);
Vec3A::from_vec4(res)
{% endif %}
}
/// Transforms the give [`Vec3A`] as 3D vector.
///
/// This is the equivalent of multiplying the [`Vec3A`] as a 4D vector where `w` is `0.0`.
#[inline]
#[must_use]
pub fn transform_vector3a(&self, rhs: Vec3A) -> Vec3A {
{% if is_scalar %}
self.transform_vector3(rhs.into()).into()
{% else %}
glam_assert!(self.row(3).abs_diff_eq({{ vec4_t }}::W, 1e-6));
let mut res = self.x_axis.mul(rhs.xxxx());
res = self.y_axis.mul(rhs.yyyy()).add(res);
res = self.z_axis.mul(rhs.zzzz()).add(res);
Vec3A::from_vec4(res)
{% endif %}
}
{% endif %}
/// Transforms a {{ dim }}D vector.
#[inline]
#[must_use]
pub fn mul_vec{{ dim }}(&self, rhs: {{ vecn_t }}) -> {{ vecn_t }} {
{% if self_t == "Mat2" and is_sse2 %}
unsafe {
use core::mem::MaybeUninit;
use crate::Align16;
let abcd = self.0;
let xxyy = _mm_set_ps(rhs.y, rhs.y, rhs.x, rhs.x);
let axbxcydy = _mm_mul_ps(abcd, xxyy);
let cydyaxbx = _mm_shuffle_ps(axbxcydy, axbxcydy, 0b01_00_11_10);
let result = _mm_add_ps(axbxcydy, cydyaxbx);
let mut out: MaybeUninit<Align16<Vec2>> = MaybeUninit::uninit();
_mm_store_ps(out.as_mut_ptr().cast(), result);
out.assume_init().0
}
{% elif self_t == "Mat2" and is_wasm32 %}
use core::mem::MaybeUninit;
let abcd = self.0;
let xxyy = f32x4(rhs.x, rhs.x, rhs.y, rhs.y);
let axbxcydy = f32x4_mul(abcd, xxyy);
let cydyaxbx = i32x4_shuffle::<2, 3, 4, 5>(axbxcydy, axbxcydy);
let result = f32x4_add(axbxcydy, cydyaxbx);
let mut out: MaybeUninit<v128> = MaybeUninit::uninit();
unsafe {
v128_store(out.as_mut_ptr(), result);
*(&out.assume_init() as *const v128 as *const Vec2)
}
{% elif self_t == "Mat2" and is_coresimd %}
let abcd = self.0;
let xxyy = f32x4::from_array([rhs.x, rhs.x, rhs.y, rhs.y]);
let axbxcydy = abcd.mul(xxyy);
let cydyaxbx = simd_swizzle!(axbxcydy, [2, 3, 0, 1]);
let result = axbxcydy.add(cydyaxbx);
unsafe { *(&result as *const f32x4 as *const Vec2) }
{% elif self_t == "Mat2" and is_neon %}
unsafe {
let abcd = self.0;
let xxyy = vld1q_f32([rhs.x, rhs.x, rhs.y, rhs.y].as_ptr());
let axbxcydy = vmulq_f32(abcd, xxyy);
// let cydyaxbx = simd_swizzle!(axbxcydy, [2, 3, 0, 1]);
let cydyaxbx = vextq_f32(axbxcydy, axbxcydy, 2);
let result = vaddq_f32(axbxcydy, cydyaxbx);
*(&result as *const float32x4_t as *const Vec2)
}
{% elif dim == 2 %}
#[allow(clippy::suspicious_operation_groupings)]
{{ col_t }}::new(
(self.x_axis.x * rhs.x) + (self.y_axis.x * rhs.y),
(self.x_axis.y * rhs.x) + (self.y_axis.y * rhs.y),
)
{% elif self_t == "Mat3A" %}
{# use the Vec3A implementation #}
self.mul_vec3a(rhs.into()).into()
{% elif dim == 3 %}
let mut res = self.x_axis.mul(rhs.x);
res = res.add(self.y_axis.mul(rhs.y));
res = res.add(self.z_axis.mul(rhs.z));
res
{% elif dim == 4 %}
{% if is_scalar %}
let mut res = self.x_axis.mul(rhs.x);
res = res.add(self.y_axis.mul(rhs.y));
res = res.add(self.z_axis.mul(rhs.z));
res = res.add(self.w_axis.mul(rhs.w));
res
{% else %}
{# use swizzles if simd #}
let mut res = self.x_axis.mul(rhs.xxxx());
res = res.add(self.y_axis.mul(rhs.yyyy()));
res = res.add(self.z_axis.mul(rhs.zzzz()));
res = res.add(self.w_axis.mul(rhs.wwww()));
res
{% endif %}
{% endif %}
}
{% if self_t == "Mat3" %}
/// Transforms a [`Vec3A`].
#[inline]
#[must_use]
pub fn mul_vec3a(&self, rhs: Vec3A) -> Vec3A {
self.mul_vec3(rhs.into()).into()
}
{% elif self_t == "Mat3A" %}
/// Transforms a [`Vec3A`].
#[inline]
#[must_use]
pub fn mul_vec3a(&self, rhs: Vec3A) -> Vec3A {
let mut res = self.x_axis.mul(rhs.xxx());
res = res.add(self.y_axis.mul(rhs.yyy()));
res = res.add(self.z_axis.mul(rhs.zzz()));
res
}
{% endif %}
/// Multiplies two {{ nxn }} matrices.
#[inline]
#[must_use]
pub fn mul_mat{{ dim }}(&self, rhs: &Self) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
unsafe {
let abcd = self.0;
let rhs = rhs.0;
let xxyy0 = _mm_shuffle_ps(rhs, rhs, 0b01_01_00_00);
let xxyy1 = _mm_shuffle_ps(rhs, rhs, 0b11_11_10_10);
let axbxcydy0 = _mm_mul_ps(abcd, xxyy0);
let axbxcydy1 = _mm_mul_ps(abcd, xxyy1);
let cydyaxbx0 = _mm_shuffle_ps(axbxcydy0, axbxcydy0, 0b01_00_11_10);
let cydyaxbx1 = _mm_shuffle_ps(axbxcydy1, axbxcydy1, 0b01_00_11_10);
let result0 = _mm_add_ps(axbxcydy0, cydyaxbx0);
let result1 = _mm_add_ps(axbxcydy1, cydyaxbx1);
Self(_mm_shuffle_ps(result0, result1, 0b01_00_01_00))
}
{% elif self_t == "Mat2" and is_wasm32 %}
let abcd = self.0;
let rhs = rhs.0;
let xxyy0 = i32x4_shuffle::<0, 0, 5, 5>(rhs, rhs);
let xxyy1 = i32x4_shuffle::<2, 2, 7, 7>(rhs, rhs);
let axbxcydy0 = f32x4_mul(abcd, xxyy0);
let axbxcydy1 = f32x4_mul(abcd, xxyy1);
let cydyaxbx0 = i32x4_shuffle::<2, 3, 4, 5>(axbxcydy0, axbxcydy0);
let cydyaxbx1 = i32x4_shuffle::<2, 3, 4, 5>(axbxcydy1, axbxcydy1);
let result0 = f32x4_add(axbxcydy0, cydyaxbx0);
let result1 = f32x4_add(axbxcydy1, cydyaxbx1);
Self(i32x4_shuffle::<0, 1, 4, 5>(result0, result1))
{% elif self_t == "Mat2" and is_coresimd %}
let abcd = self.0;
let xxyy0 = simd_swizzle!(rhs.0, [0, 0, 1, 1]);
let xxyy1 = simd_swizzle!(rhs.0, [2, 2, 3, 3]);
let axbxcydy0 = abcd * xxyy0;
let axbxcydy1 = abcd * xxyy1;
let cydyaxbx0 = simd_swizzle!(axbxcydy0, [2, 3, 0, 1]);
let cydyaxbx1 = simd_swizzle!(axbxcydy1, [2, 3, 0, 1]);
let result0 = axbxcydy0 + cydyaxbx0;
let result1 = axbxcydy1 + cydyaxbx1;
Self(simd_swizzle!(result0, result1, [0, 1, 4, 5]))
{% elif self_t == "Mat2" and is_neon %}
unsafe {
let abcd = self.0;
let xxyy0 = vzip1q_f32(rhs.0, rhs.0);
let xxyy1 = vzip2q_f32(rhs.0, rhs.0);
let axbxcydy0 = vmulq_f32(abcd, xxyy0);
let axbxcydy1 = vmulq_f32(abcd, xxyy1);
let cydyaxbx0 = vextq_f32(axbxcydy0, axbxcydy0, 2);
let cydyaxbx1 = vextq_f32(axbxcydy1, axbxcydy1, 2);
let result0 = vaddq_f32(axbxcydy0, cydyaxbx0);
let result1 = vaddq_f32(axbxcydy1, cydyaxbx1);
Self(vreinterpretq_f32_u64(
vsetq_lane_u64(vgetq_lane_u64(vreinterpretq_u64_f32(result1), 0),
vreinterpretq_u64_f32(result0), 1)))
}
{% else %}
Self::from_cols(
{% for axis in axes %}
self.mul(rhs.{{ axis }}),
{%- endfor %}
)
{% endif %}
}
/// Adds two {{ nxn }} matrices.
#[inline]
#[must_use]
pub fn add_mat{{ dim }}(&self, rhs: &Self) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
Self(unsafe { _mm_add_ps(self.0, rhs.0) })
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4_add(self.0, rhs.0))
{% elif self_t == "Mat2" and is_coresimd %}
Self(self.0 + rhs.0)
{% elif self_t == "Mat2" and is_neon %}
Self(unsafe { vaddq_f32(self.0, rhs.0) })
{% else %}
Self::from_cols(
{% for axis in axes %}
self.{{ axis }}.add(rhs.{{ axis }}),
{%- endfor %}
)
{% endif %}
}
/// Subtracts two {{ nxn }} matrices.
#[inline]
#[must_use]
pub fn sub_mat{{ dim }}(&self, rhs: &Self) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
Self(unsafe { _mm_sub_ps(self.0, rhs.0) })
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4_sub(self.0, rhs.0))
{% elif self_t == "Mat2" and is_coresimd %}
Self(self.0 - rhs.0)
{% elif self_t == "Mat2" and is_neon %}
Self(unsafe { vsubq_f32(self.0, rhs.0) })
{% else %}
Self::from_cols(
{% for axis in axes %}
self.{{ axis }}.sub(rhs.{{ axis }}),
{%- endfor %}
)
{% endif %}
}
/// Multiplies a {{ nxn }} matrix by a scalar.
#[inline]
#[must_use]
pub fn mul_scalar(&self, rhs: {{ scalar_t }}) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
Self(unsafe { _mm_mul_ps(self.0, _mm_set_ps1(rhs)) })
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4_mul(self.0, f32x4_splat(rhs)))
{% elif self_t == "Mat2" and is_coresimd %}
Self(self.0 * f32x4::splat(rhs))
{% elif self_t == "Mat2" and is_neon %}
Self(unsafe { vmulq_f32(self.0, vld1q_dup_f32(&rhs)) })
{% else %}
Self::from_cols(
{% for axis in axes %}
self.{{ axis }}.mul(rhs),
{%- endfor %}
)
{% endif %}
}
/// Divides a {{ nxn }} matrix by a scalar.
#[inline]
#[must_use]
pub fn div_scalar(&self, rhs: {{ scalar_t }}) -> Self {
{% if self_t == "Mat2" and is_sse2 %}
Self(unsafe { _mm_div_ps(self.0, _mm_set_ps1(rhs)) })
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4_div(self.0, f32x4_splat(rhs)))
{% elif self_t == "Mat2" and is_coresimd %}
Self(self.0 / f32x4::splat(rhs))
{% elif self_t == "Mat2" and is_neon %}
Self(unsafe { vdivq_f32(self.0, vld1q_dup_f32(&rhs)) })
{% else %}
let rhs = {{ col_t }}::splat(rhs);
Self::from_cols(
{% for axis in axes %}
self.{{ axis }}.div(rhs),
{%- endfor %}
)
{% endif %}
}
/// Returns true if the absolute difference of all elements between `self` and `rhs`
/// is less than or equal to `max_abs_diff`.
///
/// This can be used to compare if two matrices contain similar elements. It works best
/// when comparing with a known value. The `max_abs_diff` that should be used used
/// depends on the values being compared against.
///
/// For more see
/// [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
#[inline]
#[must_use]
pub fn abs_diff_eq(&self, rhs: Self, max_abs_diff: {{ scalar_t }}) -> bool {
{% for axis in axes %}
self.{{ axis }}.abs_diff_eq(rhs.{{ axis }}, max_abs_diff)
{% if not loop.last %} && {% endif %}
{% endfor %}
}
/// Takes the absolute value of each element in `self`
#[inline]
#[must_use]
pub fn abs(&self) -> Self {
Self::from_cols(
{% for axis in axes %}
self.{{ axis }}.abs(),
{% endfor %}
)
}
{% if scalar_t == "f32" %}
#[inline]
pub fn as_dmat{{ dim }}(&self) -> DMat{{ dim }} {
DMat{{ dim }}::from_cols(
{% for axis in axes %}
self.{{ axis }}.as_dvec{{ dim }}(),
{% endfor %}
)
}
{% elif scalar_t == "f64" %}
#[inline]
pub fn as_mat{{ dim }}(&self) -> Mat{{ dim }} {
Mat{{ dim }}::from_cols(
{% for axis in axes %}
self.{{ axis }}.as_vec{{ dim }}(),
{% endfor %}
)
}
{% endif %}
}
impl Default for {{ self_t }} {
#[inline]
fn default() -> Self {
Self::IDENTITY
}
}
impl Add<{{ self_t }}> for {{ self_t }} {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
self.add_mat{{ dim }}(&rhs)
}
}
impl AddAssign<{{ self_t }}> for {{ self_t }} {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = self.add_mat{{ dim }}(&rhs);
}
}
impl Sub<{{ self_t }}> for {{ self_t }} {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
self.sub_mat{{ dim }}(&rhs)
}
}
impl SubAssign<{{ self_t }}> for {{ self_t }} {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = self.sub_mat{{ dim }}(&rhs);
}
}
impl Neg for {{ self_t }} {
type Output = Self;
#[inline]
fn neg(self) -> Self::Output {
{% if self_t == "Mat2" and is_sse2 %}
Self(unsafe { _mm_xor_ps(self.0, _mm_set1_ps(-0.0)) })
{% elif self_t == "Mat2" and is_wasm32 %}
Self(f32x4_neg(self.0))
{% elif self_t == "Mat2" and is_coresimd %}
Self(-self.0)
{% elif self_t == "Mat2" and is_neon %}
Self(unsafe { vnegq_f32(self.0) })
{% else %}
Self::from_cols(
{% for axis in axes %}
self.{{ axis }}.neg(),
{%- endfor %}
)
{% endif %}
}
}
impl Mul<{{ self_t }}> for {{ self_t }} {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
self.mul_mat{{ dim }}(&rhs)
}
}
impl MulAssign<{{ self_t }}> for {{ self_t }} {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = self.mul_mat{{ dim }}(&rhs);
}
}
impl Mul<{{ col_t }}> for {{ self_t }} {
type Output = {{ col_t }};
#[inline]
fn mul(self, rhs: {{ col_t }}) -> Self::Output {
{% if self_t == "Mat3A" %}
self.mul_vec3a(rhs)
{% else %}
self.mul_vec{{ dim }}(rhs)
{% endif %}
}
}
impl Mul<{{ self_t }}> for {{ scalar_t }} {
type Output = {{ self_t }};
#[inline]
fn mul(self, rhs: {{ self_t }}) -> Self::Output {
rhs.mul_scalar(self)
}
}
impl Mul<{{ scalar_t }}> for {{ self_t }} {
type Output = Self;
#[inline]
fn mul(self, rhs: {{ scalar_t }}) -> Self::Output {
self.mul_scalar(rhs)
}
}
impl MulAssign<{{ scalar_t }}> for {{ self_t }} {
#[inline]
fn mul_assign(&mut self, rhs: {{ scalar_t }}) {
*self = self.mul_scalar(rhs);
}
}
impl Div<{{ self_t }}> for {{ scalar_t }} {
type Output = {{ self_t }};
#[inline]
fn div(self, rhs: {{ self_t }}) -> Self::Output {
rhs.div_scalar(self)
}
}
impl Div<{{ scalar_t }}> for {{ self_t }} {
type Output = Self;
#[inline]
fn div(self, rhs: {{ scalar_t }}) -> Self::Output {
self.div_scalar(rhs)
}
}
impl DivAssign<{{ scalar_t }}> for {{ self_t }} {
#[inline]
fn div_assign(&mut self, rhs: {{ scalar_t }}) {
*self = self.div_scalar(rhs);
}
}
{% if self_t == "Mat3" %}
impl Mul<Vec3A> for Mat3 {
type Output = Vec3A;
#[inline]
fn mul(self, rhs: Vec3A) -> Vec3A {
self.mul_vec3a(rhs)
}
}
impl From<Mat3A> for Mat3 {
#[inline]
fn from(m: Mat3A) -> Self {
Self {
x_axis: m.x_axis.into(),
y_axis: m.y_axis.into(),
z_axis: m.z_axis.into(),
}
}
}
{% elif self_t == "Mat3A" %}
impl Mul<Vec3> for Mat3A {
type Output = Vec3;
#[inline]
fn mul(self, rhs: Vec3) -> Vec3 {
self.mul_vec3a(rhs.into()).into()
}
}
impl From<Mat3> for Mat3A {
#[inline]
fn from(m: Mat3) -> Self {
Self {
x_axis: m.x_axis.into(),
y_axis: m.y_axis.into(),
z_axis: m.z_axis.into(),
}
}
}
{% endif %}
impl Sum<Self> for {{ self_t }} {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Self>,
{
iter.fold(Self::ZERO, Self::add)
}
}
impl<'a> Sum<&'a Self> for {{ self_t }} {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
iter.fold(Self::ZERO, |a, &b| Self::add(a, b))
}
}
impl Product for {{ self_t }} {
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = Self>,
{
iter.fold(Self::IDENTITY, Self::mul)
}
}
impl<'a> Product<&'a Self> for {{ self_t }} {
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
iter.fold(Self::IDENTITY, |a, &b| Self::mul(a, b))
}
}
impl PartialEq for {{ self_t }} {
#[inline]
fn eq(&self, rhs: &Self) -> bool {
{% for axis in axes %}
self.{{ axis }}.eq(&rhs.{{ axis }}) {% if not loop.last %} && {% endif %}
{% endfor %}
}
}
{% if not is_align %}
#[cfg(not(target_arch = "spirv"))]
impl AsRef<[{{ scalar_t }}; {{ size }}]> for {{ self_t }} {
#[inline]
fn as_ref(&self) -> &[{{ scalar_t }}; {{ size }}] {
unsafe { &*(self as *const Self as *const [{{ scalar_t }}; {{ size }}]) }
}
}
#[cfg(not(target_arch = "spirv"))]
impl AsMut<[{{ scalar_t }}; {{ size }}]> for {{ self_t }} {
#[inline]
fn as_mut(&mut self) -> &mut [{{ scalar_t }}; {{ size }}] {
unsafe { &mut *(self as *mut Self as *mut [{{ scalar_t }}; {{ size }}]) }
}
}
{% endif %}
{% if self_t == "Mat2" and not is_scalar %}
impl core::ops::Deref for Mat2 {
type Target = crate::deref::Cols2<Vec2>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const Self as *const Self::Target) }
}
}
impl core::ops::DerefMut for Mat2 {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self as *mut Self as *mut Self::Target) }
}
}
{% endif %}
impl fmt::Debug for {{ self_t }} {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct(stringify!({{ self_t }}))
{% for axis in axes %}
.field("{{ axis }}", &self.{{ axis }})
{% endfor %}
.finish()
}
}
impl fmt::Display for {{ self_t }} {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(p) = f.precision() {
{% if dim == 2 %}
write!(f, "[{:.*}, {:.*}]", p, self.x_axis, p, self.y_axis)
{% elif dim == 3 %}
write!(f, "[{:.*}, {:.*}, {:.*}]", p, self.x_axis, p, self.y_axis, p, self.z_axis)
{% elif dim == 4 %}
write!(f, "[{:.*}, {:.*}, {:.*}, {:.*}]", p, self.x_axis, p, self.y_axis, p, self.z_axis, p, self.w_axis)
{% endif %}
} else {
{% if dim == 2 %}
write!(f, "[{}, {}]", self.x_axis, self.y_axis)
{% elif dim == 3 %}
write!(f, "[{}, {}, {}]", self.x_axis, self.y_axis, self.z_axis)
{% elif dim == 4 %}
write!(f, "[{}, {}, {}, {}]", self.x_axis, self.y_axis, self.z_axis, self.w_axis)
{% endif %}
}
}
}