[work 73] Ameba

[work 73] Ameba

Movie

Source code

about

  • 2つの輪がくっつく
  • ofCurveVertexとofCurveVerticesを使って円を描く
  • Easingを利用した点の移動

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 "Ameba.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<std::shared_ptr<Ameba>> ameba;
};
#include "ofApp.h"


ofApp::ofApp(){
    
}

ofApp::~ofApp(){
    
}

//--------------------------------------------------------------
void ofApp::setup(){
    double fps = 30;
    
    
    ofSetFrameRate(fps);
    ofBackground(255);
    ofSetBackgroundAuto(true);
    ofSetVerticalSync(true);
    
    ameba.push_back(make_shared<Ameba>(ofVec2f(ofGetWidth() / 2 - 200, ofGetHeight() / 2), ofVec2f(1, 0)));
    ameba.back()->setup();
    ameba.push_back(make_shared<Ameba>(ofVec2f(ofGetWidth() / 2 + 200, ofGetHeight() / 2), ofVec2f(-1, 0)));
    ameba.back()->setup();
}

//--------------------------------------------------------------
void ofApp::update(){
    shared_ptr<Ameba> tmpA;
    for (shared_ptr<Ameba> a : ameba) {
        a->update();
    }
    if (ameba.size() > 1) {
        if (ameba.at(0)->approach(ameba.at(1))) {
            tmpA = Ameba::unite(ameba.at(0), ameba.at(1));
            ameba.clear();
            ameba.push_back(tmpA);
            ameba.back()->update();
        }
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    for (shared_ptr<Ameba> a : ameba) {
        a->display();
    }
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if (key == 's') {
        ofImage img;
        img.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
        img.save("screenshot.png");
    }
}
#ifndef Ameba_hpp
#define Ameba_hpp

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

#include "ofxEasing.h"

class Ameba {
public:
    Ameba();
    Ameba(ofVec2f c, ofVec2f v);
    ~Ameba();
    void setup();
    void update();
    void display();
    bool approach(shared_ptr<Ameba> a);
    
    static shared_ptr<Ameba> unite(shared_ptr<Ameba> a1, shared_ptr<Ameba> a2);
private:
    std::vector<ofVec2f> points;
    std::vector<ofVec2f> process;
    std::vector<ofVec2f> targets;
    std::vector<ofVec2f> orgs;
    ofVec2f center;
    float radius;
    ofVec2f velocity;
    int pointNum;
    int count;
};
#endif /* Ameba_hpp */
#include "Ameba.hpp"



shared_ptr<Ameba> Ameba::unite(shared_ptr<Ameba> a1, shared_ptr<Ameba> a2)
{
    auto newAmeba = make_shared<Ameba>();
    float minD = FLT_MAX;   // minimum distance
    int minIdx1 = 0;        // a1 index of minimum distance
    int minIdx2 = 0;        // a2 index of minimum distance
    int prevIdx1, prevIdx2; // previous index of minIdx
    int nextIdx1, nextIdx2; // next index of minIdx
    int startIdx1, endIdx1; // start(minIdx)->end(prevIdx) or start(nextIdx)->end(minIdx)
    int startIdx2, endIdx2;
    
    // set parameter
    newAmeba->center = (a1->center + a2->center) / 2;
    newAmeba->radius = std::sqrt(a1->radius * a1->radius + a2->radius * a2->radius);   // (pi)r^2 = (pi)(r1^2 + r2^2)
    newAmeba->velocity = (a1->radius * a1->velocity + a2->radius * a2->velocity) / (a1->radius + a2->radius);
    newAmeba->count = 0;
    
    for (int i = 0; i < a1->points.size(); i++) {
        for (int j = 0; j < a2->points.size(); j++) {
            float dist = (a1->points.at(i) - a2->points.at(j)).length();
            if (dist < minD) {
                minD = dist;
                minIdx1 = i;
                minIdx2 = j;
            }
        }
    }
    
    nextIdx1 = (minIdx1 + 1) % a1->points.size();
    if (minIdx2 != 0) {
        prevIdx2 = minIdx2 - 1;
    } else {
        prevIdx2 = a2->points.size() - 1;
    }
    float dist1 = (a1->points.at(nextIdx1) - a2->points.at(prevIdx2)).length();
    
    
    if (minIdx1 != 0) {
        prevIdx1 = minIdx1 - 1;
    } else {
        prevIdx1 = a1->points.size() - 1;
    }
    nextIdx2 = (minIdx2 + 1) % a2->points.size();
    float dist2 = (a1->points.at(prevIdx1) - a2->points.at(nextIdx2)).length();
    
    if (dist1 < dist2) {
        startIdx1 = nextIdx1;
        endIdx1 = minIdx1;
        startIdx2 = minIdx2;
        endIdx2 = prevIdx2;
    } else {
        startIdx1 = minIdx1;
        endIdx1 = prevIdx1;
        startIdx2 = nextIdx2;
        endIdx2 = minIdx2;
    }
    
    // unite a1 and a2
    for (int idx = 0; idx <= endIdx1; idx++) {
        ofVec2f p = a1->process.at(idx) + (a1->center - newAmeba->center);
        newAmeba->orgs.push_back(p);
        newAmeba->process.push_back(p);
    }
    
    if (startIdx2 != 0) {
        for (int idx = startIdx2; idx < a2->points.size(); idx++) {
            ofVec2f p = a2->process.at(idx) + (a2->center - newAmeba->center);
            newAmeba->orgs.push_back(p);
            newAmeba->process.push_back(p);
        }
    }
    
    for (int idx = 0; idx <= endIdx2; idx++) {
        ofVec2f p = a2->process.at(idx) + (a2->center - newAmeba->center);
        newAmeba->orgs.push_back(p);
        newAmeba->process.push_back(p);
    }
    
    if (startIdx1 != 0) {
        for (int idx = startIdx1; idx < a1->points.size(); idx++) {
            ofVec2f p = a1->process.at(idx) + (a1->center - newAmeba->center);
            newAmeba->orgs.push_back(p);
            newAmeba->process.push_back(p);
        }
    }
    
    newAmeba->pointNum = newAmeba->orgs.size();
    
    for (int i = 0; i < newAmeba->pointNum; i++) {
        float unitAngle = 360.0 / (float)newAmeba->pointNum;
        ofVec2f d = a1->points.at(0) - a1->center;
        d.normalize();
        // set final shape
        newAmeba->targets.push_back(d.rotate(unitAngle * i) * newAmeba->radius);
        
        newAmeba->points.push_back(ofVec2f(0, 0));
    }
    
    return newAmeba;
}


Ameba::Ameba()
{
    
}


Ameba::Ameba(ofVec2f c, ofVec2f v)
{
    center = c;
    velocity = v;
}


Ameba::~Ameba()
{
    
}


void Ameba::setup()
{
    pointNum = 10;
    radius = 100;
    count = 0;
    
    for (int i = 0; i < pointNum; i++) {
        float unitAngle = 360.0 / (float)pointNum;
        ofVec2f d = ofVec2f(1, 0).rotate(unitAngle / 2);
        targets.push_back(d.rotate(unitAngle * i) * radius);
        process.push_back(ofVec2f(0, 0));
        
        points.push_back(ofVec2f(0, 0));
        orgs.push_back(ofVec2f(0, 0));
    }
}


void Ameba::update()
{
    int maxFrame = (int)ofGetTargetFrameRate() * 6;
    
    for (int i = 0; i < points.size(); i++) {
        // Convert float to int to ignore errors
        int xP = (int)process.at(i).x;
        int yP = (int)process.at(i).y;
        int xT = (int)targets.at(i).x;
        int yT = (int)targets.at(i).y;
        
        if ((xP != xT) || (yP != yT)) {
            float step = ofxeasing::map(count, 0, maxFrame - 1, 0, 1, ofxeasing::elastic::easeOut);
            process.at(i) = orgs.at(i) + (targets.at(i) - orgs.at(i)) * step;
        }
        
        points.at(i) = center + process.at(i);
    }
    center += velocity;
    
    count = (count + 1) % maxFrame;
}


void Ameba::display()
{
    ofSetPolyMode(OF_POLY_WINDING_NONZERO);
    ofNoFill();
    ofSetColor(0);
    
    ofBeginShape();
    ofCurveVertex(points.back());   // control point for p[0]-p[1]
    ofCurveVertices(points);
    ofCurveVertex(points.at(0));    // both vertex and control point(control point for p[last index - 1]-p[last index])
    ofCurveVertex(points.at(1));    // control point for p[last index]-p[0]
    ofEndShape();
    
    for (ofVec2f p : points) {
        ofSetColor(0);
        ofDrawCircle(p, 4);
    }
    
    ofSetColor(255, 0, 0);
    ofDrawCircle(center, 4);
}


bool Ameba::approach(shared_ptr<Ameba> a)
{
    std::vector<float> dists;
    for (int i = 0; i < this->points.size(); i++) {
        for (int j = 0; j < a->points.size(); j++) {
            float dist = (this->points.at(i) - a->points.at(j)).length();
            dists.push_back(dist);
        }
    }
    std::sort(dists.begin(), dists.end());
    
    return (dists.at(0) < 20);
}

Link to the reference page

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

categoryAPI/Lib
openframeworksofCurveVertex
openframeworksofCurveVertices
openframeworksofxeasing map

Development environment

  • openframeworks 0.10.1
  • c++
  • macOS
  • Xcode