use std::fmt;
use crate::sys;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl Color {
pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
Self { r, g, b, a }
}
pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
Self::new(r, g, b, 1.0)
}
pub fn from_rgba_bytes(r: u8, g: u8, b: u8, a: u8) -> Self {
Self::new(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
a as f32 / 255.0,
)
}
pub fn from_rgb_bytes(r: u8, g: u8, b: u8) -> Self {
Self::from_rgba_bytes(r, g, b, 255)
}
pub fn from_imgui_u32(abgr: u32) -> Self {
unsafe {
let v = sys::igColorConvertU32ToFloat4(abgr);
Self::new(v.x, v.y, v.z, v.w)
}
}
pub fn from_rgb_u32(rgb: u32) -> Self {
Self::from_rgba_bytes(
((rgb >> 16) & 0xFF) as u8,
((rgb >> 8) & 0xFF) as u8,
(rgb & 0xFF) as u8,
255,
)
}
pub fn to_imgui_u32(self) -> u32 {
unsafe {
sys::igColorConvertFloat4ToU32(sys::ImVec4_c {
x: self.r,
y: self.g,
z: self.b,
w: self.a,
})
}
}
pub fn to_array(self) -> [f32; 4] {
[self.r, self.g, self.b, self.a]
}
pub fn from_array(arr: [f32; 4]) -> Self {
Self::new(arr[0], arr[1], arr[2], arr[3])
}
pub fn with_alpha(mut self, alpha: f32) -> Self {
self.a = alpha;
self
}
pub fn lerp(self, other: Self, t: f32) -> Self {
let t = t.clamp(0.0, 1.0);
Self::new(
self.r + (other.r - self.r) * t,
self.g + (other.g - self.g) * t,
self.b + (other.b - self.b) * t,
self.a + (other.a - self.a) * t,
)
}
#[doc(alias = "ColorConvertRGBtoHSV")]
pub fn to_hsv01(self) -> (f32, f32, f32) {
let mut h = 0.0;
let mut s = 0.0;
let mut v = 0.0;
unsafe {
sys::igColorConvertRGBtoHSV(self.r, self.g, self.b, &mut h, &mut s, &mut v);
}
(h, s, v)
}
#[doc(alias = "ColorConvertHSVtoRGB")]
pub fn from_hsv01(h: f32, s: f32, v: f32) -> Self {
let mut r = 0.0;
let mut g = 0.0;
let mut b = 0.0;
unsafe {
sys::igColorConvertHSVtoRGB(h, s, v, &mut r, &mut g, &mut b);
}
Self::rgb(r, g, b)
}
pub fn to_hsv(self) -> (f32, f32, f32) {
let max = self.r.max(self.g).max(self.b);
let min = self.r.min(self.g).min(self.b);
let delta = max - min;
let h = if delta == 0.0 {
0.0
} else if max == self.r {
60.0 * (((self.g - self.b) / delta) % 6.0)
} else if max == self.g {
60.0 * (((self.b - self.r) / delta) + 2.0)
} else {
60.0 * (((self.r - self.g) / delta) + 4.0)
};
let s = if max == 0.0 { 0.0 } else { delta / max };
let v = max;
(h, s, v)
}
pub fn from_hsv(h: f32, s: f32, v: f32) -> Self {
let h = h % 360.0;
let c = v * s;
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
let m = v - c;
let (r, g, b) = if h < 60.0 {
(c, x, 0.0)
} else if h < 120.0 {
(x, c, 0.0)
} else if h < 180.0 {
(0.0, c, x)
} else if h < 240.0 {
(0.0, x, c)
} else if h < 300.0 {
(x, 0.0, c)
} else {
(c, 0.0, x)
};
Self::new(r + m, g + m, b + m, 1.0)
}
}
impl Default for Color {
fn default() -> Self {
Self::new(1.0, 1.0, 1.0, 1.0)
}
}
impl From<[f32; 4]> for Color {
fn from(arr: [f32; 4]) -> Self {
Self::from_array(arr)
}
}
impl From<Color> for [f32; 4] {
fn from(color: Color) -> Self {
color.to_array()
}
}
impl From<(f32, f32, f32, f32)> for Color {
fn from((r, g, b, a): (f32, f32, f32, f32)) -> Self {
Self::new(r, g, b, a)
}
}
impl From<Color> for (f32, f32, f32, f32) {
fn from(color: Color) -> Self {
(color.r, color.g, color.b, color.a)
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"rgba({:.3}, {:.3}, {:.3}, {:.3})",
self.r, self.g, self.b, self.a
)
}
}
impl Color {
pub const TRANSPARENT: Color = Color::new(0.0, 0.0, 0.0, 0.0);
pub const BLACK: Color = Color::new(0.0, 0.0, 0.0, 1.0);
pub const WHITE: Color = Color::new(1.0, 1.0, 1.0, 1.0);
pub const RED: Color = Color::new(1.0, 0.0, 0.0, 1.0);
pub const GREEN: Color = Color::new(0.0, 1.0, 0.0, 1.0);
pub const BLUE: Color = Color::new(0.0, 0.0, 1.0, 1.0);
pub const YELLOW: Color = Color::new(1.0, 1.0, 0.0, 1.0);
pub const CYAN: Color = Color::new(0.0, 1.0, 1.0, 1.0);
pub const MAGENTA: Color = Color::new(1.0, 0.0, 1.0, 1.0);
}