# Complex Fourier Transformation using Processing

**URL:** https://discourse.processing.org/t/complex-fourier-transformation-using-processing/38946
**Category:** Gallery
**Created:** [September 23, 2022, 12:44pm UTC](https://discourse.processing.org/t/complex-fourier-transformation-using-processing/38946 "2022-09-23T12:44:43Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![NumericPrime](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/numericprime/32/16318_2.png) [@NumericPrime](https://discourse.processing.org/u/NumericPrime)
#### Post date: [September 23, 2022, 12:44pm UTC](https://discourse.processing.org/t/complex-fourier-transformation-using-processing/38946/1 "2022-09-23T12:44:43Z")

</div>

I have made a few animations using complex Fourier Transformations and processing.

> **Sketch to generate the points**
>
> ```auto
> /**
> ENGLISH
> Instructions:
> When the sketch launches you have to import a image. 
> You can adjust said image by right-clicking.
> Left-clicking adds a point to the line.
> z removes the last point.
> p saves the data directly. The curve will be closed by making a line to the first point.
> r saves a edited version of the data.The curve will be closed by going back the route to the first point.
> 
> o loads a new image
> i loads point data saved using p
> */
> /** 
> GERMAN
> Anleitung: 
> Wenn das Programm gestartet wird kann ein Bild importiert werden
> Das Bild kann mit einem Rechtsklick verschoben und vergrößert werden.
> Mit einem Linksklick kann ein Punkt hinzugefügt werden
> Mit z kann man den letzten Punkt entfernen
> 
> Mit p kann man die Daten direkt abspeichern. Die Kurve wird geschlossen, indem eine Linie zum ersten Punkt hinzugefügt werden.
> Mit r kann man die Daten abspeichern. Die Kurve wird geschlossen, indem der gleiche weg zurückgegangen wird.
> 
> Mit o kann man ein neues Bild laden.
> Mit i kann man Daten, die mit p abgespeichert wurden wieder aufrufen.
> */
> 
> import javax.swing.*;
> import java.util.*;
> 
> List<Integer> X;
> List<Integer> Y;
> int p=0;
> volatile PImage im;
> volatile boolean allFilesRead=false;
> String pushed[]=null;
> int sx=0;
> int sy=0;
> 
> float resc=1;
> int tx=0;
> int ty=0;
> void setup() {
> size(900, 900);
> X=new ArrayList();
> Y=new ArrayList();
> selectInput("Data", "loadFile");
> while (!allFilesRead);
> sx=im.width;
> sy=im.height;
> im.resize(im.width, im.height);
> println(X.size());
> p=X.size();
> }
> void draw() {
> background(255);
> image(im, tx, ty);
> fill(255, 150);
> rect(-1, -1, width+2, height+2);
> 
> if (p>=1) {
> for (int i=1; i<p; i++) line(X.get(i), Y.get(i), X.get(i-1), Y.get(i-1));
> }
> }
> void mousePressed() {
> if (mouseButton==LEFT) {
> X.add(mouseX);
> Y.add(mouseY);
> p=X.size();
> }
> if (mouseButton==RIGHT)getInfo();
> }
> void keyPressed() {
> if (key=='r') {
> String[] ret=new String[2*X.size()-1];
> int co[][]=new int[2*X.size()][2];
> for (int i=0; i<X.size(); i++) {
> ret[i]=""+X.get(i)+" "+Y.get(i);
> }
> for (int i=0; i<X.size()-1; i++) {
> ret[i+X.size()]=""+X.get(X.size()-1-i)+" "+Y.get(X.size()-1-i);
> }
> pushed=ret;
> selectOutput("Save As", "saveTxtFile");
> println(ret);
> }
> if (key=='p') {
> String[] ret=new String[X.size()];
> // int co[][]=new int[X.size()][2];
> for (int i=0; i<X.size(); i++) {
> ret[i]=X.get(i)+" "+Y.get(i);
> }
> pushed=ret;
> selectOutput("Save As", "saveTxtFile");
> println(ret);
> }
> if (key=='z') {
> X.remove(X.size()-1);
> Y.remove(Y.size()-1);
> p=X.size();
> }
> if (key=='s') {
> color c=get(mouseX, mouseY);
> for (int i=mouseX; i>=0; i--) if (c!=color(0, 0, 0)&&(abs(red(c)-red(get(i, mouseY)))>24||abs(green(c)-green(get(i, mouseY)))>24||abs(blue(c)-blue(get(i, mouseY)))>24)) {
> X.add(i);
> Y.add(mouseY);
> p=X.size();
> i=-1;
> }
> }
> if (key=='i') {
> selectInput("Open image","loadFile");
> }
> if (key=='o') {
> selectInput("Open point data","loadTextFile");
> }
> }
> void mouseDragged() {
> if (frameCount%5==0) { 
> X.add(mouseX);
> Y.add(mouseY);
> p=X.size();
> }
> }
> public void loadFile(File f) {
> im=loadImage(f.getAbsolutePath());
> allFilesRead=true;
> }
> public void loadTextFile(File f) {
> String[] load=loadStrings(f);
> X.clear();
> Y.clear();
> for (String st : load) {
> String[] splitted=st.split(" ");
> X.add(int(splitted[0]));
> Y.add(int(splitted[1]));
> println(int(splitted[0]), int(splitted[1]));
> }
> p=X.size();
> }
> public void saveTxtFile(File f) {
> saveStrings(f, pushed);
> allFilesRead=true;
> }
> public void getInfo() {
> JPanel c=new JPanel();
> JTextField txf=new JTextField(10);
> txf.setText(tx+"");
> JTextField tyf=new JTextField(10);
> tyf.setText(ty+"");
> JTextField resf=new JTextField(10);
> resf.setText(resc+"");
> c.setLayout(new java.awt.GridLayout(3, 2, 10, 10));
> c.add(new JLabel("Translation X:"));
> c.add(txf);
> c.add(new JLabel("Translation Y:"));
> c.add(tyf);
> c.add(new JLabel("Rescale:"));
> c.add(resf);
> JOptionPane.showMessageDialog(null, c);
> tx=int(txf.getText());
> ty=int(tyf.getText());
> resc=float(resf.getText());
> im.resize((int)(sx*resc), (int)(sy*resc));
> }
> 
> ```

> **Sketch to render the animation**
>
> ```auto
> /**
> ENGLISH
> How to use this program:
> When the sketch launches you select the color for the line. Next the file with the points will be selected.
> These are the instructions what to paint.
>  
> Toggleable function:
> z disables the display of the epicycles
> c disables the display of thee circles only
> g makes the slower cycles a lighter color
> ENTER makes the sketch focus the point currently painted
> f makes thee sketch fokus (0,0) as middle point
> l locks all keys
> 0 halts the program (however new images will be safed)
> q makes the program click every 75 frames
>  
> Other functions:
> w and s zoom in/out
> pressing r resets the simulation
> pressing r for at least 3 seconds allows selecting a new file to read
> t resets all toggelable functions and zooming
>  
> Settings.ini settings:
> circles: how many circles spin to the left (in reality double the circles are calculated)
> delta: Used for integrating numerically must be small but >0
> frames: Number of frames needed to complete a complete cycle
> record: When true all frames are saved to be made into a video
> */
> /**
> GERMAN
> Anleitung:
> Wenn man das Programm öffnet kann man eine Strichfarbe für die Funktion auswählen.
> Als nächstes kann man eine Datei laden, aus deren Punkte die Kurve berechnet wird.
>  
> Mit z kann man die Zeichnung der Kreise und Zeiger abschalten
> Mit c kann man nur die Zeichnung der Kreise abschalten
> Mit g kann man dafür sorgen, dass die langsameren Kreise heller werden und die schnelleren dunkler
> Mit ENTER kann man den gezeichneten Punkt fokusieren
> Mit f kann man die obere ecke fokusieren
> Mit w und s kann man rein, bzw rauszoomen
>  
> Mit r kann man die Simulation zurücksetzen
> Mit t kann man die Ansicht zurücksetzten
>  
> Mit l kann man die Tastendrücke sperren/entsperren
> Mit 0 hält das Programm an
> Mit q clickt das Programm alle 75 Bilder
>  
> Wenn man r gedrückt lässt kann man neue Punkte einlesen
>  
> Einstellungen:
> circles: wie viele Kreise drehen nach links (in wirklichkeit generiert er die Doppelte anzahl an kreien)
> delta: Benötigt für das numerische Integrieren, je nähere an 0 desto genauer wird die Berechnung der Kreise aber die Berechnung dauert länger.
> frames: Anzahl der Frames insgesammt. Bestimmt die Geschwindigkeit
> record: Bestimmt ob die einzelnen Frames abgespeichert werden sollen, sodass man ein Video erstellen kann.
> */
> //float[][] v={{100, 300}, {300, 100}, {100, 100}, {300, 300}, {300, 100},{100, 300},{300, 300}, {100, 100}};
> import javax.swing.*;
> import java.awt.*;
> import java.awt.event.*;
> Robot r;
> float[][] v={};
> float g[][];
> volatile String read[];
> float dif=0.0002;
> int fk=4000;
> ArrayList<Float> p;
> ArrayList<Float> p2;
> float vv=1;
> boolean record=false;
> boolean b=false;
> boolean c=true;
> float re[];
> boolean z=true;
> int sk=1;
> int ll=0;
> boolean lock=false;
> boolean gradient=false;
> float ak[]={0, 0};
> boolean focus=false;
> int reset=0;
> boolean autoclick=false;
> int circlenumb=700;
> boolean halt=false;
> volatile boolean allFilesRead=false;
> float[] f(float x) {
> float dist[]=new float[v.length];
> // for(int i=0;i<v.length;i++) dist[i];
> int sc2=0;
> int d=0;
> int d2=0;
> for (int i=0; i<v.length-1; i++) {
> d2+=dist(v[(i+1)%v.length][0], v[(i+1)%v.length][1], v[i][0], v[i][1]);
> }
> for (int i=0; i<v.length-1&&d+dist(v[(i+1)%v.length][0], v[(i+1)%v.length][1], v[i][0], v[i][1])<x/(2*PI)*d2; i++) {
> d+=dist(v[(i+1)%v.length][0], v[(i+1)%v.length][1], v[i][0], v[i][1]);
> sc2++;
> }
> float xp=(x/(2*PI)*d2-d)/dist(v[(sc2+1)%v.length][0], v[(sc2+1)%v.length][1], v[sc2][0], v[sc2][1]);
> // println(xp,d,(x/(2*PI)*d2-d),dist(v[(sc2+1)%v.length][0], v[(sc2+1)%v.length][1], v[sc2][0], v[sc2][1]));
> if (dist(v[(sc2+1)%v.length][0], v[(sc2+1)%v.length][1], v[sc2][0], v[sc2][1])==0) xp=1;
> // println(xp);
> /*if (xp>1) {
> exit();
> }*/
> 
> float[] ret={(v[(sc2+1)%v.length][0]-v[sc2][0])*xp+v[sc2][0], (v[(sc2+1)%v.length][1]-v[sc2][1])*xp+v[sc2][1]};
> //println(ret);
> return ret;
> }
> Color col=Color.RED;
> void setup() {
> try {
> r=new Robot();
> }
> catch(Exception e) {
> }
> 
> getSurface().setVisible(false);
> readSettings();
> JColorChooser c=new JColorChooser(Color.RED);
> JOptionPane.showMessageDialog(null, c);
> col=c.getColor();
> try {
> ps=new java.io.PrintStream(sketchPath("data/function"));
> }
> catch(Exception e) {
> }
> p=new ArrayList();
> p2=new ArrayList();
> //read=loadStrings("read.txt");
> selectInput("Data", "loadFile");
> while (!allFilesRead);
> v=new float[read.length][2];
> for (int i=0; i<v.length; i++) v[i]=new float[]{float(read[i].split(" ")[0]), float(read[i].split(" ")[1])};
> // for (int i=0; i<v.length; i++) v[i][1]=-v[i][1]+850;
> size(900, 900);
> background(255);
> noFill();
> frameRate(60);
> //strokeWeight(4);
> calculates(circlenumb);
> getSurface().setVisible(true);
> }
> int startat=0;
> boolean paintThisFrame=true;
> void draw() {
> if (!halt) {
> if (ll>0) ll--;
> 
> background(255);//=f(((float)sk/230)%(2*PI));
> //if(b) translate(-vv*re[0],-vv*re[1]);
> re=newpos(-((float)sk/fk)%(2*PI));
> //ellipse(re[0],re[1],10,10);
> stroke(255, 0, 0);
> point(vv*(re[0]+(b?-re[0]:0))+(b?width/2:0), vv*(re[1]+(b?-re[1]:0))+(b?height/2:0));
> if (((float)sk/fk)<=2.2*PI) {
> p.add(re[0]);
> p2.add(re[1]);
> }
> if(autoclick) if (sk%75==0) {
> r.mousePress(InputEvent.BUTTON1_MASK);
> delay(100);
> r.mouseRelease(InputEvent.BUTTON1_MASK);
> }
> if (frameCount%100==0) println(p.size());
> ak[0]=re[0];
> ak[1]=re[1];
> //point(re[0],re[1]);
> 
> strokeWeight(2);
> stroke(col.getRed(), col.getGreen(), col.getBlue());
> if (paintThisFrame) for (int i=0; i<sk-1&&((float)sk/fk)%(2*PI)<=2*PI; i++) {
> //re=newpos(((float)i/fk)%(2*PI));
> //ellipse(re[0],re[1],10,10);
> line(vv*(p.get(i)+(b?-re[0]:0))+(b?width/2:0), vv*(p2.get(i)+(b?-re[1]:0))+(b?height/2:0), vv*(p.get(i+1)+(b?-re[0]:0))+(b?width/2:0), vv*(p2.get(i+1)+(b?-re[1]:0))+(b?height/2:0));
> //println(re);
> }
> stroke(0);
> strokeWeight(10);
> strokeWeight(1);
> 
> float last[]={g[(g.length-1)/2][0], g[(g.length-1)/2][1]};
> 
> if (z&&paintThisFrame) for (int i=1; i<g.length; i++) {
> if (i==1) {
> strokeWeight(5);
> point(vv*last[0]+(b?-vv*ak[0]:0)+(b?width/2:0), vv*last[1]+(b?-vv*ak[1]:0)+(b?height/2:0));
> strokeWeight(1);
> }
> int n=(int)(i/2)*(int)pow(-1, i);
> float re2[]=newpos_ex(-((float)sk/fk)%(2*PI), n, (int)((i-1)/2)*(int)pow(-1, i-1));
> re[0]=re2[0];
> re[1]=re2[1];
> //float[] re2=re=newpos_ex(((float)sk/230)%(2*PI),(int)((i-1)/2)*(int)pow(-1,i-1),(int)((i-2)/2)*(int)pow(-1,i-2));
> stroke(0);
> if (gradient&&paintThisFrame) stroke(255*pow(((float)g.length-(float)i)/(float)g.length, 0.9));
> //println(pow(((float)g.length-(float)i)/(float)g.length,0.5));
> line(vv*re[0]+(b?-vv*ak[0]:0)+(b?width/2:0), vv*re[1]+(b?-vv*ak[1]:0)+(b?height/2:0), vv*last[0]+(b?-vv*ak[0]:0)+(b?width/2:0), vv*last[1]+(b?-vv*ak[1]:0)+(b?height/2:0));
> if (!gradient) stroke(90);
> if (c&&paintThisFrame)ellipse(vv*last[0]+(b?-vv*ak[0]:0)+(b?width/2:0), vv*last[1]+(b?-vv*ak[1]:0)+(b?height/2:0), vv*2*sqrt(sq(last[0]-re[0])+sq(last[1]-re[1])), vv*2*sqrt(sq(last[0]-re[0])+sq(last[1]-re[1])));
> last[0]=re[0];
> last[1]=re[1];
> }
> if (z&&paintThisFrame) line(vv*(ak[0]+(b?-ak[0]:0))+(b?width/2:0), vv*(ak[1]+(b?-ak[1]:0))+(b?height/2:0), vv*(last[0]+(b?-ak[0]:0))+(b?width/2:0), vv*(last[1]+(b?-ak[1]:0))+(b?height/2:0));
> if (!gradient) stroke(90);
> if (z&&c&&paintThisFrame) ellipse(vv*re[0]+(b?-vv*ak[0]:0)+(b?width/2:0), vv*re[1]+(b?-vv*ak[1]:0)+(b?height/2:0), vv*2*sqrt(sq(ak[0]-re[0])+sq(ak[1]-re[1])), vv*2*sqrt(sq(ak[0]-re[0])+sq(ak[1]-re[1])));
> ;
> //if(b)translate(-re[0],-re[1]);
> sk++;
> if (keyPressed) if (key=='r') {
> reset++;
> if (reset>=3*frameRate) {
> allFilesRead=false;
> //read=loadStrings("read.txt");
> selectInput("Data", "loadFile");
> while (!allFilesRead);
> v=new float[read.length][2];
> for (int i=0; i<v.length; i++) v[i]=new float[]{float(read[i].split(" ")[0]), float(read[i].split(" ")[1])};
> background(355);
> calculates(circlenumb);
> }
> } else {
> reset=0;
> }
> }
> if (frameCount>startat-5&&record)saveFrame("animation/frame-######.png");
> if (frameCount>startat-10) paintThisFrame=true;
> }
> float[] integ(int num) {
> float sum[]={0, 0};
> for (float i=0; i<2*PI; i+=dif) {
> float x[]=f(i);
> sum[0]+=(x[0]*cos(num*i)-sin(num*i)*x[1])*dif;
> sum[1]+=(x[0]*sin(num*i)+cos(num*i)*x[1])*dif;
> }
> sum[0]/=2*PI;
> sum[1]/=2*PI;
> return sum;
> }
> java.io.PrintStream ps;
> void calculates(int n) {
> JProgressBar prog=new JProgressBar(0, 2*n);
> JLabel text=new JLabel(" Calculating");
> JFrame wind=new JFrame();
> text.setFont(new Font("arial", Font.PLAIN, 22));
> wind.setUndecorated(true);
> wind.setLayout(new BorderLayout());
> wind.setSize(500, 350);
> wind.setBackground(new Color(200, 200, 200));
> wind.setLocation(displayWidth/2-250, displayHeight/2-175);
> wind.add(prog, "South");
> wind.add(text, "Center");
> prog.setStringPainted(true);
> wind.setVisible(true);
> g=new float[2*n+1][2];
> for (int i=0; i<g.length; i++) {
> g[i]=integ(i-n);
> println(((float)i/(float)g.length)*100+"%");
> prog.setValue(i);
> prog.setString((int)(((float)i/(float)g.length)*100)+"%");
> text.setText(" Calculated: "+i+"/"+g.length);
> }
> wind.setVisible(false);
> wind.dispose();
> }
> float[] newpos(float x) {
> float sum[]={0, 0};
> int l=(g.length-1)/2;
> for (int i=0; i<g.length; i++) {
> sum[0]+=g[i][0]*cos((i-l)*x)-sin((i-l)*x)*g[i][1];
> sum[1]+=g[i][0]*sin((i-l)*x)+cos((i-l)*x)*g[i][1];
> }
> return sum;
> }
> float[] newpos_ex(float x, int lu, int lo) {
> float sum[]={0, 0};
> int l=(g.length-1)/2;
> if (lu>lo) {
> int zw=lo;
> lo=lu;
> lu=zw;
> }
> for (int i=lu+l; i<=lo+l; i++) {
> sum[0]+=g[i][0]*cos((i-l)*x)-sin((i-l)*x)*g[i][1];
> sum[1]+=g[i][0]*sin((i-l)*x)+cos((i-l)*x)*g[i][1];
> }
> return sum;
> }
> void keyPressed() {
> if (!lock) {
> if (key=='w') {
> vv*=1.1;
> }
> if (key=='t') {
> vv=1;
> b=false;
> c=true;
> z=true;
> gradient=false;
> }
> if (key=='s') {
> vv/=1.1;
> }
> if (key=='g') {
> gradient=!gradient;
> }
> if (key==ENTER) {
> b=!b;
> }
> if (key=='c') {
> c=!c;
> }
> if (key=='z') {
> z=!z;
> }
> if (key=='q') {
> autoclick=!autoclick;
> }
> if (key=='r') {
> sk=1;
> p.clear();
> p2.clear();
> }
> if (key=='f') {
> if (!b) focus=!focus;
> if (focus) translate(width/2-g[(g.length-1)/2][0], height/2-g[(g.length-1)/2][1]);
> if (!focus) translate(-width/2+g[(g.length-1)/2][0], -height/2+g[(g.length-1)/2][1]);
> }
> }
> if (key=='l') {
> if (ll>0) lock=!lock;
> if (ll==0) ll=50;
> }
> if (key=='0') {
> halt=!halt;
> }
> }
> void readSettings() {
> String[] lines=loadStrings(sketchPath("settings.ini"));
> for (String line : lines) {
> String[] args=line.replaceAll(" ", "").split(":");
> switch(args[0]) {
> case "circles":
> circlenumb=int(args[1]);
> break;
> case "frames":
> fk=(int)(int(args[1])/(2*PI));
> break;
> case "delta":
> dif=float(args[1]);
> break;
> case "record":
> record=boolean(args[1]);
> println(record);
> break;
> }
> }
> }
> void loadFile(File f) {
> read=loadStrings(f);
> for (String line : read) println(line);
> allFilesRead=true;
> println(allFilesRead);
> }
> 
> ```
> 
> settings.ini
> 
> circles:1000  
> delta:0.0004  
> frames: 20000  
> record:true

> **Animations**
>
> Fourier: https://youtu.be/kWC3FJ-5Rhs  
> Einstein: https://youtu.be/aC\_QoYJXgQA
> 
> Darkest Dungeon caracters:  
> Jester: [https://youtu.be/Y5ia-B9hEVo](https://youtu.be/Y5ia-B9hEVo)  
> Grave Robber: [https://youtu.be/XOuQRB8voPI](https://youtu.be/XOuQRB8voPI)  
> Occultist: [https://youtu.be/vuXpEP7QLBE](https://youtu.be/vuXpEP7QLBE)
