Unlisted
Edited
Jul 21, 2022
1 fork
Importers
Insert cell
Insert cell
chart([bezierInterpolate(0,2,.5,-.7), bezierInterpolate(0.1,2,.5,-.1), bezierInterpolate(0.2,2,.6,0.1), bezierInterpolate(x1, y1, x2, y2)], { color: (_, i) => color.domain([-1, 3])(i)})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function bezierInterpolate(x1, y1, x2, y2) {
function bezier(x1, x2) {
const a = 1 - 3 * x2 + 3 * x1,
b = 3 * x2 - 6 * x1,
c = 3 * x1;
return t => ((a * t + b) * t + c) * t;
}
function bezierDerivate(x1, x2) {
const a = 3 - 9 * x2 + 9 * x1,
b = 6 * x2 - 12 * x1,
c = 3 * x1;
return t => (a * t + b) * t + c;
}
function solve(f, fp, y) {
let x = y;
for (let i = 0; i < 100; i++) {
const delta = (y - f(x)) / (fp(x) || epsilon),
absd = abs(delta);
x += absd < 0.1 ? delta : (0.1 * delta) / absd;
if (absd < 1e-9) break;
}
return x < 0 ? 0 : x > 1 ? 1 : x;
}
return x1 === y1 && x2 === y2 // useless(?) shortcut when y===x
? x => x
: (x, debug) => {
if (!debug) {
return bezier(y1, y2)(
solve(bezier(x1, x2), bezierDerivate(x1, x2), x)
);
} else {
const t = solve(bezier(x1, x2), bezierDerivate(x1, x2), x),
y = bezier(y1, y2)(t);

const foundx = bezier(x1, x2)(t),
err = abs(x - foundx);
return {
t,
foundx,
err,
y
};
}
};
}
Insert cell
import { abs, epsilon } from "@fil/math"
Insert cell
Insert cell
be(0.3, -1, 0.7, 2)(0.4)
Insert cell
bezierInterpolate(0.3, -1, 0.7, 2)(0.4)
Insert cell
Insert cell
// ours
{
const time = performance.now();
for (let i = 0; i < 500000; i++) {
const x1 = randoms[2 * i],
x2 = randoms[2 * i + 1];
const sol = bezierInterpolate(x1, 0, x2, 1)(v);
}
return (performance.now() - time) / 50000;
}
Insert cell
// reference
{
const time = performance.now();
for (let i = 0; i < 500000; i++) {
const x1 = randoms[2 * i],
x2 = randoms[2 * i + 1];
const sol = be(x1, 0, x2, 1)(v);
}
return (performance.now() - time) / 50000;
}
Insert cell
randoms = Float32Array.from({ length: 2 * 500000 }, Math.random)
Insert cell
Insert cell
Insert cell
bezierInterpolate(0, 1, 1, 1)(1.2e-9, true) // our solution is better
Insert cell
bezierInterpolate(0, 1, 1, 1)(1e-9, true)
Insert cell
be(0, 1, 1, 1)(1.2e-9) // 🌶 this is wrong, proof is that it gives same result for a different value (below)
Insert cell
be(0, 1, 1, 1)(1e-9)
Insert cell
Insert cell
mutable debug = undefined
Insert cell
Insert cell
{
const x1 = Math.random(),
x2 = Math.random();
const sol = bezierInterpolate(x1, -x1, x2, -x2)(v);

return { x1, x2, v, sol };
}
Insert cell
errorchart(bezierInterpolate)
Insert cell
errorchart(be)
Insert cell
import {chart, color} from "@d3/easing-color"
Insert cell
function errorchart(bezier) {
mutable debug = "testing";
const errmax = 1e-6,
w = 500,
x = t => 500 * t,
context = DOM.context2d(w, w),
color = d3.scaleSequentialLog(d3.interpolateBuPu).domain([1e-16, errmax]);

for (let i = 0; i < 15000; i++) {
const x1 = Math.random(),
x2 = Math.random(),
y1 = 2 * x1,
y2 = 2 * x2;
const sol = bezier(x1, x1, x2, x2 + 1e-16)(v);
const err = Math.abs(sol - v);
context.fillStyle =
(err > errmax ? "black" : err ? color(err) : "white") || "yellow";
context.fillRect(x(x1), 500 - x(x2), 4, 4);
}

return context.canvas;
}
Insert cell
be = require("bezier-easing@2.1.0/dist/bezier-easing.min.js")
Insert cell
d3 = require("d3@7")
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