use crate::hct::cam16::Cam16;
use crate::utils::color_utils::ColorUtils;
use crate::utils::math_utils::MathUtils;
use std::f64::consts::PI;
#[derive(Debug, Clone, PartialEq)]
pub struct ViewingConditions {
pub n: f64,
pub aw: f64,
pub nbb: f64,
pub ncb: f64,
pub c: f64,
pub nc: f64,
pub rgb_d: [f64; 3],
pub fl: f64,
pub fl_root: f64,
pub z: f64,
}
impl ViewingConditions {
#[must_use]
pub fn make(
white_point: [f64; 3],
adapting_luminance: f64,
background_lstar: f64,
surround: f64,
discounting_illuminant: bool,
) -> Self {
let background_lstar = background_lstar.max(0.1);
let matrix = Cam16::XYZ_TO_CAM16RGB;
let r_w = white_point[2].mul_add(
matrix[0][2],
white_point[0].mul_add(matrix[0][0], white_point[1] * matrix[0][1]),
);
let g_w = white_point[2].mul_add(
matrix[1][2],
white_point[0].mul_add(matrix[1][0], white_point[1] * matrix[1][1]),
);
let b_w = white_point[2].mul_add(
matrix[2][2],
white_point[0].mul_add(matrix[2][0], white_point[1] * matrix[2][1]),
);
let f = 0.8 + surround / 10.0;
let c = if f >= 0.9 {
MathUtils::lerp(0.59, 0.69, (f - 0.9) * 10.0)
} else {
MathUtils::lerp(0.525, 0.59, (f - 0.8) * 10.0)
};
let mut d = if discounting_illuminant {
1.0
} else {
f * (1.0f64 / 3.6).mul_add(-((-adapting_luminance - 42.0) / 92.0).exp(), 1.0)
};
d = d.clamp(0.0, 1.0);
let nc = f;
let rgb_d = [
d * (100.0 / r_w) + 1.0 - d,
d * (100.0 / g_w) + 1.0 - d,
d * (100.0 / b_w) + 1.0 - d,
];
let k = 1.0 / 5.0f64.mul_add(adapting_luminance, 1.0);
let k4 = k * k * k * k;
let k4_f = 1.0 - k4;
let fl = k4 * adapting_luminance + 0.1 * k4_f * k4_f * (5.0 * adapting_luminance).cbrt();
let n = ColorUtils::y_from_lstar(background_lstar) / white_point[1];
let z = 1.48 + n.sqrt();
let nbb = 0.725 / n.powf(0.2);
let ncb = nbb;
let rgb_a_factors = [
(fl * rgb_d[0] * r_w / 100.0).powf(0.42),
(fl * rgb_d[1] * g_w / 100.0).powf(0.42),
(fl * rgb_d[2] * b_w / 100.0).powf(0.42),
];
let rgb_a = [
400.0 * rgb_a_factors[0] / (rgb_a_factors[0] + 27.13),
400.0 * rgb_a_factors[1] / (rgb_a_factors[1] + 27.13),
400.0 * rgb_a_factors[2] / (rgb_a_factors[2] + 27.13),
];
let aw = 0.05f64.mul_add(rgb_a[2], 2.0f64.mul_add(rgb_a[0], rgb_a[1])) * nbb;
Self {
n,
aw,
nbb,
ncb,
c,
nc,
rgb_d,
fl,
fl_root: fl.powf(0.25),
z,
}
}
#[must_use]
pub fn default_with_background_lstar(lstar: f64) -> Self {
Self::make(
ColorUtils::white_point_d65(),
200.0 / PI * ColorUtils::y_from_lstar(50.0) / 100.0,
lstar,
2.0,
false,
)
}
}
impl Default for ViewingConditions {
fn default() -> Self {
Self::default_with_background_lstar(50.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_viewing_conditions() {
let vc = ViewingConditions::default();
assert!((vc.n - 0.18418).abs() < 0.0001);
assert!((vc.aw - 29.981).abs() < 0.001);
}
}