Created
November 17, 2020 15:37
-
-
Save yves-chevallier/bf7f2fe08bce0b52f81c67c2cd1413f5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Boid (Bird-oid). An artificial life simulation of a single | |
* boid initially developed by Craig Reynolds in 1986 and based | |
* on three rules: Separation, Cohesion and Alignment. | |
*/ | |
#include <cmath> | |
#include <functional> | |
#include <random> | |
#include "boid.hpp" | |
#include "flock.hpp" | |
#include "vector.hpp" | |
using namespace std; | |
Boid::Boid(Flock *flock, double x, double y) : Body(x, y), flock{*flock} | |
{ | |
velocity = Vector::random(flock->maxVelocity * 2.0, -flock->maxVelocity); | |
} | |
Boid::Boid(Flock *flock) : flock{*flock} | |
{ | |
position = Vector::random(); | |
velocity = Vector::random(flock->maxVelocity * 2.0, -flock->maxVelocity); | |
} | |
void Boid::compute() | |
{ | |
cohesion(); | |
separation(); | |
alignment(); | |
if (flock.wrap) | |
wrap(); | |
else | |
bounce(speed() * 5.0, speed() / 5.0); | |
velocity.limit(flock.maxVelocity); | |
position += velocity; | |
} | |
/** | |
* The boid would bounce on the edge of the map with | |
* a turn factor, at a certain distance (margin) of | |
* the edge. | |
*/ | |
void Boid::bounce(double margin, double turnFactor) | |
{ | |
if (position.x < margin) velocity.x += turnFactor; | |
if (position.y < margin) velocity.y += turnFactor; | |
if (position.x > 1.0 - margin) velocity.x -= turnFactor; | |
if (position.y > 1.0 - margin) velocity.y -= turnFactor; | |
} | |
/** | |
* The boid suddently appear at the opposite of the map | |
* if it crosses the boundaries. | |
*/ | |
void Boid::wrap() | |
{ | |
if (position.x < 0) position.x += 1.0; | |
if (position.y < 0) position.y += 1.0; | |
if (position.x > 1.0) position.x -= 1.0; | |
if (position.y > 1.0) position.y -= 1.0; | |
} | |
/** | |
* A boid tends to fly toward the center of a group of individuals. | |
*/ | |
void Boid::cohesion() | |
{ | |
// Center of the group | |
Vector center{0, 0}; | |
int neighbors = inSight([&](Boid other) { center += other.position; }, | |
flock.cohesionRadius); | |
center /= neighbors; | |
// Stir to the center | |
if (neighbors > 0) velocity += (center - position) * flock.cohesion; | |
} | |
/** | |
* A boid tends to maintain a certain distance within each others. | |
*/ | |
void Boid::separation() | |
{ | |
Vector m{0, 0}; | |
inSight([&](Boid other) { m += position - other.position; }, | |
flock.separationRadius); | |
velocity += m * flock.separation; | |
} | |
/** | |
* A boid aligns itself with the swarm's average direction. | |
*/ | |
void Boid::alignment() | |
{ | |
Vector sum; | |
int neighbors = inSight( | |
[&](Boid other) { | |
sum += other.velocity; | |
neighbors++; | |
}, | |
flock.alignmentRadius); | |
if (neighbors > 0) | |
velocity += (sum / neighbors - velocity) * flock.alignment; | |
} | |
int Boid::inSight(function<const void(Boid &boid)> callback, double radius) | |
{ | |
double amin = angle() - flock.fieldOfView / 2; | |
double amax = angle() + flock.fieldOfView / 2; | |
int neighbors = 0; | |
flock.each([&](Boid &other) { | |
double a = angleTo(other); | |
if (distanceTo(other) < radius && a > amin && a < amax) { | |
callback(other); | |
neighbors++; | |
} | |
}); | |
return neighbors; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment