This source file includes following definitions.
- velocity_
- velocity_
- SimulationTick
- DesiredVector
- TurnTowardsTarget
- AccumulateSeparation
- AccumulateAlignment
- AccumulateCohesion
#include "goose.h"
namespace {
const double kMaxSpeed = 2.0;
const double kMaxTurningForce = 0.05;
const double kNeighbourRadius = 64.0;
const double kPersonalSpace = 32.0;
const double kAttractorRadius = 320.0;
const double kMaxTurningDistance = 100.0;
const double kSeparationWeight = 2.0;
const double kAlignmentWeight = 1.0;
const double kCohesionWeight = 1.0;
}
Goose::Goose() : location_(0, 0), velocity_(0, 0) {
}
Goose::Goose(const Vector2& location, const Vector2& velocity)
: location_(location),
velocity_(velocity) {
}
void Goose::SimulationTick(const std::vector<Goose>& geese,
const std::vector<Vector2>& attractors,
const pp::Rect& flock_box) {
Vector2 acceleration = DesiredVector(geese, attractors);
velocity_.Add(acceleration);
velocity_.Clamp(kMaxSpeed);
location_.Add(velocity_);
if (!flock_box.IsEmpty()) {
while (location_.x() < flock_box.x())
location_.set_x(location_.x() + flock_box.width());
while (location_.x() >= flock_box.right())
location_.set_x(location_.x() - flock_box.width());
while (location_.y() < flock_box.y())
location_.set_y(location_.y() + flock_box.height());
while (location_.y() >= flock_box.bottom())
location_.set_y(location_.y() - flock_box.height());
}
}
Vector2 Goose::DesiredVector(const std::vector<Goose>& geese,
const std::vector<Vector2>& attractors) {
int32_t separation_count = 0;
Vector2 separation;
int32_t align_count = 0;
Vector2 alignment;
int32_t cohesion_count = 0;
Vector2 cohesion;
for (std::vector<Goose>::const_iterator goose_it = geese.begin();
goose_it < geese.end();
++goose_it) {
const Goose& goose = *goose_it;
Vector2 goose_delta = Vector2::Difference(
location_, goose.location());
double distance = goose_delta.Magnitude();
separation_count = AccumulateSeparation(
distance, goose_delta, &separation, separation_count);
align_count = AccumulateAlignment(
distance, goose, &alignment, align_count);
cohesion_count = AccumulateCohesion(
distance, goose, &cohesion, cohesion_count);
}
if (separation_count > 0) {
separation.Scale(1.0 / static_cast<double>(separation_count));
}
if (align_count > 0) {
alignment.Scale(1.0 / static_cast<double>(align_count));
alignment.Clamp(kMaxTurningForce);
}
for (size_t i = 0; i < attractors.size(); ++i) {
Vector2 attractor_direction = Vector2::Difference(
attractors[i], location_);
double distance = attractor_direction.Magnitude();
if (distance < kAttractorRadius) {
attractor_direction.Scale(1000);
cohesion.Add(attractor_direction);
cohesion_count++;
}
}
if (cohesion_count > 0) {
cohesion.Scale(1.0 / static_cast<double>(cohesion_count));
cohesion = TurnTowardsTarget(cohesion);
}
separation.Scale(kSeparationWeight);
alignment.Scale(kAlignmentWeight);
cohesion.Scale(kCohesionWeight);
Vector2 weighted_sum = cohesion;
weighted_sum.Add(alignment);
weighted_sum.Add(separation);
return weighted_sum;
}
Vector2 Goose::TurnTowardsTarget(const Vector2& target) {
Vector2 desired_direction = Vector2::Difference(target, location_);
double distance = desired_direction.Magnitude();
Vector2 new_direction;
if (distance > 0.0) {
desired_direction.Normalize();
if (distance < kMaxTurningDistance) {
desired_direction.Scale(kMaxSpeed * distance / 100.0);
} else {
desired_direction.Scale(kMaxSpeed);
}
new_direction = Vector2::Difference(desired_direction, velocity_);
new_direction.Clamp(kMaxTurningForce);
}
return new_direction;
}
int32_t Goose::AccumulateSeparation(double distance,
const Vector2& goose_delta,
Vector2* separation,
int32_t separation_count) {
if (distance > 0.0 && distance < kPersonalSpace) {
Vector2 weighted_direction = goose_delta;
weighted_direction.Normalize();
weighted_direction.Scale(1.0 / distance);
separation->Add(weighted_direction);
separation_count++;
}
return separation_count;
}
int32_t Goose::AccumulateAlignment(double distance,
const Goose& goose,
Vector2* alignment,
int32_t align_count) {
if (distance > 0.0 && distance < kNeighbourRadius) {
alignment->Add(goose.velocity());
align_count++;
}
return align_count;
}
int32_t Goose::AccumulateCohesion(double distance,
const Goose& goose,
Vector2* cohesion,
int32_t cohesion_count) {
if (distance > 0.0 && distance < kNeighbourRadius) {
cohesion->Add(goose.location());
cohesion_count++;
}
return cohesion_count;
}