Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/3f9d04609513508de3bb to your computer and use it in GitHub Desktop.
Save nitaku/3f9d04609513508de3bb to your computer and use it in GitHub Desktop.
HCL decomposition: cube1

This example shows the cube1 color scale, a variation by Matteo Niccoli on the cubeYF color scale from the same author, as well as its decomposition in the HCL color space (aka CIELCH). Copared to cubeYF, the cube1 color scale has a larger hue range, but slightly deviates from being a linearly increasing lightness scale (see the final part of the grayscale version).

palette = d3.csv.parse("""
R,G,B
120,0,133
121,0,136
122,0,139
123,1,142
124,1,145
125,2,148
126,2,151
127,3,153
128,4,156
128,5,159
129,5,162
129,6,165
130,8,167
130,9,170
131,10,173
131,11,176
131,12,178
131,13,181
131,15,184
131,17,186
131,19,189
131,22,192
130,25,194
130,28,197
130,31,200
130,34,202
129,38,205
129,42,208
129,45,210
128,49,213
128,52,215
127,55,217
127,59,220
126,61,222
126,64,224
126,66,226
125,69,228
124,71,230
123,73,233
122,75,235
121,77,237
120,79,240
119,81,242
118,83,244
117,85,246
116,87,247
115,89,249
114,91,250
113,93,252
112,94,252
111,96,253
110,98,253
109,100,253
108,102,253
107,104,253
106,107,252
105,109,252
104,111,251
103,113,251
102,115,250
102,117,249
101,119,248
100,121,247
99,123,247
98,125,246
97,126,245
96,128,244
95,130,243
94,132,242
93,134,241
92,135,240
91,137,239
90,138,238
89,140,236
88,142,235
87,143,234
86,145,232
85,146,230
83,148,229
82,149,227
81,151,226
80,152,224
79,153,222
78,155,221
77,156,219
76,158,217
74,159,215
73,161,214
72,162,212
70,164,210
69,165,208
67,167,206
66,168,204
64,170,202
63,171,201
61,173,199
60,174,197
59,175,194
58,177,192
57,178,190
57,179,188
56,181,186
56,182,184
56,183,182
56,184,180
57,185,178
57,187,176
58,188,174
58,189,171
59,190,169
60,191,167
61,192,165
62,193,163
62,194,160
63,195,158
64,196,156
65,197,154
66,198,151
67,199,149
67,200,147
68,201,145
69,202,142
69,203,140
70,203,138
71,204,135
71,205,133
72,206,131
73,207,129
73,208,126
74,208,124
75,209,122
75,210,119
76,211,117
77,211,115
77,212,113
78,213,111
79,214,108
80,215,106
80,216,104
81,216,101
82,217,98
82,218,96
83,219,93
84,220,90
84,221,87
85,222,85
86,222,82
87,223,80
87,224,78
88,225,76
89,225,75
90,226,74
91,227,73
92,227,73
94,228,73
95,229,73
97,229,73
99,230,74
101,230,74
104,231,74
106,231,75
109,232,75
111,232,76
114,233,76
117,233,77
120,234,78
122,234,78
125,234,79
128,235,79
130,235,80
133,235,80
135,235,80
137,235,81
140,235,81
142,235,82
145,235,82
147,235,82
150,236,83
152,236,83
155,236,84
157,236,84
160,236,84
162,236,85
165,236,85
167,236,85
169,236,86
171,236,86
173,236,86
175,236,87
177,236,87
180,236,87
182,236,87
184,236,88
185,236,88
187,236,88
189,236,88
191,236,89
193,236,89
195,236,89
196,236,89
198,236,89
200,236,89
201,236,90
203,236,90
204,236,90
205,236,90
207,236,90
208,235,90
209,234,91
210,234,91
211,233,91
212,232,91
213,230,91
214,229,91
215,228,91
216,226,91
217,225,91
218,224,92
219,222,92
220,221,92
221,219,92
222,218,92
223,217,92
224,215,92
226,214,92
227,213,93
229,211,93
230,210,93
231,208,93
233,206,93
234,205,93
236,203,93
237,201,94
238,200,94
239,198,94
240,196,94
241,194,94
242,192,94
243,190,94
243,188,94
244,186,94
244,184,94
245,182,94
245,180,94
246,178,94
246,176,93
247,173,93
247,171,93
248,168,93
248,166,93
248,163,92
249,161,92
249,158,92
249,156,92
249,153,91
249,150,91
""").map (d) -> d3.rgb(d.R, d.G, d.B)
svg = d3.select('svg')
svg.selectAll('.colored_band')
.data(palette)
.enter().append('rect')
.attr('class', 'colored_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 21)
.attr('width', 3.5)
.attr('height', 120)
.attr('fill', (d)->d )
svg.selectAll('.hue_band')
.data(palette)
.enter().append('rect')
.attr('class', 'hue_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 166)
.attr('width', 3.5)
.attr('height', 60)
.attr('fill', (d) ->
hcl = d3.hcl(d)
hcl.c = 60
hcl.l = 60
return hcl
)
svg.selectAll('.chroma_band')
.data(palette)
.enter().append('rect')
.attr('class', 'chroma_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 237)
.attr('width', 3.5)
.attr('height', 116)
.attr('fill', (d) ->
hcl = d3.hcl(d)
hcl.l = hcl.c/1.4
hcl.h = 80
hcl.c = 25
return hcl
)
svg.selectAll('.luminance_band')
.data(palette)
.enter().append('rect')
.attr('class', 'luminance_band')
.attr('x', (d,i)->32+3.5*i)
.attr('y', 364)
.attr('width', 3.5)
.attr('height', 116)
.attr('fill', (d) ->
hcl = d3.hcl(d)
hcl.c = 0
return hcl
)
x = d3.scale.linear()
.domain([0,255])
.range([32, 928])
h_y = d3.scale.linear()
.domain([0,360])
.range([166+60, 166])
c_y = d3.scale.linear()
.domain([0,150])
.range([237+116, 237])
l_y = d3.scale.linear()
.domain([0,100])
.range([364+116, 364])
h_line = d3.svg.line()
.x((_, i) -> x(i))
.y((d) ->
h = d3.hcl(d).h
return h_y( if h > 0 then h else h+360 )
)
c_line = d3.svg.line()
.x((_, i) -> x(i))
.y((d) -> c_y( d3.hcl(d).c ) )
l_line = d3.svg.line()
.x((_, i) -> x(i))
.y((d) -> l_y( d3.hcl(d).l ) )
svg.append('path')
.datum(palette)
.attr('class', 'hue')
.attr('d', h_line)
svg.append('path')
.datum(palette)
.attr('class', 'chroma')
.attr('d', c_line)
svg.append('path')
.datum(palette)
.attr('class', 'luminance')
.attr('d', l_line)
h_axis = d3.svg.axis()
.scale(h_y)
.tickValues([0,360])
.orient('right')
.tickSize(3,5)
c_axis = d3.svg.axis()
.scale(c_y)
.ticks(4)
.orient('right')
.tickSize(3,5)
l_axis = d3.svg.axis()
.scale(l_y)
.ticks(2)
.orient('right')
.tickSize(3,5)
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(932,0)')
.call(h_axis)
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(932,0)')
.call(c_axis)
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(932,0)')
.call(l_axis)
body {
background: #272822;
margin: 0;
padding: 0;
}
svg {
background: white;
}
.label {
text-anchor: middle;
font-size: 24px;
}
rect {
shape-rendering: crispEdges;
}
path {
fill: none;
}
path.hue {
stroke: white;
stroke-width: 1;
}
path.chroma {
stroke: #FF3D00;
stroke-width: 1;
}
path.luminance {
stroke: black;
stroke-width: 0.6;
}
.axis .domain, .axis .tick line {
stroke: black;
shape-rendering: crispEdges;
}
.axis {
font-size: 8px;
font-family: sans-serif;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="HCL decomposition: cube1" />
<title>HCL decomposition: cube1</title>
<link rel="stylesheet" href="index.css">
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg height="500" width="960">
<text class="label" x="16" y="198" dy="0.35em">H</text>
<text class="label" x="16" y="295" dy="0.35em">C</text>
<text class="label" x="16" y="420" dy="0.35em">L</text>
</svg>
<script src="index.js"></script>
</body>
</html>
(function() {
var c_axis, c_line, c_y, h_axis, h_line, h_y, l_axis, l_line, l_y, palette, svg, x;
palette = d3.csv.parse("R,G,B\n120,0,133\n121,0,136\n122,0,139\n123,1,142\n124,1,145\n125,2,148\n126,2,151\n127,3,153\n128,4,156\n128,5,159\n129,5,162\n129,6,165\n130,8,167\n130,9,170\n131,10,173\n131,11,176\n131,12,178\n131,13,181\n131,15,184\n131,17,186\n131,19,189\n131,22,192\n130,25,194\n130,28,197\n130,31,200\n130,34,202\n129,38,205\n129,42,208\n129,45,210\n128,49,213\n128,52,215\n127,55,217\n127,59,220\n126,61,222\n126,64,224\n126,66,226\n125,69,228\n124,71,230\n123,73,233\n122,75,235\n121,77,237\n120,79,240\n119,81,242\n118,83,244\n117,85,246\n116,87,247\n115,89,249\n114,91,250\n113,93,252\n112,94,252\n111,96,253\n110,98,253\n109,100,253\n108,102,253\n107,104,253\n106,107,252\n105,109,252\n104,111,251\n103,113,251\n102,115,250\n102,117,249\n101,119,248\n100,121,247\n99,123,247\n98,125,246\n97,126,245\n96,128,244\n95,130,243\n94,132,242\n93,134,241\n92,135,240\n91,137,239\n90,138,238\n89,140,236\n88,142,235\n87,143,234\n86,145,232\n85,146,230\n83,148,229\n82,149,227\n81,151,226\n80,152,224\n79,153,222\n78,155,221\n77,156,219\n76,158,217\n74,159,215\n73,161,214\n72,162,212\n70,164,210\n69,165,208\n67,167,206\n66,168,204\n64,170,202\n63,171,201\n61,173,199\n60,174,197\n59,175,194\n58,177,192\n57,178,190\n57,179,188\n56,181,186\n56,182,184\n56,183,182\n56,184,180\n57,185,178\n57,187,176\n58,188,174\n58,189,171\n59,190,169\n60,191,167\n61,192,165\n62,193,163\n62,194,160\n63,195,158\n64,196,156\n65,197,154\n66,198,151\n67,199,149\n67,200,147\n68,201,145\n69,202,142\n69,203,140\n70,203,138\n71,204,135\n71,205,133\n72,206,131\n73,207,129\n73,208,126\n74,208,124\n75,209,122\n75,210,119\n76,211,117\n77,211,115\n77,212,113\n78,213,111\n79,214,108\n80,215,106\n80,216,104\n81,216,101\n82,217,98\n82,218,96\n83,219,93\n84,220,90\n84,221,87\n85,222,85\n86,222,82\n87,223,80\n87,224,78\n88,225,76\n89,225,75\n90,226,74\n91,227,73\n92,227,73\n94,228,73\n95,229,73\n97,229,73\n99,230,74\n101,230,74\n104,231,74\n106,231,75\n109,232,75\n111,232,76\n114,233,76\n117,233,77\n120,234,78\n122,234,78\n125,234,79\n128,235,79\n130,235,80\n133,235,80\n135,235,80\n137,235,81\n140,235,81\n142,235,82\n145,235,82\n147,235,82\n150,236,83\n152,236,83\n155,236,84\n157,236,84\n160,236,84\n162,236,85\n165,236,85\n167,236,85\n169,236,86\n171,236,86\n173,236,86\n175,236,87\n177,236,87\n180,236,87\n182,236,87\n184,236,88\n185,236,88\n187,236,88\n189,236,88\n191,236,89\n193,236,89\n195,236,89\n196,236,89\n198,236,89\n200,236,89\n201,236,90\n203,236,90\n204,236,90\n205,236,90\n207,236,90\n208,235,90\n209,234,91\n210,234,91\n211,233,91\n212,232,91\n213,230,91\n214,229,91\n215,228,91\n216,226,91\n217,225,91\n218,224,92\n219,222,92\n220,221,92\n221,219,92\n222,218,92\n223,217,92\n224,215,92\n226,214,92\n227,213,93\n229,211,93\n230,210,93\n231,208,93\n233,206,93\n234,205,93\n236,203,93\n237,201,94\n238,200,94\n239,198,94\n240,196,94\n241,194,94\n242,192,94\n243,190,94\n243,188,94\n244,186,94\n244,184,94\n245,182,94\n245,180,94\n246,178,94\n246,176,93\n247,173,93\n247,171,93\n248,168,93\n248,166,93\n248,163,92\n249,161,92\n249,158,92\n249,156,92\n249,153,91\n249,150,91").map(function(d) {
return d3.rgb(d.R, d.G, d.B);
});
svg = d3.select('svg');
svg.selectAll('.colored_band').data(palette).enter().append('rect').attr('class', 'colored_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 21).attr('width', 3.5).attr('height', 120).attr('fill', function(d) {
return d;
});
svg.selectAll('.hue_band').data(palette).enter().append('rect').attr('class', 'hue_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 166).attr('width', 3.5).attr('height', 60).attr('fill', function(d) {
var hcl;
hcl = d3.hcl(d);
hcl.c = 60;
hcl.l = 60;
return hcl;
});
svg.selectAll('.chroma_band').data(palette).enter().append('rect').attr('class', 'chroma_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 237).attr('width', 3.5).attr('height', 116).attr('fill', function(d) {
var hcl;
hcl = d3.hcl(d);
hcl.l = hcl.c / 1.4;
hcl.h = 80;
hcl.c = 25;
return hcl;
});
svg.selectAll('.luminance_band').data(palette).enter().append('rect').attr('class', 'luminance_band').attr('x', function(d, i) {
return 32 + 3.5 * i;
}).attr('y', 364).attr('width', 3.5).attr('height', 116).attr('fill', function(d) {
var hcl;
hcl = d3.hcl(d);
hcl.c = 0;
return hcl;
});
x = d3.scale.linear().domain([0, 255]).range([32, 928]);
h_y = d3.scale.linear().domain([0, 360]).range([166 + 60, 166]);
c_y = d3.scale.linear().domain([0, 150]).range([237 + 116, 237]);
l_y = d3.scale.linear().domain([0, 100]).range([364 + 116, 364]);
h_line = d3.svg.line().x(function(_, i) {
return x(i);
}).y(function(d) {
var h;
h = d3.hcl(d).h;
return h_y(h > 0 ? h : h + 360);
});
c_line = d3.svg.line().x(function(_, i) {
return x(i);
}).y(function(d) {
return c_y(d3.hcl(d).c);
});
l_line = d3.svg.line().x(function(_, i) {
return x(i);
}).y(function(d) {
return l_y(d3.hcl(d).l);
});
svg.append('path').datum(palette).attr('class', 'hue').attr('d', h_line);
svg.append('path').datum(palette).attr('class', 'chroma').attr('d', c_line);
svg.append('path').datum(palette).attr('class', 'luminance').attr('d', l_line);
h_axis = d3.svg.axis().scale(h_y).tickValues([0, 360]).orient('right').tickSize(3, 5);
c_axis = d3.svg.axis().scale(c_y).ticks(4).orient('right').tickSize(3, 5);
l_axis = d3.svg.axis().scale(l_y).ticks(2).orient('right').tickSize(3, 5);
svg.append('g').attr('class', 'axis').attr('transform', 'translate(932,0)').call(h_axis);
svg.append('g').attr('class', 'axis').attr('transform', 'translate(932,0)').call(c_axis);
svg.append('g').attr('class', 'axis').attr('transform', 'translate(932,0)').call(l_axis);
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment