Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Points, phase 2 #198

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions core/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ 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;
}
Expand All @@ -45,7 +46,51 @@ impl<T> Lerp for T
where
T: Affine<Diff: Linear<Scalar = f32>>,
{
/// 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::{Lerp, vec::vec2, point::pt2};
///
/// 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);
///
/// assert_eq!(
/// vec2::<f32, ()>(-2.0, 1.0).lerp(&vec2(3.0, -1.0), 0.8),
/// vec2(2.0, -0.6)
/// );
/// assert_eq!(
/// pt2::<f32, ()>(-10.0, 5.0).lerp(&pt2(-5.0, 0.0), 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<U: Lerp, V: Lerp> Lerp for (U, V) {
fn lerp(&self, (u, v): &Self, t: f32) -> Self {
(self.0.lerp(&u, t), self.1.lerp(&v, t))
}
}
3 changes: 3 additions & 0 deletions core/src/math/angle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::math::vec::Vector;

#[cfg(feature = "fp")]
use crate::math::float::f32;
use crate::math::vary::ZDiv;
#[cfg(feature = "fp")]
use crate::math::vec::{vec2, vec3, Vec2, Vec3};

Expand Down Expand Up @@ -463,6 +464,8 @@ impl Linear for Angle {
}
}

impl ZDiv for Angle {}

//
// Foreign trait impls
//
Expand Down
23 changes: 15 additions & 8 deletions core/src/math/color.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
//! Colors and color spaces.

use core::array;
use core::fmt::{self, Debug, Formatter};
use core::marker::PhantomData;
use core::ops::Index;

use crate::math::float::f32;
use crate::math::space::{Affine, Linear};
use crate::math::vec::Vector;
use core::{
array,
fmt::{self, Debug, Formatter},
marker::PhantomData,
ops::Index,
};

use crate::math::{
float::f32,
space::{Affine, Linear},
vary::ZDiv,
vec::Vector,
};

//
// Types
Expand Down Expand Up @@ -531,6 +536,8 @@ impl<Sp, const DIM: usize> Linear for Color<[f32; DIM], Sp> {
}
}

impl<Sc, Sp, const N: usize> ZDiv for Color<[Sc; N], Sp> where Sc: ZDiv + Copy {}

