Empirical
Physics2D.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 // Physics2D - handles movement and collissions in a simple 2D world.
7 
8 
9 #ifndef EMP_PHYSICS_2D_H
10 #define EMP_PHYSICS_2D_H
11 
12 #include <unordered_set>
13 #include <functional>
14 
15 #include "base/Ptr.h"
16 #include "base/vector.h"
17 
18 using namespace std::placeholders;
19 
20 #include "Surface2D.h"
21 
22 namespace emp {
23 
24  template <typename BODY_TYPE> class Physics2D {
25  private:
27 
28  Surface_t surface; // Bodies that can collide.
29  Surface_t background; // Bodies that can't collide.
30 
31  bool detach_on_birth; // Should bodies detach from their parents when born?
32 
33  public:
34  Physics2D(double width, double height, double max_org_diameter=20, bool detach=true)
35  : surface(width, height)
36  , background(width, height)
37  , detach_on_birth(detach)
38  { ; }
39  ~Physics2D() { ; }
40 
41  const Surface_t & GetSurface() const { return surface; }
42  const Surface_t & GetBackground() const { return background; }
43  bool GetDetach() const { return detach_on_birth; }
44 
45  Physics2D & SetDetach(bool _in) {
46  detach_on_birth = _in;
47  // Set all current bodies to new detach setting.
48  auto & body_set = surface.GetBodySet();
49  for (auto cur_body : body_set) { cur_body->SetDetachOnDivide(_in); }
50  return *this;
51  }
52 
53  Physics2D & AddBody(Ptr<BODY_TYPE> in_body) { surface.AddBody(in_body); return *this; }
54  Physics2D & AddBackground(Ptr<BODY_TYPE> in_body) { background.AddBody(in_body); return *this; }
55 
56  Physics2D & Clear() { surface.Clear(); background.Clear(); return *this; }
57 
59  auto & body_set = surface.GetBodySet();
60  if (body_set.size() == 0) return *this;
61 
62  size_t oldest_id = 0;
63 
64  for (size_t i = 1; i < body_set.size(); i++) {
65  if (body_set[i]->GetBirthTime() < body_set[oldest_id]->GetBirthTime()) {
66  oldest_id = i;
67  }
68  }
69 
70  // Now kill it!
71  body_set[oldest_id].Delete();
72  body_set[oldest_id] = body_set.back();
73  body_set.resize(body_set.size() - 1);
74 
75  return *this;
76  }
77 
78  bool TestPairCollision(BODY_TYPE & body1, BODY_TYPE & body2) {
79  if (body1.IsLinked(body2)) return false; // Linked bodies can overlap.
80 
81  const Point dist = body1.GetCenter() - body2.GetCenter();
82  const double sq_pair_dist = dist.SquareMagnitude();
83  const double radius_sum = body1.GetRadius() + body2.GetRadius();
84  const double sq_min_dist = radius_sum * radius_sum;
85 
86  // If there was no collision, return false.
87  if (sq_pair_dist >= sq_min_dist) { return false; }
88 
89  if (sq_pair_dist == 0.0) {
90  // If the shapes are on top of each other, we have a problem. Shift one!
91  body2.Translate(Point(0.01, 0.01));
92  }
93 
94  // @CAO If objects can phase or explode, identify that here.
95 
96  // Re-adjust position to remove overlap.
97  const double true_dist = sqrt(sq_pair_dist);
98  const double overlap_dist = ((double) radius_sum) - true_dist;
99  const double overlap_frac = overlap_dist / true_dist;
100  const Point cur_shift = dist * (overlap_frac / 2.0);
101  body1.AddShift(cur_shift);
102  body2.AddShift(-cur_shift);
103 
104  // @CAO if we have inelastic collisions, we just take the weighted average of velocites
105  // and let the move together.
106 
107  // Assume elastic: Re-adjust velocity to reflect bounce.
108  double x1, y1, x2, y2;
109 
110  if (dist.GetX() == 0) {
111  x1 = body1.GetVelocity().GetX(); y1 = body2.GetVelocity().GetY();
112  x2 = body2.GetVelocity().GetX(); y2 = body1.GetVelocity().GetY();
113 
114  body1.SetVelocity(Point(x1, y1));
115  body2.SetVelocity(Point(x2, y2));
116  }
117  else if (dist.GetY() == 0) {
118  x1 = body2.GetVelocity().GetX(); y1 = body1.GetVelocity().GetY();
119  x2 = body1.GetVelocity().GetX(); y2 = body2.GetVelocity().GetY();
120 
121  body1.SetVelocity(Point(x1, y1));
122  body2.SetVelocity(Point(x2, y2));
123  }
124  else {
125  const Point rel_velocity(body2.GetVelocity() - body1.GetVelocity());
126  double normal_a = dist.GetY() / dist.GetX();
127  x1 = ( rel_velocity.GetX() + normal_a * rel_velocity.GetY() )
128  / ( normal_a * normal_a + 1 );
129  y1 = normal_a * x1;
130  x2 = rel_velocity.GetX() - x1;
131  y2 = - (1 / normal_a) * x2;
132 
133  body2.SetVelocity(body1.GetVelocity() + Point(x2, y2));
134  body1.SetVelocity(body1.GetVelocity() + Point(x1, y1));
135  }
136 
137 
138  return true;
139  }
140 
141  void Update() {
142  // Handle movement of bodies
143 
144  auto & body_set = surface.GetBodySet();
145 
146  for (auto cur_body : body_set) {
147  cur_body->BodyUpdate(0.25); // Let a body change size or shape, as needed.
148  cur_body->ProcessStep(0.0125); // Update position and velocity.
149  }
150 
151  // Handle collisions
152  auto collide_fun =
153  [this](BODY_TYPE & b1, BODY_TYPE & b2){ return this->TestPairCollision(b1,b2); };
154  surface.TestCollisions(collide_fun);
155 
156  // Determine which bodies we should remove.
157  size_t cur_id = 0;
158  while (cur_id < body_set.size()) {
159  emp_assert(body_set[cur_id] != nullptr);
160  const double cur_pressure = body_set[cur_id]->GetPressure();
161 
162  // @CAO Arbitrary pressure threshold!
163  if (cur_pressure > 3.0) { // If pressure too high, burst this cell!
164  body_set[cur_id].Delete(); // Delete the burst cell.
165  if (cur_id < body_set.size() - 1) { // If we are not at the end of the body set...
166  body_set[cur_id] = body_set.back(); // ...move last cell to popped position.
167  }
168  body_set.pop_back(); // Remove the last element now that it was moved away.
169  }
170  else cur_id++;
171  }
172  }
173 
174  // Access to bodies
176  return surface.GetBodySet();
177  }
179  return background.GetBodySet();
180  }
181 
182  // Access to bodies in a const physics...
184  return surface.GetConstBodySet();
185  }
187  return background.GetConstBodySet();
188  }
189  };
190 
191 }
192 
193 #endif
emp::vector< Ptr< BODY_TYPE > > & GetBodySet()
Definition: Surface2D.h:52
Physics2D & Clear()
Definition: Physics2D.h:56
constexpr TYPE GetX() const
Definition: Point2D.h:39
Definition: Physics2D.h:24
const emp::vector< Ptr< BODY_TYPE > > & GetConstBackgroundSet() const
Definition: Physics2D.h:186
constexpr double SquareMagnitude() const
Definition: Point2D.h:45
bool GetDetach() const
Definition: Physics2D.h:43
Surface2D & Clear()
Definition: Surface2D.h:62
const emp::vector< Ptr< BODY_TYPE > > & GetConstBodySet() const
Definition: Surface2D.h:53
Definition: Surface2D.h:38
Surface2D & AddBody(Ptr< BODY_TYPE > new_body)
Definition: Surface2D.h:56
Point2D<> Point
Definition: Point2D.h:99
const emp::vector< Ptr< BODY_TYPE > > & GetConstBodySet() const
Definition: Physics2D.h:183
void TestCollisions(std::function< bool(BODY_TYPE &, BODY_TYPE &)> collide_fun)
Definition: Surface2D.h:71
Physics2D & AddBody(Ptr< BODY_TYPE > in_body)
Definition: Physics2D.h:53
Physics2D & KillOldest()
Definition: Physics2D.h:58
Physics2D & SetDetach(bool _in)
Definition: Physics2D.h:45
A wrapper for pointers that does careful memory tracking (but only in debug mode).
emp::vector< Ptr< BODY_TYPE > > & GetBodySet()
Definition: Physics2D.h:175
~Physics2D()
Definition: Physics2D.h:39
bool TestPairCollision(BODY_TYPE &body1, BODY_TYPE &body2)
Definition: Physics2D.h:78
Physics2D & AddBackground(Ptr< BODY_TYPE > in_body)
Definition: Physics2D.h:54
const Surface_t & GetSurface() const
Definition: Physics2D.h:41
const Surface_t & GetBackground() const
Definition: Physics2D.h:42
emp::vector< Ptr< BODY_TYPE > > & GetBackgroundSet()
Definition: Physics2D.h:178
A drop-in wrapper for std::vector; adds on bounds checking in debug mode.
If we are in emscripten, make sure to include the header.
Definition: array.h:37
Build a debug wrapper emp::vector around std::vector.
Definition: vector.h:42
void Update()
Definition: Physics2D.h:141
#define emp_assert(...)
Definition: assert.h:199
Physics2D(double width, double height, double max_org_diameter=20, bool detach=true)
Definition: Physics2D.h:34
constexpr TYPE GetY() const
Definition: Point2D.h:40