GithubHelp home page GithubHelp logo

d3-color's Introduction

d3-color

Even though your browser understands a lot about colors, it doesn’t offer much help in manipulating colors through JavaScript. The d3-color module therefore provides representations for various color spaces, allowing specification, conversion and manipulation. (Also see d3-interpolate for color interpolation.)

For example, take the color named “steelblue”:

const c = d3.color("steelblue"); // {r: 70, g: 130, b: 180, opacity: 1}

Let’s try converting it to HSL:

const c = d3.hsl("steelblue"); // {h: 207.27…, s: 0.44, l: 0.4902…, opacity: 1}

Now rotate the hue by 90°, bump up the saturation, and format as a string for CSS:

c.h += 90;
c.s += 0.2;
c + ""; // rgb(198, 45, 205)

To fade the color slightly:

c.opacity = 0.8;
c + ""; // rgba(198, 45, 205, 0.8)

In addition to the ubiquitous and machine-friendly RGB and HSL color space, d3-color supports color spaces that are designed for humans:

Cubehelix features monotonic lightness, while CIELAB and its polar form CIELChab are perceptually uniform.

Extensions

For additional color spaces, see:

To measure color differences, see:

Installing

If you use npm, npm install d3-color. You can also download the latest release on GitHub. For vanilla HTML in modern browsers, import d3-color from Skypack:

<script type="module">

import {rgb} from "https://cdn.skypack.dev/[email protected]";

const steelblue = d3.rgb("steelblue");

</script>

For legacy environments, you can load d3-color’s UMD bundle from an npm-based CDN such as jsDelivr; a d3 global is exported:

<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<script>

const steelblue = d3.rgb("steelblue");

</script>

Try d3-color in your browser.

API Reference

# d3.color(specifier) <>

Parses the specified CSS Color Module Level 3 specifier string, returning an RGB or HSL color, along with CSS Color Module Level 4 hex specifier strings. If the specifier was not valid, null is returned. Some examples:

  • rgb(255, 255, 255)
  • rgb(10%, 20%, 30%)
  • rgba(255, 255, 255, 0.4)
  • rgba(10%, 20%, 30%, 0.4)
  • hsl(120, 50%, 20%)
  • hsla(120, 50%, 20%, 0.4)
  • #ffeeaa
  • #fea
  • #ffeeaa22
  • #fea2
  • steelblue

The list of supported named colors is specified by CSS.

Note: this function may also be used with instanceof to test if an object is a color instance. The same is true of color subclasses, allowing you to test whether a color is in a particular color space.

# color.opacity

This color’s opacity, typically in the range [0, 1].

# color.rgb() <>

Returns the RGB equivalent of this color. For RGB colors, that’s this.

# color.copy([values]) <>

Returns a copy of this color. If values is specified, any enumerable own properties of values are assigned to the new returned color. For example, to derive a copy of a color with opacity 0.5, say

color.copy({opacity: 0.5})

# color.brighter([k]) <>

Returns a brighter copy of this color. If k is specified, it controls how much brighter the returned color should be. If k is not specified, it defaults to 1. The behavior of this method is dependent on the implementing color space.

# color.darker([k]) <>

Returns a darker copy of this color. If k is specified, it controls how much darker the returned color should be. If k is not specified, it defaults to 1. The behavior of this method is dependent on the implementing color space.

# color.displayable() <>

Returns true if and only if the color is displayable on standard hardware. For example, this returns false for an RGB color if any channel value is less than zero or greater than 255 when rounded, or if the opacity is not in the range [0, 1].

# color.formatHex() <>

Returns a hexadecimal string representing this color in RGB space, such as #f7eaba. If this color is not displayable, a suitable displayable color is returned instead. For example, RGB channel values greater than 255 are clamped to 255.

# color.formatHex8() <>

Returns a hexadecimal string representing this color in RGBA space, such as #f7eaba90. If this color is not displayable, a suitable displayable color is returned instead. For example, RGB channel values greater than 255 are clamped to 255.

# color.formatHsl() <>

Returns a string representing this color according to the CSS Color Module Level 3 specification, such as hsl(257, 50%, 80%) or hsla(257, 50%, 80%, 0.2). If this color is not displayable, a suitable displayable color is returned instead by clamping S and L channel values to the interval [0, 100].

# color.formatRgb() <>

Returns a string representing this color according to the CSS Object Model specification, such as rgb(247, 234, 186) or rgba(247, 234, 186, 0.2). If this color is not displayable, a suitable displayable color is returned instead by clamping RGB channel values to the interval [0, 255].

# color.toString() <>

