Code Monkey home page Code Monkey logo

Comments (27)

mbostock avatar mbostock commented on May 2, 2024 3

A cubehelix disk, for fun:

disc((r,t) => d3.cubehelix(t, 1, Math.sqrt(1 - r / 256)))

image

from d3-color.

jrus avatar jrus commented on May 2, 2024 3

LCh and HCL as aliases of the same color space. CIELCh is cylindrical CIELAB, whereas HCL is cylindrical CIELuv

No these names are not at all consistent from source to source. People should specify L*C*abh*ab or the like if they want to be unambiguous.

There is no inherent meaning to “LCh” or “HCL”.

Nowadays it also doesn’t really make sense to use CIELUV for anything.

If CIELAB is not meeting your needs for one reason or another try IPT or CAM16 or Jzazbz or ....

It's worth noting that R's colorspace package defines HCL to be polar Luv, and not polar Lab

That link uses the names polarLAB and polarLUV, which are relatively unambiguous, and a better choice than HCL or LCh. Aliasing the name HCL to polarLUV when the order of the input parameters is [L*, C*uv, h*uv] seems like a horrible mistake on their part.

Using names like polarCIELAB or CIELAB_LCh would be the clearest.

from d3-color.

danburzo avatar danburzo commented on May 2, 2024 2

@curran, oops :) Missed that, thanks! Updated the link in the original comment. (Mind some experiments I'm currently doing with proportional R,G,B clamping)

from d3-color.

curran avatar curran commented on May 2, 2024 2

That is beautiful!

from d3-color.

petulla avatar petulla commented on May 2, 2024 2

@mbostock Mike, here's a quick and dirty implementation of R's max_chroma to validate your assumption.

https://observablehq.com/d/f2f806f521cbb1fc

Screen Shot 2019-06-28 at 6 40 09 PM

The R table is quite large (36k entry hash table).. and I didn't optimize the lookup. Curious of ideas you have for adaptation. Bst/quicksort come to mind if we can reduce the table size. But I feel like reducing the table down might be a better place to start.

Kicker.. here's the max chroma diagram (x=H, y=L)

Screen Shot 2019-06-28 at 6 38 38 PM

from d3-color.

curran avatar curran commented on May 2, 2024 1

This is fascinating work! Really appreciate the attention to detail.

from d3-color.

jrus avatar jrus commented on May 2, 2024 1

Clamping chroma to precisely the gamut boundary results in very sharp peaks.

For many purposes, a smoothed version is more useful,
https://observablehq.com/@jrus/diffused-gamut-picture

from d3-color.

mbostock avatar mbostock commented on May 2, 2024 1

If I’m understanding correctly, then, it’s a misnomer for d3-color to treat LCh and HCL as aliases of the same color space. CIELCh is cylindrical CIELAB, whereas HCL is cylindrical CIELuv. Perhaps we should change d3-color to use cylindrical CIELuv for d3.hcl.

https://en.m.wikipedia.org/wiki/CIELAB_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

I can give binary search a shot. Can you let me know the top-level API you have in mind for this?

(For visual reference, here's a video of how the sRGB gamut looks in LCh, in Lab, and in XYZ)

Also, it seems that in R fixup just clamps the R, G, B values to the [0-255] interval, if I'm looking in the right place, but I'll be curious to see if finding the largest Chroma that's displayable gives substantially different RGB values than plain clamping.

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

Hmm, we already clamp to [0, 255] when formatting RGB as a string:

d3-color/src/color.js

Lines 241 to 248 in 8c37e32

toString: function() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "rgb(" : "rgba(")
+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
+ (a === 1 ? ")" : ", " + a + ")");
}

So, maybe we don’t need this after all?

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

I wrote an Observable notebook to explore the difference between RGB clamping and Chroma clamping: https://beta.observablehq.com/@danburzo/hcl-chroma-clamping-vs-rgb-clamping

Chroma clamping looks like the 'correct' solution to me, because it does not alter the hue of the resulting color when you increase the chroma past the RGB gamut. (This happens because the RGB doesn't care how far off the R, G, and B components are out of bounds). It also maps L = 0 to pure black and L = 100 to pure white.

(Tangential question: can we somehow proportionally adjust the R, G, B components to get the same result as chroma clamping, but much faster?)

P.S. Apparently L = 100 does not result in a displayable color regardless of the chroma, so taking the L slider to the max will currently result in an infinite loop :P

from d3-color.

curran avatar curran commented on May 2, 2024

@danburzo I can't see the notebook. Did you hit "publish"?

Thanks for this cool work!

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

I've updated the notebook to reflect the chroma clamping through the bisection method in comparison with the linear method. It converges to a solution in 13–14 iterations for Δ = 0.01, so I think it should be pretty fast.

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

Nice! Chroma clamping is definitely noticeable when L is high.

I bet there is a closed form to do the clamping… but I’m not sure I’ll be able to figure it out. Another possibility is that we do simple RGB clamping when L is low, since the chroma difference there is negligible.

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

Also added some color ramps to the notebook, and hue-chroma discs in a fork.

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

Looking at the ramps and discs in the notebook, it's not readily apparent that chroma clamping is substantially better than RGB clamping for some use-cases... It does have a few useful properties, such as being able to obtain blacks and whites by adjusting the Lightness of the color, and the fact that it maintains the Hue. Maybe it's something to be implemented as a separate method, if at all.

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

Hah, that looks great! :)

