Public
Edited
May 3, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
field={
const context = DOM.context2d(width, height);
const R = [];
context.lineCap = "round";
context.lineJoin = "round";
for (let t = 0; true; ++t) {
const scale = viewof zoom.value * width / viewbox.width * 0.5;
const a = t * 2 / q * Math.PI;
context.save();
context.clearRect(0, 0, width, height);

// Calculate the current point.
let p = [0, 0];
for (let i = 0; i < M; ++i) {
p = add(p, mul(DFT[i], expim(a * K[i])));
}

// Zoom.
context.translate(width / 2, height / 2);
context.scale(scale, scale);
context.translate(-p[0], -p[1]);

// Draw circles.
context.beginPath();
for (let i = 0, p = [0, 0]; i < M; ++i) {
const r = abs(DFT[i]);
context.moveTo(p[0] + r, p[1]);
context.arc(...p, r, 0, 2 * Math.PI);
p = add(p, mul(DFT[i], expim(a * K[i])));
}
context.lineWidth = 0.25 / scale;
context.strokeStyle = "#999";
context.stroke();

// Draw lines.
context.beginPath();
context.moveTo(0, 0);
for (let i = 0, p = [0, 0]; i < M; ++i) {
context.lineTo(...p = add(p, mul(DFT[i], expim(a * K[i]))));
}
context.lineWidth = 0.75 / scale;
context.strokeStyle = "#333";
context.stroke();

// Draw arrowheads.
context.beginPath();
for (let i = 0, p = [0, 0]; i < M; ++i) {
arrow(context, p, p = add(p, mul(DFT[i], expim(a * K[i]))), {size: 8 / scale});
}
context.fillStyle = "#333";
context.fill();

// Draw the path.
if (R.length < q) R.push(p);
context.beginPath();
context.moveTo(...R[0]);
for (let i = 1, n = R.length; i < n; ++i) {
context.lineTo(...R[i]);
}
if (R.length >= q) context.closePath();
context.lineWidth = 1.5 / scale;
context.strokeStyle = "#a0a";
context.stroke();

context.restore();
yield context.canvas;
}
}
Insert cell
Insert cell
function arrow(context, [x0, y0], [x1, y1], {size = 1, delta = Math.PI / 6} = {}){
const dx = x1 - x0;
const dy = y1 - y0;
size = Math.min(size, Math.hypot(dx, dy) / 2);
const a = Math.atan2(dy, dx);
const a0 = a - delta;
const a1 = a + delta;
context.moveTo(x1 - size * Math.cos(a0), y1 - size * Math.sin(a0));
context.lineTo(x1, y1);
context.lineTo(x1 - size * Math.cos(a1), y1 - size * Math.sin(a1));
}
Insert cell
svg = {
// preload all the attached svgs manually
const fourier = await FileAttachment("fourier.svg").text();
const fourierElem = (new DOMParser).parseFromString(fourier,"image/svg+xml").documentElement;
const eye = await FileAttachment("EyeLine.svg").text();
const eyeElem = (new DOMParser).parseFromString(eye,"image/svg+xml").documentElement;
const rose = await FileAttachment("Rose@3.svg").text();
const roseElem = (new DOMParser).parseFromString(rose,"image/svg+xml").documentElement;
switch (imageName){
case "fourier":
return fourierElem;
case "EyeLine":
return eyeElem;
case "Rose":
return roseElem;
}
}
Insert cell
path = svg.querySelector("path")
Insert cell
l = path.getTotalLength()
Insert cell
P = Array.from({length: N}, (_, i) => {
const {x, y} = path.getPointAtLength(i / N * l);
return [x - viewbox.width / 2, y - viewbox.height / 2];
})
Insert cell
K = Int16Array.from({length: M}, (_, i) => (1 + i >> 1) * (i & 1 ? -1 : 1))
Insert cell
DFT = Array.from(K, k => {
let x = [0, 0];
for (let i = 0, N = P.length; i < N; ++i) {
x = add(x, mul(P[i], expim(k * i / N * 2 * -Math.PI)));
}
return [x[0] / N, x[1] / N];
})
Insert cell
//N = 2000 // number of input samples
Insert cell
//M = 1000 // number of epicycles
Insert cell
//q = 5000// 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 = 460
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