[work 34] Fourier Transform

[work 34] Fourier Transform

Movie

Source code

about

  • フーリエ変換して線を波形の合成で表す
  • 参考(https://thecodingtrain.com/CodingChallenges/125-fourier-series.html)
  1. マウスで絵を描く
  2. 線の座標を取得する
  3. フーリエ変換を行う
  4. 波形の合成で1で描いた絵を再描画する

file

  • 上部にあるファイル名が表示されているボタンを押すと、表示されるファイルが切り替わります
  • 別ウィンドウ表示したい時や行番号などが無いRawMode表示したい時は、コード内右上のボタンを押してください(ボタンはマウスオーバーすると表示されます)
#include "ofMain.h"
#include "ofApp.h"

//================================
int main( ){

    // 4K:4096x2160
    // 2K:2048x1080
    // FullHD:1920x1080
    // HD:1440x1080
    // HD720p:1280x720
    // DVD:720x480
    // setup the GL context
	ofSetupOpenGL(1280,720, OF_WINDOW);

	// this kicks off the running of my app
	// can be OF_WINDOW or OF_FULLSCREEN
	// pass in width and height too:
	ofRunApp( new ofApp());

}
#pragma once

#include "ofMain.h"

#include "Fourier.hpp"


class ofApp : public ofBaseApp{
public:
    ofApp();
    ~ofApp();
    
    void setup();
    void update();
    void draw();
    
    void keyPressed(int key);
    void keyReleased(int key);
    void mouseMoved(int x, int y);
    void mouseDragged(int x, int y, int button);
    void mousePressed(int x, int y, int button);
    void mouseReleased(int x, int y, int button);
    void mouseEntered(int x, int y);
    void mouseExited(int x, int y);
    void windowResized(int w, int h);
    void dragEvent(ofDragInfo dragInfo);
    void gotMessage(ofMessage msg);
    
private:
    std::vector<float> pointsX;
    std::vector<float> pointsY;
    unique_ptr<FourierTransform> ftX;
    unique_ptr<FourierTransform> ftY;
    std::vector<ofVec2f> traces;
    std::vector<ofVec2f> org;       // mouse dragged points
    bool traceStart;
};
#include "ofApp.h"


ofApp::ofApp(){
    
}

ofApp::~ofApp(){
    
}

//--------------------------------------------------------------
void ofApp::setup(){
    double fps = 30;
    
    
    ofSetFrameRate(fps);
    ofBackground(0);
    ofSetBackgroundAuto(true);
    ofSetVerticalSync(true);
    
    
    traceStart = false;
}

//--------------------------------------------------------------
void ofApp::update(){
    if (traceStart) {
        ftX->update();
        ftY->update();
        if (ftX->isLoop()) {
            traces.clear();
        } else {
            traces.push_back(ofVec2f(ftX->getLocation().x, ftY->getLocation().y));
        }
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    if (traceStart) {
        ftX->display();
        ftY->display();
        
        ofPushMatrix();
        ofNoFill();
        ofSetLineWidth(5);
        ofSetColor(255);
        ofBeginShape();
        for (auto itr = traces.begin(); itr != traces.end(); ++itr) {
            ofVertex(*itr);
        }
        ofEndShape();
        ofPopMatrix();
        
        ofNoFill();
        ofSetLineWidth(1);
        ofSetColor(255);
        ofDrawLine(ftX->getLocation().x, ftX->getLocation().y, traces.back().x, traces.back().y);
        ofNoFill();
        ofSetLineWidth(1);
        ofSetColor(255);
        ofDrawLine(ftY->getLocation().x, ftY->getLocation().y, traces.back().x, traces.back().y);
    } else {
        ofPushMatrix();
        ofNoFill();
        ofSetLineWidth(5);
        ofSetColor(255);
        ofBeginShape();
        for (auto itr = org.begin(); itr != org.end(); ++itr) {
            ofVertex(*itr);
        }
        ofEndShape();
        ofPopMatrix();
    }
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if (key == 's') {
        ofImage img;
        img.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
        img.save("screenshot.png");
    } else if (key == 'c') {
        org.clear();
        pointsX.clear();
        pointsY.clear();
        traces.clear();
        traceStart = false;
    }
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
    traceStart = false;
    org.push_back(ofVec2f(x, y));
    pointsX.push_back(x - ofGetWidth() / 2);
    pointsY.push_back(y - ofGetHeight() / 2);
}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
    ftX = make_unique<FourierTransform>(pointsX);
    ftX->setup();
    ftX->setPosition(ofVec2f(ofGetWidth() / 2, 100));
    ftX->setRotation(0);
    
    ftY = make_unique<FourierTransform>(pointsY);
    ftY->setup();
    ftY->setPosition(ofVec2f(100, ofGetHeight() / 2));
    ftY->setRotation(PI / 2);
    
    traceStart = true;
}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}
#ifndef Fourier_hpp
#define Fourier_hpp

