OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "nacl_app/goose.h" | |
6 | |
7 namespace { | |
8 // The maximum speed of a goose. Measured in meters/second. | |
9 const double kMaxSpeed = 2.0; | |
10 | |
11 // The maximum force that can be applied to turn a goose when computing the | |
12 // aligment. Measured in meters/second/second. | |
13 const double kMaxTurningForce = 0.05; | |
14 | |
15 // The neighbour radius of a goose. Only geese within this radius will affect | |
16 // the flocking computations of this goose. Measured in pixels. | |
17 const double kNeighbourRadius = 64.0; | |
18 | |
19 // The minimum distance that a goose can be from this goose. If another goose | |
20 // comes within this distance of this goose, the flocking algorithm tries to | |
21 // move the geese apart. Measured in pixels. | |
22 const double kPersonalSpace = 32.0; | |
23 | |
24 // The distance at which attractors have effect on a goose's direction. | |
25 const double kAttractorRadius = 320.0; | |
26 | |
27 // The goose will try to turn towards geese within this distance (computed | |
28 // during the cohesion phase). Measured in pixels. | |
29 const double kMaxTurningDistance = 100.0; | |
30 | |
31 // The weights used when computing the weighted sum the three flocking | |
32 // components. | |
33 const double kSeparationWeight = 2.0; | |
34 const double kAlignmentWeight = 1.0; | |
35 const double kCohesionWeight = 1.0; | |
36 | |
37 } // namespace | |
38 | |
39 namespace flocking_geese { | |
40 | |
41 Goose::Goose() : location_(0, 0) { | |
42 velocity_ = Vector2::RandomUnit(); | |
43 } | |
44 | |
45 Goose::Goose(const Vector2& location) : location_(location) { | |
46 velocity_ = Vector2::RandomUnit(); | |
47 } | |
48 | |
49 void Goose::SimulationTick(const std::vector<Goose>& geese, | |
50 const std::vector<Vector2>& attractors, | |
51 const pp::Rect& flockBox) { | |
52 Vector2 acceleration = Flock(geese, attractors); | |
53 velocity_.Add(acceleration); | |
54 // Limit the velocity to a maximum speed. | |
55 velocity_.Clamp(kMaxSpeed); | |
56 location_.Add(velocity_); | |
57 // Wrap the goose location to the flock box. | |
58 if (!flockBox.IsEmpty()) { | |
59 if (location_.x() < flockBox.x()) { | |
60 location_.set_x(flockBox.right() - 1); | |
61 } | |
62 if (location_.x() >= flockBox.right()) { | |
63 location_.set_x(flockBox.x()); | |
64 } | |
65 if (location_.y() < flockBox.y()) { | |
66 location_.set_y(flockBox.bottom() - 1); | |
67 } | |
68 if (location_.y() >= flockBox.bottom()) { | |
69 location_.set_y(flockBox.y()); | |
70 } | |
71 } | |
72 } | |
73 | |
74 Vector2 Goose::Flock(const std::vector<Goose>& geese, | |
75 const std::vector<Vector2>& attractors) { | |
76 // Loop over all the neighbouring geese in the flock, accumulating | |
77 // the separation mean, the alignment mean and the cohesion mean. | |
78 int32_t separationCount = 0; | |
79 Vector2 separation; | |
80 int32_t alignCount = 0; | |
81 Vector2 alignment; | |
82 int32_t cohesionCount = 0; | |
83 Vector2 cohesion; | |
84 | |
85 for (std::vector<Goose>::const_iterator goose_it = geese.begin(); | |
86 goose_it < geese.end(); | |
87 ++goose_it) { | |
88 const Goose& goose = *goose_it; | |
89 // Compute the distance from this goose to its neighbour. | |
90 Vector2 gooseDirection = Vector2::Difference( | |
91 location_, goose.location()); | |
92 double distance = gooseDirection.Magnitude(); | |
93 separationCount = AccumulateSeparation( | |
94 distance, gooseDirection, &separation, separationCount); | |
95 alignCount = AccumulateAlignment( | |
96 distance, goose, &alignment, alignCount); | |
97 cohesionCount = AccumulateCohesion( | |
98 distance, goose, &cohesion, cohesionCount); | |
99 } | |
100 // Compute the means and create a weighted sum. This becomes the goose's new | |
101 // acceleration. | |
102 if (separationCount > 0) { | |
103 separation.Scale(1.0 / static_cast<double>(separationCount)); | |
104 } | |
105 if (alignCount > 0) { | |
106 alignment.Scale(1.0 / static_cast<double>(alignCount)); | |
107 // Limit the effect that alignment has on the final acceleration. The | |
108 // alignment component can overpower the others if there is a big | |
109 // difference between this goose's velocity and its neighbours'. | |
110 alignment.Clamp(kMaxTurningForce); | |
111 } | |
112 | |
113 // Compute the effect of the attractors and blend this in with the flock | |
114 // cohesion component. An attractor has to be within kAttractorRadius to | |
115 // effect the heading of a goose. | |
116 for (size_t i = 0; i < attractors.size(); ++i) { | |
117 Vector2 attractorDirection = Vector2::Difference( | |
118 attractors[i], location_); | |
119 double distance = attractorDirection.Magnitude(); | |
120 if (distance < kAttractorRadius) { | |
121 attractorDirection.Scale(1000); // Each attractor acts like 1000 geese. | |
122 cohesion.Add(attractorDirection); | |
123 cohesionCount++; | |
124 } | |
125 } | |
126 | |
127 // If there is a non-0 cohesion component, steer the goose so that it tries | |
128 // to follow the flock. | |
129 if (cohesionCount > 0) { | |
130 cohesion.Scale(1.0 / static_cast<double>(cohesionCount)); | |
131 cohesion = TurnTowardsTarget(cohesion); | |
132 } | |
133 // Compute the weighted sum. | |
134 separation.Scale(kSeparationWeight); | |
135 alignment.Scale(kAlignmentWeight); | |
136 cohesion.Scale(kCohesionWeight); | |
137 Vector2 weightedSum = cohesion; | |
138 weightedSum.Add(alignment); | |
139 weightedSum.Add(separation); | |
140 return weightedSum; | |
141 } | |
142 | |
143 Vector2 Goose::TurnTowardsTarget(const Vector2& target) { | |
144 Vector2 desiredDirection = Vector2::Difference(target, location_); | |
145 double distance = desiredDirection.Magnitude(); | |
146 Vector2 newDirection; | |
147 if (distance > 0.0) { | |
148 desiredDirection.Normalize(); | |
149 // If the target is within the turning affinity distance, then make the | |
150 // desired direction based on distance to the target. Otherwise, base | |
151 // the desired direction on MAX_SPEED. | |
152 if (distance < kMaxTurningDistance) { | |
153 // Some pretty arbitrary dampening. | |
154 desiredDirection.Scale(kMaxSpeed * distance / 100.0); | |
155 } else { | |
156 desiredDirection.Scale(kMaxSpeed); | |
157 } | |
158 newDirection = Vector2::Difference(desiredDirection, velocity_); | |
159 newDirection.Clamp(kMaxTurningForce); | |
160 } | |
161 return newDirection; | |
162 } | |
163 | |
164 int32_t Goose::AccumulateSeparation(double distance, | |
165 const Vector2& gooseDirection, | |
166 Vector2* separation, /* inout */ | |
167 int32_t separationCount) { | |
168 if (distance > 0.0 && distance < kPersonalSpace) { | |
169 Vector2 weightedDirection = gooseDirection; | |
170 weightedDirection.Normalize(); | |
171 weightedDirection.Scale(1.0 / distance); | |
172 separation->Add(weightedDirection); | |
173 separationCount++; | |
174 } | |
175 return separationCount; | |
176 } | |
177 | |
178 int32_t Goose::AccumulateAlignment(double distance, | |
179 const Goose& goose, | |
180 Vector2* alignment, /* inout */ | |
181 int32_t alignCount) { | |
182 if (distance > 0.0 && distance < kNeighbourRadius) { | |
183 alignment->Add(goose.velocity()); | |
184 alignCount++; | |
185 } | |
186 return alignCount; | |
187 } | |
188 | |
189 int32_t Goose::AccumulateCohesion(double distance, | |
190 const Goose& goose, | |
191 Vector2* cohesion, /* inout */ | |
192 int32_t cohesionCount) { | |
193 if (distance > 0.0 && distance < kNeighbourRadius) { | |
194 cohesion->Add(goose.location()); | |
195 cohesionCount++; | |
196 } | |
197 return cohesionCount; | |
198 } | |
199 | |
200 } // namespace flocking_geese | |
201 | |
OLD | NEW |