//
// Foreign trait impls
//
Expand Down
21 changes: 12 additions & 9 deletions core/src/math/mat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ impl<Src> Mat4x4<RealToProj<Src>> {
/// \ · · M33 / \ 1 /
/// ```
#[must_use]
pub fn apply(&self, v: &Point3<Src>) -> ProjVec4 {
let v = Vector::from([v.x(), v.y(), v.z(), 1.0]);
pub fn apply(&self, p: &Point3<Src>) -> 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),
Expand Down Expand Up @@ -624,13 +624,16 @@ pub fn perspective(
/// * `lbn`: The left-bottom-near corner of the projection box.
/// * `rtf`: The right-bottom-far corner of the projection box.
// TODO Change to take points
pub fn orthographic(lbn: Vec3, rtf: Vec3) -> Mat4x4<ViewToProj> {
let [dx, dy, dz] = (rtf - lbn).0;
let [sx, sy, sz] = (rtf + lbn).0;
pub fn orthographic(lbn: Point3, rtf: Point3) -> Mat4x4<ViewToProj> {
let half_d = 0.5 * (rtf - lbn);
let center_pt = lbn + half_d;

let [dx, dy, dz] = half_d.0;
let [cx, cy, cz] = center_pt.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],
[1.0 / dx, 0.0, 0.0, -cx / dx],
[0.0, 1.0 / dy, 0.0, -cy / dy],
[0.0, 0.0, 1.0 / dz, -cz / dz],
[0.0, 0.0, 0.0, 1.0],
]
.into()
Expand Down Expand Up @@ -934,7 +937,7 @@ mod tests {
let lbn = pt3(-20.0, 0.0, 0.01);
let rtf = pt3(100.0, 50.0, 100.0);

let m = orthographic(lbn.to_vec(), rtf.to_vec());
let m = orthographic(lbn, rtf);

assert_approx_eq!(m.apply(&lbn.to()), [-1.0, -1.0, -1.0, 1.0].into());
assert_approx_eq!(m.apply(&rtf.to()), [1.0, 1.0, 1.0, 1.0].into());
Expand Down
10 changes: 10 additions & 0 deletions core/src/math/point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::{
ops::{Add, Index, Sub},
};

use crate::math::vary::ZDiv;
use crate::math::{
space::{Affine, Linear, Real},
vec::Vector,
Expand Down Expand Up @@ -149,6 +150,15 @@ where
}
}

impl<Sc, Sp, const N: usize> ZDiv for Point<[Sc; N], Sp>
where
Sc: ZDiv + Copy,
{
fn z_div(self, z: f32) -> Self {
Self(self.0.map(|c| c.z_div(z)), Pd)
}
}

impl<Sc: ApproxEq, Sp, const N: usize> ApproxEq<Self, Sc>
for Point<[Sc; N], Sp>
{
Expand Down
8 changes: 2 additions & 6 deletions core/src/math/space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use core::fmt::{Debug, Formatter};
use core::marker::PhantomData;

use crate::math::vary::{Iter, Vary};
use crate::math::vary::{Iter, Vary, ZDiv};

/// Trait for types representing elements of an affine space.
///
Expand Down Expand Up @@ -158,7 +158,7 @@ impl Affine for u32 {

impl<V: Clone> Vary for V
where
Self: Linear<Scalar = f32>,
Self: Affine<Diff: Linear<Scalar = f32> + Clone> + ZDiv,
{
type Iter = Iter<Self>;
type Diff = <Self as Affine>::Diff;
Expand All @@ -177,10 +177,6 @@ where
fn step(&self, delta: &Self::Diff) -> Self {
self.add(delta)
}

fn z_div(&self, z: f32) -> Self {
self.mul(z.recip())
}
}

impl<const DIM: usize, B> Debug for Real<DIM, B>
Expand Down
27 changes: 18 additions & 9 deletions core/src/math/vary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@

use core::mem;

pub trait ZDiv: Sized {
#[must_use]
fn z_div(self, _z: f32) -> Self {
self
}
}

/// A trait for types that can be linearly interpolated and distributed
/// between two endpoints.
///
/// This trait is designed particularly for *varyings:* types that are
/// meant to be interpolated across the face of a polygon when rendering,
/// but the methods are useful for various purposes.
pub trait Vary: Sized + Clone {
pub trait Vary: ZDiv + Sized + Clone {
/// The iterator returned by the [vary][Self::vary] method.
type Iter: Iterator<Item = Self>;
/// The difference type of `Self`.
Expand Down Expand Up @@ -50,10 +57,6 @@ pub trait Vary: Sized + Clone {
#[must_use]
fn step(&self, delta: &Self::Diff) -> Self;

/// Performs perspective division.
#[must_use]
fn z_div(&self, z: f32) -> Self;

/// Linearly interpolates between `self` and `other`.
///
/// This method does not panic if `t < 0.0` or `t > 1.0`, or if `t`
Expand Down Expand Up @@ -94,9 +97,8 @@ impl Vary for () {
}
fn dv_dt(&self, _: &Self, _: f32) {}
fn step(&self, _: &Self::Diff) {}

fn z_div(&self, _: f32) {}
}
impl ZDiv for () {}

impl<T: Vary, U: Vary> Vary for (T, U) {
type Iter = Iter<Self>;
Expand All @@ -114,12 +116,19 @@ impl<T: Vary, U: Vary> Vary for (T, U) {
fn step(&self, (d0, d1): &Self::Diff) -> Self {
(self.0.step(d0), self.1.step(d1))
}

fn z_div(&self, z: f32) -> Self {
}
impl<T: ZDiv, U: ZDiv> ZDiv for (T, U) {
fn z_div(self, z: f32) -> Self {
(self.0.z_div(z), self.1.z_div(z))
}
}

impl ZDiv for f32 {
fn z_div(self, z: f32) -> Self {
self / z
}
}

impl<T: Vary> Iterator for Iter<T> {
type Item = T;

Expand Down
27 changes: 20 additions & 7 deletions core/src/math/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::math::approx::ApproxEq;
use crate::math::float::f32;
use crate::math::point::Point;
use crate::math::space::{Affine, Linear, Proj4, Real};
use crate::math::vary::ZDiv;

//
// Types
Expand Down Expand Up @@ -155,6 +156,9 @@ impl<Sp, const N: usize> Vector<[f32; N], Sp> {
/// // Clamp to the unit cube
/// let v = v.clamp(&splat(-1.0), &splat(1.0));
/// assert_eq!(v, vec3(0.5, 1.0, -1.0));
// TODO f32 and f64 have inherent clamp methods because they're not Ord.
// A generic clamp for Sc: Ord would conflict with this one. There is
// currently no clean way to support both floats and impl Ord types.
#[must_use]
pub fn clamp(&self, min: &Self, max: &Self) -> Self {
array::from_fn(|i| self[i].clamp(min[i], max[i])).into()
Expand Down Expand Up @@ -214,13 +218,15 @@ where
{
other.mul(self.scalar_project(other))
}
}

impl<Sc: Copy, Sp, const N: usize> Vector<[Sc; N], Sp> {
/// Returns a vector of the same dimension as `self` by applying `f`
/// component-wise.
#[inline]
#[must_use]
pub fn map<T>(self, mut f: impl FnMut(Sc) -> T) -> Vector<[T; N], Sp> {
array::from_fn(|i| f(self[i])).into()
array::from_fn(|i| f(self.0[i])).into()
}
}

Expand Down Expand Up @@ -333,8 +339,7 @@ where

impl<Sc, Sp, const DIM: usize> Affine for Vector<[Sc; DIM], Sp>
where
Sc: Affine,
Sc::Diff: Linear<Scalar = Sc::Diff> + Copy,
Sc: Affine<Diff: Linear<Scalar = Sc::Diff> + Copy>,
{
type Space = Sp;
// TODO Vectors always Linear once Point used for affine stuff
Expand All @@ -356,7 +361,6 @@ where

impl<Sc, Sp, const DIM: usize> Linear for Vector<[Sc; DIM], Sp>
where
Self: Affine<Diff = Self>,
Sc: Linear<Scalar = Sc> + Copy,
{
type Scalar = Sc;
Expand All @@ -368,11 +372,20 @@ where
}
#[inline]
fn neg(&self) -> Self {
Self(array::from_fn(|i| self.0[i].neg()), Pd)
self.map(|c| c.neg())
}
#[inline]
fn mul(&self, scalar: Self::Scalar) -> Self {
Self(array::from_fn(|i| self.0[i].mul(scalar)), Pd)
self.map(|c| c.mul(scalar))
}
}

impl<Sc, Sp, const N: usize> ZDiv for Vector<[Sc; N], Sp>
where
Sc: ZDiv + Copy,
{
fn z_div(self, z: f32) -> Self {
self.map(|c| c.z_div(z))
}
}

Expand Down Expand Up @@ -556,7 +569,7 @@ where
{
#[inline]
fn div_assign(&mut self, rhs: f32) {
debug_assert!(f32::abs(rhs) > 1e-7);
debug_assert!(f32::abs(rhs) > 1e-7, "divisor {rhs} < epsilon");
*self = Linear::mul(&*self, rhs.recip());
}
}
Expand Down
Loading
Loading