Skip to content

Commit

Permalink
Implement arithmetic operators for colors
Browse files Browse the repository at this point in the history
Fixes #163.
  • Loading branch information
jdahlstrom committed Sep 29, 2024
1 parent e94367c commit 00192bd
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 40 deletions.
20 changes: 20 additions & 0 deletions core/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ pub use vary::{lerp, Vary};
pub use vec::{vec2, vec3};
pub use vec::{Vec2, Vec2i, Vec3, Vec3i, Vector};

/// Implements an operator trait in terms of an op-assign trait.
macro_rules! impl_op {
($trait:ident :: $method:ident, $self:ident, $rhs:ty, $op:tt) => {
impl_op!($trait::$method, $self, $rhs, $op, bound=Linear);
};
($trait:ident :: $method:ident, $self:ident, $rhs:ty, $op:tt, bound=$bnd:path) => {
impl<R, Sp> $trait<$rhs> for $self<R, Sp>
where
Self: $bnd,
{
type Output = Self;
/// TODO
#[inline]
fn $method(mut self, rhs: $rhs) -> Self {
self $op rhs; self
}
}
};
}

pub mod angle;
pub mod approx;
pub mod color;
Expand Down
135 changes: 130 additions & 5 deletions core/src/math/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
use core::array;
use core::fmt::{self, Debug, Formatter};
use core::marker::PhantomData;
use core::ops::Index;
use core::ops::{
Add, AddAssign, Div, DivAssign, Index, Mul, MulAssign, Neg, Sub, SubAssign,
};

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

//
// Types
Expand Down Expand Up @@ -548,12 +550,101 @@ impl<R, Sp> From<R> for Color<R, Sp> {
}
}

//
// Arithmetic trait impls
//

//
// Arithmetic traits
//

/// The color += color operator.
impl<R, Sp> AddAssign<<Self as Affine>::Diff> for Color<R, Sp>
where
Self: Affine,
{
#[inline]
fn add_assign(&mut self, rhs: <Self as Affine>::Diff) {
*self = Affine::add(&*self, &rhs);
}
}

/// The color -= color operator.
impl<R, Sp> SubAssign<<Self as Affine>::Diff> for Color<R, Sp>
where
Self: Affine,
{
#[inline]
fn sub_assign(&mut self, rhs: <Self as Affine>::Diff) {
*self += rhs.neg();
}
}

// The color *= scalar operator.
impl<R, Sp> MulAssign<<Self as Linear>::Scalar> for Color<R, Sp>
where
Self: Linear,
{
#[inline]
fn mul_assign(&mut self, rhs: <Self as Linear>::Scalar) {
*self = Linear::mul(&*self, rhs);
}
}

// The color /= scalar operator.
impl<R, Sp> DivAssign<f32> for Color<R, Sp>
where
Self: Linear<Scalar = f32>,
{
#[inline]
fn div_assign(&mut self, rhs: f32) {
use crate::math::ApproxEq;
debug_assert!(!rhs.approx_eq(&0.0));
*self = Linear::mul(&*self, rhs.recip());
}
}

/// The color negation operator.
impl<R, Sp> Neg for Color<R, Sp>
where
Self: Linear,
{
type Output = Self;

#[inline]
fn neg(self) -> Self::Output {
<Self as Linear>::neg(&self)
}
}

/// The scalar * color operator.
impl<R, Sp> Mul<Color<R, Sp>> for <Color<R, Sp> as Linear>::Scalar
where
Color<R, Sp>: Linear,
{
type Output = Color<R, Sp>;

#[inline]
fn mul(self, rhs: Color<R, Sp>) -> Self::Output {
rhs * self
}
}

