My work is finally DONE!
After a week and a half of head scratching, troubleshooting, debugging and hard-coding through this problem, I’ve finally come up with the 1.0 version of my Fourier transformed series grapher.
This program allows you to have a random pull of N complex values (I had to code a little Cmplx function in order to make it work) and it can draw the graphic of two functions:
- the periodic function that is related to the Fourier series of that pull of numbers;
- the graph of the Fourier transform that rounds about N complex vectors, that round about each other with the same frequency
But I think that seeing it working is much, much better and explicative than words: so here’s a little image of the working program for you:
The graph on the left circles about itself, drawing the first red line, meanwhile the green line draws the waveform of the function itself, so you can visualize both the series (on the right) and the transform (on the left).
Here’s the actual code, split into 2 different files:
First file:
//if you want to modify the number of periodic wave that are combined
//into the Fourier series, you'll change the value of the "startDim" variable
float time=TWO_PI, tOff=0;
static int startDim=13, offset=550;
float[] yVals=new float[startDim];
Cmplx[] vals, funct=new Cmplx[startDim], wave=new Cmplx[startDim];
void keyTyped() {
//to change the rotation frequency (time)
if (key=='w') tOff+=0.005;
if (key=='s') tOff-=0.005;
}
void setup() {
size(1300, 1000);
noFill();
for (int a=0; a<startDim; a++) {
//setting up the arrays
wave[a]=new Cmplx();
yVals[a]=(noise((2.36*a)/31)*pow(a, 3)-5*a)/startDim;
funct[a]=new Cmplx((noise(5.41*a-0.04)-pow(5*a, 2)+4.52*a)/startDim, yVals[a]);
}
//calculating the Discrete Fourier transform of the complex values set
vals=dft(funct);
}
//this function draws the left side of the graph, but not the function itself
//also, it calculates the X/Y values that the function will assume as a function of the time
Cmplx fourierDraw(float x, float y, Cmplx[] f, float t) {
for (int i=0; i<f.length; i++) {
float pX=x, pY=y, n=f[i].freq, r=f[i].amp;
x+=r*cos(n*t+f[i].phase);
y-=r*sin(n*t+f[i].phase);
//draw every circle
stroke(200);
circle(pX, pY, r*2);
//draw the rotating radius
stroke(0, 0, 255);
fill(0, 0, 255);
line(pX, pY, x, y);
circle(x, y, 2);
noFill();
}
return new Cmplx(x, y);
}
void draw() {
background(255);
stroke(0);
translate(200, height/2);
//calculate the function
Cmplx v=fourierDraw(120, 0, vals, time);
//update the resulting wave graphs
wave=(Cmplx[])splice(wave, v, 0);
yVals=splice(yVals, v.im, 0);
//draw the functions
stroke(255, 0, 0);
//left function
beginShape();
for (int h=0; h<wave.length; h++) vertex(wave[h].re, wave[h].im);
endShape();
//right function
beginShape();
for (int h=0; h<yVals.length; h++) vertex(h+offset, yVals[h]);
endShape();
//connect the rotating circle and the wave graph with the green line
stroke(0, 255, 0);
line(v.re, v.im, offset, yVals[0]);
//updating global coords
time+=0.005+tOff;
//set the maximum number of values of the periodic function to be drawn (memory saving)
if (yVals.length>width/3) yVals=shorten(yVals);
//reset of the left-hand function every cycle
if (time>=TWO_PI || time<=-TWO_PI) {
time=0;
for (int a=wave.length-1; a>=0; a--) wave=(Cmplx[])shorten(wave);
}
}
Second file:
class Cmplx {
float re, im; //real and imaginary parts of the complex number
float freq, amp, phase; //I also add the polar coords directly into it
Cmplx add(Cmplx c) {
this.re+=c.re;
this.im+=c.im;
return this;
}
Cmplx mult(Cmplx c) {
float r=this.re*c.re-this.im*c.im;
float i=this.re*c.im+this.im*c.re;
return new Cmplx(r, i);
}
//I will need two different constructors for different purposes, one of them is clear
Cmplx() {
}
Cmplx(float a, float b) {
this.re=a;
this.im=b;
}
}
//calculating the DFT of the periodic function
Cmplx[] dft(float[] x) {
Cmplx[] X=new Cmplx[x.length];
for (int a=0; a<x.length; a++) X[a]=new Cmplx();
final int N=X.length;
for (int k=0; k<N; k++) {
float r=0, i=0;
for (int n=0; n<N; n++) {
final float angle=(TWO_PI*k*n)/N;
r+=x[n]*cos(angle);
i+=x[n]*sin(angle);
}
r/=N;
i/=N;
X[k].re=r;
X[k].im=i;
X[k].freq=k;
X[k].amp=sqrt(pow(r, 2)+pow(i, 2));
X[k].phase=atan2(i, r);
}
return X;
}
//calculating the DFT of the complex Fourier transform
Cmplx[] dft(Cmplx[] x) {
Cmplx[] X=new Cmplx[x.length];
for (int a=0; a<x.length; a++) X[a]=new Cmplx();
final int N=X.length;
for (int k=0; k<N; k++) {
final Cmplx sum=new Cmplx();
for (int n=0; n<N; n++) {
final float angle=(TWO_PI*k*n)/N;
Cmplx c=new Cmplx(cos(angle), -sin(angle));
sum.add(x[n].mult(c));
}
sum.re/=N;
sum.im/=N;
X[k].re=sum.re;
X[k].im=sum.im;
X[k].freq=k;
X[k].amp=sqrt(pow(sum.re, 2)+pow(sum.im, 2));
X[k].phase=atan2(sum.im, sum.re);
}
return X;
}
In order to make this work, you’ll split the two codes into 2 different Files and name the folder as the “main” file, the first one that contains the setup()
and draw()
functions in it. Then you’ll compile it and you will be able to run it! Enjoy!