root/native_client_sdk/src/examples/demo/flock/goose.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. velocity_
  2. velocity_
  3. SimulationTick
  4. DesiredVector
  5. TurnTowardsTarget
  6. AccumulateSeparation
  7. AccumulateAlignment
  8. AccumulateCohesion

  // Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "goose.h"

namespace {
// The maximum speed of a goose.  Measured in meters/second.
const double kMaxSpeed = 2.0;

// The maximum force that can be applied to turn a goose when computing the
// aligment.  Measured in meters/second/second.
const double kMaxTurningForce = 0.05;

// The neighbour radius of a goose.  Only geese within this radius will affect
// the flocking computations of this goose.  Measured in pixels.
const double kNeighbourRadius = 64.0;

// The minimum distance that a goose can be from this goose.  If another goose
// comes within this distance of this goose, the flocking algorithm tries to
// move the geese apart.  Measured in pixels.
const double kPersonalSpace = 32.0;

// The distance at which attractors have effect on a goose's direction.
const double kAttractorRadius = 320.0;

// The goose will try to turn towards geese within this distance (computed
// during the cohesion phase).  Measured in pixels.
const double kMaxTurningDistance = 100.0;

// The weights used when computing the weighted sum the three flocking
// components.
const double kSeparationWeight = 2.0;
const double kAlignmentWeight = 1.0;
const double kCohesionWeight = 1.0;

}  // namespace


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);

  // Limit the velocity to a maximum speed.
  velocity_.Clamp(kMaxSpeed);

  location_.Add(velocity_);

  // Wrap the goose location to the flock box.
  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) {
  // Loop over all the neighbouring geese in the flock, accumulating
  // the separation mean, the alignment mean and the cohesion mean.
  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;

  // Compute the distance from this goose to its neighbour.
    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);
  }

  // Compute the means and create a weighted sum.  This becomes the goose's new
  // acceleration.
  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));
    // Limit the effect that alignment has on the final acceleration.  The
    // alignment component can overpower the others if there is a big
    // difference between this goose's velocity and its neighbours'.
    alignment.Clamp(kMaxTurningForce);
  }

  // Compute the effect of the attractors and blend this in with the flock
  // cohesion component.  An attractor has to be within kAttractorRadius to
  // effect the heading of a goose.
  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);  // Each attractor acts like 1000 geese.
      cohesion.Add(attractor_direction);
      cohesion_count++;
    }
  }

  // If there is a non-0 cohesion component, steer the goose so that it tries
  // to follow the flock.
  if (cohesion_count > 0) {
    cohesion.Scale(1.0 / static_cast<double>(cohesion_count));
    cohesion = TurnTowardsTarget(cohesion);
  }
  // Compute the weighted sum.
  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 the target is within the turning affinity distance, then make the
    // desired direction based on distance to the target.  Otherwise, base
    // the desired direction on MAX_SPEED.
    if (distance < kMaxTurningDistance) {
      // Some pretty arbitrary dampening.
      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, /* inout */
                                    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, /* inout */
                                   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, /* inout */
                                  int32_t cohesion_count) {
  if (distance > 0.0 && distance < kNeighbourRadius) {
    cohesion->Add(goose.location());
    cohesion_count++;
  }
  return cohesion_count;
}

/* [<][>][^][v][top][bottom][index][help] */