use std::f64;
pub fn euclidean_distance(p1: [f64; 2], p2: [f64; 2]) -> f64 {
((p1[0] - p2[0]).powi(2) + (p1[1] - p2[1]).powi(2)).sqrt()
}
pub fn xyz_to_lab(xyz: [f64; 3], white_point: [f64; 2]) -> [f64; 3] {
let _wp_sum = white_point[0] + white_point[1] + (1.0 - white_point[0] - white_point[1]);
let xn = white_point[0] / white_point[1];
let yn = 1.0;
let zn = (1.0 - white_point[0] - white_point[1]) / white_point[1];
let x_norm = xyz[0] / xn;
let y_norm = xyz[1] / yn;
let z_norm = xyz[2] / zn;
let epsilon = 216.0 / 24389.0;
let kappa = 24389.0 / 27.0;
let fx = if x_norm > epsilon {
x_norm.powf(1.0 / 3.0)
} else {
(kappa * x_norm + 16.0) / 116.0
};
let fy = if y_norm > epsilon {
y_norm.powf(1.0 / 3.0)
} else {
(kappa * y_norm + 16.0) / 116.0
};
let fz = if z_norm > epsilon {
z_norm.powf(1.0 / 3.0)
} else {
(kappa * z_norm + 16.0) / 116.0
};
let l = 116.0 * fy - 16.0;
let a = 500.0 * (fx - fy);
let b = 200.0 * (fy - fz);
[l, a, b]
}
pub fn lab_to_lchab(lab: [f64; 3]) -> [f64; 3] {
let l = lab[0];
let a = lab[1];
let b = lab[2];
let c = (a * a + b * b).sqrt();
let h = b.atan2(a).to_degrees();
let h = if h < 0.0 { h + 360.0 } else { h };
[l, c, h]
}
pub fn lchab_to_munsell_specification(lch: [f64; 3]) -> [f64; 4] {
let (l, c, hab) = (lch[0], lch[1], lch[2]);
let code = if hab == 0.0 { 8 }
else if hab <= 36.0 { 7 }
else if hab <= 72.0 { 6 }
else if hab <= 108.0 { 5 }
else if hab <= 144.0 { 4 }
else if hab <= 180.0 { 3 }
else if hab <= 216.0 { 2 }
else if hab <= 252.0 { 1 }
else if hab <= 288.0 { 10 }
else if hab <= 324.0 { 9 }
else { 8 };
let hue_raw = (hab % 36.0) * 10.0 / 36.0;
let hue = if hue_raw == 0.0 { 10.0 } else { hue_raw };
let value = l / 10.0;
let chroma = c / 5.0;
[hue, value, chroma, code as f64]
}
pub fn xyy_to_xyz(xyy: [f64; 3]) -> [f64; 3] {
let (x, y, big_y) = (xyy[0], xyy[1], xyy[2]);
if y.abs() < 1e-10 {
return [0.0, 0.0, 0.0];
}
let big_x = x * big_y / y;
let big_z = (1.0 - x - y) * big_y / y;
[big_x, big_y, big_z]
}
pub fn xyz_to_xy(xyz: [f64; 3]) -> [f64; 2] {
let sum = xyz[0] + xyz[1] + xyz[2];
if sum.abs() < 1e-10 {
return [0.3127, 0.3290]; }
[xyz[0] / sum, xyz[1] / sum]
}
pub fn is_within_macadam_limits(xyy: [f64; 3], _illuminant: &str) -> bool {
let (x, y, _) = (xyy[0], xyy[1], xyy[2]);
if x < 0.0 || x > 1.0 || y < 0.0 || y > 1.0 {
return false;
}
let vertices = [
(0.17, 0.00), (0.00, 0.83), (0.73, 0.27), ];
let v0 = (vertices[2].0 - vertices[0].0, vertices[2].1 - vertices[0].1);
let v1 = (vertices[1].0 - vertices[0].0, vertices[1].1 - vertices[0].1);
let v2 = (x - vertices[0].0, y - vertices[0].1);
let dot00 = v0.0 * v0.0 + v0.1 * v0.1;
let dot01 = v0.0 * v1.0 + v0.1 * v1.1;
let dot02 = v0.0 * v2.0 + v0.1 * v2.1;
let dot11 = v1.0 * v1.0 + v1.1 * v1.1;
let dot12 = v1.0 * v2.0 + v1.1 * v2.1;
let inv_denom = 1.0 / (dot00 * dot11 - dot01 * dot01);
let u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
let v = (dot00 * dot12 - dot01 * dot02) * inv_denom;
(u >= 0.0) && (v >= 0.0) && (u + v <= 1.0)
}
pub struct LinearInterpolator {
x_values: Vec<f64>,
y_values: Vec<f64>,
}
impl LinearInterpolator {
pub fn new(x: Vec<f64>, y: Vec<f64>) -> Self {
Self {
x_values: x,
y_values: y,
}
}
pub fn interpolate(&self, x: f64) -> f64 {
if self.x_values.len() < 2 {
return self.y_values[0];
}
let mut i = 0;
while i < self.x_values.len() - 1 && self.x_values[i + 1] < x {
i += 1;
}
if i == self.x_values.len() - 1 {
i = self.x_values.len() - 2;
}
let x1 = self.x_values[i];
let x2 = self.x_values[i + 1];
let y1 = self.y_values[i];
let y2 = self.y_values[i + 1];
y1 + (x - x1) * (y2 - y1) / (x2 - x1)
}
}
pub struct Extrapolator {
interpolator: LinearInterpolator,
}
impl Extrapolator {
pub fn new(interpolator: LinearInterpolator) -> Self {
Self { interpolator }
}
pub fn extrapolate(&self, x: f64) -> f64 {
self.interpolator.interpolate(x)
}
}