An alias for color.formatRgb.

# d3.rgb(r, g, b[, opacity]) <>
# d3.rgb(specifier)
# d3.rgb(color)

Constructs a new RGB color. The channel values are exposed as r, g and b properties on the returned instance. Use the RGB color picker to explore this color space.

If r, g and b are specified, these represent the channel values of the returned color; an opacity may also be specified. If a CSS Color Module Level 3 specifier string is specified, it is parsed and then converted to the RGB color space. See color for examples. If a color instance is specified, it is converted to the RGB color space using color.rgb. Note that unlike color.rgb this method always returns a new instance, even if color is already an RGB color.

# rgb.clamp() <>

Returns a new RGB color where the r, g, and b channels are clamped to the range [0, 255] and rounded to the nearest integer value, and the opacity is clamped to the range [0, 1].

# d3.hsl(h, s, l[, opacity]) <>
# d3.hsl(specifier)
# d3.hsl(color)

Constructs a new HSL color. The channel values are exposed as h, s and l properties on the returned instance. Use the HSL color picker to explore this color space.

If h, s and l are specified, these represent the channel values of the returned color; an opacity may also be specified. If a CSS Color Module Level 3 specifier string is specified, it is parsed and then converted to the HSL color space. See color for examples. If a color instance is specified, it is converted to the RGB color space using color.rgb and then converted to HSL. (Colors already in the HSL color space skip the conversion to RGB.)

# hsl.clamp() <>

Returns a new HSL color where the h channel is clamped to the range [0, 360), and the s, l, and opacity channels are clamped to the range [0, 1].

# d3.lab(l, a, b[, opacity]) <>
# d3.lab(specifier)
# d3.lab(color)

Constructs a new CIELAB color. The channel values are exposed as l, a and b properties on the returned instance. Use the CIELAB color picker to explore this color space. The value of l is typically in the range [0, 100], while a and b are typically in [-160, +160].

If l, a and b are specified, these represent the channel values of the returned color; an opacity may also be specified. If a CSS Color Module Level 3 specifier string is specified, it is parsed and then converted to the CIELAB color space. See color for examples. If a color instance is specified, it is converted to the RGB color space using color.rgb and then converted to CIELAB. (Colors already in the CIELAB color space skip the conversion to RGB, and colors in the HCL color space are converted directly to CIELAB.)

# d3.gray(l[, opacity]) <>

Constructs a new CIELAB color with the specified l value and a = b = 0.

# d3.hcl(h, c, l[, opacity]) <>
# d3.hcl(specifier)
# d3.hcl(color)

Equivalent to d3.lch, but with reversed argument order.

# d3.lch(l, c, h[, opacity]) <>
# d3.lch(specifier)
# d3.lch(color)

Constructs a new CIELChab color. The channel values are exposed as l, c and h properties on the returned instance. Use the CIELChab color picker to explore this color space. The value of l is typically in the range [0, 100], c is typically in [0, 230], and h is typically in [0, 360).

If l, c, and h are specified, these represent the channel values of the returned color; an opacity may also be specified. If a CSS Color Module Level 3 specifier string is specified, it is parsed and then converted to CIELChab color space. See color for examples. If a color instance is specified, it is converted to the RGB color space using color.rgb and then converted to CIELChab. (Colors already in CIELChab color space skip the conversion to RGB, and colors in CIELAB color space are converted directly to CIELChab.)

# d3.cubehelix(h, s, l[, opacity]) <>
# d3.cubehelix(specifier)
# d3.cubehelix(color)

Constructs a new Cubehelix color. The channel values are exposed as h, s and l properties on the returned instance. Use the Cubehelix color picker to explore this color space.

If h, s and l are specified, these represent the channel values of the returned color; an opacity may also be specified. If a CSS Color Module Level 3 specifier string is specified, it is parsed and then converted to the Cubehelix color space. See color for examples. If a color instance is specified, it is converted to the RGB color space using color.rgb and then converted to Cubehelix. (Colors already in the Cubehelix color space skip the conversion to RGB.)

d3-color's People

Contributors

curran avatar danburzo avatar dependabot[bot] avatar devgru avatar fil avatar jasondavies avatar mbostock avatar mef avatar rich-harris avatar tschaub avatar vijithassar avatar zerovox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-color's Issues

Some alpha-values not recognized

The following is a valid color (black with 50% opacity) but will not be recognized:

d3.color("rgba(0,0,0,.5)") === null // true

It seems that at least one digit is always required before the decimal point. I believe this can be fixed in the corresponding regex.

Replace clamping with validation for RGB values

README says:

