Published
Edited
Jul 5, 2020
1 fork
Importers
20 stars
Insert cell
Insert cell
Insert cell
Insert cell
[
d3.interpolatePiYG,
interpolateRgbCatmullRom(d3.schemePiYG[11]),
interpolateLabCatmullRom(d3.schemePiYG[11]),
interpolateLabCatmullRom(d3.schemePiYG[11]),
interpolateLabCubic(d3.schemePiYG[11]),
interpolateRgbCubic(d3.schemePiYG[11]),
(() => {
const a = interpolateLabCubic(d3.schemePiYG[11]);
return t => a(t).hex();
})(),
(() => {
const a = interpolateRgbCubic(d3.schemePiYG[11]);
return t => a(t).hex();
})()
].map(time)
Insert cell
function time(interpolate) {
const time = performance.now();
for (let i = 0; i < 50000; i++) interpolate(i / 50000);
return performance.now() - time;
}
Insert cell
Insert cell
html`${Object.keys(d3)
.filter(d => d.match(/^scheme/))
.map(d => d.replace(/^scheme/, ""))
.map(s => {
return md`### <div style="margin-top: 20px;">${s}</span>
${ramps(s)}
`;
})}`
Insert cell
ramps = scheme => {
const array = d3["scheme" + scheme][0]
? d3["scheme" + scheme]
: d3["scheme" + scheme].slice().pop();
return html`
<div style="font: 10px sans-serif; color: ${
d3.hsl(array[0]).l > .5 ? "black" : "white"
};">
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">${scheme}</div>
${ramp(t => array[Math.round((array.length - 1) * t)])}

${
d3["interpolate" + scheme]
? html`<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">
${"interpolate" + scheme}</div>${ramp(d3["interpolate" + scheme])}`
: html``
}
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">interpolateRgbCatmullRom(${scheme})</div>
${ramp(interpolateRgbCatmullRom(array))}
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">interpolateLabCatmullRom(${scheme})</div>
${ramp(interpolateLabCatmullRom(array))}
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">interpolateLabCubic(${scheme})</div>
${ramp(interpolateLabCubic(array))}
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">interpolateRgbCubic(${scheme})</div>
${ramp(interpolateRgbCubic(array))}
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">interpolateRgbMonotone(${scheme})</div>
${ramp(interpolateRgbMonotone(array))}
<div style="position: relative; top:0; left: 0px; height:0; border-top: 1px solid white">d3.interpolateRgbBasis(${scheme})</div>
${ramp(d3.interpolateRgbBasis(array))}
`;
}
Insert cell
interpolateLabCatmullRom = colors => {
const getpoint = CatmullRomSpline3D(
colors.map(d => d3.lab(d)).map(d => [d.l, d.a, d.b]),
{
closed: false,
tension: 0.5
}
).getPoint;

return t => d3.lab(...getpoint(t)).hex();
}
Insert cell
interpolateRgbCatmullRom = colors => {
const getpoint = CatmullRomSpline3D(
colors.map(d => d3.rgb(d)).map(d => [d.r, d.g, d.b]),
{
closed: false,
tension: 0.5
}
).getPoint;

return t => d3.rgb(...getpoint(t)).hex();
}
Insert cell
interpolateLabCubic = colors => {
const { l, a, b } = transpose(colors.map(d => d3.lab(d)));
const L = cubic(l),
A = cubic(a),
B = cubic(b);
return t => d3.lab(L(t), A(t), B(t));
}
Insert cell
interpolateRgbCubic = colors => {
const { r, g, b } = transpose(colors.map(d => d3.rgb(d)));
const R = cubic(r),
G = cubic(g),
B = cubic(b);
return t => d3.rgb(R(t), G(t), B(t));
}
Insert cell
function cubic(values, type = "default") {
const n = values.length - 1;
values = values.slice();
switch (type) {
case "default":
values.push(2 * values[n] - values[n - 1]);
values.unshift(2 * values[0] - values[1]);
return t => cubic(Math.max(0, Math.min(t, 1)));
case "closed":
values.push(values[0]);
values.unshift(values[n + 1]);
return t => cubic(t - Math.floor(t));
case "open":
throw new Error('open monotone spline not implemented yet');
}

function cubic(t) {
const i = Math.min(n - 1, Math.floor(t * n)),
v0 = values[i],
v1 = values[i + 1],
v2 = values[i + 2],
v3 = values[i + 3],
d = t * n - i,
s20 = v2 - v0,
s31 = v3 - v1,
s21 = (v2 - v1) * 2;
return (
(d / 2) *
(((s20 + s31 - 2 * s21) * d + (3 * s21 - 2 * s20 - s31)) * d + s20) +
v1
);
}
}
Insert cell
interpolateRgbMonotone = colors => {
const { r, g, b } = transpose(colors.map(d => d3.rgb(d)));
const R = monotone(r),
G = monotone(g),
B = monotone(b);
return t => d3.rgb(R(t), G(t), B(t));
}
Insert cell
function cubicClosed(values) {
return cubic(values, "closed");
}
Insert cell
function monotone(values, type) {
let n = values.length - 1,
k;
values = values.slice();
switch (type) {
case "closed":
values.unshift(values[n]);
values.push(values[1]);
values.push(values[2]);
n += 2;
k = 1 - 1 / n;
return t => monotone(k * frac(t));
case "open":
throw new Error('open monotone spline not implemented yet');
case "clamped":
default:
values.push(2 * values[n] - values[n - 1]);
values.unshift(2 * values[0] - values[1]);
return t => monotone(clamp(t, 0, 1));
}

function monotone(t) {
const i = Math.min(n - 1, Math.floor(t * n)),
y_im1 = values[i],
y_i = values[i + 1],
y_ip1 = values[i + 2],
y_ip2 = values[i + 3],
d = t * n - i,
s_im1 = n * (y_i - y_im1),
s_i = n * (y_ip1 - y_i),
s_ip1 = n * (y_ip2 - y_ip1),
yp_i =
(sign(s_im1) + sign(s_i)) *
min(abs(s_im1), abs(s_i), 0.25 * n * abs(y_ip1 - y_im1)),
yp_ip1 =
(sign(s_i) + sign(s_ip1)) *
min(abs(s_i), abs(s_ip1), 0.25 * n * abs(y_ip2 - y_i));

return (
(((yp_i + yp_ip1 - 2 * s_i) * d + (3 * s_i - 2 * yp_i - yp_ip1)) * d +
yp_i) *
(d / n) +
y_i
);
}
}
Insert cell
function monotoneClosed(values) {
return monotone(values, "closed");
}
Insert cell
import { abs, floor, max, min, sign } from "@fil/math"
Insert cell
function frac(t) {
return t - floor(t);
}
Insert cell
function clamp(t, lo, hi) {
return t < lo ? lo : t > hi ? hi : t;
}
Insert cell
monotoneClosed([0, 1, 2, 3])(1)
Insert cell
import { transpose } from "dff751d1da207b30"
Insert cell
d3 = require("d3")
Insert cell
import { ramp } from "@mbostock/color-ramp"
Insert cell
import { select } from "@jashkenas/inputs"
Insert cell
import { CatmullRomSpline as CatmullRomSpline3D } from "@mattdesl/perceptually-smooth-multi-color-linear-gradients"
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