#[cfg(all(not(feature = "std"), not(feature = "libm")))]
compile_error!(
"color requires either the `std` or `libm` feature. \
Add color = {{ features = [\"std\"] }} to your Cargo.toml."
);
pub trait Float:
Copy
+ 'static
+ PartialOrd
+ core::ops::Add<Output = Self>
+ core::ops::Sub<Output = Self>
+ core::ops::Mul<Output = Self>
+ core::ops::Div<Output = Self>
+ core::ops::Neg<Output = Self>
{
const ZERO: Self;
const ONE: Self;
const MIN_POSITIVE: Self;
fn from_f64(v: f64) -> Self;
fn powf(self, exp: Self) -> Self;
fn cbrt(self) -> Self;
fn sqrt(self) -> Self;
fn ln(self) -> Self;
fn exp(self) -> Self;
fn sin(self) -> Self;
fn cos(self) -> Self;
fn atan2(self, x: Self) -> Self;
fn round(self) -> Self;
fn powi(self, n: i32) -> Self;
fn abs(self) -> Self;
fn rem_euclid(self, rhs: Self) -> Self;
fn clamp(self, min: Self, max: Self) -> Self;
fn max(self, other: Self) -> Self;
fn min(self, other: Self) -> Self;
}
#[cfg(feature = "std")]
macro_rules! impl_float_std {
($t:ty, $zero:expr, $one:expr, $min_pos:expr, $from_f64:expr) => {
impl Float for $t {
const ZERO: $t = $zero;
const ONE: $t = $one;
const MIN_POSITIVE: $t = $min_pos;
#[inline(always)] fn from_f64(v: f64) -> $t { ($from_f64)(v) }
#[inline(always)] fn powf(self, exp: $t) -> $t { self.powf(exp) }
#[inline(always)] fn cbrt(self) -> $t { self.cbrt() }
#[inline(always)] fn sqrt(self) -> $t { self.sqrt() }
#[inline(always)] fn ln(self) -> $t { self.ln() }
#[inline(always)] fn exp(self) -> $t { self.exp() }
#[inline(always)] fn sin(self) -> $t { self.sin() }
#[inline(always)] fn cos(self) -> $t { self.cos() }
#[inline(always)] fn atan2(self, x: $t) -> $t { self.atan2(x) }
#[inline(always)] fn round(self) -> $t { self.round() }
#[inline(always)] fn powi(self, n: i32) -> $t { self.powi(n) }
#[inline(always)] fn abs(self) -> $t { self.abs() }
#[inline(always)] fn rem_euclid(self, rhs: $t) -> $t { self.rem_euclid(rhs) }
#[inline(always)] fn clamp(self, min: $t, max: $t) -> $t { self.clamp(min, max) }
#[inline(always)] fn max(self, other: $t) -> $t { self.max(other) }
#[inline(always)] fn min(self, other: $t) -> $t { self.min(other) }
}
};
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
macro_rules! impl_float_libm {
($t:ty, $zero:expr, $one:expr, $min_pos:expr, $from_f64:expr,
$powf:path, $cbrt:path, $sqrt:path, $ln:path, $exp:path,
$sin:path, $cos:path, $atan2:path, $round:path,
$powi_base:path, $powi_cast:ty,
$fabs:path) => {
impl Float for $t {
const ZERO: $t = $zero;
const ONE: $t = $one;
const MIN_POSITIVE: $t = $min_pos;
#[inline(always)] fn from_f64(v: f64) -> $t { ($from_f64)(v) }
#[inline(always)] fn powf(self, exp: $t) -> $t { $powf(self, exp) }
#[inline(always)] fn cbrt(self) -> $t { $cbrt(self) }
#[inline(always)] fn sqrt(self) -> $t { $sqrt(self) }
#[inline(always)] fn ln(self) -> $t { $ln(self) }
#[inline(always)] fn exp(self) -> $t { $exp(self) }
#[inline(always)] fn sin(self) -> $t { $sin(self) }
#[inline(always)] fn cos(self) -> $t { $cos(self) }
#[inline(always)] fn atan2(self, x: $t) -> $t { $atan2(self, x) }
#[inline(always)] fn round(self) -> $t { $round(self) }
#[inline(always)] fn powi(self, n: i32) -> $t { $powi_base(self, n as $powi_cast) }
#[inline(always)] fn abs(self) -> $t { $fabs(self) }
#[inline(always)] fn rem_euclid(self, rhs: $t) -> $t {
let r = self % rhs;
if r < $zero { r + $fabs(rhs) } else { r }
}
#[inline(always)] fn clamp(self, min: $t, max: $t) -> $t {
if self < min { min } else if self > max { max } else { self }
}
#[inline(always)] fn max(self, other: $t) -> $t { if self >= other { self } else { other } }
#[inline(always)] fn min(self, other: $t) -> $t { if self <= other { self } else { other } }
}
};
}
#[cfg(feature = "std")]
impl_float_std!(f32, 0.0, 1.0, f32::MIN_POSITIVE, |v: f64| v as f32);
#[cfg(feature = "std")]
impl_float_std!(f64, 0.0, 1.0, f64::MIN_POSITIVE, |v: f64| v);
#[cfg(all(not(feature = "std"), feature = "libm"))]
impl_float_libm!(f32, 0.0f32, 1.0f32, f32::MIN_POSITIVE, |v: f64| v as f32,
libm::powf, libm::cbrtf, libm::sqrtf, libm::logf, libm::expf,
libm::sinf, libm::cosf, libm::atan2f, libm::roundf,
libm::powf, f32,
libm::fabsf);
#[cfg(all(not(feature = "std"), feature = "libm"))]
impl_float_libm!(f64, 0.0f64, 1.0f64, f64::MIN_POSITIVE, |v: f64| v,
libm::pow, libm::cbrt, libm::sqrt, libm::log, libm::exp,
libm::sin, libm::cos, libm::atan2, libm::round,
libm::pow, f64,
libm::fabs);
macro_rules! impl_mat3 {
($name:ident, $scalar:ty, $zero:expr) => {
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct $name {
/// Column 0, rows [0..2] with padding at index 3.
pub col0: [$scalar; 4],
pub col1: [$scalar; 4],
pub col2: [$scalar; 4],
}
impl $name {
pub const fn mul(a: &Self, b: &Self) -> Self {
macro_rules! e {
($r:expr, $c:expr) => {
a.col0[$r] * $c[0] + a.col1[$r] * $c[1] + a.col2[$r] * $c[2]
};
}
Self {
col0: [e!(0, b.col0), e!(1, b.col0), e!(2, b.col0), $zero],
col1: [e!(0, b.col1), e!(1, b.col1), e!(2, b.col1), $zero],
col2: [e!(0, b.col2), e!(1, b.col2), e!(2, b.col2), $zero],
}
}
pub const fn invert(m: &Self) -> Self {
let [a, b, c, _] = m.col0;
let [d, e, f, _] = m.col1;
let [g, h, i, _] = m.col2;
let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
Self {
col0: [
(e * i - f * h) / det,
-(b * i - c * h) / det,
(b * f - c * e) / det,
$zero,
],
col1: [
-(d * i - f * g) / det,
(a * i - c * g) / det,
-(a * f - c * d) / det,
$zero,
],
col2: [
(d * h - e * g) / det,
-(a * h - b * g) / det,
(a * e - b * d) / det,
$zero,
],
}
}
pub const fn from_rows(m: [[$scalar; 3]; 3]) -> Self {
Self {
col0: [m[0][0], m[1][0], m[2][0], $zero],
col1: [m[0][1], m[1][1], m[2][1], $zero],
col2: [m[0][2], m[1][2], m[2][2], $zero],
}
}
pub const fn to_rows(self) -> [[$scalar; 3]; 3] {
[
[self.col0[0], self.col1[0], self.col2[0]],
[self.col0[1], self.col1[1], self.col2[1]],
[self.col0[2], self.col1[2], self.col2[2]],
]
}
#[inline(always)]
pub const fn apply(&self, v: [$scalar; 3]) -> [$scalar; 3] {
[
self.col0[0] * v[0] + self.col1[0] * v[1] + self.col2[0] * v[2],
self.col0[1] * v[0] + self.col1[1] * v[1] + self.col2[1] * v[2],
self.col0[2] * v[0] + self.col1[2] * v[1] + self.col2[2] * v[2],
]
}
#[inline(always)]
pub const fn luminance_weights(&self) -> [$scalar; 3] {
[self.col0[1], self.col1[1], self.col2[1]]
}
}
};
}
impl_mat3!(Mat3, f32, 0.0f32);
impl_mat3!(DMat3, f64, 0.0f64);
impl Mat3 {
#[cfg(feature = "glam")]
#[inline]
pub fn apply_glam(&self, v: glam::Vec4) -> glam::Vec4 {
let m = glam::Mat3::from_cols(
glam::Vec3::from_array([self.col0[0], self.col0[1], self.col0[2]]),
glam::Vec3::from_array([self.col1[0], self.col1[1], self.col1[2]]),
glam::Vec3::from_array([self.col2[0], self.col2[1], self.col2[2]]),
);
(m * v.truncate()).extend(v.w)
}
}