Colors are now validated upon construction.

In fact they are clamped, not validated.

IMHO clamping isn't a good idea for RGB constructor, it hides "invalid" LAB/HCL colors instead of notifying about them.
Would you mind throwing exception or returning special value for invalid colors?

It is the only way to check whether LAB/HCL color is "valid" or not.

I'm using this kind of validation here:
https://github.com/devgru/postcss-color-hcl/blob/master/index.js#L22

Default cubehelix color interpolators?

Maybe this should be in d3-scale, but it would be nice to have some standard Cubehelix color scales:

var defaultCubehelix = color.interpolateCubehelix(
  color.cubehelix(300, 0.5, 0.0),
  color.cubehelix(-240, 0.5, 1.0)
);

var perceptualRainbow1 = color.interpolateCubehelix(
  color.cubehelix(-100, 0.75, 0.35),
  color.cubehelix(80, 1.50, 0.8)
);

var perceptualRainbow2 = color.interpolateCubehelix(
  color.cubehelix(80, 1.50, 0.8),
  color.cubehelix(260, 0.75, 0.35)
);

Issue with latest d3-color version (1.4.0)

Hi Mike,

Application was working fine until the new release of d3 color version(1.4.0). As soon as this new version has come, kubernetes started failing with below mentioned error where as same code is working fine in local. After lot of research we could identify the issue and as an interim fix we are overriding with older version "1.3.0" and it is working fine now. Request you to extend your help on this.
image

https://github.com/d3/d3-color

Unexpected conversion to HSL

Convertion to HSL gives unexpected results. Consider:

import { hcl } from "d3-color";
const c = hcl(200, 100, 150);
console.log(c.formatHex());
console.log(c.formatRgb());
console.log(c.formatHsl());

Actual output in Chrome 86 on Mac:

#00ffff
rgb(0, 255, 255)
hsl(180.380573958081, -163.7644455443205%, -293.13231330226415%)

The hex and rgb notations are the same color: bright cyan. The HSL notation is different. The documentation also says the saturation and lightness values are clamped to the interval of 0-100, but they are not. Do I misunderstand how non-displayable colors are converted or is there a bug in the hsl converstion here?

Here’s another way to look at this:

const a = rgb(0, 255, 255);
const b = rgb(hcl(200, 100, 150));
a.formatRgb(); // rgb(0, 255, 255)
b.formatRgb(); // rgb(0, 255, 255)
a.formatHsl(); // hsl(180, 100%, 50%)
b.formatHsl(); // hsl(180.380573958081, -163.7644455443205%, -293.13231330226415%)

Various updates?

I am working on a color library that aims to support all the CSS Color Level 4 formats, and would love to contribute to d3-color with some updates:

  • HEX 4 and 8 formats
  • RGB(A) and HSL(A) current syntax (rgb(255 255 255 / 0.1), etc.)

And could add HSV and HSI color spaces.

(+ more as I sort them out)

I was wondering which of these are in the scope of d3-color and whether I can submit PRs.

Thanks!

color.fade([delta=0.2])

Similar to color.brighter, but for succinctly modifying the opacity channel. It could return a new color whose opacity is max(0, min(1, this.opacity - delta)).

Should Lab and HCL colorspaces clamp luminance to [0, 100]?

They currently do, but I don’t think it makes sense. It only makes sense for RGB because of machine limitations. I suppose we could force the luminance to be non-negative, but I don’t think there’s any reason to limit the maximum value to 100.

color.hex() is not a function

IM trying to use this as the documentation suggests but when I create any color.hex() is throwing an error that it is not a function.

var white = d3.color("white"); console.log(white.hex());// Uncaught TypeError: white.hex is not a function

Parsing rgba() and hsla() values

Have you considered parsing strings with alpha channels? I know they wouldn't map to hex values, but it would be pretty cool if the alpha channel was stripped out and it returned a valid color (and not null).

Great work on these modules, really digging it.

Incorrect conversion from HCL / LCH to hex or rgb

Firstly, thank you @mbostock for all of your incredible work, every time i have no clue how i am going to achieve something, it seems that d3 has already created a solution.

One small issue i have spotted is that when creating a d3.hcl object (example used is d3.hcl(15,100,65)) and converting to either rgb or hex (using both .rgb or d3.rgb()), it provides the wrong values. Output from this conversion is #FF0077 and it should be #F8766D.

Tested with d3 5.9.2 & 6.2.0

Edit: The conversion seems to be the same as Chroma.js so maybe this isn't a d3 issue? The output varies massively to this http://hclwizard.org/hclcolorpicker/ which i think is using R to generate the colors / graphics

