se2ez
collision.cpp
Go to the documentation of this file.
1 /* Author: Zachary Kingston */
2 
3 #include <iostream>
4 
5 #include <Box2D/Box2D.h>
6 
7 #include <se2ez/core/log.h>
8 #include <se2ez/core/geometry.h>
9 #include <se2ez/core/frame.h>
10 #include <se2ez/core/robot.h>
11 #include <se2ez/core/state.h>
12 #include <se2ez/core/collision.h>
15 
16 using namespace se2ez;
17 
18 /** \brief Container class for Box2D's world representation.
19  */
21 {
22 public:
23  /** \brief Constructor.
24  */
25  Box2DWorld() : world(b2Vec2(0, 0))
26  {
27  }
28 
29  void setup()
30  {
31  world.SetAllowSleeping(false);
32  world.SetAutoClearForces(true);
33  world.SetContinuousPhysics(false);
34  world.SetSubStepping(false);
35  }
36 
37  b2World world; ///< Box2D world.
38 };
39 
40 /** \brief Container class for Box2D's contact representation.
41  */
43 {
44 public:
45  /** \brief Constructor.
46  */
47  Box2DContact(b2Contact *contact) : contact(contact)
48  {
49  }
50 
51  /** \brief Get contact manifold this contact.
52  * \param[in] world The world contact manifold.
53  * \return The contact manifold.
54  */
55  b2Manifold *getManifolds(b2WorldManifold &world) const
56  {
57  contact->GetWorldManifold(&world);
58  return contact->GetManifold();
59  }
60 
61  b2Contact *contact; ///< Box2D contact.
62 };
63 
64 /** \brief Container class that converts se2ez::Geometry into a Box2D geometry.
65  */
67 {
68 public:
69  /** \brief Constructor.
70  * \param[in] name Name of frame this geometry is associated with.
71  * \param[in] index Index within frames' geometry vector this geometry is associated with.
72  * \param[in] geometry Geometry to convert.
73  * \param[in] world Box2D world.
74  * \param[in] movable Does this frame move?
75  */
76  Box2DShape(const std::string &name, const unsigned int index, const GeometryPtr &geometry,
77  Box2DWorldPtr world, bool movable)
78  : movable(movable), name(name), index(index), geometry_(geometry)
79  {
80  // Create Box2D Body
81  if (movable)
82  bodyDef_.type = b2_dynamicBody;
83  else
84  bodyDef_.type = b2_staticBody;
85 
86  bodyDef_.userData = this;
87 
88  body_ = world->world.CreateBody(&bodyDef_);
89 
90  // Create Box2D fixture
91  createShapes();
92  shapeDef_.resize(shapes_.size());
93 
94  for (unsigned int i = 0; i < shapes_.size(); ++i)
95  {
96  shapeDef_[i].shape = shapes_[i];
97  shapeDef_[i].density = 1;
98 
99  fixture_.emplace_back(body_->CreateFixture(&shapeDef_[i]));
100  }
101  }
102 
104  {
105  for (unsigned int i = 0; i < shapes_.size(); ++i)
106  delete shapes_[i];
107 
108  for (unsigned int i = 0; i < points_.size(); ++i)
109  delete[] points_[i].first;
110  }
111 
112  /** \brief Sets the pose of this body.
113  * \param[in] pose Pose of this body.
114  */
115  void setPose(const Eigen::Isometry2d &pose)
116  {
117  const auto &x = pose.translation()[0];
118  const auto &y = pose.translation()[1];
119  body_->SetTransform(b2Vec2(x, y), tf::getRotation(pose));
120  }
121 
122  /** \brief Get the current pose of this body in the Box2D world.
123  * \return The pose of the body.
124  */
125  Eigen::Isometry2d getPose() const
126  {
127  const auto &pos = body_->GetPosition();
128  const auto &rot = body_->GetAngle();
129  return tf::toIsometry(pos.x, pos.y, rot);
130  }
131 
132  /** \brief Get the current pose of this body in the Box2D world.
133  * \return The pose of the body.
134  */
135  const b2Transform &getTransform() const
136  {
137  return body_->GetTransform();
138  }
139 
140  /** \brief Get the list of fixtures attached to this body.
141  * \return The list of fixtures.
142  */
144  {
145  return fixture_;
146  }
147 
148  const bool movable; ///< Is this a moving frame piece of geometry?
149  const std::string name; ///< Name of the frame this geometry is attached to.
150  const unsigned int index; ///< Index in frame this geometry is.
151 
152 private:
153  b2PolygonShape *createPolygon(const GeometryPtr &geometry)
154  {
155  auto *poly = new b2PolygonShape;
156 
157  unsigned int count = geometry->points.size();
158  b2Vec2 *verts = new b2Vec2[count];
159 
160  // SE2EZ_DEBUG("Creating polygon with %1% vertices:", count);
161  for (unsigned int i = 0; i < count; ++i)
162  {
163  // SE2EZ_DEBUG(" Vertex %1%: %2%, %3%", i, geometry->points[i](0), geometry->points[i](1));
164  verts[i].Set(geometry->points[i](0), geometry->points[i](1));
165  }
166 
167  poly->Set(verts, count);
168  points_.emplace_back(verts, count);
169 
170  return poly;
171  }
172 
173  /** \brief Convert the geometry of this body into a Box2D shape.
174  */
176  {
177  switch (geometry_->type)
178  {
179  case Geometry::BOX:
180  {
181  auto *box = new b2PolygonShape;
182 
183  // A little magic here. Box2D internally pads polygons with a "skin" so that they appear to be
184  // not in collision. Thus, as we want precision, we subtract that skin out so we get our exact
185  // box.
186  box->SetAsBox(geometry_->dimensions[0] - b2_polygonRadius,
187  geometry_->dimensions[1] - b2_polygonRadius);
188 
189  shapes_.emplace_back(box);
190  break;
191  }
192 
193  case Geometry::CIRCLE:
194  {
195  auto *circle = new b2CircleShape;
196  circle->m_p.Set(0, 0);
197  circle->m_radius = geometry_->dimensions[0];
198 
199  shapes_.emplace_back(circle);
200  break;
201  }
202 
203  case Geometry::CONVEX:
204  {
205  // Need to make sure that we stay under polygon limit.
206  if (geometry_->points.size() > b2_maxPolygonVertices)
207  {
208  geometries_ = geo::shatter(geometry_);
209  for (const auto &geo : geometries_)
210  shapes_.emplace_back(createPolygon(geo));
211  }
212  else
213  shapes_.emplace_back(createPolygon(geometry_));
214 
215  break;
216  }
217 
218  case Geometry::SIMPLE:
219  {
220  // Need to break geometry into convex chunks
221  auto partitions = geo::convexifyOptimal(geometry_);
222  for (const auto &partition : partitions)
223  {
224  if (partition->points.size() > b2_maxPolygonVertices)
225  {
226  auto triangles = geo::shatter(partition);
227  for (const auto &geo : triangles)
228  {
229  geometries_.emplace_back(geo);
230  shapes_.emplace_back(createPolygon(geo));
231  }
232  }
233  else
234  {
235  geometries_.emplace_back(partition);
236  shapes_.emplace_back(createPolygon(partition));
237  }
238  }
239  break;
240  }
241  default:
242  throw std::runtime_error("Shape type not implemented!");
243  };
244  }
245 
246  const GeometryPtr &geometry_; ///< Original geometry.
247  std::vector<GeometryPtr> geometries_; ///< Geometry (created from decompositions).
248 
249  b2BodyDef bodyDef_; ///< Box2D body definition to create body.
250  std::vector<b2FixtureDef> shapeDef_; ///< Box2d shape definitions to create fixtures.
251  std::vector<b2Shape *> shapes_; ///< Box2D geometries;
252 
253  b2Body *body_; ///< Box2D body.
254  std::vector<b2Fixture *> fixture_; ///< Box2D fixtures.
255 
257 };
258 
259 ///
260 /// Box2D Collision manager
261 ///
262 
264 {
265  compile();
266 }
267 
269 {
270 }
271 
273 {
275 
276  shapes_.clear();
277 
278  world_ = std::make_shared<Box2DWorld>();
279  acm_ = robot_->getACM();
280 
281  for (const auto &name : robot_->getFrameNames())
282  {
283  const auto &data = robot_->getFrameDataConst(name);
284  const auto &frame = data->frame;
285  const auto &geometry = frame->getGeometry();
286 
287  if (not geometry.empty())
288  {
289  auto en = shapes_.emplace(std::piecewise_construct, //
291 
292  for (unsigned int i = 0; i < geometry.size(); ++i)
293  en.first->second.emplace_back(
294  std::make_shared<Box2DShape>(name, i, geometry[i], world_, data->movable));
295  }
296  }
297 
298  world_->setup();
299 }
300 
302 {
303  state->fk(robot_);
304 
305  for (const auto &e : shapes_)
306  {
307  const auto &poses = state->getGeometryPoses(robot_, e.first);
308  for (unsigned int i = 0; i < poses.size(); ++i)
309  e.second[i]->setPose(poses[i]);
310  }
311 
312  world_->world.Step(1, 0, 0);
313  world_->world.Step(1, 0, 0);
314 }
315 
317 {
318  b2Contact *contact = world_->world.GetContactList();
319  while (contact != nullptr)
320  {
321  auto fa = static_cast<Box2DShape *>(contact->GetFixtureA()->GetBody()->GetUserData());
322  auto fb = static_cast<Box2DShape *>(contact->GetFixtureB()->GetBody()->GetUserData());
323 
324  if (contact->IsTouching() and acm_->collide(fa->name, fb->name))
325  {
326  Box2DContact container(contact);
327  if (not callback(fa, fb, container))
328  return false;
329  }
330 
331  contact = contact->GetNext();
332  }
333 
334  return true;
335 }
336 
338 {
339  // Iterate through all combinations of shapes.
340  for (auto ita = shapes_.begin(); ita != shapes_.end(); ++ita)
341  {
342  const auto &name_a = ita->first;
343  const auto &shapes_a = ita->second;
344 
345  for (auto itb = ita; itb != shapes_.end(); ++itb)
346  {
347  const auto &name_b = itb->first;
348  const auto &shapes_b = itb->second;
349 
350  // Ignore distances for frames that cannot collide.
351  if (not acm_->collide(name_a, name_b))
352  continue;
353 
354  for (const auto &sa : shapes_a)
355  {
356  for (const auto &sb : shapes_b)
357  {
358  if (not sa->movable and not sb->movable)
359  continue;
360 
361  if (not callback(sa.get(), sb.get()))
362  return false;
363  }
364  }
365  }
366  }
367 
368  return true;
369 }
370 
372 {
374  update(state);
375 
376  return not checkContacts(
377  [](const Box2DShape *, const Box2DShape *, const Box2DContact &) -> bool { return false; });
378 }
379 
381 {
383  update(state);
384 
385  frames.clear();
386  checkContacts([&frames](const Box2DShape *fa, const Box2DShape *fb, const Box2DContact &) -> bool {
387  frames.emplace(fa->name);
388  frames.emplace(fb->name);
389 
390  return true;
391  });
392 
393  return frames.empty();
394 }
395 
397  const Box2DShape *fb) const
398 {
399  SignedDistance sd;
400 
401  // Setup input.
402  b2DistanceInput input;
403  input.useRadii = true;
404 
405  // Set transforms (same across fixtures)
406  input.transformA = fa->getTransform();
407  input.transformB = fb->getTransform();
408 
409  // Iterate through all fixtures in each shape.
410  for (const auto &faf : fa->getFixtures())
411  {
412  for (const auto &fbf : fb->getFixtures())
413  {
414  // Have to zero memory for cache.
415  b2SimplexCache cache;
416  bzero(&cache, sizeof(cache));
417 
418  // Index is ignored for non-chain shapes.
419  input.proxyA.Set(faf->GetShape(), 0);
420  input.proxyB.Set(fbf->GetShape(), 0);
421 
422  b2DistanceOutput output;
423  b2Distance(&output, &cache, &input);
424 
425  if (output.distance < sd.distance)
426  {
427  sd.distance = output.distance;
428  sd.point = {output.pointA.x, output.pointA.y};
429  sd.normal = {output.pointB.x - output.pointA.x, output.pointB.y - output.pointA.y};
430  }
431  }
432  }
433 
434  return sd;
435 }
436 
438 {
439  b2WorldManifold world;
440  auto *manifold = contact.getManifolds(world);
441 
442  SignedDistance sd;
443  sd.normal = {world.normal.x, world.normal.y};
444 
445  for (int i = 0; i < manifold->pointCount; ++i)
446  if (world.separations[i] < sd.distance)
447  {
448  sd.distance = world.separations[i];
449  sd.point = {world.points[i].x, world.points[i].y};
450  }
451 
452  sd.normal *= sd.distance;
453 
454  return sd;
455 }
456 
458 {
460  update(state);
461 
462  SignedDistance d;
463  const auto &checkSD = [&](const Box2DShape *fa, const Box2DShape *fb, SignedDistance &sd) {
464  if (fa->movable)
465  {
466  sd.frame = fa->name;
467  sd.collider = fb->name;
468  }
469  else if (fb->movable)
470  {
471  sd.frame = fb->name;
472  sd.collider = fa->name;
473  sd.flip();
474  }
475 
476  if ((fa->movable or fb->movable) and sd.distance < d.distance)
477  d = sd;
478  };
479 
480  // First check for penetrating collisions.
481  checkContacts([&](const Box2DShape *fa, const Box2DShape *fb, const Box2DContact &contact) -> bool {
482  auto sd = computeDistance(contact);
483  checkSD(fa, fb, sd);
484 
485  return true;
486  });
487 
488  // If no touching contacts, check distances to all other shapes.
489  // Otherwise, terminate early.
490  if (d.distance <= 0)
491  return d;
492 
493  // Iterate through all combinations of shapes.
494  checkPotentials([&](const Box2DShape *fa, const Box2DShape *fb) -> bool {
495  auto sd = computeDistance(fa, fb);
496  checkSD(fa, fb, sd);
497 
498  return true;
499  });
500 
501  return d;
502 }
503 
505 {
507  update(state);
508 
509  frames.clear();
510  SignedDistance d;
511 
512  // Update the signed distance map if signed distance is less, or create entry if it does not exist.
513  const auto &updateFrame = [&frames](const SignedDistance &sd) {
514  auto f = frames.find(sd.frame);
515  if (f == frames.end())
516  frames.emplace(sd.frame, sd);
517 
518  else if (sd.distance < f->second.distance)
519  f->second = sd;
520  };
521 
522  // Checks if signed distance information needs to be set for either shape (only movable)
523  const auto &checkSD = [&](const Box2DShape *fa, const Box2DShape *fb, SignedDistance &sd) {
524  sd.frame = fa->name;
525  sd.collider = fb->name;
526  updateFrame(sd);
527 
528  sd.frame = fb->name;
529  sd.collider = fa->name;
530  sd.flip();
531 
532  updateFrame(sd);
533 
534  if (sd.distance < d.distance)
535  d = sd;
536  };
537 
538  // First check for penetrating collisions.
539  checkContacts([&](const Box2DShape *fa, const Box2DShape *fb, const Box2DContact &contact) -> bool {
540  auto sd = computeDistance(contact);
541  checkSD(fa, fb, sd);
542 
543  return true;
544  });
545 
546  // Iterate through all combinations of shapes.
547  checkPotentials([&](const Box2DShape *fa, const Box2DShape *fb) -> bool {
548  auto sd = computeDistance(fa, fb);
549  checkSD(fa, fb, sd);
550 
551  return true;
552  });
553 
554  return d;
555 }
T empty(T... args)
SignedDistance distance(StatePtr &state) override
Computes the signed distance of a scene to collision.
Definition: collision.cpp:457
b2World world
Box2D world.
Definition: collision.cpp:37
const b2Transform & getTransform() const
Get the current pose of this body in the Box2D world.
Definition: collision.cpp:135
A shared pointer wrapper for se2ez::State.
Box2DCollisionManager(const RobotPtr &robot)
Constructor.
Definition: collision.cpp:263
T forward_as_tuple(T... args)
A rectangular prism.
Definition: geometry.h:33
Eigen::Vector2d normal
Normal vector to collision.
Definition: collision.h:81
const GeometryPtr & geometry_
Original geometry.
Definition: collision.cpp:246
b2Contact * contact
Box2D contact.
Definition: collision.cpp:61
A filled circle.
Definition: geometry.h:34
A convex polygon.
Definition: geometry.h:35
std::vector< std::pair< b2Vec2 *, unsigned int > > points_
Polygon points.
Definition: collision.cpp:256
std::vector< GeometryPtr > shatter(const GeometryPtr &geometry)
"Shatters" a convex polygon into a fan of triangles using the first point as the root for all triangl...
Definition: polygon.cpp:167
std::mutex mutex_
Mutex for collision checking.
Definition: collision.h:134
double getRotation(const Eigen::Isometry2d &frame)
Gets rotation from a frame from the X-axis (t)
Definition: math.cpp:25
const std::string name
Name of the frame this geometry is attached to.
Definition: collision.cpp:149
T end(T... args)
virtual ~Box2DCollisionManager()
Destructor.
Definition: collision.cpp:268
const bool movable
Is this a moving frame piece of geometry?
Definition: collision.cpp:148
b2Manifold * getManifolds(b2WorldManifold &world) const
Get contact manifold this contact.
Definition: collision.cpp:55
A shared pointer wrapper for se2ez::Box2DWorld.
std::vector< b2Shape * > shapes_
Box2D geometries;.
Definition: collision.cpp:251
A class that contains all information about the signed distance of a frame to another.
Definition: collision.h:75
Box2DWorld()
Constructor.
Definition: collision.cpp:25
const unsigned int index
Index in frame this geometry is.
Definition: collision.cpp:150
bool checkContacts(const ContactCallback &callback)
Check all Box2D contacts in the world, and call a callback function for each.
Definition: collision.cpp:316
void compile() override
Compiles internal data structures for collision checking. Needs to be called if the robot is updated...
Definition: collision.cpp:272
A shared pointer wrapper for se2ez::Geometry.
const RobotPtr robot_
Robot model used for collision checking.
Definition: collision.h:131
Eigen::Isometry2d toIsometry(double x, double y, double theta)
Converts a translation (x, y) and rotation from X-axis (theta) into a transform.
Definition: math.cpp:11
Eigen::Isometry2d getPose() const
Get the current pose of this body in the Box2D world.
Definition: collision.cpp:125
std::map< std::string, std::vector< Box2DShapePtr > > shapes_
Map of frames to Box2D shapes.
Definition: collision.h:133
SignedDistance computeDistance(const Box2DContact &contact) const
Compute distance from a contact.
Definition: collision.cpp:437
b2Body * body_
Box2D body.
Definition: collision.cpp:253
const std::vector< b2Fixture * > & getFixtures() const
Get the list of fixtures attached to this body.
Definition: collision.cpp:143
void createShapes()
Convert the geometry of this body into a Box2D shape.
Definition: collision.cpp:175
T clear(T... args)
A simple polygon (no holes).
Definition: geometry.h:36
Container class for Box2D&#39;s contact representation.
Definition: collision.cpp:42
void update(StatePtr &state)
Updates internal world structure based upon robot state.
Definition: collision.cpp:301
Box2DWorldPtr world_
Box2D world model.
Definition: collision.h:132
T find(T... args)
std::string frame
Frame closet to collision.
Definition: collision.h:82
void setPose(const Eigen::Isometry2d &pose)
Sets the pose of this body.
Definition: collision.cpp:115
std::vector< GeometryPtr > geometries_
Geometry (created from decompositions).
Definition: collision.cpp:247
Eigen::Vector2d point
Point on fixture where normal begins.
Definition: collision.h:80
std::vector< b2FixtureDef > shapeDef_
Box2d shape definitions to create fixtures.
Definition: collision.cpp:250
std::vector< GeometryPtr > convexifyOptimal(const GeometryPtr &geometry)
Performs an optimal convex decomposition on the geometry.
Definition: polygon.cpp:141
std::vector< b2Fixture * > fixture_
Box2D fixtures.
Definition: collision.cpp:254
A shared pointer wrapper for se2ez::Robot.
ACMPtr acm_
ACM for collision manager.
Definition: collision.h:140
T emplace(T... args)
Container class that converts se2ez::Geometry into a Box2D geometry.
Definition: collision.cpp:66
Main namespace.
Definition: collision.h:11
Container class for Box2D&#39;s world representation.
Definition: collision.cpp:20
b2BodyDef bodyDef_
Box2D body definition to create body.
Definition: collision.cpp:249
Box2DContact(b2Contact *contact)
Constructor.
Definition: collision.cpp:47
Box2DShape(const std::string &name, const unsigned int index, const GeometryPtr &geometry, Box2DWorldPtr world, bool movable)
Constructor.
Definition: collision.cpp:76
bool checkPotentials(const PotentialCallback &callback)
Check all potential collisions in the world, and call a callback function for each.
Definition: collision.cpp:337
SE2EZ_EIGEN_CLASS double distance
Distance to collision.
Definition: collision.h:79
bool collide(StatePtr &state) override
Check if a state is in collision.
Definition: collision.cpp:371
b2PolygonShape * createPolygon(const GeometryPtr &geometry)
Definition: collision.cpp:153