Back in comment #32 (comment) you wrote about three ways to bring a WideRgb value back into the valid RGB range (bring it into the RGB gamut):
- The simplest would be to clamp the out-of-range R, G, or B value, either on <0.0 or >1.0 values.
- The second approach would be to transform all R, G, and B values by using an offset and scaling, effectively desaturating the colors and scaling the luminance.
- A third one would be to convert an XYZ or RGB value to a CIELAB or CIECAM value, and scale the chroma value down, keeping the hue value the same, until the RGB value is within gamut. Hue lines are not straight lines in the CIE xy color diagrams...
The first two options are covered by WideRgb::clamp and WideRgb::compress respectively. I'm now looking into the third option as a way to improve the rendering of my chromaticity diagram. I would love to add a method for this third option onto WideRgb so it becomes part of the library.
I'm toying with this at the moment to try to understand more about CIECAM. I'm rendering the "hue lines" to try to understand them. But I get weird results. I'm probably just doing something fundamentally wrong. I'm opening this issue to try to understand what, in order to be able to implement and contribute something like WideRgb::constrain_keeping_hue() -> Rgb.
Here is my code for taking an XYZ value and lowering the chroma until no RGB value is negative. And drawing a line along the chromaticity coordinates visited along the way. I then run this for a set of XYZ values along the observer's spectral locus.
The abort condition or the step size is not important right now, but rather the fact that it starts out far outside the chromaticity diagram, and then when passing the boundary it does so in a very different spot than the original XYZ value. This means that if I convert an XYZ value to CIECAM16 and then back to XYZ again, I never get the same result back, no matter the chroma used (even the original chroma). Why not?
fn compute_ciecam_hue_line(xyz: XYZ, observer: Observer) -> Vec<Vertex> {
let xyzn = observer.xyz_d65();
let viewconditions = ViewConditions::default();
let xyz_to_vertex = |xyz: XYZ| {
let chromaticity = xyz.chromaticity();
Vertex {
position: [chromaticity.x() as f32, chromaticity.y() as f32],
color: [0.5, 0.5, 0.5],
}
};
let mut vertices = vec![];
// Start with the original XYZ value
vertices.push(xyz_to_vertex(xyz));
// The lightness and hue stay fixed. We only lower the chroma.
let [lightness, mut chroma, hue] =
CieCam16::from_xyz(xyz, xyzn, viewconditions).unwrap().jch();
loop {
let cam = CieCam16::new([lightness, chroma, hue], xyzn, viewconditions);
let xyz = cam.xyz(None, None).unwrap();
vertices.push(xyz_to_vertex(xyz));
// When there are no negative RGB values, we stop.
if xyz.rgb(None).values().iter().all(|&v| v >= 0.0) {
break;
}
chroma *= 0.95;
}
vertices
}

Back in comment #32 (comment) you wrote about three ways to bring a
WideRgbvalue back into the valid RGB range (bring it into the RGB gamut):The first two options are covered by
WideRgb::clampandWideRgb::compressrespectively. I'm now looking into the third option as a way to improve the rendering of my chromaticity diagram. I would love to add a method for this third option ontoWideRgbso it becomes part of the library.I'm toying with this at the moment to try to understand more about CIECAM. I'm rendering the "hue lines" to try to understand them. But I get weird results. I'm probably just doing something fundamentally wrong. I'm opening this issue to try to understand what, in order to be able to implement and contribute something like
WideRgb::constrain_keeping_hue() -> Rgb.Here is my code for taking an XYZ value and lowering the chroma until no RGB value is negative. And drawing a line along the chromaticity coordinates visited along the way. I then run this for a set of XYZ values along the observer's spectral locus.
The abort condition or the step size is not important right now, but rather the fact that it starts out far outside the chromaticity diagram, and then when passing the boundary it does so in a very different spot than the original XYZ value. This means that if I convert an XYZ value to CIECAM16 and then back to XYZ again, I never get the same result back, no matter the chroma used (even the original chroma). Why not?