Public
Edited
Jan 29, 2024
Importers
10 stars
Insert cell
Insert cell
width = 400
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
DFT2 = {
var DFT2 = JSON.parse(JSON.stringify(DFT));
for(var ii=0; ii<30; ii++){
DFT2[ii][0] = demo_x[ii];
DFT2[ii][1] = demo_y[ii];
}
return DFT2;
}
Insert cell
Insert cell
Insert cell
svg = 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
P = {
const path = svg.querySelector("path");
const pathSampler = new PathSampler(path.getAttribute("d"));
return Array.from({length: N}, (_, i) => pathSampler.pointAt(i / N));
}
Insert cell
K = Int16Array.from({length: M}, (_, i) => (1 + i >> 1) * (i & 1 ? -1 : 1))
Insert cell
d3.min(DFT.map(x => x[1]))
Insert cell
DFT = Array.from(K, k => {
let x = [0, 0];
for (let i = 0; i < N; ++i) {
x = add(x, mul(P[i], expim(k * i * 2 / N * -Math.PI)));
}
return [x[0] / N, x[1] / N];
})
Insert cell
N = 2000 // number of input samples
Insert cell
M = 350 // maximum number of epicycles
Insert cell
q = 2000 // number of output samples
Insert cell
function abs([re, im]) {
return Math.hypot(re, im);
}
Insert cell
function expim(im) {
return [Math.cos(im), Math.sin(im)];
}
Insert cell
function add([rea, ima], [reb, imb]) {
return [rea + reb, ima + imb];
}
Insert cell
function mul([rea, ima], [reb, imb]) {
return [rea * reb - ima * imb, rea * imb + ima * reb];
}
Insert cell
viewbox = svg.viewBox.baseVal
Insert cell
height = width
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