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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more