use super::{CamJCh, CamTransforms, ViewConditions};
use nalgebra::Vector3;
use crate::{
error::Error,
observer::Observer,
rgb::{RgbSpace, WideRgb},
xyz::{RelXYZ, XYZ},
};
#[derive(Debug)]
pub struct CieCam02(CamJCh);
impl CieCam02 {
pub fn new(jch: [f64; 3], xyzn: XYZ, vc: ViewConditions) -> Self {
Self(CamJCh {
jch: Vector3::from(jch),
xyzn,
vc,
})
}
pub fn from_xyz(rxyz: RelXYZ, vc: ViewConditions) -> Self {
Self(CamJCh::from_xyz(rxyz, vc, super::Cam::CieCam02))
}
pub fn xyz(
&self,
opt_xyzn: Option<XYZ>,
opt_viewconditions: Option<ViewConditions>,
) -> Result<RelXYZ, Error> {
self.0
.xyz(opt_xyzn, opt_viewconditions, super::Cam::CieCam02)
}
pub fn rgb(
&self,
rgbspace: RgbSpace,
opt_viewconditions: Option<ViewConditions>,
) -> Result<WideRgb, Error> {
self.0
.rgb(rgbspace, opt_viewconditions, super::Cam::CieCam02)
}
}
impl CamTransforms for CieCam02 {
fn jch_vec(&self) -> &Vector3<f64> {
&self.0.jch
}
fn view_conditions(&self) -> &ViewConditions {
&self.0.vc
}
fn observer(&self) -> Observer {
self.0.xyzn.observer
}
fn xyzn(&self) -> &Vector3<f64> {
&self.0.xyzn.xyz
}
}
#[cfg(test)]
mod cam02_test {
use approx::assert_abs_diff_eq;
use nalgebra::Matrix3;
use crate::cam::{CamTransforms, CieCam02, ViewConditions, M16, M16INV};
use crate::observer::Observer;
use crate::xyz::{RelXYZ, XYZ};
#[test]
fn test_m16() {
approx::assert_abs_diff_eq!(M16INV * M16, Matrix3::identity(), epsilon = 1E-8);
}
#[test]
fn test_worked_example() {
let white_point = XYZ::new([98.88, 90.0, 32.03], Observer::Cie1931);
let rxyz = RelXYZ::new([19.31, 23.93, 10.14], white_point);
let vc = ViewConditions::new(20.0, 18.0, 0.69, 1.0, 1.0, None);
let cam = CieCam02::from_xyz(rxyz, vc);
let jch = cam.jch_vec();
let &[j, c, h] = jch.as_ref();
assert_abs_diff_eq!(j, 47.6856, epsilon = 1E-4); assert_abs_diff_eq!(c, 36.0527, epsilon = 1E-4); assert_abs_diff_eq!(h, 185.3445, epsilon = 1E-4);
let vc = ViewConditions::new(200.0, 18.0, 0.69, 1.0, 1.0, None);
let cam = CieCam02::from_xyz(rxyz, vc);
let jch = cam.jch_vec();
let &[j, c, h] = jch.as_ref();
assert_abs_diff_eq!(j, 48.0314, epsilon = 1E-4); assert_abs_diff_eq!(c, 38.7789, epsilon = 1E-4); assert_abs_diff_eq!(h, 191.0452, epsilon = 1E-4); }
}
#[cfg(test)]
mod cam02_round_trip_tests {
use crate::{
cam::{CamTransforms, CieCam02, ViewConditions},
observer::Observer::{self, Cie1931},
xyz::{RelXYZ, XYZ},
};
use approx::assert_abs_diff_eq;
#[test]
fn xyz_jch_xyz_round_trip() {
let samples = &[
[19.01, 20.00, 21.78],
[41.24, 21.26, 1.93],
[95.05, 100.00, 108.88],
[50.00, 50.00, 50.0],
[0.00, 0.00, 0.00],
[1.00, 1.00, 1.00],
[70.00, 50.00, 30.00],
[30.00, 60.00, 90.00],
[12.14, 28.56, 5.00],
[5.00, 12.14, 28.56],
[100.00, 100.00, 100.00],
[50.00, 50.00, 50.00],
[20.00, 30.00, 40.00],
[10.00, 20.00, 30.00],
];
for &xyz_arr in samples {
let xyz = XYZ::new(xyz_arr, Observer::Cie1931);
let xyz_d65 = Cie1931.xyz_d65();
let rxyz = RelXYZ::from_xyz(xyz, xyz_d65).unwrap();
let cam = CieCam02::from_xyz(rxyz, ViewConditions::default());
let jch = cam.jch();
let cam_back = CieCam02::new(jch, xyz_d65, ViewConditions::default());
let xyz_back = cam_back.xyz(None, None).unwrap();
assert_abs_diff_eq!(rxyz, xyz_back, epsilon = 1e-6);
}
}
}