1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use clamped::Clamp;
use num_traits::AsPrimitive;
use std::ops::Rem;

#[derive(Debug, Copy, Clone, Default)]
pub struct Color {
    pub r: f32,
    pub g: f32,
    pub b: f32,
    pub a: f32,
}

impl Color {
    pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
        Color { r, g, b, a }
    }

    pub fn rgb(r: f32, g: f32, b: f32) -> Color {
        Color { r, g, b, a: 1.0 }
    }

    pub fn rgba_i(r: u8, g: u8, b: u8, a: u8) -> Color {
        Color {
            r: r as f32 / 255.0,
            g: g as f32 / 255.0,
            b: b as f32 / 255.0,
            a: a as f32 / 255.0,
        }
    }

    pub fn rgb_i(r: u8, g: u8, b: u8) -> Color {
        Self::rgba_i(r, g, b, 255)
    }

    pub fn lerp(self, c: Color, u: f32) -> Color {
        let u = u.clamped(0.0, 1.0);
        let om = 1.0 - u;
        Color {
            r: self.r * om + c.r * u,
            g: self.g * om + c.g * u,
            b: self.b * om + c.b * u,
            a: self.a * om + c.a * u,
        }
    }

    pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Color {
        let mut h = h.rem(1.0);
        if h < 0.0 {
            h += 1.0;
        }
        let s = s.clamped(0.0, 1.0);
        let l = l.clamped(0.0, 1.0);
        let m2 = if l <= 0.5 {
            l * (1.0 + s)
        } else {
            l + s - l * s
        };
        let m1 = 2.0 * l - m2;
        Color {
            r: hue(h + 1.0 / 3.0, m1, m2).clamped(0.0, 1.0),
            g: hue(h, m1, m2).clamped(0.0, 1.0),
            b: hue(h - 1.0 / 3.0, m1, m2).clamped(0.0, 1.0),
            a,
        }
    }

    pub fn hsl(h: f32, s: f32, l: f32) -> Color {
        Self::hsla(h, s, l, 1.0)
    }
}

impl<T: AsPrimitive<f32>> From<(T, T, T)> for Color {
    fn from((r, g, b): (T, T, T)) -> Self {
        Color::rgb(r.as_(), g.as_(), b.as_())
    }
}

impl<T: AsPrimitive<f32>> From<(T, T, T, T)> for Color {
    fn from((r, g, b, a): (T, T, T, T)) -> Self {
        Color::rgba(r.as_(), g.as_(), b.as_(), a.as_())
    }
}

fn hue(mut h: f32, m1: f32, m2: f32) -> f32 {
    if h < 0.0 {
        h += 1.0;
    }
    if h > 1.0 {
        h -= 1.0
    };
    if h < 1.0 / 6.0 {
        return m1 + (m2 - m1) * h * 6.0;
    } else if h < 3.0 / 6.0 {
        m2
    } else if h < 4.0 / 6.0 {
        m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6.0
    } else {
        m1
    }
}