[work 74] Amebas
![[work 74] Amebas](https://i0.wp.com/tsukuru.hayato-works.com/wp-content/uploads/2019/07/outFrameImg0110.png?fit=1024%2C576&ssl=1)
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