use lina::{Point3, Vec3};
use crate::cast;
use super::PrimitiveNum;
pub trait Pos3Like: Copy {
type Scalar: PrimitiveNum;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self;
fn x(&self) -> Self::Scalar;
fn y(&self) -> Self::Scalar;
fn z(&self) -> Self::Scalar;
fn convert<P: Pos3Like<Scalar = Self::Scalar>>(&self) -> P {
P::from_coords(self.x(), self.y(), self.z())
}
fn map_scalar<P: Pos3Like>(&self, mut f: impl FnMut(Self::Scalar) -> P::Scalar) -> P {
P::from_coords(
f(self.x()),
f(self.y()),
f(self.z()),
)
}
fn to_point3(&self) -> Point3<Self::Scalar> {
self.convert()
}
}
impl<T: PrimitiveNum> Pos3Like for Point3<T> {
type Scalar = T;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
Self::new(x, y, z)
}
fn x(&self) -> Self::Scalar { self.x }
fn y(&self) -> Self::Scalar { self.y }
fn z(&self) -> Self::Scalar { self.z }
}
impl<T: PrimitiveNum> Pos3Like for (T, T, T) {
type Scalar = T;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
(x, y, z)
}
fn x(&self) -> Self::Scalar { self.0 }
fn y(&self) -> Self::Scalar { self.1 }
fn z(&self) -> Self::Scalar { self.2 }
}
impl<T: PrimitiveNum> Pos3Like for [T; 3] {
type Scalar = T;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
[x, y, z]
}
fn x(&self) -> Self::Scalar { self[0] }
fn y(&self) -> Self::Scalar { self[1] }
fn z(&self) -> Self::Scalar { self[2] }
}
pub trait Vec3Like: Copy {
type Scalar: PrimitiveNum;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self;
fn x(&self) -> Self::Scalar;
fn y(&self) -> Self::Scalar;
fn z(&self) -> Self::Scalar;
fn convert<V: Vec3Like<Scalar = Self::Scalar>>(&self) -> V {
V::from_coords(self.x(), self.y(), self.z())
}
fn map_scalar<V: Vec3Like>(&self, mut f: impl FnMut(Self::Scalar) -> V::Scalar) -> V {
V::from_coords(
f(self.x()),
f(self.y()),
f(self.z()),
)
}
fn to_vec3(&self) -> Vec3<Self::Scalar> {
self.convert()
}
}
impl<T: PrimitiveNum> Vec3Like for Vec3<T> {
type Scalar = T;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
Self::new(x, y, z)
}
fn x(&self) -> Self::Scalar { self.x }
fn y(&self) -> Self::Scalar { self.y }
fn z(&self) -> Self::Scalar { self.z }
}
impl<T: PrimitiveNum> Vec3Like for (T, T, T) {
type Scalar = T;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
(x, y, z)
}
fn x(&self) -> Self::Scalar { self.0 }
fn y(&self) -> Self::Scalar { self.1 }
fn z(&self) -> Self::Scalar { self.2 }
}
impl<T: PrimitiveNum> Vec3Like for [T; 3] {
type Scalar = T;
fn from_coords(x: Self::Scalar, y: Self::Scalar, z: Self::Scalar) -> Self {
[x, y, z]
}
fn x(&self) -> Self::Scalar { self[0] }
fn y(&self) -> Self::Scalar { self[1] }
fn z(&self) -> Self::Scalar { self[2] }
}
pub trait PrimitiveColorChannel: PrimitiveNum {
const MAX_INTENSITY: Self;
fn color_cast_from<SrcT: PrimitiveColorChannel>(src: SrcT) -> Self {
let mult = cast::lossy::<_, f64>(Self::MAX_INTENSITY)
/ cast::lossy::<_, f64>(SrcT::MAX_INTENSITY);
cast::lossy(mult * cast::lossy::<_, f64>(src))
}
}
impl PrimitiveColorChannel for u8 {
const MAX_INTENSITY: Self = u8::max_value();
}
impl PrimitiveColorChannel for u16 {
const MAX_INTENSITY: Self = u16::max_value();
}
impl PrimitiveColorChannel for u32 {
const MAX_INTENSITY: Self = u32::max_value();
}
impl PrimitiveColorChannel for f32 {
const MAX_INTENSITY: Self = 1.0;
}
impl PrimitiveColorChannel for f64 {
const MAX_INTENSITY: Self = 1.0;
}
pub trait ColorLike: Copy {
type Channel: PrimitiveColorChannel;
const HAS_ALPHA: bool;
fn from_rgb(r: Self::Channel, g: Self::Channel, b: Self::Channel) -> Self;
fn from_rgba(r: Self::Channel, g: Self::Channel, b: Self::Channel, _a: Self::Channel) -> Self {
Self::from_rgb(r, g, b)
}
fn red(&self) -> Self::Channel;
fn green(&self) -> Self::Channel;
fn blue(&self) -> Self::Channel;
fn alpha(&self) -> Option<Self::Channel> {
assert!(!Self::HAS_ALPHA);
None
}
fn convert<T: ColorLike<Channel = Self::Channel>>(&self) -> T {
let [r, g, b] = [self.red(), self.green(), self.blue()];
if let Some(a) = self.alpha() {
T::from_rgba(r, g, b, a)
} else {
T::from_rgb(r, g, b)
}
}
fn cast<T: ColorLike>(&self) -> T {
self.map_channel(T::Channel::color_cast_from)
}
fn map_channel<T: ColorLike>(&self, mut f: impl FnMut(Self::Channel) -> T::Channel) -> T {
let [r, g, b] = [
f(self.red()),
f(self.green()),
f(self.blue())
];
if let Some(a) = self.alpha() {
T::from_rgba(r, g, b, f(a))
} else {
T::from_rgb(r, g, b)
}
}
}
impl<T: PrimitiveColorChannel> ColorLike for (T, T, T) {
type Channel = T;
const HAS_ALPHA: bool = false;
fn from_rgb(r: Self::Channel, g: Self::Channel, b: Self::Channel) -> Self {
(r, g, b)
}
fn red(&self) -> Self::Channel { self.0 }
fn green(&self) -> Self::Channel { self.1 }
fn blue(&self) -> Self::Channel { self.2 }
}
impl<T: PrimitiveColorChannel> ColorLike for [T; 3] {
type Channel = T;
const HAS_ALPHA: bool = false;
fn from_rgb(r: Self::Channel, g: Self::Channel, b: Self::Channel) -> Self {
[r, g, b]
}
fn red(&self) -> Self::Channel { self[0] }
fn green(&self) -> Self::Channel { self[1] }
fn blue(&self) -> Self::Channel { self[2] }
}
impl<T: PrimitiveColorChannel> ColorLike for (T, T, T, T) {
type Channel = T;
const HAS_ALPHA: bool = true;
fn from_rgb(r: Self::Channel, g: Self::Channel, b: Self::Channel) -> Self {
Self::from_rgba(r, g, b, T::MAX_INTENSITY)
}
fn from_rgba(r: Self::Channel, g: Self::Channel, b: Self::Channel, a: Self::Channel) -> Self {
(r, g, b, a)
}
fn red(&self) -> Self::Channel { self.0 }
fn green(&self) -> Self::Channel { self.1 }
fn blue(&self) -> Self::Channel { self.2 }
fn alpha(&self) -> Option<Self::Channel> { Some(self.3) }
}
impl<T: PrimitiveColorChannel> ColorLike for [T; 4] {
type Channel = T;
const HAS_ALPHA: bool = true;
fn from_rgb(r: Self::Channel, g: Self::Channel, b: Self::Channel) -> Self {
Self::from_rgba(r, g, b, T::MAX_INTENSITY)
}
fn from_rgba(r: Self::Channel, g: Self::Channel, b: Self::Channel, a: Self::Channel) -> Self {
[r, g, b, a]
}
fn red(&self) -> Self::Channel { self[0] }
fn green(&self) -> Self::Channel { self[1] }
fn blue(&self) -> Self::Channel { self[2] }
fn alpha(&self) -> Option<Self::Channel> { Some(self[3]) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn color_cast() {
assert_eq!(u8::color_cast_from(0u8), 0);
assert_eq!(u8::color_cast_from(u8::max_value()), 255);
assert_eq!(u8::color_cast_from(0u16), 0);
assert_eq!(u8::color_cast_from(u16::max_value()), 255);
assert_eq!(u8::color_cast_from(0u32), 0);
assert_eq!(u8::color_cast_from(u32::max_value()), 255);
assert_eq!(u8::color_cast_from(0.0f32), 0);
assert_eq!(u8::color_cast_from(1.0f32), 255);
assert_eq!(u8::color_cast_from(0.0f64), 0);
assert_eq!(u8::color_cast_from(1.0f64), 255);
assert_eq!(u16::color_cast_from(0u8), 0);
assert_eq!(u16::color_cast_from(u8::max_value()), u16::max_value());
assert_eq!(u16::color_cast_from(0u16), 0);
assert_eq!(u16::color_cast_from(u16::max_value()), u16::max_value());
assert_eq!(u16::color_cast_from(0u32), 0);
assert_eq!(u16::color_cast_from(u32::max_value()), u16::max_value());
assert_eq!(u16::color_cast_from(0.0f32), 0);
assert_eq!(u16::color_cast_from(1.0f32), u16::max_value());
assert_eq!(u16::color_cast_from(0.0f64), 0);
assert_eq!(u16::color_cast_from(1.0f64), u16::max_value());
assert_eq!(u32::color_cast_from(0u8), 0);
assert_eq!(u32::color_cast_from(u8::max_value()), u32::max_value());
assert_eq!(u32::color_cast_from(0u16), 0);
assert_eq!(u32::color_cast_from(u16::max_value()), u32::max_value());
assert_eq!(u32::color_cast_from(0u32), 0);
assert_eq!(u32::color_cast_from(u32::max_value()), u32::max_value());
assert_eq!(u32::color_cast_from(0.0f32), 0);
assert_eq!(u32::color_cast_from(1.0f32), u32::max_value());
assert_eq!(u32::color_cast_from(0.0f64), 0);
assert_eq!(u32::color_cast_from(1.0f64), u32::max_value());
assert_eq!(f32::color_cast_from(0u8), 0.0);
assert_eq!(f32::color_cast_from(u8::max_value()), 1.0);
assert_eq!(f32::color_cast_from(0u16), 0.0);
assert_eq!(f32::color_cast_from(u16::max_value()), 1.0);
assert_eq!(f32::color_cast_from(0u32), 0.0);
assert_eq!(f32::color_cast_from(u32::max_value()), 1.0);
assert_eq!(f32::color_cast_from(0.0f32), 0.0);
assert_eq!(f32::color_cast_from(1.0f32), 1.0);
assert_eq!(f32::color_cast_from(0.0f64), 0.0);
assert_eq!(f32::color_cast_from(1.0f64), 1.0);
assert_eq!(f64::color_cast_from(0u8), 0.0);
assert_eq!(f64::color_cast_from(u8::max_value()), 1.0);
assert_eq!(f64::color_cast_from(0u16), 0.0);
assert_eq!(f64::color_cast_from(u16::max_value()), 1.0);
assert_eq!(f64::color_cast_from(0u32), 0.0);
assert_eq!(f64::color_cast_from(u32::max_value()), 1.0);
assert_eq!(f64::color_cast_from(0.0f32), 0.0);
assert_eq!(f64::color_cast_from(1.0f32), 1.0);
assert_eq!(f64::color_cast_from(0.0f64), 0.0);
assert_eq!(f64::color_cast_from(1.0f64), 1.0);
}
}