#include <stdio.h>
#include "ofMain.h"

struct ComplexPlane {
    double re;  // real
    double im;  // imaginary
    int freq;    // frequency
    double amp; // amplitude
    double phase;   // phase(radian)
    
    bool operator< (const ComplexPlane &cp) const
    {
        return amp < cp.amp;
    };
    
    bool operator> (const ComplexPlane &cp) const
    {
        return amp > cp.amp;
    };
};

class FourierTransform {
public:
    FourierTransform(std::vector<float> p);
    ~FourierTransform();
    void setup();
    void update();
    void display();
    ofVec2f getLocation();
    void setPosition(ofVec2f p);
    void setRotation(float r);
    bool isLoop();
private:
    void dft();     // discrete Fourier transform
    void epiCycles();
    
    std::vector<float> points;      // Input
    std::vector<ComplexPlane> cps;  // complex plane settings
    std::vector<ofVec2f> locations; // Output
    ofVec2f position;   // center of epicycle
    float rotation;     // direction of epicycle
    double time;        // differentiation
    bool loop;
};
#endif /* Fourier_hpp */
#include "Fourier.hpp"



FourierTransform::FourierTransform(std::vector<float> p)
{
    points = p;
    time = 0;
    position = ofVec2f(0, 0);
    rotation = 0;
    loop = false;
}


FourierTransform::~FourierTransform()
{
    
}


void FourierTransform::setup()
{
    dft();
}


void FourierTransform::update()
{
    epiCycles();
}


void FourierTransform::display()
{
    ofPushMatrix();
    ofFill();
    ofSetLineWidth(1);
    ofSetColor(255);
    ofDrawCircle(locations[0], 2);
    for (int i = 0; i < locations.size() - 1; i++) {
        ofNoFill();
        ofSetLineWidth(1);
        ofDrawLine(locations[i], locations[i+1]);
        ofDrawCircle(locations[i+1], 2);
        
        ofNoFill();
        ofSetLineWidth(1);
        ofSetColor(ofColor(255), 100);
        ofDrawCircle(locations[i].x, locations[i].y, (locations[i+1]-locations[i]).length());
    }
    ofPopMatrix();
}


ofVec2f FourierTransform::getLocation()
{
    return locations.back();
}


void FourierTransform::setPosition(ofVec2f p)
{
    position = p;
}


void FourierTransform::setRotation(float r)
{
    rotation = r;
}


bool FourierTransform::isLoop()
{
    return loop;
}


void FourierTransform::dft()
{
    ComplexPlane cp;
    int n_max = points.size();
    
    for (int n = 0; n < n_max; n++) {
        double re = 0;
        double im = 0;
        for (int k = 0; k < n_max; k++) {
            float theta = float(TWO_PI * k * n) / (float)n_max;
            re += points[k] * std::cos(theta);
            im -= points[k] * std::sin(theta);
        }
        re = re / n_max;
        im = im / n_max;
        
        cp.re = re;
        cp.im = im;
        cp.freq = n;
        cp.amp = std::sqrt(cp.re * cp.re + cp.im * cp.im);
        cp.phase = std::atan2(im, re);
        cps.push_back(cp);
    }
    
    std::sort(cps.begin(), cps.end(), std::greater<ComplexPlane>());
}


void FourierTransform::epiCycles()
{
    ofVec2f p = position;
    locations.clear();
    locations.push_back(p);
    
    for (int i = 0; i < cps.size(); i++) {
        int freq = cps[i].freq;
        double radius = cps[i].amp;
        double phase = cps[i].phase;
        p.x += radius * std::cos(freq * time + phase + rotation);
        p.y += radius * std::sin(freq * time + phase + rotation);
        locations.push_back(p);
    }
    
    double dt = TWO_PI / cps.size();
    time += dt;
    if (time > (double)TWO_PI) {
        time = 0;
        loop = true;
    } else {
        loop = false;
    }
}

Link to the reference page

ソースコードで使用したAPIの中から要点になりそうなものをいくつか選んでリストアップしました。

categoryAPI/Lib
c++std::vector
c++std::sort

Development environment

  • openframeworks 0.10.1
  • c++
  • macOS
  • Xcode