Skip to content

Instantly share code, notes, and snippets.

@yves-chevallier
Created November 17, 2020 15:37
Show Gist options
  • Save yves-chevallier/bf7f2fe08bce0b52f81c67c2cd1413f5 to your computer and use it in GitHub Desktop.
Save yves-chevallier/bf7f2fe08bce0b52f81c67c2cd1413f5 to your computer and use it in GitHub Desktop.
/**
* 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