use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
use std::ops::{Add, Mul, Sub};
const RESET: &str = "\x1b[0m";
const BOLD: &str = "\x1b[1m";
const DIM: &str = "\x1b[2m";
const BLACK: &str = "\x1b[30m";
const RED: &str = "\x1b[31m";
const GREEN: &str = "\x1b[32m";
const YELLOW: &str = "\x1b[33m";
const BLUE: &str = "\x1b[34m";
const MAGENTA: &str = "\x1b[35m";
const CYAN: &str = "\x1b[36m";
const WHITE: &str = "\x1b[37m";
pub const CURSOR_TO_START: &str = "\r";
pub const ERASE_LINE_TO_END: &str = "\x1b[K";
pub trait Colors {
fn bold(&self) -> String;
fn dimmed(&self) -> String;
fn black(&self) -> String;
fn red(&self) -> String;
fn green(&self) -> String;
fn yellow(&self) -> String;
fn blue(&self) -> String;
fn magenta(&self) -> String;
fn cyan(&self) -> String;
fn white(&self) -> String;
fn to_line_start(&self) -> String;
}
impl<T> Colors for T
where
T: Display,
{
fn bold(&self) -> String {
format!("{BOLD}{self}{RESET}")
}
fn dimmed(&self) -> String {
format!("{DIM}{self}{RESET}")
}
fn black(&self) -> String {
format!("{BLACK}{self}{RESET}")
}
fn red(&self) -> String {
format!("{RED}{self}{RESET}")
}
fn green(&self) -> String {
format!("{GREEN}{self}{RESET}")
}
fn yellow(&self) -> String {
format!("{YELLOW}{self}{RESET}")
}
fn blue(&self) -> String {
format!("{BLUE}{self}{RESET}")
}
fn magenta(&self) -> String {
format!("{MAGENTA}{self}{RESET}")
}
fn cyan(&self) -> String {
format!("{CYAN}{self}{RESET}")
}
fn white(&self) -> String {
format!("{WHITE}{self}{RESET}")
}
fn to_line_start(&self) -> String {
format!("{CURSOR_TO_START}{self}{ERASE_LINE_TO_END}")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
pub struct ColorRGB {
pub red: f64,
pub green: f64,
pub blue: f64,
}
impl ColorRGB {
#[inline(always)]
pub fn new(red: f64, green: f64, blue: f64) -> Self {
Self { red, green, blue }
}
#[inline(always)]
pub fn from_slice(slice: &[u8]) -> Self {
Self {
red: slice[0] as f64 / 255.0,
green: slice[1] as f64 / 255.0,
blue: slice[2] as f64 / 255.0,
}
}
#[inline(always)]
pub fn write_to_slice(&self, slice: &mut [u8]) {
slice[0] = (self.red * 255.0).clamp(0.0, 255.0) as u8;
slice[1] = (self.green * 255.0).clamp(0.0, 255.0) as u8;
slice[2] = (self.blue * 255.0).clamp(0.0, 255.0) as u8;
}
#[inline(always)]
pub fn lerp(self, other: Self, t: f64) -> Self {
let t_clamp = t.clamp(0.0, 1.0);
self + (other - self) * t_clamp
}
#[inline(always)]
pub fn rotated(&self) -> Self {
Self {
red: self.green,
green: self.blue,
blue: self.red,
}
}
#[inline(always)]
pub const fn to_array(&self) -> [f64; 3] {
[self.red, self.green, self.blue]
}
#[inline(always)]
pub fn max_component(&self) -> f64 {
self.red.max(self.green).max(self.blue)
}
#[inline(always)]
pub fn saturate_components(&self) -> Self {
let max = self.max_component();
if max > 0.0 {
Self {
red: self.red / max,
green: self.green / max,
blue: self.blue / max,
}
} else {
*self
}
}
#[inline(always)]
pub fn scale(self, factor: f64) -> Self {
self * factor
}
#[inline(always)]
pub fn clamp_bounds(&self) -> Self {
Self {
red: self.red.clamp(0.0, 1.0),
green: self.green.clamp(0.0, 1.0),
blue: self.blue.clamp(0.0, 1.0),
}
}
#[inline(always)]
pub fn squared(&self) -> Self {
Self {
red: self.red * self.red,
green: self.green * self.green,
blue: self.blue * self.blue,
}
}
#[inline(always)]
pub fn sqrt(&self) -> Self {
Self {
red: self.red.sqrt(),
green: self.green.sqrt(),
blue: self.blue.sqrt(),
}
}
#[inline(always)]
pub fn luminance(self) -> f64 {
self.red
.mul_add(0.2126, self.green.mul_add(0.7152, self.blue * 0.0722))
}
#[inline(always)]
pub fn saturate(self) -> Self {
Self {
red: self.red.clamp(0.0, 1.0),
green: self.green.clamp(0.0, 1.0),
blue: self.blue.clamp(0.0, 1.0),
}
}
#[inline(always)]
pub fn gamma2(self) -> Self {
Self {
red: self.red * self.red,
green: self.green * self.green,
blue: self.blue * self.blue,
}
}
#[inline(always)]
pub fn ungamma2(self) -> Self {
Self {
red: self.red.sqrt(),
green: self.green.sqrt(),
blue: self.blue.sqrt(),
}
}
#[inline(always)]
pub fn blend(self, other: Self, alpha: f64) -> Self {
self.gamma2().lerp(other.gamma2(), alpha).ungamma2()
}
#[inline(always)]
pub fn splat(v: f64) -> Self {
Self::new(v, v, v)
}
#[inline(always)]
pub fn complementary(self) -> Self {
1.0 - self
}
#[inline(always)]
pub fn boost(self, factor: f64) -> Self {
(self * factor).saturate()
}
}
impl Add for ColorRGB {
type Output = Self;
#[inline(always)]
fn add(self, other: Self) -> Self::Output {
Self {
red: self.red + other.red,
green: self.green + other.green,
blue: self.blue + other.blue,
}
}
}
impl Sub for ColorRGB {
type Output = Self;
#[inline(always)]
fn sub(self, other: Self) -> Self::Output {
Self {
red: self.red - other.red,
green: self.green - other.green,
blue: self.blue - other.blue,
}
}
}
impl Sub<ColorRGB> for f64 {
type Output = ColorRGB;
#[inline(always)]
fn sub(self, other: ColorRGB) -> Self::Output {
ColorRGB {
red: self - other.red,
green: self - other.green,
blue: self - other.blue,
}
}
}
impl Mul<f64> for ColorRGB {
type Output = Self;
#[inline(always)]
fn mul(self, scalar: f64) -> Self::Output {
Self {
red: self.red * scalar,
green: self.green * scalar,
blue: self.blue * scalar,
}
}
}
impl Mul for ColorRGB {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
Self::new(
self.red * rhs.red,
self.green * rhs.green,
self.blue * rhs.blue,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct NeonColor {
pub color_rgb: ColorRGB,
pub name: &'static str,
}
impl NeonColor {
#[inline(always)]
pub fn rotated(&self) -> ColorRGB {
self.color_rgb.rotated()
}
#[inline(always)]
pub fn lerp(&self, other: &Self, t: f64) -> ColorRGB {
self.color_rgb.lerp(other.color_rgb, t)
}
#[inline(always)]
pub const fn to_array(&self) -> [f64; 3] {
self.color_rgb.to_array()
}
}
impl fmt::Display for NeonColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"RGB [{:.2}, {:.2}, {:.2}] {}",
self.color_rgb.red, self.color_rgb.green, self.color_rgb.blue, self.name
)
}
}
pub const NEON_PALETTES: [NeonColor; 20] = [
NeonColor {
color_rgb: ColorRGB {
red: 1.0,
green: 0.05,
blue: 0.55,
},
name: "Neon Rose - Punchy Pink",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.0,
green: 0.95,
blue: 0.85,
},
name: "Vivid Turquoise - Electric Teal",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.95,
green: 0.45,
blue: 0.0,
},
name: "Tangerine Dream - Electric Orange",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.4,
green: 0.9,
blue: 0.0,
},
name: "Radioactive Lime - Acid Green",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.9,
green: 0.9,
blue: 0.0,
},
name: "Laser Lemon - Bright Yellow",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.05,
green: 0.8,
blue: 1.0,
},
name: "Blue Bolt - Intense Sky Cyan",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.85,
green: 0.15,
blue: 1.0,
},
name: "Radiant Orchid - Electric Violet",
},
NeonColor {
color_rgb: ColorRGB {
red: 1.0,
green: 0.35,
blue: 0.1,
},
name: "Fiery Coral - Sunset Flame",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.1,
green: 0.95,
blue: 0.4,
},
name: "Mint Spark - Spring Neon Green",
},
NeonColor {
color_rgb: ColorRGB {
red: 1.0,
green: 0.0,
blue: 0.35,
},
name: "Cherry Flare - Electric Ruby",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.75,
green: 0.95,
blue: 0.0,
},
name: "Vibrant Chartreuse - Pear Glow",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.0,
green: 1.0,
blue: 0.7,
},
name: "Aqua Glow - Supercharged seafoam",
},
NeonColor {
color_rgb: ColorRGB {
red: 1.0,
green: 0.55,
blue: 0.4,
},
name: "Warm Apricot - Peach Spark",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.9,
green: 0.2,
blue: 0.9,
},
name: "Cyberpunk Fuchsia - Hot Magenta",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.35,
green: 1.0,
blue: 0.5,
},
name: "Bright Emerald - Neon Clover",
},
NeonColor {
color_rgb: ColorRGB {
red: 1.0,
green: 0.85,
blue: 0.0,
},
name: "Solar Flare - Amber Gold",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.55,
green: 0.35,
blue: 1.0,
},
name: "Electric Amethyst - Bright Violet",
},
NeonColor {
color_rgb: ColorRGB {
red: 1.0,
green: 0.25,
blue: 0.5,
},
name: "Flamingo Neon - Warm Pink",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.2,
green: 0.9,
blue: 0.9,
},
name: "Glacier Cyan - Ice Blue Glow",
},
NeonColor {
color_rgb: ColorRGB {
red: 0.85,
green: 1.0,
blue: 0.2,
},
name: "Radioactive Melon - Lemon Lime Flare",
},
];