CIEDE2000 color difference

In the spirit of I couldn't help it I've ported all the color difference metrics to work with d3-color here 👉 https://github.com/danburzo/d3-color-difference

Out of them the euclidean distances are trivial to compute by end-users, and from the rest CIEDE2000 is supposedly the most accurate, and a hassle to implement. May be a good addition to d3-color itself, or is it preferable as a separate package?

(Oooh, I missed this notebook!)

non-es6 version?

Can you create a transpiled version of this project to your bower repo for browsers that arent es6 compliant?

L*a*b* <-> sRGB conversions?

Most importantly: Thanks for your work on d3! 🎉 , ❤️ and 🥇!

I was trying to use d3-color for sRGB to CIE-L*a*b* conversions today:

$ node
> const d3 = require("d3-color")
undefined
> d3.lab(0,100,100).rgb()
Rgb {
  r: 109.26297015067321,
  g: -126.7018621426472,
  b: -236.7921286790844,
  opacity: 1 }
> d3.lab(50,-50,-50).rgb()
Rgb {
  r: -868.5123725460351,
  g: 143.16534084117143,
  b: 203.4499609542193,
  opacity: 1 }

You expect L*a*b* values to be [[0-100], [-100,100], [-100,100]], correct? What colorspace are these RGB values in? I was expecting [0,255].

Am I using the API incorrectly?

(This is with node v8.9.0, fwiw)

LUV and XYZ.

Per #33 (comment), d3-color should support CIELUV in addition to CIELAB, and for compatibility with R, should redefine d3.hcl as polar CIELUV, but preserve d3.lch as polar CIELAB. We should also expose CIEXYZ for completeness.

Alpha value of 0 breaks rgb

When using an alpha value with d3.rgb, via rgba or hsla colors strings, the color values return NaN.

Is this expected behavior?

tested on [email protected]

d3.rgb('hsla(240,100%,25%,0)');

// returns
{
  r: NaN,
  b: NaN,
  g: NaN,
  opacity: 0,
}
d3.rgb('rgba(0,0,0,0)');

// returns
{
  r: NaN,
  b: NaN,
  g: NaN,
  opacity: 0,
}

Also interesting to note that the behavior of non-zero, but close to zero, alpha values return the expected color.

d3.rgb('rgba(0,0,0,0.001)');

// returns
{
  r: 0,
  b: 0,
  g: 0,
  opacity: 0.001,
}

Brightening black in RGB

What is the desired behaviour of brightening black colour? Right now it will return black.

const brighter = d3.rgb("#000000").brighter(); // {b: 0, g: 0, opacity: 1, r: 0}

oklab?

https://bottosson.github.io/posts/oklab/

Here's a list of the design choices behind oklab:

  • Should be an opponent color space, similar to for example CIELAB.
  • Should predict lightness, chroma and hue well. LL, CC and hh should be perceived as orthogonal, so one can be altered without affecting the other two. This is useful for things like turning an image black and white and increasing colorfulness without introducing hue shifts etc.
  • Blending two colors should result in even transitions. The transition colors should appear to be in between the blended colors (e.g. passing through a warmer color than either original color is not good).
  • Should assume a D65 whitepoint. This is what common color spaces like sRGB, rec2020 and Display P3 uses.
  • Should behave well numerically. The model should be easy to compute, numerically stable and differentiable.
  • Should assume normal well lit viewing conditions. The complexity of supporting different viewing conditions is not practical in most applications. Other models could be used in conjunction if this is needed in some case.
  • If the scale/exposure of colors are changed, the perceptual coordinates should just be scaled by a factor. More complex models that depend on absolute luminance should be avoided since the viewing conditions can not be accurately controlled and incorrect behavior would be confusing.

Control over shortest path hue interpolation?

It’s useful to sometimes take the longer path when interpolating hue. For example, hue in the default cubehelix ramp goes through 540°. But this need extends beyond cubehelix—it seems reasonable to allow “long” hue interpolation in other cylindrical colorspaces, such as HCL and HSL, too.

This means: 1. Color instances in those spaces must be capable of representing hues outside of [0°,360°). Having hue default to that range when converting from another colorspace seems reasonable, though. 2. We need both “short” and “long” forms of interpolation for cylindrical colorspaces. It seems reasonable to default to short.

(I suppose it would also be possible to default to “long” if the change in hue is greater than or equal to 360°. But… that seems a little brittle, since you definitely might want to use “long” hue interpolation when the hue delta is in [180°,360°), too. So probably best to always make it explicit.)

Add formatHex8() to include opacity

