Published
Edited
Oct 27, 2019
6 stars
Insert cell
Insert cell
Insert cell
d3.schemeSpectral
Insert cell
Insert cell
d3.interpolateSpectral(0.5)
Insert cell
Insert cell
ramp(d3.interpolateSpectral)
Insert cell
Insert cell
Insert cell
viewof gl = {
const canvas = DOM.canvas(width + 28, height);
canvas.style.cssText = `margin: 0 -14px;width:${width + 28}px;height:${height}px;`;
canvas.value = canvas.getContext("webgl");
return canvas;
}
Insert cell
schemeList = ({
Spectral: d3.schemeSpectral,
Blues: d3.schemeBlues,
BrBG: d3.schemeBrBG,
PuBuGn: d3.schemePuBuGn,
// ... and more
});
Insert cell
// Discrete scheme in d3-scale-chromatic
scheme = {
const scheme = schemeList[selectedScheme];
return scheme[scheme.length - 1];
}
Insert cell
// Hex color => Array of RGB in range [0, 1]
rgbColors = scheme.map(hex => {
const color = d3.color(hex);
return [color.r/255, color.g/255, color.b/255]
});
Insert cell
draw = {
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.enableVertexAttribArray(a_vertex);
gl.vertexAttribPointer(a_vertex, 2, gl.FLOAT, false, 0, 0);
gl.uniform1f(u_size, Math.max(viewof gl.width, viewof gl.height));

// Pass discrete scheme and its length
gl.uniform1i(u_n, rgbColors.length);
gl.uniform3fv(u_colorScheme, [].concat.apply([], rgbColors));

gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
}
Insert cell
// Since GLSL does not support dynamic-length uniform array, we allocate MAX_SCHEME_LENGTH.
fragmentShader = {
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, `
#define MAX_SCHEME_LENGTH 16

precision highp float;

uniform float u_size;
uniform int u_n;
uniform vec3 u_colorScheme[MAX_SCHEME_LENGTH];


// Use this function because we cannot use dynamic value for array index in GLSL
vec3 getData(float x) {
for (int j=0; j<MAX_SCHEME_LENGTH; j++) {
if (j == int(floor(x))) return u_colorScheme[j];
}
return vec3(0.0, 0.0, 0.0);
}

// GLSL translation of basis() in d3
// https://github.com/d3/d3-interpolate/blob/master/src/basis.js
vec3 basis(float t1, vec3 v0, vec3 v1, vec3 v2, vec3 v3) {
float t2 = t1 * t1;
float t3 = t2 * t1;
return ((1.0 - 3.0 * t1 + 3.0 * t2 - t3) * v0
+ (4.0 - 6.0 * t2 + 3.0 * t3) * v1
+ (1.0 + 3.0 * t1 + 3.0 * t2 - 3.0 * t3) * v2
+ t3 * v3) / 6.0;
}

// GLSL translation of exported function in the URL below
// https://github.com/d3/d3-interpolate/blob/master/src/basis.js
vec3 getColor(float t) {
int n = u_n - 1;

float i = t <= 0.0 ? 0.0 : (t >= 1.0 ? float(n) - 1.0 : floor(t * float(n)));
float t2 = t <= 0.0 ? 0.0 : (t >= 1.0 ? 1.0 : t);
vec3 v1 = getData(i);
vec3 v2 = getData(i+1.0);
vec3 v0 = i > 0.0 ? getData(i-1.0) : 2.0 * v1 - v2;
vec3 v3 = i < float(n) - 1.0 ? getData(i+2.0) : 2.0 * v2 - v1;

return basis((t2 - i / float(n)) * float(n), v0, v1, v2, v3);
}


void main() {
// Fill pixels based on x coordinate
vec2 pos = gl_FragCoord.xy / u_size;
gl_FragColor = vec4(getColor(pos.x), 1.0);
}
`);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
throw new Error(gl.getShaderInfoLog(shader));
}
Insert cell
vertexShader = {
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, `
attribute vec2 a_vertex;

void main(void) {
gl_Position = vec4(a_vertex, 0.0, 1.0);
}
`);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
throw new Error(gl.getShaderInfoLog(shader));
}
Insert cell
a_vertex = gl.getAttribLocation(program, "a_vertex")
Insert cell
u_size = gl.getUniformLocation(program, "u_size")
Insert cell
u_n = gl.getUniformLocation(program, "u_n");
Insert cell
u_colorScheme = gl.getUniformLocation(program, "u_colorScheme")
Insert cell
vertexBuffer = {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, Float32Array.of(-1, -1, +1, -1, +1, +1, -1, +1), gl.STATIC_DRAW);
return buffer;
}
Insert cell
program = {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS)) return program;
throw new Error(gl.getProgramInfoLog(program));
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more