From 867f0eeb00b933074b318ef5a17e5288d92c1f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Dahlstr=C3=B6m?= Date: Sat, 13 Dec 2025 15:22:58 +0200 Subject: [PATCH] Impl From/Into consistently for points, vectors, and colors Adds conversion from/to appropriately sized tuples. --- core/src/math/color.rs | 100 ++++++++++++++++++++++++++++++++++++++--- core/src/math/point.rs | 49 ++++++++++++++------ core/src/math/vec.rs | 48 ++++++++++++++++---- 3 files changed, 169 insertions(+), 28 deletions(-) diff --git a/core/src/math/color.rs b/core/src/math/color.rs index eab9acab..16efe755 100644 --- a/core/src/math/color.rs +++ b/core/src/math/color.rs @@ -106,13 +106,15 @@ pub const INV_GAMMA: f32 = 1.0 / GAMMA; // Inherent impls // -impl Color<[Ch; N], Sp> { - /// Returns a new `Color` with the given channels. +impl Color { + /// Returns a new `Color` with the given representation. #[inline] - pub const fn new(chs: [Ch; N]) -> Self { - Self(chs, PhantomData) + pub const fn new(repr: R) -> Self { + Self(repr, PhantomData) } +} +impl Color<[Ch; N], Sp> { /// Returns `self` with each channel mapped with the given function. #[inline] pub fn map(&self, f: impl FnMut(Ch) -> C) -> Color<[C; N], Sp> @@ -661,10 +663,43 @@ impl PartialEq for Color { } } +// Color <-> repr conversions impl From for Color { #[inline] - fn from(els: R) -> Self { - Self(els, PhantomData) + fn from(repr: R) -> Self { + Self::new(repr) + } +} +impl From> for [Sc; N] { + #[inline] + fn from(c: Color<[Sc; N], Sp>) -> Self { + c.0 + } +} + +// Color <-> tuple conversions +impl From<(Sc, Sc, Sc)> for Color<[Sc; 3], Sp> { + #[inline] + fn from(chs: (Sc, Sc, Sc)) -> Self { + Self::new(chs.into()) + } +} +impl From> for (Sc, Sc, Sc) { + #[inline] + fn from(c: Color<[Sc; 3], Sp>) -> Self { + c.0.into() + } +} +impl From<(Sc, Sc, Sc, Sc)> for Color<[Sc; 4], Sp> { + #[inline] + fn from(chs: (Sc, Sc, Sc, Sc)) -> Self { + Self::new(chs.into()) + } +} +impl From> for (Sc, Sc, Sc, Sc) { + #[inline] + fn from(c: Color<[Sc; 4], Sp>) -> Self { + c.0.into() } } @@ -676,7 +711,6 @@ impl, Sp> Index for Color { &self.0[i] } } - impl, Sp> IndexMut for Color { #[inline] fn index_mut(&mut self, i: usize) -> &mut Self::Output { @@ -798,6 +832,58 @@ impl_op!(Div::div, Color, f32, /=, bound=Linear); mod tests { use super::*; + #[test] + fn color3_from() { + let c: Color3 = [1, 2, 3].into(); + assert_eq!(c.0, [1, 2, 3]); + + let c: Color3 = (1, 2, 3).into(); + assert_eq!(c.0, [1, 2, 3]); + + let c: Color3f = [1.0, 2.0, 3.0].into(); + assert_eq!(c.0, [1.0, 2.0, 3.0]); + + let c: Color3f = (1.0, 2.0, 3.0).into(); + assert_eq!(c.0, [1.0, 2.0, 3.0]); + } + + #[test] + fn color3_into() { + let c = rgb(1, 2, 3); + assert_eq!(<[_; 3]>::from(c), [1, 2, 3]); + assert_eq!(<(_, _, _)>::from(c), (1, 2, 3)); + + let c = rgb(1.0, 2.0, 3.0); + assert_eq!(<[_; 3]>::from(c), [1.0, 2.0, 3.0]); + assert_eq!(<(_, _, _)>::from(c), (1.0, 2.0, 3.0)); + } + + #[test] + fn color4_from() { + let c: Color4 = [1, 2, 3, 4].into(); + assert_eq!(c.0, [1, 2, 3, 4]); + + let c: Color4 = (1, 2, 3, 4).into(); + assert_eq!(c.0, [1, 2, 3, 4]); + + let c: Color4f = [1.0, 2.0, 3.0, 4.0].into(); + assert_eq!(c.0, [1.0, 2.0, 3.0, 4.0]); + + let c: Color4f = (1.0, 2.0, 3.0, 4.0).into(); + assert_eq!(c.0, [1.0, 2.0, 3.0, 4.0]); + } + + #[test] + fn color4_into() { + let c = rgba(1, 2, 3, 4); + assert_eq!(<[_; 4]>::from(c), [1, 2, 3, 4]); + assert_eq!(<(_, _, _, _)>::from(c), (1, 2, 3, 4)); + + let c = rgba(1.0, 2.0, 3.0, 4.0); + assert_eq!(<[_; 4]>::from(c), [1.0, 2.0, 3.0, 4.0]); + assert_eq!(<(_, _, _, _)>::from(c), (1.0, 2.0, 3.0, 4.0)); + } + #[test] fn rgb_components() { assert_eq!(rgb(0xFF, 0, 0).r(), 0xFF); diff --git a/core/src/math/point.rs b/core/src/math/point.rs index 0cc6572d..3c5c6f8a 100644 --- a/core/src/math/point.rs +++ b/core/src/math/point.rs @@ -1,5 +1,3 @@ -use super::{Affine, ApproxEq, Linear, Vector, space::Real, vary::ZDiv}; -use crate::math::space::Hom; use core::{ array, fmt::{Debug, Formatter}, @@ -8,6 +6,12 @@ use core::{ ops::{AddAssign, DivAssign, MulAssign, SubAssign}, }; +use super::{ + Affine, ApproxEq, Linear, Vector, + space::{Hom, Real}, + vary::ZDiv, +}; + #[repr(transparent)] pub struct Point(pub Repr, Pd); @@ -334,26 +338,45 @@ impl PartialEq for Point { } } +// Point <-> repr conversions impl From for Point { #[inline] fn from(repr: R) -> Self { - Self(repr, Pd) + Self::new(repr) } } -/* -impl From> for HomVec3 { - fn from(p: Point3) -> Self { - let [x, y, z] = p.0; - [x, y, z, 1.0].into() +impl From> for [Sc; N] { + #[inline] + fn from(v: Point<[Sc; N], Sp>) -> Self { + v.0 } } -impl From> for HomVec2 { - fn from(p: Point2) -> Self { - let [x, y] = p.0; - [x, y, 1.0].into() +// Point <-> tuple conversions +impl From<(Sc, Sc)> for Point<[Sc; 2], Sp> { + #[inline] + fn from(xy: (Sc, Sc)) -> Self { + Self::new(xy.into()) + } +} +impl From> for (Sc, Sc) { + #[inline] + fn from(v: Point<[Sc; 2], Sp>) -> Self { + v.0.into() } -}*/ +} +impl From<(Sc, Sc, Sc)> for Point<[Sc; 3], Sp> { + #[inline] + fn from(xyz: (Sc, Sc, Sc)) -> Self { + Self::new(xyz.into()) + } +} +impl From> for (Sc, Sc, Sc) { + #[inline] + fn from(v: Point<[Sc; 3], Sp>) -> Self { + v.0.into() + } +} impl, Sp> Index for Point { type Output = R::Output; diff --git a/core/src/math/vec.rs b/core/src/math/vec.rs index b0067e21..1a904e34 100644 --- a/core/src/math/vec.rs +++ b/core/src/math/vec.rs @@ -2,12 +2,6 @@ //! //! TODO -use super::{ - Affine, ApproxEq, Linear, Point, - space::{Proj3, Real}, - vary::ZDiv, -}; -use crate::math::space::Hom; use core::{ array, fmt::{Debug, Formatter}, @@ -17,6 +11,12 @@ use core::{ ops::{AddAssign, DivAssign, MulAssign, SubAssign}, }; +use super::{ + Affine, ApproxEq, Linear, Point, + space::{Hom, Proj3, Real}, + vary::ZDiv, +}; + // // Types // @@ -722,14 +722,20 @@ impl Debug for Vector { } } +// Vector <-> repr conversions impl From for Vector { #[inline] fn from(repr: R) -> Self { Self::new(repr) } } - -impl From for Vector<[Sc; DIM], Sp> { +impl From> for [Sc; N] { + #[inline] + fn from(v: Vector<[Sc; N], Sp>) -> Self { + v.0 + } +} +impl From for Vector<[Sc; N], Sp> { /// Returns a vector with all components equal to `scalar`. /// /// This operation is also called "splat" or "broadcast". @@ -739,6 +745,32 @@ impl From for Vector<[Sc; DIM], Sp> { } } +// Vector <-> tuple conversions +impl From<(Sc, Sc)> for Vector<[Sc; 2], Sp> { + #[inline] + fn from(xy: (Sc, Sc)) -> Self { + Self::new(xy.into()) + } +} +impl From> for (Sc, Sc) { + #[inline] + fn from(v: Vector<[Sc; 2], Sp>) -> Self { + v.0.into() + } +} +impl From<(Sc, Sc, Sc)> for Vector<[Sc; 3], Sp> { + #[inline] + fn from(xyz: (Sc, Sc, Sc)) -> Self { + Self::new(xyz.into()) + } +} +impl From> for (Sc, Sc, Sc) { + #[inline] + fn from(v: Vector<[Sc; 3], Sp>) -> Self { + v.0.into() + } +} + impl Index for Vector where Self: Affine,