use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
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: f32,
pub green: f32,
pub blue: f32,
}
impl ColorRGB {
#[inline(always)]
pub fn new(red: f32, green: f32, blue: f32) -> Self {
Self { red, green, blue }
}
#[inline(always)]
pub fn lerp(&self, other: &Self, t: f32) -> Self {
let t_clamp = t.clamp(0.0, 1.0);
Self {
red: self.red * t_clamp + other.red * (1.0 - t_clamp),
green: self.green * t_clamp + other.green * (1.0 - t_clamp),
blue: self.blue * t_clamp + other.blue * (1.0 - 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) -> [f32; 3] {
[self.red, self.green, self.blue]
}
#[inline(always)]
pub fn complementary(&self) -> Self {
Self {
red: 1.0 - self.red,
green: 1.0 - self.green,
blue: 1.0 - self.blue,
}
}
#[inline(always)]
pub fn max_component(&self) -> f32 {
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: f32) -> Self {
Self {
red: self.red * factor,
green: self.green * factor,
blue: self.blue * 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),
}
}
}
#[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: f32) -> ColorRGB {
self.color_rgb.lerp(&other.color_rgb, t)
}
#[inline(always)]
pub const fn to_array(&self) -> [f32; 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",
},
];