Drawing With Discrete Fourier Transform
by bldrkamal in Teachers > Math
3276 Views, 3 Favorites, 0 Comments
Drawing With Discrete Fourier Transform
In this instructable, we are going to input a bunch of signals by drawing or sketching anything on an HTML canvas and the signal will be fed into a discrete Fourier transform algorithm which in turn will trace our input signal or sketch, thereby redrawing what we have drawn already just like this:
you can follow this Link and try to sketch, then see what happens
The discrete Fourier transform transforms a sequence of N complex numbers {xn}:x1,x2,x3 ---xn into another sequence of complex numbers.
Supplies
- any code editor.
Create a Javascript Class to Implement Complex Number Addtion and Multiplication
class Complex { constructor(a, b) { this.re = a; this.im = b; } add(c) { this.re += c.re; this.im += c.im; } mult(c) { const re = this.re * c.re - this.im * c.im; const im = this.re * c.im + this.im * c.re; return new Complex(re, im); } }
Create a Javascript Function to That Will Implement Discrete Furier Transform(DFT)
function dft(x) { const X = []; const N = x.length; for (let k = 0; k < N; k++) { let sum = new Complex(0, 0); for (let n = 0; n < N; n++) { const phi = (TWO_PI * k * n) / N; const c = new Complex(cos(phi), -sin(phi)); sum.add(x[n].mult(c)); } sum.re = sum.re / N; sum.im = sum.im / N; let freq = k; let amp = sqrt(sum.re * sum.re + sum.im * sum.im); let phase = atan2(sum.im, sum.re); X[k] = { re: sum.re, im: sum.im, freq, amp, phase }; } return X; }
Drawing Phase- Define the Variables and Constants That Will Change When User Is Active and Drawing Path
const USER = 0; const FOURIER = 1; let x = []; let fourierX; let time = 0; let path = []; let drawing = []; let state =
This Function Will Keep the State of the Drawing When a User Pressed the Mouse
function mousePressed() { state = USER; drawing = []; x = []; time = 0; path = []; }
This Function Controls the State of the Fourier Algorithm to Start Drawing When the User Released the Mouse
function mouseReleased() { state = FOURIER; const skip = 1; for (let i = 0; i < drawing.length; i += skip) { x.push(new Complex(drawing[i].x, drawing[i].y)); } fourierX = dft(x); fourierX.sort((a, b) => b.amp - a.amp); }
This Function Setup Is Responsible for Drawing Canvas Using the P5.js Library
function setup() { createCanvas(windowWidth, windowHeight); background(100); fill(255); textAlign(CENTER); textSize(64); text("Draw Something!", width/2, height/2); strokeWeight(5); }
This Function Sets Up the Rotating Epipcyles That You Will See on Screen.
function epicycles(x, y, rotation, fourier) { for (let i = 0; i < fourier.length; i++) { let prevx = x; let prevy = y; let freq = fourier[i].freq; let radius = fourier[i].amp; let phase = fourier[i].phase; x += radius * cos(freq * time + phase + rotation); y += radius * sin(freq * time + phase + rotation); stroke(255, 100); noFill(); ellipse(prevx, prevy, radius * 2); stroke(255); line(prevx, prevy, x, y); } return createVector(x, y); }
This Is the Actual Function That Draws and Loops When You Are Sketching and When the DFT Starts Rendering Your Drawing
function draw() { if (state == USER) { background(100); let point = createVector(mouseX - width / 2, mouseY - height / 2); drawing.push(point); stroke(255); noFill(); beginShape(); for (let v of drawing) { vertex(v.x + width / 2, v.y + height / 2); } endShape(); } else if (state == FOURIER) { background(100); let v = epicycles(width / 2, height / 2, 0, fourierX); path.unshift(v); beginShape(); noFill(); strokeWeight(5); stroke(0, 255,255); for (let i = 0; i < path.length; i++) { vertex(path[i].x, path[i].y); } endShape(); const dt = TWO_PI / fourierX.length; time += dt; if (time > TWO_PI) { time = 0; path = []; } } }
This Is the Complete Code. You Can Run It in Your Browser
open any code editor, copy and paste the code and save it as index.html.
Click the file to open in your browser.
<!DOCTYPE html><html><head> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.js"></script> <link rel="stylesheet" type="text/css" href="style.css"> <meta charset="utf-8"> </head> <body> <script> class Complex { constructor(a, b) { this.re = a; this.im = b; } add(c) { this.re += c.re; this.im += c.im; } mult(c) { const re = this.re * c.re - this.im * c.im; const im = this.re * c.im + this.im * c.re; return new Complex(re, im); } } function dft(x) { const X = []; const N = x.length; for (let k = 0; k < N; k++) { let sum = new Complex(0, 0); for (let n = 0; n < N; n++) { const phi = (TWO_PI * k * n) / N; const c = new Complex(cos(phi), -sin(phi)); sum.add(x[n].mult(c)); } sum.re = sum.re / N; sum.im = sum.im / N; let freq = k; let amp = sqrt(sum.re * sum.re + sum.im * sum.im); let phase = atan2(sum.im, sum.re); X[k] = { re: sum.re, im: sum.im, freq, amp, phase }; } return X; } </script> <script> const USER = 0; const FOURIER = 1; let x = []; let fourierX; let time = 0; let path = []; let drawing = []; let state = -1; function mousePressed() { state = USER; drawing = []; x = []; time = 0; path = []; } function mouseReleased() { state = FOURIER; const skip = 1; for (let i = 0; i < drawing.length; i += skip) { x.push(new Complex(drawing[i].x, drawing[i].y)); } fourierX = dft(x); fourierX.sort((a, b) => b.amp - a.amp); } function setup() { createCanvas(windowWidth, windowHeight); background(100); fill(255); textAlign(CENTER); textSize(64); text("Draw Something!", width/2, height/2); strokeWeight(5); } function epicycles(x, y, rotation, fourier) { for (let i = 0; i < fourier.length; i++) { let prevx = x; let prevy = y; let freq = fourier[i].freq; let radius = fourier[i].amp; let phase = fourier[i].phase; x += radius * cos(freq * time + phase + rotation); y += radius * sin(freq * time + phase + rotation); stroke(255, 100); noFill(); ellipse(prevx, prevy, radius * 2); stroke(255); line(prevx, prevy, x, y); } return createVector(x, y); } function draw() { if (state == USER) { background(100); let point = createVector(mouseX - width / 2, mouseY - height / 2); drawing.push(point); stroke(255); noFill(); beginShape(); for (let v of drawing) { vertex(v.x + width / 2, v.y + height / 2); } endShape(); } else if (state == FOURIER) { background(100); let v = epicycles(width / 2, height / 2, 0, fourierX); path.unshift(v); beginShape(); noFill(); strokeWeight(5); stroke(0, 255,255); for (let i = 0; i < path.length; i++) { vertex(path[i].x, path[i].y); } endShape(); const dt = TWO_PI / fourierX.length; time += dt; if (time > TWO_PI) { time = 0; path = []; } } } </script> </body></html>