[work 74] Amebas
Movie
Source code
about
- 複数のAmebaが枠内を移動する
- Ameba同士が近づくと合体する
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; ofRectangle area; };
#include "ofApp.h" ofApp::ofApp(){ } ofApp::~ofApp(){ } //-------------------------------------------------------------- void ofApp::setup(){ double fps = 30; ofSetFrameRate(fps); ofBackground(255); ofSetBackgroundAuto(true); ofSetVerticalSync(true); for (int i = 0; i < 15; i++) { ameba.push_back(make_shared<Ameba>()); ameba.back()->setup(); } area = ofRectangle(50, 50, ofGetWidth() - 100, ofGetHeight() - 100); } //-------------------------------------------------------------- void ofApp::update(){ for (shared_ptr<Ameba> a : ameba) { a->update(); a->rangeCheck(area); } for (auto itr = ameba.begin(); itr != ameba.end(); ++itr) { bool erase = false; for (auto dst = itr + 1; dst != ameba.end(); ++dst) { if ((*itr)->approach((*dst))) { ameba.push_back(Ameba::unite((*itr), (*dst))); ameba.back()->update(); ameba.erase(dst); erase = true; break; } } if (erase) { ameba.erase(itr); } } } //-------------------------------------------------------------- void ofApp::draw(){ for (shared_ptr<Ameba> a : ameba) { a->display(); } ofNoFill(); ofSetColor(255, 0, 0); ofDrawRectangle(area); } //-------------------------------------------------------------- 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(); void setup(); void update(); void display(); bool approach(shared_ptr<Ameba> a); void rangeCheck(ofRectangle r); 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; float coreRadius; ofVec2f velocity; int pointNum; ofColor color; std::vector<int> counts; ofRectangle area; }; #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->coreRadius = newAmeba->radius * 3 / 4; newAmeba->velocity = (a1->radius * a1->velocity + a2->radius * a2->velocity) / (a1->radius + a2->radius); newAmeba->color = ofColor(ofRandom(255), ofRandom(255), ofRandom(255)); 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)); newAmeba->counts.push_back(0); } return newAmeba; } Ameba::Ameba() { } Ameba::~Ameba() { } void Ameba::setup() { radius = (int)ofRandom(20, 50); pointNum = (int)radius / 4; coreRadius = radius * 3 / 4; color = ofColor(ofRandom(255), ofRandom(255), ofRandom(255)); int offset = 100; center = ofVec2f(ofRandom(offset, ofGetWidth() - offset), ofRandom(offset, ofGetHeight() - offset)); velocity = ofVec2f(2, 0).rotate(ofRandom(360)); 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)); counts.push_back(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(counts.at(i), 0, maxFrame - 1, 0, 1, ofxeasing::elastic::easeOut); process.at(i) = orgs.at(i) + (targets.at(i) - orgs.at(i)) * step; } else { orgs.at(i) = process.at(i); } points.at(i) = center + process.at(i); counts.at(i) = (counts.at(i) + 1) % maxFrame; } center += velocity; } void Ameba::display() { ofSetPolyMode(OF_POLY_WINDING_NONZERO); ofFill(); ofSetColor(color); 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(); } 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); } void Ameba::rangeCheck(ofRectangle r) { int offset = 5; for (int i = 0; i < process.size(); i++) { if (center.x + process.at(i).x < r.getLeft()) { process.at(i).x += r.getLeft() - (center.x + process.at(i).x) + offset; orgs.at(i).x = process.at(i).x; counts.at(i) = 0; } if (center.x + process.at(i).x > r.getRight()) { process.at(i).x -= (((center.x + process.at(i).x) - r.getRight()) + offset); orgs.at(i).x = process.at(i).x; counts.at(i) = 0; } if (center.y + process.at(i).y < r.getTop()) { process.at(i).y += r.getTop() - (center.y + process.at(i).y) + offset; orgs.at(i).y = process.at(i).y; counts.at(i) = 0; } if (center.y + process.at(i).y > r.getBottom()) { process.at(i).y -= (((center.y + process.at(i).y) - r.getBottom()) + offset); orgs.at(i).y = process.at(i).y; counts.at(i) = 0; } } if ((center.x - coreRadius < r.getLeft()) || (center.x + coreRadius > r.getRight())) { velocity.x *= -1; } if ((center.y - coreRadius < r.getTop()) || (center.y + coreRadius > r.getBottom())) { velocity.y *= -1; } }
Link to the reference page
ソースコードで使用したAPIの中から要点になりそうなものをいくつか選んでリストアップしました。
category | API/Lib |
openframeworks | ofCurveVertex |
openframeworks | ofCurveVertices |
openframeworks | ofxeasing map |
c++ | std::vector |
Development environment
- openframeworks 0.10.1
- c++
- macOS
- Xcode