from d3-color.

danburzo avatar danburzo commented on May 2, 2024

Stumbled upon this by accident: http://epicbeardquest.blogspot.ro/2015/11/the-problem-with-finding-boundary.html Seems to explain why an analytical solution to the problem is hard to find.
(There's also a Matlab implementation)

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

Interestingly, in my effort to fix achromatic interpolation, I’ve exacerbated the problem described in this issue: now when interpolating from black in HCL space, we use black with non-zero chroma, which causes us to start from non-black.

As an extreme example, d3.interpolateHcl("black", "blue") now looks like this, which is obviously very wrong (but consistent with Chroma.js, which does the same thing):

Whereas d3.interpolateHcl(d3.hcl(NaN, 0, 0), "blue") looks like what you expect:

On the bright side, d3.interpolateHcl("black", "#9ad0ef") is better:

Previously it was too gray because the black didn’t inherit the target chroma:

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

Here’s a working prototype using Dan’s bisection method for chroma clamping:

https://observablehq.com/d/3cef25e76ec972cf

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

This also means that the fix for white in 1.2.5 was wrong. Both white and black should be treated as having undefined chroma, not zero.

The problem is that when interpolating to white, and with the white inheriting the chroma and hue of the opposite color, it’s much more likely to result in an out-of-gamut color. So the correct fix is not to change the representation of white, but to implement chroma clamping when converting to RGB. This results in a slightly more colorful (better) gradient in the third example below.

Screen Shot 2019-06-27 at 7 58 38 AM

The only reason that Chroma.js avoids this problem is luck: it considers white to have a chroma of ~0.000017 rather than 0.

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

Unfortunately, the bisection method doesn’t work well for yellows. You get a discontinuity where it switches from a displayable color to a non-displayable color, and then suddenly drops the chroma.

I think the solution for this is probably something like R’s max_chroma, which uses a pre-computed table of the maximum chroma for a given hue and luminance. But… I’m not going to implement that today.

from d3-color.

cscheid avatar cscheid commented on May 2, 2024

I'm totally mindblown by this:

image

(from the post linked at by danburzo above.)

Keeping hue constant, the gamut is a disconnected set at large luminances! Doesn't this mean that any approach that clamps to maximum chroma is doomed to fail? For example, what should the expected behavior be as you trace a curve of decreasing hue values which "goes over the yellow cliff"?

The only way I can imagine this making a pleasant-looking scale is by "hugging the cliff", but would require having the scale manipulate the HCL coordinates independently...

from d3-color.

jrus avatar jrus commented on May 2, 2024

Binary search mostly works okay but as folks have pointed out, the ray at uniform lightness from neutral -> the blue primary leaves the gamut for a while, which can be a problem.

If you want to get fancy you can separately intersect with every one of the six planes {R,G,B} = {0, 1}.

You can then decide on a policy for when you get 3 intersections with a given hue/lightness ray instead of the expected 1, depending where your starting point was.

If convergence is too slow for bisection (e.g. the color model is very expensive) you can try Newton’s method or the secant method or whatever other generic root-finding algorithm.

from d3-color.

mbostock avatar mbostock commented on May 2, 2024

Here’s my plot of the maximum displayable chroma by x=H & y=L (sorry for the lack of labels).

Screen Shot 2019-06-28 at 5 17 54 PM

https://observablehq.com/d/4313e22e44483a1b

from d3-color.

cscheid avatar cscheid commented on May 2, 2024

(In case other people are bitten by this as they're investigating) It's worth noting that R's colorspace package defines HCL to be polar Luv, and not polar Lab (https://rdrr.io/rforge/colorspace/src/R/colorspace.R) So be careful when you make comparisons! Like that blog post mentioned above, polar Luv does not have this discontinuity issue in its C coordinate. This explains why R's max_chroma actually works out, I think.

from d3-color.

VamuveTV avatar VamuveTV commented on May 2, 2024

This is a great work. What is the math related to those conversions ? Im trying to recreate it in C, but it seems that yours rgb to LCH and LCH to RGB are a bit different ones that i´m using.

from d3-color.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.