// The color + color operator.
impl_op!(Add::add, Color, <Self as Affine>::Diff, +=, bound=Affine);
// The color - color operator.
impl_op!(Sub::sub, Color, <Self as Affine>::Diff, -=, bound=Affine);
// The color * scalar operator.
impl_op!(Mul::mul, Color, <Self as Linear>::Scalar, *=);
// The color / scalar operator.
impl_op!(Div::div, Color, f32, /=, bound=Linear<Scalar = f32>);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn color_components() {
fn rgb_components() {
assert_eq!(rgb(0xFF, 0, 0).r(), 0xFF);
assert_eq!(rgb(0, 0xFF, 0).g(), 0xFF);
assert_eq!(rgb(0, 0, 0xFF).b(), 0xFF);
Expand All @@ -563,6 +654,18 @@ mod tests {
assert_eq!(rgba(0, 0, 0xFF, 0).b(), 0xFF);
assert_eq!(rgba(0, 0, 0, 0xFF).a(), 0xFF);
}
#[test]
fn hsl_components() {
assert_eq!(hsl(0xFF, 0, 0).h(), 0xFF);
assert_eq!(hsl(0, 0xFF, 0).s(), 0xFF);
assert_eq!(hsl(0, 0, 0xFF).l(), 0xFF);

assert_eq!(hsla(0xFF, 0, 0, 0).h(), 0xFF);
assert_eq!(hsla(0, 0xFF, 0, 0).s(), 0xFF);
assert_eq!(hsla(0, 0, 0xFF, 0).l(), 0xFF);
assert_eq!(hsla(0, 0, 0, 0xFF).a(), 0xFF);
}

#[test]
fn rgb_to_u32() {
assert_eq!(rgb(0x11, 0x22, 0x33).to_rgb_u32(), 0x00_11_22_33);
Expand All @@ -573,6 +676,28 @@ mod tests {
assert_eq!(rgba(0x11, 0x22, 0x33, 0x44).to_argb_u32(), 0x44_11_22_33);
}

#[test]
fn rgb_f32_ops() {
let lhs = rgb(0.5, 0.625, 0.75);
let rhs = rgb(0.125, 0.25, 0.375);

assert_eq!(lhs + rhs, rgb(0.625, 0.875, 1.125));
assert_eq!(lhs - rhs, rgb(0.375, 0.375, 0.375));
assert_eq!(lhs * 0.5, rgb(0.25, 0.3125, 0.375));
assert_eq!(0.5 * lhs, rgb(0.25, 0.3125, 0.375));
assert_eq!(lhs / 2.0, rgb(0.25, 0.3125, 0.375));
assert_eq!(-lhs, rgb(-0.5, -0.625, -0.75));
}

#[test]
fn rgb_u8_ops() {
let lhs = rgb(0x77, 0x88, 0x99);
let rhs = [0x11_i32, 0x33, 0x55].into();

assert_eq!(lhs + rhs, rgb(0x88, 0xBB, 0xEE));
assert_eq!(lhs - rhs, rgb(0x66, 0x55, 0x44));
}

#[test]
fn rgb_to_hsl() {
let cases = [
Expand Down
45 changes: 12 additions & 33 deletions core/src/math/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use core::marker::PhantomData;
use core::ops::{Add, Div, Index, IndexMut, Mul, Neg, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, SubAssign};

use crate::math::approx::ApproxEq;
use crate::math::float::f32;
use crate::math::space::{Affine, Linear, Proj4, Real};
use super::approx::ApproxEq;
use super::float::f32;
use super::space::{Affine, Linear, Proj4, Real};

//
// Types
Expand Down Expand Up @@ -495,26 +495,6 @@ where
// Arithmetic traits
//

/// Implements an operator trait in terms of an op-assign trait.
macro_rules! impl_op {
($trait:ident :: $method:ident, $rhs:ty, $op:tt) => {
impl_op!($trait::$method, $rhs, $op, bound=Linear);
};
($trait:ident :: $method:ident, $rhs:ty, $op:tt, bound=$bnd:path) => {
impl<R, Sp> $trait<$rhs> for Vector<R, Sp>
where
Self: $bnd,
{
type Output = Self;
/// TODO
#[inline]
fn $method(mut self, rhs: $rhs) -> Self {
self $op rhs; self
}
}
};
}

/// The vector += vector operator.
impl<R, Sp> AddAssign<<Self as Affine>::Diff> for Vector<R, Sp>
where
Expand All @@ -525,8 +505,6 @@ where
*self = Affine::add(&*self, &rhs);
}
}
// The vector + vector operator.
impl_op!(Add::add, <Self as Affine>::Diff, +=, bound=Affine);

/// The vector -= vector operator.
impl<R, Sp> SubAssign<<Self as Affine>::Diff> for Vector<R, Sp>
Expand All @@ -539,9 +517,6 @@ where
}
}

// The vector - vector operator.
impl_op!(Sub::sub, <Self as Affine>::Diff, -=, bound=Affine);

// The vector *= scalar operator.
impl<R, Sp> MulAssign<<Self as Linear>::Scalar> for Vector<R, Sp>
where
Expand All @@ -552,8 +527,6 @@ where
*self = Linear::mul(&*self, rhs);
}
}
// The vector * scalar operator.
impl_op!(Mul::mul, <Self as Linear>::Scalar, *=);

// The vector /= scalar operator.
impl<R, Sp> DivAssign<f32> for Vector<R, Sp>
Expand All @@ -567,9 +540,6 @@ where
}
}

// The vector / scalar operator.
impl_op!(Div::div, f32, /=, bound=Linear<Scalar = f32>);

/// The vector negation operator.
impl<R, Sp> Neg for Vector<R, Sp>
where
Expand Down Expand Up @@ -617,6 +587,15 @@ where
}
}

// The vector + vector operator.
impl_op!(Add::add, Vector, <Self as Affine>::Diff, +=, bound=Affine);
// The vector - vector operator.
impl_op!(Sub::sub, Vector, <Self as Affine>::Diff, -=, bound=Affine);
// The vector * scalar operator.
impl_op!(Mul::mul, Vector, <Self as Linear>::Scalar, *=);
// The vector / scalar operator.
impl_op!(Div::div, Vector, f32, /=, bound=Linear<Scalar = f32>);

//
// Unit tests
//
Expand Down
2 changes: 1 addition & 1 deletion demos/src/bin/crates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn main() {
cam.mode
.rotate_to(degs(-0.4 * mx), degs(0.4 * (my - 240.0)));
cam.mode
.translate(cam_vel.mul(frame.dt.as_secs_f32()));
.translate(cam_vel * frame.dt.as_secs_f32());

let flip = scale(vec3(1.0, -1.0, -1.0)).to();

Expand Down
2 changes: 1 addition & 1 deletion demos/src/bin/solids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fn main() {
let diffuse = (norm.z() + 0.2).max(0.2) * 0.8;
// Visualize normal by mapping to RGB values
let [r, g, b] = (0.45 * (v.attrib + splat(1.1))).0;
let col = rgb(r, g, b).mul(diffuse);
let col = diffuse * rgb(r, g, b);
vertex(mvp.apply(&v.pos), col)
}

Expand Down

0 comments on commit 00192bd

Please sign in to comment.