To reproduce:

console.log(d3.color("#fff1").formatRgb());  // rgba(255, 255, 255, 0.06666666666666667)
console.log(d3.color("#fff1").formatHex());  // #ffffff <-- shouldn't this be #ffffff11?

I'd expect that formatHex would preserve opacity just as formatRgb does; however, opacity is clearly ignored in the function definition

function rgb_formatHex() {

Add CIECAM02.

Possibly a better perceptual colorspace than Lab/Lch and Cubehelix?

Automatically pick N distinct colors?

This is not an "issue" per se with d3-color, but just an idea / research problem that you may want to consider.

I always thought it would be great to have an algorithm that automagically picks N distinct colors that are as far apart perceptually as possible, for representing categorical values. Perhaps d3.scale.category20() and friends fills this need currently in practice, but I feel like there should be an algorithm for this.

Here is a first attempt at such an algorithm that places points on a circle in LAB space (L is fixed, and the points vary only in (A, B), so they have equal luminance but different hue) http://bl.ocks.org/curran/dd73d3d8925cdf50df86.

Perhaps a better solution would be use Mitchell’s best-candidate algorithm for generating a Poisson-disc distribution in a limited region of (A, B) color space (perhaps a circle?), where the user could specify as input a fixed L value and the desired number of distinct colors. Also Lloyd relaxation might be another nice way to do this.

The API documentation might look something like this:


color.distinct(n, [L])

Returns an array of n colors that are perceptually distinct. If L is specified, it controls the fixed L value in LAB color space that the algorithm uses. If L is not specified, it defaults to 50.


Just an idea.

It's great to see all this activity on the new D3, excited to try it out!

Clamp chroma to displayable RGB gamut?

In R, fixup defaults to TRUE. We need some equivalent to this (but faster, hopefully, at least using binary search):

function fixup(c) {
  while (!c.displayable()) --c.c;
  return c;
}

This makes a pretty big difference if you want to, say, emulate ggplot2’s hue color scale.

Cubehelix gamma?

The Cubehelix color scheme allows a γ (gamma) parameter. I’m not sure how best to represent this concept if we define Cubehelix as a colorspace. Probably the colorspace assumes γ = 1, but then we allow interpolateCubehelixGamma(γ)? Ugh, with #4 we’ll also need interpolateCubehelixGammaLong.

Document hcl functions

It would be lovely to have documentation for the argument ranges of the hcl color space, as there is for the other color spaces.

What’s the range of *s* in Cubehelix?

in the color picker s has range [0,2]
but after i Played with it looks like values more then 2 are allowed
d3Color.cubehelix(370,3.4,0.15).toString() // "rgb(200, 0, 0)"
d3Color.cubehelix(370,3,0.15).toString() // "rgb(181, 0, 0)"
d3Color.cubehelix(370,2,0.15).toString() // "rgb(133, 5, 0)"
d3Color.cubehelix(370,1,0.15).toString() // "rgb(86, 22, 0)"

So I do not quite understand how is that working, can you add some comments or link to some resources (I'm familiar with original page and paper, but I have not seen implementation of it as a color space), (I'm thinking on implementing it as part of purescript-contrib/purescript-colors#31)

Link to d3 repositories for other color spaces in the README

Looking through the other pull requests on d3-color, I realized the approach for this repo is to keep a fairly minimal API and prefer creating separate plugins for the other color spaces. To that end, I'll close my vague issue #41 and propose and addition to the README that links to these other related repos, which I just realized exist:

(and any others I may be missing)

I'll gladly do the PR if that's okay!

L*a*b* chromatic adaptation

While investigating why d3's lab() method gives subtly different results than, for example, Wolfram Alpha, I came to understand d3-color is missing a chromatic adaptation between the D65 (sRGB) and D50 (Lab) illuminants:

The process should be: Lab XYZ (D50) XYZ(D65) RGB

I'm not sure how important this adaptation is to the purpose of the library, but it boils down to using the matrices from this page:

const d50_to_d6 = ({ x, y, z }) => ({
	x: x * 0.9555766 - y * 0.0230393 + z * 0.0631636,
	y: x * -0.0282895 + y * 1.0099416 + z * 0.0210077,
	z: x * 0.0122982 - y * 0.0204830 + z * 1.3299098
});

const d65_to_d50 = ({ x, y, z }) => ({
	x: x * 1.0478112 + y * 0.0228866 - z * 0.0501270,
	y: x * 0.0295424 + y * 0.9904844 - z * 0.0170491,
	z: x * -0.0092345 + y * 0.0150436 + z * 0.7521316
});

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.