[work 73] Ameba
![[work 73] Ameba](https://i0.wp.com/tsukuru.hayato-works.com/wp-content/uploads/2019/07/outFrameImg0100.png?fit=1280%2C720&ssl=1)
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の中から要点になりそうなものをいくつか選んでリストアップしました。
| category | API/Lib |
| openframeworks | ofCurveVertex |
| openframeworks | ofCurveVertices |
| openframeworks | ofxeasing map |
Development environment
- openframeworks 0.10.1
- c++
- macOS
- Xcode