use std::f64::consts::PI;
use super::parsing::hue_number_to_string;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MunsellCartesian {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl MunsellCartesian {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
pub fn distance(&self, other: &MunsellCartesian) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
let dz = self.z - other.z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MunsellSpec {
pub hue_number: f64,
pub value: f64,
pub chroma: f64,
}
impl MunsellSpec {
pub fn new(hue_number: f64, value: f64, chroma: f64) -> Self {
Self {
hue_number,
value,
chroma,
}
}
pub fn neutral(value: f64) -> Self {
Self {
hue_number: 0.0,
value,
chroma: 0.0,
}
}
pub fn to_cartesian(&self) -> MunsellCartesian {
let theta = self.hue_number * 9.0 * PI / 180.0;
MunsellCartesian {
x: self.chroma * theta.cos(),
y: self.chroma * theta.sin(),
z: self.value,
}
}
pub fn from_cartesian(cart: &MunsellCartesian) -> Self {
let chroma = (cart.x * cart.x + cart.y * cart.y).sqrt();
if chroma < 1e-10 {
return Self::neutral(cart.z);
}
let mut theta = cart.y.atan2(cart.x);
if theta < 0.0 {
theta += 2.0 * PI;
}
let hue_number = theta * 180.0 / PI / 9.0;
Self {
hue_number: hue_number % 40.0,
value: cart.z,
chroma,
}
}
pub fn to_notation(&self) -> String {
if self.chroma < 0.5 {
return format!("N {:.1}/", self.value);
}
let (hue_str, _) = hue_number_to_string(self.hue_number);
format!("{} {:.1}/{:.1}", hue_str, self.value, self.chroma)
}
pub fn distance_from(&self, other: &MunsellSpec) -> f64 {
self.to_cartesian().distance(&other.to_cartesian())
}
}