From 00192bd25ce2e3992c6292d30b24ad94654dab4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dahlstr=C3=B6m?= Date: Thu, 1 Aug 2024 01:27:02 +0300 Subject: [PATCH] Implement arithmetic operators for colors Fixes #163. --- core/src/math.rs | 20 ++++++ core/src/math/color.rs | 135 ++++++++++++++++++++++++++++++++++++++-- core/src/math/vec.rs | 45 ++++---------- demos/src/bin/crates.rs | 2 +- demos/src/bin/solids.rs | 2 +- 5 files changed, 164 insertions(+), 40 deletions(-) diff --git a/core/src/math.rs b/core/src/math.rs index c3e41915..223fb259 100644 --- a/core/src/math.rs +++ b/core/src/math.rs @@ -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 $trait<$rhs> for $self + 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; diff --git a/core/src/math/color.rs b/core/src/math/color.rs index 3c8c5850..ca4bff44 100644 --- a/core/src/math/color.rs +++ b/core/src/math/color.rs @@ -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 @@ -548,12 +550,101 @@ impl From for Color { } } +// +// Arithmetic trait impls +// + +// +// Arithmetic traits +// + +/// The color += color operator. +impl AddAssign<::Diff> for Color +where + Self: Affine, +{ + #[inline] + fn add_assign(&mut self, rhs: ::Diff) { + *self = Affine::add(&*self, &rhs); + } +} + +/// The color -= color operator. +impl SubAssign<::Diff> for Color +where + Self: Affine, +{ + #[inline] + fn sub_assign(&mut self, rhs: ::Diff) { + *self += rhs.neg(); + } +} + +// The color *= scalar operator. +impl MulAssign<::Scalar> for Color +where + Self: Linear, +{ + #[inline] + fn mul_assign(&mut self, rhs: ::Scalar) { + *self = Linear::mul(&*self, rhs); + } +} + +// The color /= scalar operator. +impl DivAssign for Color +where + Self: Linear, +{ + #[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 Neg for Color +where + Self: Linear, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + ::neg(&self) + } +} + +/// The scalar * color operator. +impl Mul> for as Linear>::Scalar +where + Color: Linear, +{ + type Output = Color; + + #[inline] + fn mul(self, rhs: Color) -> Self::Output { + rhs * self + } +} + +// The color + color operator. +impl_op!(Add::add, Color, ::Diff, +=, bound=Affine); +// The color - color operator. +impl_op!(Sub::sub, Color, ::Diff, -=, bound=Affine); +// The color * scalar operator. +impl_op!(Mul::mul, Color, ::Scalar, *=); +// The color / scalar operator. +impl_op!(Div::div, Color, f32, /=, bound=Linear); + #[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); @@ -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); @@ -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 = [ diff --git a/core/src/math/vec.rs b/core/src/math/vec.rs index c5432ebc..c1117406 100644 --- a/core/src/math/vec.rs +++ b/core/src/math/vec.rs @@ -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 @@ -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 $trait<$rhs> for Vector - where - Self: $bnd, - { - type Output = Self; - /// TODO - #[inline] - fn $method(mut self, rhs: $rhs) -> Self { - self $op rhs; self - } - } - }; -} - /// The vector += vector operator. impl AddAssign<::Diff> for Vector where @@ -525,8 +505,6 @@ where *self = Affine::add(&*self, &rhs); } } -// The vector + vector operator. -impl_op!(Add::add, ::Diff, +=, bound=Affine); /// The vector -= vector operator. impl SubAssign<::Diff> for Vector @@ -539,9 +517,6 @@ where } } -// The vector - vector operator. -impl_op!(Sub::sub, ::Diff, -=, bound=Affine); - // The vector *= scalar operator. impl MulAssign<::Scalar> for Vector where @@ -552,8 +527,6 @@ where *self = Linear::mul(&*self, rhs); } } -// The vector * scalar operator. -impl_op!(Mul::mul, ::Scalar, *=); // The vector /= scalar operator. impl DivAssign for Vector @@ -567,9 +540,6 @@ where } } -// The vector / scalar operator. -impl_op!(Div::div, f32, /=, bound=Linear); - /// The vector negation operator. impl Neg for Vector where @@ -617,6 +587,15 @@ where } } +// The vector + vector operator. +impl_op!(Add::add, Vector, ::Diff, +=, bound=Affine); +// The vector - vector operator. +impl_op!(Sub::sub, Vector, ::Diff, -=, bound=Affine); +// The vector * scalar operator. +impl_op!(Mul::mul, Vector, ::Scalar, *=); +// The vector / scalar operator. +impl_op!(Div::div, Vector, f32, /=, bound=Linear); + // // Unit tests // diff --git a/demos/src/bin/crates.rs b/demos/src/bin/crates.rs index dc1bc1e9..ee9950c6 100644 --- a/demos/src/bin/crates.rs +++ b/demos/src/bin/crates.rs @@ -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(); diff --git a/demos/src/bin/solids.rs b/demos/src/bin/solids.rs index bc48c14f..d72bf3fd 100644 --- a/demos/src/bin/solids.rs +++ b/demos/src/bin/solids.rs @@ -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) }