Published
Edited
Jul 4, 2019
2 forks
33 stars
Insert cell
Insert cell
M = 2000 // The number of samples. Try editing!
Insert cell
linearSamples = {
const L = path.getTotalLength();
return Array.from({length: M}, (_, i) => {
const {x, y} = path.getPointAtLength(i / M * L);
return [x, y];
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
pathSampler = new PathSampler(path.getAttribute("d"))
Insert cell
parametricSamples = Array.from({length: M}, (_, i) => pathSampler.pointAt(i / M))
Insert cell
Insert cell
Insert cell
Insert cell
image = fetch("https://gist.githubusercontent.com/mbostock/a4fd7a68925d4039c22996cc1d4862ce/raw/d813a42956d311d73fee336e1b5aac899c835883/fourier.svg")
.then(response => response.text())
.then(text => (new DOMParser).parseFromString(text, "image/svg+xml"))
.then(svg => svg.documentElement)
Insert cell
path = image.querySelector("path")
Insert cell
class PathSampler {
constructor(source) {
const data = PathData.parse(source, {normalize: true});
const d0 = data[0];
const d1 = data[data.length - 1];
if (d0.type !== "M") throw new Error("expected M");
if (d1.type !== "Z") throw new Error("expected Z");
const segments = Array.from({length: data.length - 2}, (_, i) => {
const {type, values} = data[i + 1];
switch (type) {
case "C": return new C(data[i].values.slice(-2).concat(values));
case "L": return new L(data[i].values.slice(-2).concat(values));
}
});
const start = d0.values.slice(0, 2);
const end = data[data.length - 2].values.slice(-2);
if (start[0] !== end[0] || start[1] !== end[1]) {
segments.push(new L(end.concat(start)));
}
this.segments = segments;
}
pointAt(t) {
const n = this.segments.length;
if (!((t *= n) >= t)) return;
const i = Math.max(0, Math.min(n - 1, Math.floor(t)));
return this.segments[i].pointAt(t % 1);
}
}
Insert cell
class L {
constructor(values) {
this.values = values;
}
pointAt(t) {
const [x0, y0, x1, y1] = this.values;
const a = t;
const b = 1 - t;
return [
a * x0 + b * x1,
a * y0 + b * y1
];
}
}
Insert cell
class C {
constructor(values) {
this.values = values;
}
pointAt(t) {
const [x0, y0, x1, y1, x2, y2, x3, y3] = this.values;
const a = (1 - t) ** 3;
const b = 3 * (1 - t) ** 2 * t;
const c = 3 * (1 - t) * t ** 2;
const d = t ** 3;
return [
a * x0 + b * x1 + c * x2 + d * x3,
a * y0 + b * y1 + c * y2 + d * y3
];
}
}
Insert cell
PathData = require("path-data@0.0.2")
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