= Vertex, A>;
+
/// Triangle, defined by three vertices.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(transparent)]
@@ -31,3 +38,5 @@ pub type Normal2 = Vec2;
pub const fn vertex(pos: P, attrib: A) -> Vertex
{
Vertex { pos, attrib }
}
+
+pub struct Ray(pub Orig, pub Dir);
diff --git a/core/src/geom/mesh.rs b/core/src/geom/mesh.rs
index ce204d5b..92b1a024 100644
--- a/core/src/geom/mesh.rs
+++ b/core/src/geom/mesh.rs
@@ -1,23 +1,17 @@
//! Triangle meshes.
+use alloc::{vec, vec::Vec};
use core::{
fmt::{Debug, Formatter},
iter::zip,
};
-use alloc::{vec, vec::Vec};
-
-use crate::math::{
- mat::{Mat4x4, RealToReal},
- space::Linear,
- vec::Vec3,
+use crate::{
+ math::{mat::RealToReal, Linear, Mat4x4, Point3},
+ render::Model,
};
-use crate::render::Model;
-use super::{vertex, Normal3, Tri};
-
-/// Convenience type alias for a mesh vertex.
-pub type Vertex = super::Vertex, A>;
+use super::{vertex, Normal3, Tri, Vertex3};
/// A triangle mesh.
///
@@ -32,7 +26,7 @@ pub struct Mesh {
/// to the `verts` vector. Several faces can share a vertex.
pub faces: Vec>,
/// The vertices of the mesh.
- pub verts: Vec>,
+ pub verts: Vec>,
}
/// A builder type for creating meshes.
@@ -53,17 +47,19 @@ impl Mesh {
///
/// # Examples
/// ```
- /// # use retrofire_core::geom::{Tri, Mesh, vertex};
- /// # use retrofire_core::math::vec3;
+ /// use retrofire_core::geom::{Tri, Mesh, vertex};
+ /// use retrofire_core::math::pt3;
+ ///
/// let verts = [
- /// vec3(0.0, 0.0, 0.0),
- /// vec3(1.0, 0.0, 0.0),
- /// vec3(0.0, 1.0, 0.0),
- /// vec3(0.0, 0.0, 1.0)
+ /// pt3(0.0, 0.0, 0.0),
+ /// pt3(1.0, 0.0, 0.0),
+ /// pt3(0.0, 1.0, 0.0),
+ /// pt3(0.0, 0.0, 1.0)
/// ]
/// .map(|v| vertex(v, ()));
///
/// let faces = [
+ /// // Indices point to the verts array
/// Tri([0, 1, 2]),
/// Tri([0, 1, 3]),
/// Tri([0, 2, 3]),
@@ -78,7 +74,7 @@ impl Mesh {
pub fn new(faces: F, verts: V) -> Self
where
F: IntoIterator- >,
- V: IntoIterator
- >,
+ V: IntoIterator
- >,
{
let faces: Vec<_> = faces.into_iter().collect();
let verts: Vec<_> = verts.into_iter().collect();
@@ -129,16 +125,16 @@ impl Builder {
}
/// Appends a vertex with the given position and attribute.
- pub fn push_vert(&mut self, pos: Vec3, attrib: A) {
+ pub fn push_vert(&mut self, pos: Point3, attrib: A) {
self.mesh.verts.push(vertex(pos.to(), attrib));
}
/// Appends all the vertices yielded by the given iterator.
pub fn push_verts(&mut self, verts: Vs)
where
- Vs: IntoIterator
- ,
+ Vs: IntoIterator
- ,
{
- let vs = verts.into_iter().map(|(v, a)| vertex(v.to(), a));
+ let vs = verts.into_iter().map(|(p, a)| vertex(p.to(), a));
self.mesh.verts.extend(vs);
}
@@ -167,7 +163,7 @@ impl Builder<()> {
.mesh
.verts
.into_iter()
- .map(|v| vertex(tf.apply(&v.pos), v.attrib))
+ .map(|v| vertex(tf.apply_pt(&v.pos), v.attrib))
.collect(),
};
mesh.into_builder()
@@ -191,8 +187,8 @@ impl Builder<()> {
// Compute weighted face normals...
let face_normals = faces.iter().map(|Tri(vs)| {
- // TODO If n-gonal faces are supported some day,
- // the cross product is not proportional to area anymore
+ // TODO If n-gonal faces are supported some day, the cross
+ // product is not proportional to area anymore
let [a, b, c] = vs.map(|i| verts[i].pos);
(b - a).cross(&(c - a)).to()
});
@@ -256,9 +252,10 @@ impl Default for Builder {
mod tests {
use core::f32::consts::FRAC_1_SQRT_2;
- use crate::geom::vertex;
- use crate::math::vec3;
- use crate::prelude::splat;
+ use crate::{
+ geom::vertex,
+ math::{pt3, splat, vec3},
+ };
use super::*;
@@ -268,9 +265,9 @@ mod tests {
let _: Mesh<()> = Mesh::new(
[Tri([0, 1, 2]), Tri([1, 2, 3])],
[
- vertex(vec3(0.0, 0.0, 0.0), ()),
- vertex(vec3(1.0, 1.0, 1.0), ()),
- vertex(vec3(2.0, 2.0, 2.0), ()),
+ vertex(pt3(0.0, 0.0, 0.0), ()),
+ vertex(pt3(1.0, 1.0, 1.0), ()),
+ vertex(pt3(2.0, 2.0, 2.0), ()),
],
);
}
@@ -281,9 +278,9 @@ mod tests {
let mut b = Mesh::builder();
b.push_faces([[0, 1, 2], [1, 2, 3]]);
b.push_verts([
- (vec3(0.0, 0.0, 0.0), ()),
- (vec3(1.0, 1.0, 1.0), ()),
- (vec3(2.0, 2.0, 2.0), ()),
+ (pt3(0.0, 0.0, 0.0), ()),
+ (pt3(1.0, 1.0, 1.0), ()),
+ (pt3(2.0, 2.0, 2.0), ()),
]);
_ = b.build();
}
@@ -295,10 +292,10 @@ mod tests {
let mut b = Mesh::builder();
b.push_faces([[0, 2, 1], [0, 1, 3], [0, 3, 2]]);
b.push_verts([
- (vec3(0.0, 0.0, 0.0), ()),
- (vec3(1.0, 0.0, 0.0), ()),
- (vec3(0.0, 1.0, 0.0), ()),
- (vec3(0.0, 0.0, 1.0), ()),
+ (pt3(0.0, 0.0, 0.0), ()),
+ (pt3(1.0, 0.0, 0.0), ()),
+ (pt3(0.0, 1.0, 0.0), ()),
+ (pt3(0.0, 0.0, 1.0), ()),
]);
let b = b.with_vertex_normals();
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 4f580117..ceea8267 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -51,25 +51,10 @@ pub mod util;
/// Prelude module exporting many frequently used items.
pub mod prelude {
- #[cfg(feature = "fp")]
- pub use crate::math::mat::{rotate_x, rotate_y, rotate_z};
- pub use crate::math::{
- angle::{degs, rads, turns, Angle},
- color::{hsl, hsla, rgb, rgba, Color3, Color3f, Color4, Color4f},
- mat::{
- perspective, scale, translate, viewport, Mat3x3, Mat4x4, Matrix,
- },
- rand::Distrib,
- space::{Affine, Linear},
- vary::{lerp, Vary},
- vec::{splat, vec2, vec3, Vec2, Vec2i, Vec2u, Vec3, Vec3i, Vector},
- };
-
- pub use crate::geom::{vertex, Mesh, Normal2, Normal3, Tri, Vertex};
-
- pub use crate::render::{raster::Frag, shader::Shader};
-
- pub use crate::util::buf::{
- AsMutSlice2, AsSlice2, Buf2, MutSlice2, Slice2,
+ pub use crate::{
+ geom::{vertex, Mesh, Normal2, Normal3, Tri, Vertex, Vertex2, Vertex3},
+ math::*,
+ render::{raster::Frag, shader::Shader, *},
+ util::buf::{AsMutSlice2, AsSlice2, Buf2, MutSlice2, Slice2},
};
}
diff --git a/core/src/math.rs b/core/src/math.rs
index c3e41915..9b5539ef 100644
--- a/core/src/math.rs
+++ b/core/src/math.rs
@@ -17,21 +17,93 @@
//! to matching vectors. Angles are strongly typed as well, to allow working
//! with different angular units without confusion.
-pub use angle::{degs, polar, rads, spherical, turns, Angle};
-pub use approx::ApproxEq;
-pub use mat::{Mat3x3, Mat4x4, Matrix};
-pub use space::{Affine, Linear};
-pub use vary::{lerp, Vary};
-pub use vec::{vec2, vec3};
-pub use vec::{Vec2, Vec2i, Vec3, Vec3i, Vector};
+#[cfg(feature = "fp")]
+pub use {
+ angle::{acos, asin, atan2},
+ mat::{orient_y, orient_z, rotate_x, rotate_y, rotate_z},
+};
+pub use {
+ angle::{
+ degs, polar, rads, spherical, turns, Angle, PolarVec, SphericalVec,
+ },
+ approx::ApproxEq,
+ color::{rgb, rgba, Color, Color3, Color3f, Color4, Color4f},
+ mat::{
+ orthographic, perspective, scale, translate, viewport, Mat3x3, Mat4x4,
+ Matrix,
+ },
+ point::{pt2, pt3, Point, Point2, Point2u, Point3},
+ space::{Affine, Linear},
+ spline::{smootherstep, smoothstep, BezierSpline, CubicBezier},
+ vary::Vary,
+ vec::{splat, vec2, vec3, Vec2, Vec2i, Vec2u, Vec3, Vec3i, Vector},
+};
pub mod angle;
pub mod approx;
pub mod color;
pub mod float;
pub mod mat;
+pub mod point;
pub mod rand;
pub mod space;
pub mod spline;
pub mod vary;
pub mod vec;
+
+/// Trait for linear interpolation between two values.
+pub trait Lerp {
+ fn lerp(&self, other: &Self, t: f32) -> Self;
+}
+
+impl Lerp for T
+where
+ T: Affine>,
+{
+ /// Linearly interpolates between `self` and `other`.
+ ///
+ /// if `t` = 0, returns `self`; if `t` = 1, returns `other`.
+ /// For 0 < `t` < 1, returns the affine combination
+ /// ```text
+ /// self * (1 - t) + other * t
+ /// ```
+ /// or rearranged:
+ /// ```text
+ /// self + t * (other - self)
+ /// ```
+ ///
+ /// This method does not panic if `t < 0.0` or `t > 1.0`, or if `t`
+ /// is a `NaN`, but the return value in those cases is unspecified.
+ /// Individual implementations may offer stronger guarantees.
+ ///
+ /// # Examples
+ /// ```
+ /// use retrofire_core::math::*;
+ ///
+ /// assert_eq!(2.0.lerp(&5.0, 0.0), 2.0);
+ /// assert_eq!(2.0.lerp(&5.0, 0.25), 2.75);
+ /// assert_eq!(2.0.lerp(&5.0, 0.75), 4.25);
+ /// assert_eq!(2.0.lerp(&5.0, 1.0), 5.0);
+ ///
+ /// let v0: Vec2 = vec2(-2.0, 1.0);
+ /// let v1 = vec2(3.0, -1.0);
+ /// assert_eq!(v0.lerp(&v1, 0.8), vec2(2.0, -0.6));
+ ///
+ /// let p0: Point2 = pt2(-10.0, 5.0);
+ /// let p1 = pt2(-5.0, 0.0);
+ /// assert_eq!(p0.lerp(&p1, 0.4),pt2(-8.0, 3.0));
+ /// ```
+ fn lerp(&self, other: &Self, t: f32) -> Self {
+ self.add(&other.sub(self).mul(t))
+ }
+}
+
+impl Lerp for () {
+ fn lerp(&self, _: &Self, _: f32) {}
+}
+
+impl Lerp for (U, V) {
+ fn lerp(&self, (u, v): &Self, t: f32) -> Self {
+ (self.0.lerp(&u, t), self.1.lerp(&v, t))
+ }
+}
diff --git a/core/src/math/angle.rs b/core/src/math/angle.rs
index b823a7bb..5ebd0953 100644
--- a/core/src/math/angle.rs
+++ b/core/src/math/angle.rs
@@ -1,17 +1,15 @@
//! Angular quantities, including scalar angles and angular vectors.
-use core::f32::consts::{PI, TAU};
-use core::fmt::{self, Debug, Display};
-use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
+use core::{
+ f32::consts::{PI, TAU},
+ fmt::{self, Debug, Display},
+ ops::{Add, Div, Mul, Neg, Rem, Sub},
+};
-use crate::math::approx::ApproxEq;
-use crate::math::space::{Affine, Linear};
-use crate::math::vec::Vector;
+use crate::math::{vary::ZDiv, Affine, ApproxEq, Linear, Vector};
#[cfg(feature = "fp")]
-use crate::math::float::f32;
-#[cfg(feature = "fp")]
-use crate::math::vec::{vec2, vec3, Vec2, Vec3};
+use crate::math::{float::f32, vec2, vec3, Vec2, Vec3};
//
// Types
@@ -66,8 +64,9 @@ pub fn turns(a: f32) -> Angle {
///
/// # Examples
/// ```
-/// # use retrofire_core::assert_approx_eq;
-/// # use retrofire_core::math::angle::*;
+/// use retrofire_core::assert_approx_eq;
+/// use retrofire_core::math::{degs, asin};
+///
/// assert_approx_eq!(asin(1.0), degs(90.0));
/// assert_approx_eq!(asin(-1.0), degs(-90.0));
/// ```
@@ -85,8 +84,9 @@ pub fn asin(x: f32) -> Angle {
///
/// # Examples
/// ```
-/// # use retrofire_core::assert_approx_eq;
-/// # use retrofire_core::math::angle::*;
+/// use retrofire_core::assert_approx_eq;
+/// use retrofire_core::math::{acos, degs};
+///
/// assert_approx_eq!(acos(1.0), degs(0.0));
/// ```
/// # Panics
@@ -102,7 +102,8 @@ pub fn acos(x: f32) -> Angle {
///
/// # Examples
/// ```
-/// # use retrofire_core::math::angle::*;
+/// use retrofire_core::math::{atan2, degs};
+///
/// assert_eq!(atan2(0.0, 1.0), degs(0.0));
/// assert_eq!(atan2(2.0, 2.0), degs(45.0));
/// assert_eq!(atan2(3.0, 0.0), degs(90.0));
@@ -145,8 +146,9 @@ impl Angle {
/// Returns the value of `self` in radians.
/// # Examples
/// ```
- /// # use std::f32;
- /// # use retrofire_core::math::degs;
+ /// use std::f32;
+ /// use retrofire_core::math::degs;
+ ///
/// assert_eq!(degs(90.0).to_rads(), f32::consts::FRAC_PI_2);
/// ```
pub const fn to_rads(self) -> f32 {
@@ -155,7 +157,8 @@ impl Angle {
/// Returns the value of `self` in degrees.
/// # Examples
/// ```
- /// # use retrofire_core::math::turns;
+ /// use retrofire_core::math::turns;
+ ///
/// assert_eq!(turns(2.0).to_degs(), 720.0);
pub fn to_degs(self) -> f32 {
self.0 / RADS_PER_DEG
@@ -163,7 +166,8 @@ impl Angle {
/// Returns the value of `self` in turns.
/// # Examples
/// ```
- /// # use retrofire_core::math::degs;
+ /// use retrofire_core::math::degs;
+ ///
/// assert_eq!(degs(180.0).to_turns(), 0.5);
/// ```
pub fn to_turns(self) -> f32 {
@@ -182,13 +186,12 @@ impl Angle {
///
/// # Examples
/// ```
- /// # use retrofire_core::math::angle::degs;
+ /// use retrofire_core::math::degs;
+ ///
/// let (min, max) = (degs(0.0), degs(45.0));
///
/// assert_eq!(degs(100.0).clamp(min, max), max);
- ///
/// assert_eq!(degs(30.0).clamp(min, max), degs(30.0));
- ///
/// assert_eq!(degs(-10.0).clamp(min, max), min);
/// ```
#[must_use]
@@ -202,8 +205,9 @@ impl Angle {
/// Returns the sine of `self`.
/// # Examples
/// ```
- /// # use retrofire_core::assert_approx_eq;
- /// # use retrofire_core::math::angle::*;
+ /// use retrofire_core::assert_approx_eq;
+ /// use retrofire_core::math::degs;
+ ///
/// assert_approx_eq!(degs(30.0).sin(), 0.5)
/// ```
pub fn sin(self) -> f32 {
@@ -212,8 +216,9 @@ impl Angle {
/// Returns the cosine of `self`.
/// # Examples
/// ```
- /// # use retrofire_core::assert_approx_eq;
- /// # use retrofire_core::math::angle::*;
+ /// use retrofire_core::assert_approx_eq;
+ /// use retrofire_core::math::degs;
+ ///
/// assert_approx_eq!(degs(60.0).cos(), 0.5)
/// ```
pub fn cos(self) -> f32 {
@@ -222,8 +227,9 @@ impl Angle {
/// Simultaneously computes the sine and cosine of `self`.
/// # Examples
/// ```
- /// # use retrofire_core::assert_approx_eq;
- /// # use retrofire_core::math::angle::*;
+ /// use retrofire_core::assert_approx_eq;
+ /// use retrofire_core::math::degs;
+ ///
/// let (sin, cos) = degs(90.0).sin_cos();
/// assert_approx_eq!(sin, 1.0);
/// assert_approx_eq!(cos, 0.0);
@@ -234,7 +240,7 @@ impl Angle {
/// Returns the tangent of `self`.
/// # Examples
/// ```
- /// # use retrofire_core::math::angle::*;
+ /// use retrofire_core::math::degs;
/// assert_eq!(degs(45.0).tan(), 1.0)
/// ```
pub fn tan(self) -> f32 {
@@ -245,8 +251,10 @@ impl Angle {
///
/// # Examples
/// ```
- /// # use retrofire_core::assert_approx_eq;
- /// # use retrofire_core::math::angle::*;
+ /// use retrofire_core::assert_approx_eq;
+ /// use retrofire_core::math::{degs, turns};
+ ///
+ /// // 400 (mod 360) = 40
/// assert_approx_eq!(degs(400.0).wrap(turns(0.0), turns(1.0)), degs(40.0))
/// ```
#[must_use]
@@ -284,14 +292,12 @@ impl PolarVec {
///
/// # Examples
/// ```
- /// # use retrofire_core::assert_approx_eq;
- /// # use retrofire_core::math::angle::{polar, degs};
- /// # use retrofire_core::math::vec::vec2;
- /// assert_approx_eq!(polar(2.0, degs(0.0)).to_cart(), vec2(2.0, 0.0));
- ///
- /// assert_approx_eq!(polar(2.0, degs(90.0)).to_cart(), vec2(0.0, 2.0));
+ /// use retrofire_core::assert_approx_eq;
+ /// use retrofire_core::math::{vec2, polar, degs};
///
- /// assert_approx_eq!(polar(2.0, degs(-180.0)).to_cart(), vec2(-2.0, 0.0));
+ /// assert_approx_eq!(polar(2.0, degs(0.0)).to_cart(), vec2(2.0, 0.0));
+ /// assert_approx_eq!(polar(3.0, degs(90.0)).to_cart(), vec2(0.0, 3.0));
+ /// assert_approx_eq!(polar(4.0, degs(-180.0)).to_cart(), vec2(-4.0, 0.0));
///
/// ```
#[cfg(feature = "fp")]
@@ -321,7 +327,6 @@ impl SphericalVec {
/// Returns `self` converted to the equivalent Cartesian 3-vector.
///
/// # Examples
- ///
/// TODO examples
#[cfg(feature = "fp")]
pub fn to_cart(&self) -> Vec3 {
@@ -355,8 +360,9 @@ impl Vec2 {
///
/// # Examples
/// ```
- /// # use retrofire_core::assert_approx_eq;
- /// # use retrofire_core::math::{*, angle::*};
+ /// use retrofire_core::assert_approx_eq;
+ /// use retrofire_core::math::{vec2, degs};
+ ///
/// // A non-negative x and zero y maps to zero azimuth
/// assert_eq!(vec2(0.0, 0.0).to_polar().az(), degs(0.0));
/// assert_eq!(vec2(1.0, 0.0).to_polar().az(), degs(0.0));
@@ -391,7 +397,8 @@ impl Vec3 {
///
/// # Examples
/// ```
- /// # use retrofire_core::math::{*, angle::*};
+ /// use retrofire_core::math::{vec3, spherical, degs};
+ ///
/// // The positive x-axis lies at zero azimuth and altitude
/// assert_eq!(
/// vec3(2.0, 0.0, 0.0).to_spherical(),
@@ -463,6 +470,8 @@ impl Linear for Angle {
}
}
+impl ZDiv for Angle {}
+
//
// Foreign trait impls
//
@@ -570,8 +579,10 @@ impl From for SphericalVec {
mod tests {
use core::f32::consts::{PI, TAU};
- use crate::assert_approx_eq;
- use crate::math::vary::Vary;
+ use crate::{
+ assert_approx_eq,
+ math::{Lerp, Vary},
+ };
use super::*;
diff --git a/core/src/math/approx.rs b/core/src/math/approx.rs
index f602ff35..093d8b2c 100644
--- a/core/src/math/approx.rs
+++ b/core/src/math/approx.rs
@@ -138,13 +138,13 @@ macro_rules! assert_approx_eq {
}
};
($a:expr, $b:expr, $fmt:literal $(, $args:expr)*) => {{
- use $crate::math::approx::ApproxEq;
+ use $crate::math::ApproxEq;
match (&$a, &$b) {
(a, b) => assert!(ApproxEq::approx_eq(a, b), $fmt $(, $args)*)
}
}};
($a:expr, $b:expr, eps = $eps:literal, $fmt:literal $(, $args:expr)*) => {{
- use $crate::math::approx::ApproxEq;
+ use $crate::math::ApproxEq;
match (&$a, &$b) {
(a, b) => assert!(
ApproxEq::approx_eq_eps(a, b, &$eps),
@@ -196,16 +196,23 @@ mod tests {
fn zero_not_approx_eq_to_one() {
assert_approx_eq!(0.0, 1.0);
}
+
#[test]
#[should_panic]
- fn one_not_approx_eq_to_1_00001() {
- assert_approx_eq!(1.0, 1.00001);
+ fn one_not_approx_eq_to_1_01() {
+ if cfg!(any(feature = "std", feature = "libm")) {
+ assert_approx_eq!(1.0, 1.00001);
+ } else {
+ assert_approx_eq!(1.0, 1.01);
+ }
}
+
#[test]
#[should_panic]
fn inf_not_approx_eq_to_inf() {
assert_approx_eq!(f32::INFINITY, f32::INFINITY);
}
+
#[test]
#[should_panic]
fn nan_not_approx_eq_to_nan() {
diff --git a/core/src/math/color.rs b/core/src/math/color.rs
index 3c8c5850..0c21e9dc 100644
--- a/core/src/math/color.rs
+++ b/core/src/math/color.rs
@@ -1,13 +1,13 @@
//! Colors and color spaces.
-use core::array;
-use core::fmt::{self, Debug, Formatter};
-use core::marker::PhantomData;
-use core::ops::Index;
+use core::{
+ array,
+ fmt::{self, Debug, Formatter},
+ marker::PhantomData,
+ ops::Index,
+};
-use crate::math::float::f32;
-use crate::math::space::{Affine, Linear};
-use crate::math::vec::Vector;
+use crate::math::{float::f32, vary::ZDiv, Affine, Linear, Vector};
//
// Types
@@ -531,6 +531,8 @@ impl Linear for Color<[f32; DIM], Sp> {
}
}
+impl ZDiv for Color<[Sc; N], Sp> where Sc: ZDiv + Copy {}
+
//
// Foreign trait impls
//
diff --git a/core/src/math/mat.rs b/core/src/math/mat.rs
index 9a19e2e4..02eb080b 100644
--- a/core/src/math/mat.rs
+++ b/core/src/math/mat.rs
@@ -1,16 +1,24 @@
#![allow(clippy::needless_range_loop)]
//! Matrices and linear transforms.
+//!
+//! TODO Docs
-use core::array;
-use core::fmt::{self, Debug, Formatter};
-use core::marker::PhantomData;
-use core::ops::Range;
+use core::{
+ array,
+ fmt::{self, Debug, Formatter},
+ marker::PhantomData,
+ ops::Range,
+};
use crate::render::{NdcToScreen, ViewToProj};
-use super::space::{Linear, Proj4, Real};
-use super::vec::{ProjVec4, Vec2, Vec2u, Vec3, Vector};
+use super::{
+ float::f32,
+ point::{Point2, Point2u, Point3},
+ space::{Linear, Proj4, Real},
+ vec::{ProjVec4, Vec2, Vec3, Vector},
+};
/// A linear transform from one space (or basis) to another.
///
@@ -82,6 +90,7 @@ where
Map: LinearMap,
{
/// Returns the row vector of `self` with index `i`.
+ ///
/// The returned vector is in space `Map::Source`.
///
/// # Panics
@@ -133,10 +142,11 @@ impl Matrix<[[f32; N]; N], Map> {
impl Mat4x4 {
/// Constructs a matrix from a set of basis vectors.
pub const fn from_basis(i: Vec3, j: Vec3, k: Vec3) -> Self {
+ let (i, j, k) = (i.0, j.0, k.0);
Self::new([
- [i.0[0], i.0[1], i.0[2], 0.0],
- [j.0[0], j.0[1], j.0[2], 0.0],
- [k.0[0], k.0[1], k.0[2], 0.0],
+ [i[0], j[0], k[0], 0.0],
+ [i[1], j[1], k[1], 0.0],
+ [i[2], j[2], k[2], 0.0],
[0.0, 0.0, 0.0, 1.0],
])
}
@@ -199,9 +209,16 @@ impl Mat3x3> {
/// ```
#[must_use]
pub fn apply(&self, v: &Vec2) -> Vec2 {
- let v = [v.x(), v.y(), 1.0].into();
+ let v = [v.x(), v.y(), 1.0].into(); // TODO w=0.0
array::from_fn(|i| self.row_vec(i).dot(&v)).into()
}
+
+ // TODO Add trait to overload apply or similar
+ #[must_use]
+ pub fn apply_pt(&self, p: &Point2) -> Point2 {
+ let p = [p.x(), p.y(), 1.0].into();
+ array::from_fn(|i| self.row_vec(i).dot(&p)).into()
+ }
}
impl Mat4x4> {
@@ -218,10 +235,17 @@ impl Mat4x4> {
/// ```
#[must_use]
pub fn apply(&self, v: &Vec3) -> Vec3 {
- let v = [v.x(), v.y(), v.z(), 1.0].into();
+ let v = [v.x(), v.y(), v.z(), 1.0].into(); // TODO w=0.0
array::from_fn(|i| self.row_vec(i).dot(&v)).into()
}
+ // TODO Add trait to overload apply or similar
+ #[must_use]
+ pub fn apply_pt(&self, p: &Point3) -> Point3 {
+ let p = [p.x(), p.y(), p.z(), 1.0].into();
+ array::from_fn(|i| self.row_vec(i).dot(&p)).into()
+ }
+
/// Returns the determinant of `self`.
///
/// Given a matrix M,
@@ -365,8 +389,8 @@ impl Mat4x4> {
/// \ · · M33 / \ 1 /
/// ```
#[must_use]
- pub fn apply(&self, v: &Vec3) -> ProjVec4 {
- let v = Vector::from([v.x(), v.y(), v.z(), 1.0]);
+ pub fn apply(&self, p: &Point3) -> ProjVec4 {
+ let v = Vector::from([p.x(), p.y(), p.z(), 1.0]);
[
self.row_vec(0).dot(&v),
self.row_vec(1).dot(&v),
@@ -460,7 +484,7 @@ impl From for Matrix {
///
/// Tip: use [`splat`][super::vec::splat] to scale uniformly:
/// ```
-/// # use retrofire_core::math::{mat::scale, vec::splat};
+/// use retrofire_core::math::{scale, splat};
/// let m = scale(splat(2.0));
/// assert_eq!(m.0[0][0], 2.0);
/// assert_eq!(m.0[1][1], 2.0);
@@ -510,7 +534,7 @@ pub fn orient_z(new_z: Vec3, x: Vec3) -> Mat4x4> {
#[cfg(feature = "fp")]
fn orient(new_y: Vec3, new_z: Vec3) -> Mat4x4> {
- use crate::math::{space::Linear, ApproxEq};
+ use crate::math::{ApproxEq, Linear};
assert!(!new_y.approx_eq(&Vec3::zero()));
assert!(!new_z.approx_eq(&Vec3::zero()));
@@ -602,30 +626,33 @@ pub fn perspective(
/// # Parameters
/// * `lbn`: The left-bottom-near corner of the projection box.
/// * `rtf`: The right-bottom-far corner of the projection box.
-pub fn orthographic(lbn: Vec3, rtf: Vec3) -> Mat4x4 {
- let [dx, dy, dz] = (rtf - lbn).0;
- let [sx, sy, sz] = (rtf + lbn).0;
+pub fn orthographic(lbn: Point3, rtf: Point3) -> Mat4x4 {
+ let half_d = (rtf - lbn) / 2.0;
+ let [cx, cy, cz] = (lbn + half_d).0;
+ let [idx, idy, idz] = half_d.map(f32::recip).0;
[
- [2.0 / dx, 0.0, 0.0, -sx / dx],
- [0.0, 2.0 / dy, 0.0, -sy / dy],
- [0.0, 0.0, 2.0 / dz, -sz / dz],
+ [idx, 0.0, 0.0, -cx * idx],
+ [0.0, idy, 0.0, -cy * idy],
+ [0.0, 0.0, idz, -cz * idz],
[0.0, 0.0, 0.0, 1.0],
]
.into()
}
-/// Creates a viewport transform matrix. A viewport matrix is used to
-/// transform points from the NDC space to screen space for rasterization.
+/// Creates a viewport transform matrix with the given pixel space bounds.
///
-/// # Parameters
-/// * `bounds`: the left-top and right-bottom coordinates of the viewport.
-pub fn viewport(bounds: Range) -> Mat4x4 {
- let Range { start, end } = bounds;
- let h = (end.x() - start.x()) as f32 / 2.0;
- let v = (end.y() - start.y()) as f32 / 2.0;
+/// A viewport matrix is used to transform points from the NDC space to screen
+/// space for rasterization. NDC coordinates (-1, -1, z) are mapped to
+/// `bounds.start` and NDC coordinates (1, 1, z) to `bounds.end`.
+pub fn viewport(bounds: Range) -> Mat4x4 {
+ let s = bounds.start.map(|c| c as f32);
+ let e = bounds.end.map(|c| c as f32);
+ let half_d = (e - s) / 2.0;
+ let [dx, dy] = half_d.0;
+ let [cx, cy] = (s + half_d).0;
[
- [h, 0.0, 0.0, h + start.x() as f32],
- [0.0, v, 0.0, v + start.y() as f32],
+ [dx, 0.0, 0.0, cx],
+ [0.0, dy, 0.0, cy],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
@@ -635,10 +662,10 @@ pub fn viewport(bounds: Range) -> Mat4x4 {
#[cfg(test)]
mod tests {
use crate::assert_approx_eq;
- use crate::math::vec::{splat, vec2, vec3};
+ use crate::math::{pt2, pt3, splat, vec2, vec3};
#[cfg(feature = "fp")]
- use crate::math::angle::degs;
+ use crate::math::degs;
use super::*;
@@ -650,8 +677,13 @@ mod tests {
type Map = RealToReal;
type InvMap = RealToReal;
+ const X: Vec3 = vec3(1.0, 0.0, 0.0);
+ const Y: Vec3 = vec3(0.0, 1.0, 0.0);
+ const Z: Vec3 = vec3(0.0, 0.0, 1.0);
+
mod mat3x3 {
use super::*;
+ use crate::math::pt2;
const MAT: Mat3x3