Empirical
Body2D.h
Go to the documentation of this file.
1 // This file is part of Empirical, https://github.com/devosoft/Empirical
2 // Copyright (C) Michigan State University, 2016-2018.
3 // Released under the MIT Software license; see doc/LICENSE
4 //
5 //
6 // This file defines classes to represent bodies that exist on a 2D surface.
7 // Each class should be able to:
8 // * Maintain a pointer to information about the full organism associated with this body.
9 // * provide a circular perimeter of the body (for phase1 of collision detection)
10 // * Provide body an anchor point and center point of the body (typically the same)
11 //
12 // Currently, the only type of body we have is:
13 //
14 // CircleBody2D - One individual circular object in the 2D world.
15 //
16 //
17 // Development notes:
18 // * If we are going to have a lot of links, we may want a better data structure than vector.
19 // (if we don't have a lot, vector may be the best choice...)
20 
21 #ifndef EMP_BODY_2D_H
22 #define EMP_BODY_2D_H
23 
24 #include "../base/assert.h"
25 #include "../base/Ptr.h"
26 #include "../base/vector.h"
27 
28 #include "../tools/alert.h"
29 #include "../tools/mem_track.h"
30 
31 #include "Angle2D.h"
32 #include "Circle2D.h"
33 
34 namespace emp {
35 
36  class Body2D_Base {
37  protected:
38  // Bodies can be linked in seveal ways.
39  // DEFAULT -> Joined together with no extra meaning
40  // REPRODUCTION -> "from" is gestating "to"
41  // ATTACK -> "from" is trying to eat "to"
42  // PARASITE -> "from" is stealing resources from "to"
44 
45  template <typename BODY_TYPE>
46  struct BodyLink {
47  LINK_TYPE type; // DEFAULT, REPRODUCTION, ATTACK, PARASITE
48  Ptr<BODY_TYPE> from; // Initiator of the connection (e.g., parent, attacker)
49  Ptr<BODY_TYPE> to; // Target of the connection (e.g., offspring, prey/host)
50  double cur_dist; // How far are bodies currently being kept apart?
51  double target_dist; // How far should the be moved to? (e.g., if growing)
52 
53  BodyLink() : type(LINK_TYPE::DEFAULT), from(nullptr), to(nullptr), cur_dist(0)
54  , target_dist(0) { ; }
55  BodyLink(LINK_TYPE t, Ptr<BODY_TYPE> _frm, Ptr<BODY_TYPE> _to, double cur=0, double target=0)
56  : type(t), from(_frm), to(_to), cur_dist(cur), target_dist(target) { ; }
57  BodyLink(const BodyLink &) = default;
58  ~BodyLink() { ; }
59  };
60 
61  double birth_time; // At what time point was this organism born?
62  Angle orientation; // Which way is body facing?
63  Point velocity; // Speed and direction of movement
64  double mass; // "Weight" of this object (@CAO not used yet..)
65  uint32_t color_id; // Which color should this body appear?
66  int repro_count; // Number of offspring currently being produced.
67 
68  Point shift; // How should this body be updated to minimize overlap.
69  Point cum_shift; // Build up of shift not yet acted upon.
70  Point total_abs_shift; // Total absolute-value of shifts (to calculate pressure)
71  double pressure; // Current pressure on this body.
72 
73  bool detach_on_divide; // Should offspring detach when born (or stay linked to parent)
74  public:
75  Body2D_Base() : birth_time(0.0), orientation(), velocity(), mass(1.0), color_id(0), repro_count(0)
76  , shift(), cum_shift(), total_abs_shift(), pressure(0), detach_on_divide(true) { ; }
77  virtual ~Body2D_Base() { ; }
78 
79  double GetBirthTime() const { return birth_time; }
80  const Angle & GetOrientation() const { return orientation; }
81  const Point & GetVelocity() const { return velocity; }
82  double GetMass() const { return mass; }
83  uint32_t GetColorID() const { return color_id; }
84  bool IsReproducing() const { return repro_count; }
85  int GetReproCount() const { return repro_count; }
86  Point GetShift() const { return shift; }
87  double GetPressure() const { return pressure; }
88  bool GetDetachOnDivide() const { return detach_on_divide; }
89 
90  void SetBirthTime(double in_time) { birth_time = in_time; }
91  void SetColorID(uint32_t in_id) { color_id = in_id; }
92 
93  // Orientation control...
94  void TurnLeft(int steps=1) { orientation.RotateDegrees(45); }
95  void TurnRight(int steps=1) { orientation.RotateDegrees(-45); }
96 
97  // Velocity control...
98  void IncSpeed(const Point & offset) { velocity += offset; }
99  void IncSpeed() { velocity += orientation.GetPoint(); }
100  void DecSpeed() { velocity -= orientation.GetPoint(); }
101  void SetVelocity(double x, double y) { velocity.Set(x, y); }
102  void SetVelocity(const Point & v) { velocity = v; }
103 
104  // Shift to apply next update.
105  void AddShift(const Point & s) { shift += s; total_abs_shift += s.Abs(); }
106 
107  // Controls about replication
108  void SetDetachOnDivide(bool in=true) { detach_on_divide = in; }
109  };
110 
111  class CircleBody2D : public Body2D_Base {
112  private:
113  Circle2D<double> perimeter; // Includes position and size.
114  double target_radius; // For growing/shrinking
115 
116  // Information about other bodies that this one is linked to.
117  emp::vector< Ptr< BodyLink<CircleBody2D> > > from_links; // Active links initiated by body
118  emp::vector< Ptr< BodyLink<CircleBody2D> > > to_links; // Active links targeting body
119 
120  public:
122  : perimeter(_p), target_radius(_p.GetRadius()), from_links(0), to_links(0)
123  {
124  EMP_TRACK_CONSTRUCT(CircleBody2D);
125  }
127  // Remove any remaining links from this body.
128  while (from_links.size()) RemoveLink(from_links[0]);
129  while (to_links.size()) RemoveLink(to_links[0]);
130 
131  EMP_TRACK_DESTRUCT(CircleBody2D);
132  }
133 
134  const Circle2D<double> & GetPerimeter() const { return perimeter; }
135  const Point & GetAnchor() const { return perimeter.GetCenter(); }
136  const Point & GetCenter() const { return perimeter.GetCenter(); }
137  double GetRadius() const { return perimeter.GetRadius(); }
138  double GetTargetRadius() const { return target_radius; }
139 
140  void SetPosition(const Point & p) { perimeter.SetCenter(p); }
141  void SetRadius(double r) { perimeter.SetRadius(r); }
142  void SetTargetRadius(double t) { target_radius = t; }
143 
144  // Translate immediately (ignoring physics)
145  void Translate(const Point & t) { perimeter.Translate(t); }
146 
147  // Creating, testing, and unlinking other organisms
148  bool IsLinkedFrom(const CircleBody2D & link_org) const {
149  for (auto cur_link : from_links) if (cur_link->to == &link_org) return true;
150  return false;
151  }
152  bool IsLinkedTo(const CircleBody2D & link_org) const { return link_org.IsLinkedFrom(*this); }
153  bool IsLinked(const CircleBody2D & link_org) const {
154  return IsLinkedFrom(link_org) || IsLinkedTo(link_org);
155  }
156 
157  size_t GetLinkCount() const { return from_links.size() + to_links.size(); }
158 
159  void AddLink(LINK_TYPE type, CircleBody2D & link_org, double cur_dist, double target_dist) {
160  emp_assert(!IsLinked(link_org)); // Don't link twice!
161 
162  // Build connections in both directions.
163  auto new_link = NewPtr< BodyLink<CircleBody2D> >(type, this, &link_org, cur_dist, target_dist);
164  from_links.push_back(new_link);
165  link_org.to_links.push_back(new_link);
166  }
167 
168 
170  // We should always initiate link removal from the FROM side.
171  if (link->to == ToPtr(this)) {
172  link->from->RemoveLink(link);
173  return;
174  }
175 
176  // Find and remove the associated FROM link from this body.
177  for (size_t i = 0; i < from_links.size(); i++) {
178  if (from_links[i]->to == link->to) {
179  from_links[i] = from_links.back();
180  from_links.pop_back();
181  break;
182  }
183  }
184 
185  // Find and remove the TO link from the attached body.
186  const size_t to_size = link->to->to_links.size();
187  for (size_t i = 0; i < to_size; i++) {
188  if (link->to->to_links[i]->from == ToPtr(this)) {
189  auto & other_links = link->to->to_links;
190  other_links[i] = other_links.back();
191  other_links.pop_back();
192  break;
193  }
194  }
195 
196  link.Delete();
197  }
198 
199  const BodyLink<CircleBody2D> & FindLink(const CircleBody2D & link_org) const {
200  emp_assert(IsLinked(link_org));
201  for (auto link : from_links) if ( link->to == &link_org) return *link;
202  return link_org.FindLink(*this);
203  }
204 
206  emp_assert(IsLinked(link_org));
207  for (auto link : from_links) if ( link->to == ToPtr(&link_org) ) return *link;
208  return link_org.FindLink(*this);
209  }
210 
211  double GetLinkDist(const CircleBody2D & link_org) const {
212  emp_assert(IsLinked(link_org));
213  return FindLink(link_org).cur_dist;
214  }
215  double GetTargetLinkDist(const CircleBody2D & link_org) const {
216  emp_assert(IsLinked(link_org));
217  return FindLink(link_org).target_dist;
218  }
219  void ShiftLinkDist(CircleBody2D & link_org, double change) {
220  auto & link = FindLink(link_org);
221  link.cur_dist += change;
222  }
223 
225  // Offspring cannot be right on top of parent.
226  emp_assert(offset.GetX() != 0 || offset.GetY() != 0);
227 
228  // Create the offspring as a paired link.
229  auto offspring = NewPtr<CircleBody2D>(perimeter);
230  AddLink(LINK_TYPE::REPRODUCTION, *offspring, offset.Magnitude(), perimeter.GetRadius()*2.0);
231  offspring->Translate(offset);
232  repro_count++;
233 
234  return offspring;
235  }
236 
237  // If a body is not at its target radius, grow it or shrink it, as needed.
238  void BodyUpdate(double change_factor=1) {
239  // Test if this body needs to grow or shrink.
240  if ((int) target_radius > (int) GetRadius()) SetRadius(GetRadius() + change_factor);
241  else if ((int) target_radius < (int) GetRadius()) SetRadius(GetRadius() - change_factor);
242 
243  // Test if the link distance for this body needs to be updated
244  for (size_t i = 0; i < from_links.size(); i++) {
245  auto link = from_links[i];
246  if (link->cur_dist == link->target_dist) continue; // No adjustment needed.
247 
248  // If we're within the change_factor, just set pair_dist to target.
249  if (std::abs(link->cur_dist - link->target_dist) <= change_factor) {
250  link->cur_dist = link->target_dist;
251  // IF this organism was gestating, finish the reproduction.
252  if (link->type == LINK_TYPE::REPRODUCTION) {
253  emp_assert(repro_count > 0);
254  repro_count--;
255  if (detach_on_divide) { // Flag link for removal!
256  RemoveLink(link); // Remove the link.
257  i--; // Check this position again.
258  }
259  }
260  }
261  else {
262  if (link->cur_dist < link->target_dist) link->cur_dist += change_factor;
263  else link->cur_dist -= change_factor;
264  }
265  }
266 
267 
268  }
269 
270 
271  // Move this body by its velocity and reduce velocity based on friction.
272  void ProcessStep(double friction=0) {
273  if (velocity.NonZero()) {
274  perimeter.Translate(velocity);
275  const double velocity_mag = velocity.Magnitude();
276 
277  // If body is close to stopping stop it!
278  if (friction > velocity_mag) { velocity.ToOrigin(); }
279 
280  // Otherwise slow it down proportionately in the x and y directions.
281  else { velocity *= 1.0 - ((double) friction) / ((double) velocity_mag); }
282  }
283  }
284 
285 
286  // Determine where the circle will end up and force it to be within a bounding box.
287  void FinalizePosition(const Point & max_coords) {
288  const double max_x = max_coords.GetX() - GetRadius();
289  const double max_y = max_coords.GetY() - GetRadius();
290 
291  // Update the caclulcation for pressure.
292 
293  // Act on the accumulated shifts only when they add up enough.
294  cum_shift += shift;
295  if (cum_shift.SquareMagnitude() > 0.25) {
296  perimeter.Translate(cum_shift);
298  }
299  pressure = (total_abs_shift - shift.Abs()).SquareMagnitude();
300  shift.ToOrigin(); // Clear out the shift for the next round.
302 
303  // If this body is linked to another, enforce the distance between them.
304  for (auto link : from_links) {
305  if (GetAnchor() == link->to->GetAnchor()) {
306  // If two organisms are on top of each other... shift one.
307  Translate(Point(0.01, 0.01));
308  }
309 
310  // Figure out how much each oragnism should move so that they will be properly spaced.
311  const double start_dist = GetAnchor().Distance(link->to->GetAnchor());
312  const double link_dist = link->cur_dist;
313  const double frac_change = (1.0 - ((double) link_dist) / ((double) start_dist)) / 2.0;
314 
315  Point dist_move = (GetAnchor() - link->to->GetAnchor()) * frac_change;
316 
317  perimeter.Translate(-dist_move);
318  link->to->perimeter.Translate(dist_move);
319  }
320 
321  // Adjust the organism so it stays within the bounding box of the world.
322  if (GetCenter().GetX() < GetRadius()) {
323  perimeter.SetCenterX(GetRadius()); // Put back in range...
324  velocity.NegateX(); // Bounce off left side.
325  } else if (GetCenter().GetX() > max_x) {
326  perimeter.SetCenterX(max_x); // Put back in range...
327  velocity.NegateX(); // Bounce off right side.
328  }
329 
330  if (GetCenter().GetY() < GetRadius()) {
331  perimeter.SetCenterY(GetRadius()); // Put back in range...
332  velocity.NegateY(); // Bounce off top.
333  } else if (GetCenter().GetY() > max_y) {
334  perimeter.SetCenterY(max_y); // Put back in range...
335  velocity.NegateY(); // Bounce off bottom.
336  }
337  }
338 
339  // Check to make sure there are no obvious issues with this object.
340  bool OK() {
341  for (auto link : from_links) {
342  (void) link;
343  emp_assert(link->cur_dist >= 0); // Distances cannot be negative.
344  emp_assert(link->target_dist >= 0); // Distances cannot be negative.
345  }
346 
347  return true;
348  }
349 
350  };
351 }
352 
353 #endif
constexpr bool NonZero() const
Definition: Point2D.h:48
Angle & RotateDegrees(double degrees)
Definition: Angle2D.h:131
Ptr< T > ToPtr(T *_in, bool own=false)
Convert a T* to a Ptr<T>. By default, don&#39;t track.
Definition: Ptr.h:816
const Circle2D< double > & GetPerimeter() const
Definition: Body2D.h:134
Body2D_Base()
Definition: Body2D.h:75
CircleBody2D(const Circle2D< double > &_p)
Definition: Body2D.h:121
void Translate(const Point &t)
Definition: Body2D.h:145
uint32_t color_id
Definition: Body2D.h:65
void BodyUpdate(double change_factor=1)
Definition: Body2D.h:238
bool GetDetachOnDivide() const
Definition: Body2D.h:88
void SetRadius(double r)
Definition: Body2D.h:141
Point total_abs_shift
Definition: Body2D.h:70
double GetTargetRadius() const
Definition: Body2D.h:138
Circle2D< TYPE > & Translate(Point2D< TYPE > shift)
Definition: Circle2D.h:40
constexpr TYPE GetX() const
Definition: Point2D.h:39
void SetVelocity(double x, double y)
Definition: Body2D.h:101
Definition: Body2D.h:36
Point2D & NegateX()
Definition: Point2D.h:74
void TurnRight(int steps=1)
Definition: Body2D.h:95
constexpr double SquareMagnitude() const
Definition: Point2D.h:45
bool OK()
Definition: Body2D.h:340
double birth_time
Definition: Body2D.h:61
void SetVelocity(const Point &v)
Definition: Body2D.h:102
bool IsLinkedFrom(const CircleBody2D &link_org) const
Definition: Body2D.h:148
Circle2D< TYPE > & SetCenterX(TYPE x)
Definition: Circle2D.h:34
void TurnLeft(int steps=1)
Definition: Body2D.h:94
const Angle & GetOrientation() const
Definition: Body2D.h:80
void SetTargetRadius(double t)
Definition: Body2D.h:142
double pressure
Definition: Body2D.h:71
void IncSpeed(const Point &offset)
Definition: Body2D.h:98
void AddShift(const Point &s)
Definition: Body2D.h:105
Circle2D< TYPE > & SetRadius(TYPE new_radius)
Definition: Circle2D.h:36
Point shift
Definition: Body2D.h:68
void push_back(PB_Ts &&...args)
Definition: vector.h:189
bool detach_on_divide
Definition: Body2D.h:73
Point GetShift() const
Definition: Body2D.h:86
void SetPosition(const Point &p)
Definition: Body2D.h:140
size_t size() const
Definition: vector.h:151
Point2D & Set(TYPE _x, TYPE _y)
Definition: Point2D.h:43
Point2D<> Point
Definition: Point2D.h:99
const Point & GetVelocity() const
Definition: Body2D.h:81
double GetPressure() const
Definition: Body2D.h:87
double GetTargetLinkDist(const CircleBody2D &link_org) const
Definition: Body2D.h:215
const Point & GetCenter() const
Definition: Body2D.h:136
bool IsLinkedTo(const CircleBody2D &link_org) const
Definition: Body2D.h:152
Ptr< CircleBody2D > BuildOffspring(Point offset)
Definition: Body2D.h:224
void ShiftLinkDist(CircleBody2D &link_org, double change)
Definition: Body2D.h:219
Circle2D< TYPE > & SetCenter(const Point2D< TYPE > &c)
Definition: Circle2D.h:32
void pop_back()
Definition: vector.h:194
double GetRadius() const
Definition: Body2D.h:137
bool IsLinked(const CircleBody2D &link_org) const
Definition: Body2D.h:153
Point2D & ToOrigin()
Definition: Point2D.h:73
void ProcessStep(double friction=0)
Definition: Body2D.h:272
constexpr Point2D Abs() const
Definition: Point2D.h:65
void IncSpeed()
Definition: Body2D.h:99
bool IsReproducing() const
Definition: Body2D.h:84
Angle orientation
Definition: Body2D.h:62
virtual ~Body2D_Base()
Definition: Body2D.h:77
~CircleBody2D()
Definition: Body2D.h:126
void SetBirthTime(double in_time)
Definition: Body2D.h:90
void SetDetachOnDivide(bool in=true)
Definition: Body2D.h:108
Circle2D< TYPE > & SetCenterY(TYPE y)
Definition: Circle2D.h:35
double GetMass() const
Definition: Body2D.h:82
double mass
Definition: Body2D.h:64
If we are in emscripten, make sure to include the header.
Definition: array.h:37
constexpr const Point2D< TYPE > & GetCenter() const
Definition: Circle2D.h:26
Definition: Angle2D.h:78
void RemoveLink(Ptr< BodyLink< CircleBody2D > > link)
Definition: Body2D.h:169
void AddLink(LINK_TYPE type, CircleBody2D &link_org, double cur_dist, double target_dist)
Definition: Body2D.h:159
void FinalizePosition(const Point &max_coords)
Definition: Body2D.h:287
Build a debug wrapper emp::vector around std::vector.
Definition: vector.h:42
int GetReproCount() const
Definition: Body2D.h:85
const BodyLink< CircleBody2D > & FindLink(const CircleBody2D &link_org) const
Definition: Body2D.h:199
Definition: Body2D.h:111
void DecSpeed()
Definition: Body2D.h:100
#define emp_assert(...)
Definition: assert.h:199
LINK_TYPE
Definition: Body2D.h:43
T & back()
Definition: vector.h:183
uint32_t GetColorID() const
Definition: Body2D.h:83
BodyLink< CircleBody2D > & FindLink(CircleBody2D &link_org)
Definition: Body2D.h:205
Point cum_shift
Definition: Body2D.h:69
const Point & GetAnchor() const
Definition: Body2D.h:135
size_t GetLinkCount() const
Definition: Body2D.h:157
Point velocity
Definition: Body2D.h:63
void SetColorID(uint32_t in_id)
Definition: Body2D.h:91
Point2D & NegateY()
Definition: Point2D.h:75
constexpr TYPE GetY() const
Definition: Point2D.h:40
constexpr TYPE GetRadius() const
Definition: Circle2D.h:29
int repro_count
Definition: Body2D.h:66
double GetBirthTime() const
Definition: Body2D.h:79
double GetLinkDist(const CircleBody2D &link_org) const
Definition: Body2D.h:211
constexpr double Magnitude() const
Definition: Point2D.h:46
Point GetPoint(double distance=1.0) const
Definition: Angle2D.h:167