use bevy::prelude::*;
pub fn kelvin_to_color(kelvin: f32) -> Color {
let temp = kelvin / 100.0;
let r = if temp <= 66.0 {
255.0
} else {
let x = temp - 60.0;
(329.698_73 * x.powf(-0.133_204_76)).clamp(0.0, 255.0)
};
let g = if temp <= 66.0 {
(99.470_8 * temp.ln() - 161.119_57).clamp(0.0, 255.0)
} else {
let x = temp - 60.0;
(288.122_16 * x.powf(-0.075_514_846)).clamp(0.0, 255.0)
};
let b = if temp >= 66.0 {
255.0
} else if temp <= 19.0 {
0.0
} else {
let x = temp - 10.0;
(138.517_73 * x.ln() - 305.044_8).clamp(0.0, 255.0)
};
Color::srgb(r / 255.0, g / 255.0, b / 255.0)
}
pub fn parse_color_temperature(appearance: &str) -> Option<f32> {
let mut digits = String::new();
for ch in appearance.chars() {
if ch.is_ascii_digit() {
digits.push(ch);
if digits.len() == 4 {
if let Ok(kelvin) = digits.parse::<f32>() {
if (1000.0..=20000.0).contains(&kelvin) {
return Some(kelvin);
}
}
}
} else {
digits.clear();
}
}
None
}
pub fn parse_cri(group: &str) -> f32 {
let group = group.trim().to_uppercase();
match group.as_str() {
"1A" | "1" => 95.0,
"1B" => 85.0,
"2A" | "2" => 75.0,
"2B" => 65.0,
"3" => 50.0,
"4" => 30.0,
_ => {
group.parse::<f32>().unwrap_or(80.0)
}
}
}
pub fn apply_cri_adjustment(color: Color, cri: f32) -> Color {
let saturation_factor = if cri >= 90.0 {
1.0
} else {
let t = ((cri - 50.0) / 40.0).clamp(0.0, 1.0);
0.7 + 0.3 * t
};
let linear = color.to_linear();
let luminance = 0.2126 * linear.red + 0.7152 * linear.green + 0.0722 * linear.blue;
let r = luminance + (linear.red - luminance) * saturation_factor;
let g = luminance + (linear.green - luminance) * saturation_factor;
let b = luminance + (linear.blue - luminance) * saturation_factor;
Color::linear_rgb(r, g, b)
}
pub fn heatmap_color(value: f64) -> (f32, f32, f32) {
let v = value.clamp(0.0, 1.0) as f32;
if v < 0.25 {
let t = v / 0.25;
(0.0, t, 1.0) } else if v < 0.5 {
let t = (v - 0.25) / 0.25;
(0.0, 1.0, 1.0 - t) } else if v < 0.75 {
let t = (v - 0.5) / 0.25;
(t, 1.0, 0.0) } else {
let t = (v - 0.75) / 0.25;
(1.0, 1.0 - t, 0.0) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_color_temperature() {
assert_eq!(parse_color_temperature("3000K"), Some(3000.0));
assert_eq!(parse_color_temperature("4000 Kelvin"), Some(4000.0));
assert_eq!(parse_color_temperature("LED 5000K CRI90"), Some(5000.0));
assert_eq!(parse_color_temperature("unknown"), None);
assert_eq!(parse_color_temperature("123"), None); }
#[test]
fn test_parse_cri() {
assert_eq!(parse_cri("1A"), 95.0);
assert_eq!(parse_cri("1B"), 85.0);
assert_eq!(parse_cri("2A"), 75.0);
assert_eq!(parse_cri("2B"), 65.0);
assert_eq!(parse_cri("3"), 50.0);
assert_eq!(parse_cri("4"), 30.0);
assert_eq!(parse_cri("90"), 90.0); assert_eq!(parse_cri("unknown"), 80.0); }
#[test]
fn test_heatmap_color() {
let (r, g, b) = heatmap_color(0.0);
assert_eq!((r, g, b), (0.0, 0.0, 1.0));
let (r, g, b) = heatmap_color(1.0);
assert_eq!((r, g, b), (1.0, 0.0, 0.0)); }
}