|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
body { |
|
font-family: helvetica, sans-serif; |
|
font-size: 14px; |
|
background-color: #20b9cb; |
|
} |
|
#red-luminance, |
|
#green-luminance, |
|
#blue-luminance { |
|
width: 480px; |
|
height: 250px; |
|
float: left; |
|
} |
|
|
|
text { fill: black; } |
|
line, |
|
path { |
|
fill: transparent; |
|
stroke-width: 2; |
|
stroke: black; |
|
} |
|
|
|
text.title { |
|
font-size: 18px; |
|
fill: white; |
|
} |
|
.title tspan { font-size: 14px; } |
|
|
|
.bar rect { |
|
stroke-width: 1; |
|
} |
|
</style> |
|
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> |
|
<div id="red-luminance"></div> |
|
<div id="green-luminance"></div> |
|
<div id="blue-luminance"></div> |
|
<script> |
|
//find luminance values by one primary color |
|
function lumValsByPrime(prime) { |
|
var results = [], |
|
colors = { |
|
r: function(r, i) { return [r, i, i] }, |
|
g: function(g, i) { return [i, g, i] }, |
|
b: function(b, i) { return [i, i, b] } |
|
} |
|
|
|
for (var p = 0; p < 256; p++) { |
|
for (var i = 0; i < 256; i++) { |
|
//push the the integer of L* only |
|
var luminance = Math.floor( xyz2Lab(rgb2xyz( colors[prime](p, i) ) )[0] ) |
|
results.push(luminance) |
|
} |
|
} |
|
return results |
|
} |
|
//helper functions |
|
function rgb2xyz(color) { |
|
color = [color[0]/255, color[1]/255, color[2]/255] |
|
var xyz = [0, 0, 0], |
|
matrix = [[0.412453, 0.357580, 0.180423], |
|
[0.212671, 0.715160, 0.072169], |
|
[0.019334, 0.119193, 0.950227]] |
|
|
|
matrix.forEach(function(row, i) { |
|
row.forEach(function(cell, j) { |
|
xyz[i] += color[j] * cell * 100 |
|
}) |
|
}) |
|
|
|
return xyz |
|
} |
|
function xyz2Lab(color) { |
|
var d65 = [95.0456, 100, 108.8754], |
|
xyzN = [], |
|
L, a, b |
|
//find X/Xn, Y/Yn, Z/Zn |
|
color.forEach(function(d, i) { |
|
xyzN.push(d/d65[i]) |
|
}) |
|
|
|
var f = function(t) { |
|
if (t > 0.008856) |
|
return Math.pow(t, 1/3) |
|
else |
|
return 7.787 * t + 16/116 |
|
} |
|
|
|
if (xyzN[1] > 0.008856) |
|
L = 116 * Math.pow(xyzN[1], 1/3) - 16 |
|
else |
|
L = 903.3 * xyzN[1] |
|
|
|
a = 500 * ( f(xyzN[0]) - f(xyzN[1]) ) |
|
b = 200 * ( f(xyzN[1]) - f(xyzN[2]) ) |
|
|
|
return [L, a, b] |
|
} |
|
|
|
//create visualization |
|
function colorVis(color) { |
|
//config |
|
var vis = { |
|
'container': '#'+color+'-luminance', |
|
'title': 'Histogram of ' + |
|
color.replace(/\D+/, function(t) { |
|
return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase() }) + |
|
' Luminance Values', |
|
'subtitle': 'in thousands of colors', |
|
'data': lumValsByPrime(color.slice(0,1)) |
|
} |
|
//calculated config |
|
vis.svg = { |
|
width: d3.select(vis.container).node().clientWidth, |
|
height: d3.select(vis.container).node().clientHeight |
|
} |
|
vis.pad = {top: 80, right: 40, bottom: 40, left: 40} |
|
vis.width = vis.svg.width - vis.pad.left - vis.pad.right |
|
vis.height = vis.svg.height - vis.pad.top - vis.pad.bottom |
|
vis.colors = { |
|
r: function(r, i) { return 'rgb('+r+','+i+','+i+')' }, |
|
g: function(g, i) { return 'rgb('+i+','+g+','+i+')' }, |
|
b: function(b, i) { return 'rgb('+i+','+i+','+b+')' } |
|
} |
|
|
|
var x = d3.scale.linear() |
|
.domain([0, 100]) |
|
.range([0, vis.width]) |
|
|
|
var histogram = d3.layout.histogram() |
|
.bins(x.ticks(100)) |
|
(vis.data) |
|
|
|
var y = d3.scale.linear() |
|
.domain([0, 2000]) |
|
.range([vis.height, 0]) |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(x) |
|
.orient('bottom') |
|
var yAxis = d3.svg.axis() |
|
.scale(y) |
|
.ticks(4) |
|
//display units as e3 |
|
.tickFormat(function(d) { return d / 1000 }) |
|
.orient('left') |
|
|
|
var svg = d3.select(vis.container).append('svg') |
|
.attr('width', vis.svg.width) |
|
.attr('height', vis.svg.height) |
|
|
|
var title = svg.append('text') |
|
.attr('class', 'title') |
|
.attr('text-anchor', 'middle') |
|
.attr('transform', 'translate(' + vis.svg.width / 2 + ',' + vis.pad.top / 2 + ')') |
|
.text(vis.title) |
|
.append('tspan') |
|
.attr('x', 0) |
|
.attr('dy', 1.1+'em') |
|
.text(vis.subtitle) |
|
|
|
var graph = svg.append('g') |
|
.attr('class', 'graph') |
|
.attr('width', vis.width) |
|
.attr('height', vis.height) |
|
.attr('transform', 'translate('+vis.pad.left+','+vis.pad.top+')') |
|
|
|
var bar = graph.selectAll('.bar') |
|
.data(histogram) |
|
.enter().append('g') |
|
.attr('class', 'bar') |
|
.attr('transform', function(d) { |
|
return 'translate('+x(d.x)+','+y(d.y)+')' |
|
}) |
|
|
|
bar.append('rect') |
|
.attr('x', 1) |
|
.attr('width', x(histogram[0].dx) - 1) |
|
.attr('height', function(d) { |
|
return vis.height - y(d.y) |
|
}) |
|
.attr('fill', function(d) { |
|
var n = Math.round(d.x * 2.55) |
|
return vis.colors[color.slice(0,1)](255, n) |
|
}) |
|
|
|
graph.append('g') |
|
.attr('class', 'x axis') |
|
.attr('transform', 'translate(0,' + vis.height + ')') |
|
.call(xAxis) |
|
graph.append('g') |
|
.attr('class', 'y axis') |
|
.call(yAxis) |
|
|
|
} |
|
|
|
colorVis('red') |
|
colorVis('green') |
|
colorVis('blue') |
|
|
|
</script> |