bodies.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2008, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Ioan Sucan */
36 
40 
41 #include <console_bridge/console.h>
42 
43 extern "C" {
44 #ifdef GEOMETRIC_SHAPES_HAVE_QHULL_2011
45 #include <libqhull/libqhull.h>
46 #include <libqhull/mem.h>
47 #include <libqhull/qset.h>
48 #include <libqhull/geom.h>
49 #include <libqhull/merge.h>
50 #include <libqhull/poly.h>
51 #include <libqhull/io.h>
52 #include <libqhull/stat.h>
53 #else
54 #include <qhull/qhull.h>
55 #include <qhull/mem.h>
56 #include <qhull/qset.h>
57 #include <qhull/geom.h>
58 #include <qhull/merge.h>
59 #include <qhull/poly.h>
60 #include <qhull/io.h>
61 #include <qhull/stat.h>
62 #endif
63 }
64 
65 #include <boost/math/constants/constants.hpp>
66 #include <limits>
67 #include <cstdio>
68 #include <cmath> // std::fmin, std::fmax
69 #include <algorithm>
70 #include <Eigen/Geometry>
71 #include <unordered_map>
72 #include <mutex>
73 
74 namespace bodies
75 {
76 namespace detail
77 {
78 static const double ZERO = 1e-9;
79 
82 static inline double distanceSQR(const Eigen::Vector3d& p, const Eigen::Vector3d& origin, const Eigen::Vector3d& dir)
83 {
84  Eigen::Vector3d a = p - origin;
85  double d = dir.normalized().dot(a);
86  return a.squaredNorm() - d * d;
87 }
88 
89 // temp structure for intersection points (used for ordering them)
90 struct intersc
91 {
92  intersc(const Eigen::Vector3d& _pt, const double _tm) : pt(_pt), time(_tm)
93  {
94  }
95 
96  Eigen::Vector3d pt;
97  double time;
98 
99  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
100 };
101 
102 // define order on intersection points
104 {
105  bool operator()(const intersc& a, const intersc& b) const
106  {
107  return a.time < b.time;
108  }
109 };
110 
117 void filterIntersections(std::vector<detail::intersc>& ipts, EigenSTL::vector_Vector3d* intersections,
118  const size_t count)
119 {
120  if (intersections == nullptr || ipts.empty())
121  return;
122 
123  std::sort(ipts.begin(), ipts.end(), interscOrder());
124  const auto n = count > 0 ? std::min<size_t>(count, ipts.size()) : ipts.size();
125 
126  for (const auto& p : ipts)
127  {
128  if (intersections->size() == n)
129  break;
130  if (!intersections->empty() && p.pt.isApprox(intersections->back(), ZERO))
131  continue;
132  intersections->push_back(p.pt);
133  }
134 }
135 
136 // HACK: The global map g_triangle_for_plane_ is needed for ABI compatibility with the melodic version of
137 // geometric_shapes; in newer releases, it should instead be added as a member to ConvexMesh::MeshData.
138 std::unordered_map<const ConvexMesh*, std::map<size_t, size_t>> g_triangle_for_plane_;
140 static std::map<size_t, size_t>& getTriangleForPlane(const ConvexMesh* mesh)
141 {
142  std::lock_guard<std::mutex> lock(g_triangle_for_plane_mutex);
143  auto it = g_triangle_for_plane_.find(mesh);
144  if (it == detail::g_triangle_for_plane_.end())
145  return detail::g_triangle_for_plane_.emplace(mesh, std::map<size_t, size_t>()).first->second;
146  else
147  return it->second;
148 }
149 } // namespace detail
150 
151 inline Eigen::Vector3d normalize(const Eigen::Vector3d& dir)
152 {
153  const double norm = dir.squaredNorm();
154 #if EIGEN_VERSION_AT_LEAST(3, 3, 0)
155  return ((norm - 1) > 1e-9) ? (dir / Eigen::numext::sqrt(norm)) : dir;
156 #else // used in kinetic
157  return ((norm - 1) > 1e-9) ? (Eigen::Vector3d)(dir / sqrt(norm)) : dir;
158 #endif
159 }
160 } // namespace bodies
161 
163 {
164  setDimensionsDirty(shape);
165  updateInternalData();
166 }
167 
169  Eigen::Vector3d& result) const
170 {
171  BoundingSphere bs;
173  for (unsigned int i = 0; i < max_attempts; ++i)
174  {
175  result = Eigen::Vector3d(rng.uniformReal(bs.center.x() - bs.radius, bs.center.x() + bs.radius),
176  rng.uniformReal(bs.center.y() - bs.radius, bs.center.y() + bs.radius),
177  rng.uniformReal(bs.center.z() - bs.radius, bs.center.z() + bs.radius));
178  if (containsPoint(result))
179  return true;
180  }
181  return false;
182 }
183 
184 bool bodies::Sphere::containsPoint(const Eigen::Vector3d& p, bool /* verbose */) const
185 {
186  return (center_ - p).squaredNorm() <= radius2_;
187 }
188 
189 void bodies::Sphere::useDimensions(const shapes::Shape* shape) // radius
190 {
191  radius_ = static_cast<const shapes::Sphere*>(shape)->radius;
192 }
193 
194 std::vector<double> bodies::Sphere::getDimensions() const
195 {
196  return { radius_ };
197 }
198 
199 std::vector<double> bodies::Sphere::getScaledDimensions() const
200 {
201  return { radiusU_ };
202 }
203 
205 {
206  const auto tmpRadiusU = radius_ * scale_ + padding_;
207  if (tmpRadiusU < 0)
208  throw std::runtime_error("Sphere radius must be non-negative.");
209  radiusU_ = tmpRadiusU;
210  radius2_ = radiusU_ * radiusU_;
211  center_ = pose_.translation();
212 }
213 
214 std::shared_ptr<bodies::Body> bodies::Sphere::cloneAt(const Eigen::Isometry3d& pose, double padding, double scale) const
215 {
216  auto s = std::allocate_shared<Sphere>(Eigen::aligned_allocator<Sphere>());
217  s->radius_ = radius_;
218  s->padding_ = padding;
219  s->scale_ = scale;
220  s->pose_ = pose;
221  s->updateInternalData();
222  return s;
223 }
224 
226 {
227  return 4.0 * boost::math::constants::pi<double>() * radiusU_ * radiusU_ * radiusU_ / 3.0;
228 }
229 
231 {
232  sphere.center = center_;
233  sphere.radius = radiusU_;
234 }
235 
237 {
238  cylinder.pose = pose_;
239  cylinder.radius = radiusU_;
240  cylinder.length = 2.0 * radiusU_;
241 }
242 
244 {
245  bbox.setEmpty();
246 
247  // it's a sphere, so we do not rotate the bounding box
248  Eigen::Isometry3d transform = Eigen::Isometry3d::Identity();
249  transform.translation() = getPose().translation();
250 
251  bbox.extendWithTransformedBox(transform, Eigen::Vector3d(2 * radiusU_, 2 * radiusU_, 2 * radiusU_));
252 }
253 
255 {
256  // it's a sphere, so we do not rotate the bounding box
257  Eigen::Isometry3d transform = Eigen::Isometry3d::Identity();
258  transform.translation() = getPose().translation();
259 
260  bbox.setPoseAndExtents(transform, 2 * Eigen::Vector3d(radiusU_, radiusU_, radiusU_));
261 }
262 
264  Eigen::Vector3d& result) const
265 {
266  for (unsigned int i = 0; i < max_attempts; ++i)
267  {
268  const double minX = center_.x() - radiusU_;
269  const double maxX = center_.x() + radiusU_;
270  const double minY = center_.y() - radiusU_;
271  const double maxY = center_.y() + radiusU_;
272  const double minZ = center_.z() - radiusU_;
273  const double maxZ = center_.z() + radiusU_;
274  // we are sampling in a box; the probability of success after 20 attempts is 99.99996% given the ratio of box volume
275  // to sphere volume
276  for (int j = 0; j < 20; ++j)
277  {
278  result = Eigen::Vector3d(rng.uniformReal(minX, maxX), rng.uniformReal(minY, maxY), rng.uniformReal(minZ, maxZ));
279  if (containsPoint(result))
280  return true;
281  }
282  }
283  return false;
284 }
285 
286 bool bodies::Sphere::intersectsRay(const Eigen::Vector3d& origin, const Eigen::Vector3d& dir,
287  EigenSTL::vector_Vector3d* intersections, unsigned int count) const
288 {
289  // this is faster than always calling dir.normalized() in case the vector is already unit
290  const Eigen::Vector3d dirNorm = normalize(dir);
291 
292  if (detail::distanceSQR(center_, origin, dirNorm) > radius2_)
293  return false;
294 
295  bool result = false;
296 
297  Eigen::Vector3d cp = origin - center_;
298  double dpcpv = cp.dot(dirNorm);
299 
300  Eigen::Vector3d w = cp - dpcpv * dirNorm;
301  Eigen::Vector3d Q = center_ + w;
302  double x = radius2_ - w.squaredNorm();
303 
304  if (fabs(x) < detail::ZERO)
305  {
306  w = Q - origin;
307  double dpQv = w.dot(dirNorm);
308  if (dpQv > detail::ZERO)
309  {
310  if (intersections)
311  intersections->push_back(Q);
312  result = true;
313  }
314  }
315  else if (x > 0.0)
316  {
317  x = sqrt(x);
318  w = dirNorm * x;
319  Eigen::Vector3d A = Q - w;
320  Eigen::Vector3d B = Q + w;
321  w = A - origin;
322  double dpAv = w.dot(dirNorm);
323  w = B - origin;
324  double dpBv = w.dot(dirNorm);
325 
326  if (dpAv > detail::ZERO)
327  {
328  result = true;
329  if (intersections)
330  {
331  intersections->push_back(A);
332  if (count == 1)
333  return result;
334  }
335  }
336 
337  if (dpBv > detail::ZERO)
338  {
339  result = true;
340  if (intersections)
341  intersections->push_back(B);
342  }
343  }
344  return result;
345 }
346 
347 bool bodies::Cylinder::containsPoint(const Eigen::Vector3d& p, bool /* verbose */) const
348 {
349  Eigen::Vector3d v = p - center_;
350  double pH = v.dot(normalH_);
351 
352  if (fabs(pH) > length2_)
353  return false;
354 
355  double pB1 = v.dot(normalB1_);
356  double remaining = radius2_ - pB1 * pB1;
357 
358  if (remaining < 0.0)
359  return false;
360  else
361  {
362  double pB2 = v.dot(normalB2_);
363  return pB2 * pB2 <= remaining;
364  }
365 }
366 
367 void bodies::Cylinder::useDimensions(const shapes::Shape* shape) // (length, radius)
368 {
369  length_ = static_cast<const shapes::Cylinder*>(shape)->length;
370  radius_ = static_cast<const shapes::Cylinder*>(shape)->radius;
371 }
372 
373 std::vector<double> bodies::Cylinder::getDimensions() const
374 {
375  return { radius_, length_ };
376 }
377 
378 std::vector<double> bodies::Cylinder::getScaledDimensions() const
379 {
380  return { radiusU_, 2 * length2_ };
381 }
382 
384 {
385  const auto tmpRadiusU = radius_ * scale_ + padding_;
386  if (tmpRadiusU < 0)
387  throw std::runtime_error("Cylinder radius must be non-negative.");
388  const auto tmpLength2 = scale_ * length_ / 2.0 + padding_;
389  if (tmpLength2 < 0)
390  throw std::runtime_error("Cylinder length must be non-negative.");
391  radiusU_ = tmpRadiusU;
392  length2_ = tmpLength2;
393  radius2_ = radiusU_ * radiusU_;
394  center_ = pose_.translation();
395  radiusBSqr_ = length2_ * length2_ + radius2_;
396  radiusB_ = sqrt(radiusBSqr_);
397 
398  ASSERT_ISOMETRY(pose_);
399  Eigen::Matrix3d basis = pose_.linear();
400  normalB1_ = basis.col(0);
401  normalB2_ = basis.col(1);
402  normalH_ = basis.col(2);
403 
404  double tmp = -normalH_.dot(center_);
405  d1_ = tmp + length2_;
406  d2_ = tmp - length2_;
407 }
408 
410  Eigen::Vector3d& result) const
411 {
412  // sample a point on the base disc of the cylinder
413  double a = rng.uniformReal(-boost::math::constants::pi<double>(), boost::math::constants::pi<double>());
414  double r = rng.uniformReal(-radiusU_, radiusU_);
415  double x = cos(a) * r;
416  double y = sin(a) * r;
417 
418  // sample e height
419  double z = rng.uniformReal(-length2_, length2_);
420 
421  result = pose_ * Eigen::Vector3d(x, y, z);
422  return true;
423 }
424 
425 std::shared_ptr<bodies::Body> bodies::Cylinder::cloneAt(const Eigen::Isometry3d& pose, double padding,
426  double scale) const
427 {
428  auto c = std::allocate_shared<Cylinder>(Eigen::aligned_allocator<Cylinder>());
429  c->length_ = length_;
430  c->radius_ = radius_;
431  c->padding_ = padding;
432  c->scale_ = scale;
433  c->pose_ = pose;
434  c->updateInternalData();
435  return c;
436 }
437 
439 {
440  return 2.0 * boost::math::constants::pi<double>() * radius2_ * length2_;
441 }
442 
444 {
445  sphere.center = center_;
446  sphere.radius = radiusB_;
447 }
448 
450 {
451  cylinder.pose = pose_;
452  cylinder.radius = radiusU_;
453  cylinder.length = 2 * length2_;
454 }
455 
457 {
458  bbox.setEmpty();
459 
460  // method taken from http://www.iquilezles.org/www/articles/diskbbox/diskbbox.htm
461 
462  const auto a = normalH_;
463  const auto e = radiusU_ * (Eigen::Vector3d::Ones() - a.cwiseProduct(a) / a.dot(a)).cwiseSqrt();
464  const auto pa = center_ + length2_ * normalH_;
465  const auto pb = center_ - length2_ * normalH_;
466 
467  bbox.extend(pa - e);
468  bbox.extend(pa + e);
469  bbox.extend(pb - e);
470  bbox.extend(pb + e);
471 }
472 
474 {
475  bbox.setPoseAndExtents(getPose(), 2 * Eigen::Vector3d(radiusU_, radiusU_, length2_));
476 }
477 
478 bool bodies::Cylinder::intersectsRay(const Eigen::Vector3d& origin, const Eigen::Vector3d& dir,
479  EigenSTL::vector_Vector3d* intersections, unsigned int count) const
480 {
481  // this is faster than always calling dir.normalized() in case the vector is already unit
482  const Eigen::Vector3d dirNorm = normalize(dir);
483 
484  if (detail::distanceSQR(center_, origin, dirNorm) > radiusBSqr_)
485  return false;
486 
487  std::vector<detail::intersc> ipts;
488 
489  // intersect bases
490  double tmp = normalH_.dot(dirNorm);
491  if (fabs(tmp) > detail::ZERO)
492  {
493  double tmp2 = -normalH_.dot(origin);
494  double t1 = (tmp2 - d1_) / tmp;
495 
496  if (t1 > 0.0)
497  {
498  Eigen::Vector3d p1(origin + dirNorm * t1);
499  Eigen::Vector3d v1(p1 - center_);
500  v1 = v1 - normalH_.dot(v1) * normalH_;
501  if (v1.squaredNorm() < radius2_ + detail::ZERO)
502  {
503  if (intersections == nullptr)
504  return true;
505 
506  detail::intersc ip(p1, t1);
507  ipts.push_back(ip);
508  }
509  }
510 
511  double t2 = (tmp2 - d2_) / tmp;
512  if (t2 > 0.0)
513  {
514  Eigen::Vector3d p2(origin + dirNorm * t2);
515  Eigen::Vector3d v2(p2 - center_);
516  v2 = v2 - normalH_.dot(v2) * normalH_;
517  if (v2.squaredNorm() < radius2_ + detail::ZERO)
518  {
519  if (intersections == nullptr)
520  return true;
521 
522  detail::intersc ip(p2, t2);
523  ipts.push_back(ip);
524  }
525  }
526  }
527 
528  if (ipts.size() < 2)
529  {
530  // intersect with infinite cylinder
531  Eigen::Vector3d VD(normalH_.cross(dirNorm));
532  Eigen::Vector3d ROD(normalH_.cross(origin - center_));
533  double a = VD.squaredNorm();
534  double b = 2.0 * ROD.dot(VD);
535  double c = ROD.squaredNorm() - radius2_;
536  double d = b * b - 4.0 * a * c;
537  if (d >= 0.0 && fabs(a) > detail::ZERO)
538  {
539  d = sqrt(d);
540  double e = -a * 2.0;
541  double t1 = (b + d) / e;
542  double t2 = (b - d) / e;
543 
544  if (t1 > 0.0)
545  {
546  Eigen::Vector3d p1(origin + dirNorm * t1);
547  Eigen::Vector3d v1(center_ - p1);
548 
549  if (fabs(normalH_.dot(v1)) < length2_ + detail::ZERO)
550  {
551  if (intersections == nullptr)
552  return true;
553 
554  detail::intersc ip(p1, t1);
555  ipts.push_back(ip);
556  }
557  }
558 
559  if (t2 > 0.0)
560  {
561  Eigen::Vector3d p2(origin + dirNorm * t2);
562  Eigen::Vector3d v2(center_ - p2);
563 
564  if (fabs(normalH_.dot(v2)) < length2_ + detail::ZERO)
565  {
566  if (intersections == nullptr)
567  return true;
568  detail::intersc ip(p2, t2);
569  ipts.push_back(ip);
570  }
571  }
572  }
573  }
574 
575  if (ipts.empty())
576  return false;
577 
578  // If a ray hits exactly the boundary between a side and a base, it is reported twice.
579  // We want to only return the intersection once, thus we need to filter them.
580  detail::filterIntersections(ipts, intersections, count);
581  return true;
582 }
583 
585  Eigen::Vector3d& result) const
586 {
587  result = pose_ * Eigen::Vector3d(rng.uniformReal(-length2_, length2_), rng.uniformReal(-width2_, width2_),
588  rng.uniformReal(-height2_, height2_));
589  return true;
590 }
591 
592 bool bodies::Box::containsPoint(const Eigen::Vector3d& p, bool /* verbose */) const
593 {
594  const Eigen::Vector3d aligned = (pose_.linear().transpose() * (p - center_)).cwiseAbs();
595  return aligned[0] <= length2_ && aligned[1] <= width2_ && aligned[2] <= height2_;
596 }
597 
598 void bodies::Box::useDimensions(const shapes::Shape* shape) // (x, y, z) = (length, width, height)
599 {
600  const double* size = static_cast<const shapes::Box*>(shape)->size;
601  length_ = size[0];
602  width_ = size[1];
603  height_ = size[2];
604 }
605 
606 std::vector<double> bodies::Box::getDimensions() const
607 {
608  return { length_, width_, height_ };
609 }
610 
611 std::vector<double> bodies::Box::getScaledDimensions() const
612 {
613  return { 2 * length2_, 2 * width2_, 2 * height2_ };
614 }
615 
617 {
618  double s2 = scale_ / 2.0;
619  const auto tmpLength2 = length_ * s2 + padding_;
620  const auto tmpWidth2 = width_ * s2 + padding_;
621  const auto tmpHeight2 = height_ * s2 + padding_;
622 
623  if (tmpLength2 < 0 || tmpWidth2 < 0 || tmpHeight2 < 0)
624  throw std::runtime_error("Box dimensions must be non-negative.");
625 
626  length2_ = tmpLength2;
627  width2_ = tmpWidth2;
628  height2_ = tmpHeight2;
629 
630  center_ = pose_.translation();
631 
632  radius2_ = length2_ * length2_ + width2_ * width2_ + height2_ * height2_;
633  radiusB_ = sqrt(radius2_);
634 
635  ASSERT_ISOMETRY(pose_);
636  Eigen::Matrix3d basis = pose_.linear();
637  normalL_ = basis.col(0);
638  normalW_ = basis.col(1);
639  normalH_ = basis.col(2);
640 
641  // rotation is intentionally not applied, the corners are used in intersectsRay()
642  const Eigen::Vector3d tmp(length2_, width2_, height2_);
643  corner1_ = center_ - tmp;
644  corner2_ = center_ + tmp;
645 }
646 
647 std::shared_ptr<bodies::Body> bodies::Box::cloneAt(const Eigen::Isometry3d& pose, double padding, double scale) const
648 {
649  auto b = std::allocate_shared<Box>(Eigen::aligned_allocator<Box>());
650  b->length_ = length_;
651  b->width_ = width_;
652  b->height_ = height_;
653  b->padding_ = padding;
654  b->scale_ = scale;
655  b->pose_ = pose;
656  b->updateInternalData();
657  return b;
658 }
659 
661 {
662  return 8.0 * length2_ * width2_ * height2_;
663 }
664 
666 {
667  sphere.center = center_;
668  sphere.radius = radiusB_;
669 }
670 
672 {
673  double a, b;
674 
675  if (length2_ > width2_ && length2_ > height2_)
676  {
677  cylinder.length = length2_ * 2.0;
678  a = width2_;
679  b = height2_;
680  Eigen::Isometry3d rot(Eigen::AngleAxisd(90.0f * (M_PI / 180.0f), Eigen::Vector3d::UnitY()));
681  cylinder.pose = pose_ * rot;
682  }
683  else if (width2_ > height2_)
684  {
685  cylinder.length = width2_ * 2.0;
686  a = height2_;
687  b = length2_;
688  cylinder.radius = sqrt(height2_ * height2_ + length2_ * length2_);
689  Eigen::Isometry3d rot(Eigen::AngleAxisd(90.0f * (M_PI / 180.0f), Eigen::Vector3d::UnitX()));
690  cylinder.pose = pose_ * rot;
691  }
692  else
693  {
694  cylinder.length = height2_ * 2.0;
695  a = width2_;
696  b = length2_;
697  cylinder.pose = pose_;
698  }
699  cylinder.radius = sqrt(a * a + b * b);
700 }
701 
703 {
704  bbox.setEmpty();
705 
706  bbox.extendWithTransformedBox(getPose(), 2 * Eigen::Vector3d(length2_, width2_, height2_));
707 }
708 
710 {
711  bbox.setPoseAndExtents(getPose(), 2 * Eigen::Vector3d(length2_, width2_, height2_));
712 }
713 
714 bool bodies::Box::intersectsRay(const Eigen::Vector3d& origin, const Eigen::Vector3d& dir,
715  EigenSTL::vector_Vector3d* intersections, unsigned int count) const
716 {
717  // this is faster than always calling dir.normalized() in case the vector is already unit
718  const Eigen::Vector3d dirNorm = normalize(dir);
719 
720  // Brian Smits. Efficient bounding box intersection. Ray tracing news 15(1), 2002
721 
722  // The implemented method only works for axis-aligned boxes. So we treat ours as such, cancel its rotation, and
723  // rotate the origin and dir instead. corner1_ and corner2_ are corners with canceled rotation.
724  const Eigen::Matrix3d invRot = pose_.linear().transpose();
725  const Eigen::Vector3d o(invRot * (origin - center_) + center_);
726  const Eigen::Vector3d d(invRot * dirNorm);
727 
728  Eigen::Vector3d tmpTmin, tmpTmax;
729  tmpTmin = (corner1_ - o).cwiseQuotient(d);
730  tmpTmax = (corner2_ - o).cwiseQuotient(d);
731 
732  // In projection to each axis, if the ray has positive direction, it goes from min corner (corner1_) to max corner
733  // (corner2_). If its direction is negative, the first intersection is at max corner and then at min corner.
734  for (size_t i = 0; i < 3; ++i)
735  {
736  if (d[i] < 0)
737  std::swap(tmpTmin[i], tmpTmax[i]);
738  }
739 
740  // tmin and tmax are such values of t in "p = o + t * d" in which the line intersects the box faces.
741  // The box is viewed projected from all three directions, values of t are computed for each of the projections,
742  // and a final constraint on tmin and tmax is updated by each of these projections. If tmin > tmax, there is no
743  // intersection between the line and the box.
744 
745  double tmin, tmax;
746  // use fmax/fmin to handle NaNs which can sneak in when dividing by d in tmpTmin and tmpTmax
747  tmin = std::fmax(tmpTmin.x(), std::fmax(tmpTmin.y(), tmpTmin.z()));
748  tmax = std::fmin(tmpTmax.x(), std::fmin(tmpTmax.y(), tmpTmax.z()));
749 
750  // tmin > tmax, there is no intersection between the line and the box
751  if (tmax - tmin < -detail::ZERO)
752  return false;
753 
754  // As we're doing intersections with a ray and not a line, cases where tmax is negative mean that the intersection is
755  // with the opposite ray and not the one we are working with.
756  if (tmax < 0)
757  return false;
758 
759  if (intersections)
760  {
761  if (tmax - tmin > detail::ZERO)
762  {
763  // tmax > tmin, we have two distinct intersection points
764  if (tmin > detail::ZERO)
765  {
766  // tmin > 0, both intersections lie on the ray
767  intersections->push_back(tmin * dirNorm + origin);
768  if (count == 0 || count > 1)
769  intersections->push_back(tmax * dirNorm + origin);
770  }
771  else
772  {
773  // tmin <= 0 && tmax >= 0, the first intersection point is on the opposite ray and the second one on the correct
774  // ray - this means origin of the ray lies inside the box and we should only report one intersection.
775  intersections->push_back(tmax * dirNorm + origin);
776  }
777  }
778  else
779  {
780  // tmax == tmin, there is exactly one intersection at a corner or edge
781  intersections->push_back(tmax * dirNorm + origin);
782  }
783  }
784 
785  return true;
786 }
787 
788 bool bodies::ConvexMesh::containsPoint(const Eigen::Vector3d& p, bool /* verbose */) const
789 {
790  if (!mesh_data_)
791  return false;
792  if (bounding_box_.containsPoint(p))
793  {
794  // Transform the point to the "base space" of this mesh
795  Eigen::Vector3d ip(i_pose_ * p);
796  return isPointInsidePlanes(ip);
797  }
798  else
799  return false;
800 }
801 
803 {
804  for (unsigned int i = 0; i < mesh_data_->triangles_.size(); i += 3)
805  {
806  Eigen::Vector3d d1 =
807  mesh_data_->vertices_[mesh_data_->triangles_[i]] - mesh_data_->vertices_[mesh_data_->triangles_[i + 1]];
808  Eigen::Vector3d d2 =
809  mesh_data_->vertices_[mesh_data_->triangles_[i]] - mesh_data_->vertices_[mesh_data_->triangles_[i + 2]];
810  // expected computed normal from triangle vertex order
811  Eigen::Vector3d tri_normal = d1.cross(d2);
812  tri_normal.normalize();
813  // actual plane normal
814  Eigen::Vector3d normal(mesh_data_->planes_[mesh_data_->plane_for_triangle_[i / 3]].x(),
815  mesh_data_->planes_[mesh_data_->plane_for_triangle_[i / 3]].y(),
816  mesh_data_->planes_[mesh_data_->plane_for_triangle_[i / 3]].z());
817  bool same_dir = tri_normal.dot(normal) > 0;
818  if (!same_dir)
819  {
820  std::swap(mesh_data_->triangles_[i], mesh_data_->triangles_[i + 1]);
821  }
822  }
823 }
824 
826 {
827  mesh_data_ = std::allocate_shared<MeshData>(Eigen::aligned_allocator<MeshData>());
828  const shapes::Mesh* mesh = static_cast<const shapes::Mesh*>(shape);
829 
830  double maxX = -std::numeric_limits<double>::infinity(), maxY = -std::numeric_limits<double>::infinity(),
831  maxZ = -std::numeric_limits<double>::infinity();
832  double minX = std::numeric_limits<double>::infinity(), minY = std::numeric_limits<double>::infinity(),
833  minZ = std::numeric_limits<double>::infinity();
834 
835  for (unsigned int i = 0; i < mesh->vertex_count; ++i)
836  {
837  double vx = mesh->vertices[3 * i];
838  double vy = mesh->vertices[3 * i + 1];
839  double vz = mesh->vertices[3 * i + 2];
840 
841  if (maxX < vx)
842  maxX = vx;
843  if (maxY < vy)
844  maxY = vy;
845  if (maxZ < vz)
846  maxZ = vz;
847 
848  if (minX > vx)
849  minX = vx;
850  if (minY > vy)
851  minY = vy;
852  if (minZ > vz)
853  minZ = vz;
854  }
855 
856  if (maxX < minX)
857  maxX = minX = 0.0;
858  if (maxY < minY)
859  maxY = minY = 0.0;
860  if (maxZ < minZ)
861  maxZ = minZ = 0.0;
862 
863  mesh_data_->box_size_ = Eigen::Vector3d(maxX - minX, maxY - minY, maxZ - minZ);
864 
865  mesh_data_->box_offset_ = Eigen::Vector3d((minX + maxX) / 2.0, (minY + maxY) / 2.0, (minZ + maxZ) / 2.0);
866 
867  mesh_data_->planes_.clear();
868  mesh_data_->triangles_.clear();
869  mesh_data_->vertices_.clear();
870  mesh_data_->mesh_radiusB_ = 0.0;
871  mesh_data_->mesh_center_ = Eigen::Vector3d();
872 
873  double xdim = maxX - minX;
874  double ydim = maxY - minY;
875  double zdim = maxZ - minZ;
876 
877  double pose1;
878  double pose2;
879 
880  unsigned int off1;
881  unsigned int off2;
882 
883  /* compute bounding cylinder */
884  double cyl_length;
885  double maxdist = -std::numeric_limits<double>::infinity();
886  if (xdim > ydim && xdim > zdim)
887  {
888  off1 = 1;
889  off2 = 2;
890  pose1 = mesh_data_->box_offset_.y();
891  pose2 = mesh_data_->box_offset_.z();
892  cyl_length = xdim;
893  }
894  else if (ydim > zdim)
895  {
896  off1 = 0;
897  off2 = 2;
898  pose1 = mesh_data_->box_offset_.x();
899  pose2 = mesh_data_->box_offset_.z();
900  cyl_length = ydim;
901  }
902  else
903  {
904  off1 = 0;
905  off2 = 1;
906  pose1 = mesh_data_->box_offset_.x();
907  pose2 = mesh_data_->box_offset_.y();
908  cyl_length = zdim;
909  }
910 
911  /* compute convex hull */
912  coordT* points = (coordT*)calloc(mesh->vertex_count * 3, sizeof(coordT));
913  for (unsigned int i = 0; i < mesh->vertex_count; ++i)
914  {
915  points[3 * i + 0] = (coordT)mesh->vertices[3 * i + 0];
916  points[3 * i + 1] = (coordT)mesh->vertices[3 * i + 1];
917  points[3 * i + 2] = (coordT)mesh->vertices[3 * i + 2];
918 
919  double dista = mesh->vertices[3 * i + off1] - pose1;
920  double distb = mesh->vertices[3 * i + off2] - pose2;
921  double dist = sqrt(((dista * dista) + (distb * distb)));
922  if (dist > maxdist)
923  maxdist = dist;
924  }
925  mesh_data_->bounding_cylinder_.radius = maxdist;
926  mesh_data_->bounding_cylinder_.length = cyl_length;
927 
928  static FILE* null = fopen("/dev/null", "w");
929 
930  char flags[] = "qhull Tv Qt";
931  int exitcode = qh_new_qhull(3, mesh->vertex_count, points, true, flags, null, null);
932 
933  if (exitcode != 0)
934  {
935  CONSOLE_BRIDGE_logWarn("Convex hull creation failed");
936  qh_freeqhull(!qh_ALL);
937  int curlong, totlong;
938  qh_memfreeshort(&curlong, &totlong);
939  return;
940  }
941 
942  int num_facets = qh num_facets;
943 
944  int num_vertices = qh num_vertices;
945  mesh_data_->vertices_.reserve(num_vertices);
946  Eigen::Vector3d sum(0, 0, 0);
947 
948  // necessary for FORALLvertices
949  std::map<unsigned int, unsigned int> qhull_vertex_table;
950  vertexT* vertex;
951  FORALLvertices
952  {
953  Eigen::Vector3d vert(vertex->point[0], vertex->point[1], vertex->point[2]);
954  qhull_vertex_table[vertex->id] = mesh_data_->vertices_.size();
955  sum += vert;
956  mesh_data_->vertices_.push_back(vert);
957  }
958 
959  mesh_data_->mesh_center_ = sum / (double)(num_vertices);
960  for (unsigned int j = 0; j < mesh_data_->vertices_.size(); ++j)
961  {
962  double dist = (mesh_data_->vertices_[j] - mesh_data_->mesh_center_).squaredNorm();
963  if (dist > mesh_data_->mesh_radiusB_)
964  mesh_data_->mesh_radiusB_ = dist;
965  }
966 
967  mesh_data_->mesh_radiusB_ = sqrt(mesh_data_->mesh_radiusB_);
968  mesh_data_->triangles_.reserve(num_facets);
969 
970  // HACK: only needed for ABI compatibility with melodic
971  std::map<size_t, size_t>& triangle_for_plane = detail::getTriangleForPlane(this);
972  triangle_for_plane.clear();
973 
974  // neccessary for qhull macro
975  facetT* facet;
976  FORALLfacets
977  {
978  Eigen::Vector4d planeEquation(facet->normal[0], facet->normal[1], facet->normal[2], facet->offset);
979  if (!mesh_data_->planes_.empty())
980  {
981  // filter equal planes - assuming same ones follow each other
982  if ((planeEquation - mesh_data_->planes_.back()).cwiseAbs().maxCoeff() > 1e-6) // max diff to last
983  mesh_data_->planes_.push_back(planeEquation);
984  }
985  else
986  {
987  mesh_data_->planes_.push_back(planeEquation);
988  }
989 
990  // Needed by FOREACHvertex_i_
991  int vertex_n, vertex_i;
992  FOREACHvertex_i_((*facet).vertices)
993  {
994  mesh_data_->triangles_.push_back(qhull_vertex_table[vertex->id]);
995  }
996 
997  mesh_data_->plane_for_triangle_[(mesh_data_->triangles_.size() - 1) / 3] = mesh_data_->planes_.size() - 1;
998  triangle_for_plane[mesh_data_->planes_.size() - 1] = (mesh_data_->triangles_.size() - 1) / 3;
999  }
1000  qh_freeqhull(!qh_ALL);
1001  int curlong, totlong;
1002  qh_memfreeshort(&curlong, &totlong);
1003 }
1004 
1005 std::vector<double> bodies::ConvexMesh::getDimensions() const
1006 {
1007  return {};
1008 }
1009 
1010 std::vector<double> bodies::ConvexMesh::getScaledDimensions() const
1011 {
1012  return {};
1013 }
1014 
1016 {
1017  // compute the scaled vertices, if needed
1018  if (padding_ == 0.0 && scale_ == 1.0)
1019  {
1020  scaled_vertices_ = &mesh_data_->vertices_;
1021  return;
1022  }
1023 
1024  if (!scaled_vertices_storage_)
1025  scaled_vertices_storage_.reset(new EigenSTL::vector_Vector3d());
1026  scaled_vertices_ = scaled_vertices_storage_.get();
1027  scaled_vertices_storage_->resize(mesh_data_->vertices_.size());
1028  // project vertices along the vertex - center line to the scaled and padded plane
1029  // take the average of all tri's planes around that vertex as the result
1030  // is not unique
1031 
1032  // First figure out, which tris are connected to each vertex
1033  std::map<unsigned int, std::vector<unsigned int>> vertex_to_tris;
1034  for (unsigned int i = 0; i < mesh_data_->triangles_.size() / 3; ++i)
1035  {
1036  vertex_to_tris[mesh_data_->triangles_[3 * i + 0]].push_back(i);
1037  vertex_to_tris[mesh_data_->triangles_[3 * i + 1]].push_back(i);
1038  vertex_to_tris[mesh_data_->triangles_[3 * i + 2]].push_back(i);
1039  }
1040 
1041  for (unsigned int i = 0; i < mesh_data_->vertices_.size(); ++i)
1042  {
1043  Eigen::Vector3d v(mesh_data_->vertices_[i] - mesh_data_->mesh_center_);
1044  EigenSTL::vector_Vector3d projected_vertices;
1045  for (unsigned int t : vertex_to_tris[i])
1046  {
1047  const Eigen::Vector4d& plane = mesh_data_->planes_[mesh_data_->plane_for_triangle_[t]];
1048  Eigen::Vector3d plane_normal(plane.x(), plane.y(), plane.z());
1049  double d_scaled_padded =
1050  scale_ * plane.w() - (1 - scale_) * mesh_data_->mesh_center_.dot(plane_normal) - padding_;
1051 
1052  // intersect vert - center with scaled/padded plane equation
1053  double denom = v.dot(plane_normal);
1054  if (fabs(denom) < 1e-3)
1055  continue;
1056  double lambda = (-mesh_data_->mesh_center_.dot(plane_normal) - d_scaled_padded) / denom;
1057  Eigen::Vector3d vert_on_plane = v * lambda + mesh_data_->mesh_center_;
1058  projected_vertices.push_back(vert_on_plane);
1059  }
1060  if (projected_vertices.empty())
1061  {
1062  double l = v.norm();
1063  scaled_vertices_storage_->at(i) =
1064  mesh_data_->mesh_center_ + v * (scale_ + (l > detail::ZERO ? padding_ / l : 0.0));
1065  }
1066  else
1067  {
1068  Eigen::Vector3d sum(0, 0, 0);
1069  for (const Eigen::Vector3d& vertex : projected_vertices)
1070  {
1071  sum += vertex;
1072  }
1073  sum /= projected_vertices.size();
1074  scaled_vertices_storage_->at(i) = sum;
1075  }
1076  }
1077 }
1078 
1080 {
1081  if (!mesh_data_)
1082  return;
1083  Eigen::Isometry3d pose = pose_;
1084  pose.translation() = Eigen::Vector3d(pose_ * mesh_data_->box_offset_);
1085 
1086  shapes::Box box_shape(mesh_data_->box_size_.x(), mesh_data_->box_size_.y(), mesh_data_->box_size_.z());
1087  bounding_box_.setPoseDirty(pose);
1088  // The real effect of padding will most likely be smaller due to the mesh padding algorithm, but in "worst case" it
1089  // can inflate the primitive bounding box by the padding_ value.
1090  bounding_box_.setPaddingDirty(padding_);
1091  bounding_box_.setScaleDirty(scale_);
1092  bounding_box_.setDimensionsDirty(&box_shape);
1093  bounding_box_.updateInternalData();
1094 
1095  i_pose_ = pose_.inverse();
1096  center_ = pose_ * mesh_data_->mesh_center_;
1097  radiusB_ = mesh_data_->mesh_radiusB_ * scale_ + padding_;
1098  radiusBSqr_ = radiusB_ * radiusB_;
1099 
1100  // compute the scaled vertices, if needed
1101  if (padding_ == 0.0 && scale_ == 1.0)
1102  scaled_vertices_ = &mesh_data_->vertices_;
1103  else
1104  {
1105  if (!scaled_vertices_storage_)
1106  scaled_vertices_storage_.reset(new EigenSTL::vector_Vector3d());
1107  scaled_vertices_ = scaled_vertices_storage_.get();
1108  scaled_vertices_storage_->resize(mesh_data_->vertices_.size());
1109  for (unsigned int i = 0; i < mesh_data_->vertices_.size(); ++i)
1110  {
1111  Eigen::Vector3d v(mesh_data_->vertices_[i] - mesh_data_->mesh_center_);
1112  double l = v.norm();
1113  scaled_vertices_storage_->at(i) =
1114  mesh_data_->mesh_center_ + v * (scale_ + (l > detail::ZERO ? padding_ / l : 0.0));
1115  }
1116  }
1117 }
1118 const std::vector<unsigned int>& bodies::ConvexMesh::getTriangles() const
1119 {
1120  static const std::vector<unsigned int> empty;
1121  return mesh_data_ ? mesh_data_->triangles_ : empty;
1122 }
1123 
1125 {
1126  static const EigenSTL::vector_Vector3d empty;
1127  return mesh_data_ ? mesh_data_->vertices_ : empty;
1128 }
1129 
1131 {
1132  return scaled_vertices_ ? *scaled_vertices_ : getVertices();
1133 }
1134 
1136 {
1137  static const EigenSTL::vector_Vector4d empty;
1138  return mesh_data_ ? mesh_data_->planes_ : empty;
1139 }
1140 
1141 std::shared_ptr<bodies::Body> bodies::ConvexMesh::cloneAt(const Eigen::Isometry3d& pose, double padding,
1142  double scale) const
1143 {
1144  auto m = std::allocate_shared<ConvexMesh>(Eigen::aligned_allocator<ConvexMesh>());
1145  m->mesh_data_ = mesh_data_;
1146  m->padding_ = padding;
1147  m->scale_ = scale;
1148  m->pose_ = pose;
1149  m->updateInternalData();
1150  return m;
1151 }
1152 
1154 {
1155  sphere.center = center_;
1156  sphere.radius = radiusB_;
1157 }
1158 
1160 {
1161  // the padding contibution might be smaller in reality, but we want to get it right for the worst case
1162  cylinder.length = mesh_data_ ? mesh_data_->bounding_cylinder_.length * scale_ + 2 * padding_ : 0.0;
1163  cylinder.radius = mesh_data_ ? mesh_data_->bounding_cylinder_.radius * scale_ + padding_ : 0.0;
1164  // need to do rotation correctly to get pose, which bounding box does
1165  BoundingCylinder cyl;
1166  bounding_box_.computeBoundingCylinder(cyl);
1167  cylinder.pose = cyl.pose;
1168 }
1169 
1171 {
1172  bbox.setEmpty();
1173 
1174  bounding_box_.computeBoundingBox(bbox);
1175 }
1176 
1178 {
1179  bounding_box_.computeBoundingBox(bbox);
1180 }
1181 
1182 bool bodies::ConvexMesh::isPointInsidePlanes(const Eigen::Vector3d& point) const
1183 {
1184  unsigned int numplanes = mesh_data_->planes_.size();
1185  const std::map<size_t, size_t>& triangle_for_plane = detail::getTriangleForPlane(this);
1186  for (unsigned int i = 0; i < numplanes; ++i)
1187  {
1188  const Eigen::Vector4d& plane = mesh_data_->planes_[i];
1189  Eigen::Vector3d plane_vec(plane.x(), plane.y(), plane.z());
1190  // w() needs to be recomputed from a scaled vertex as normally it refers to the unscaled plane
1191  // we also cannot simply subtract padding_ from it, because padding of the points on the plane causes a different
1192  // effect than adding padding along this plane's normal (padding effect is direction-dependent)
1193  const auto scaled_point_on_plane = scaled_vertices_->at(mesh_data_->triangles_[3 * triangle_for_plane.at(i)]);
1194  const double w_scaled_padded = -plane_vec.dot(scaled_point_on_plane);
1195  const double dist = plane_vec.dot(point) + w_scaled_padded - detail::ZERO;
1196  if (dist > 0.0)
1197  return false;
1198  }
1199  return true;
1200 }
1201 
1202 unsigned int bodies::ConvexMesh::countVerticesBehindPlane(const Eigen::Vector4f& planeNormal) const
1203 {
1204  unsigned int numvertices = mesh_data_->vertices_.size();
1205  unsigned int result = 0;
1206  for (unsigned int i = 0; i < numvertices; ++i)
1207  {
1208  Eigen::Vector3d plane_vec(planeNormal.x(), planeNormal.y(), planeNormal.z());
1209  double dist = plane_vec.dot(mesh_data_->vertices_[i]) + planeNormal.w() - 1e-6;
1210  if (dist > 0.0)
1211  result++;
1212  }
1213  return result;
1214 }
1215 
1217 {
1218  double volume = 0.0;
1219  if (mesh_data_)
1220  for (unsigned int i = 0; i < mesh_data_->triangles_.size() / 3; ++i)
1221  {
1222  const Eigen::Vector3d& v1 = mesh_data_->vertices_[mesh_data_->triangles_[3 * i + 0]];
1223  const Eigen::Vector3d& v2 = mesh_data_->vertices_[mesh_data_->triangles_[3 * i + 1]];
1224  const Eigen::Vector3d& v3 = mesh_data_->vertices_[mesh_data_->triangles_[3 * i + 2]];
1225  volume += v1.x() * v2.y() * v3.z() + v2.x() * v3.y() * v1.z() + v3.x() * v1.y() * v2.z() -
1226  v1.x() * v3.y() * v2.z() - v2.x() * v1.y() * v3.z() - v3.x() * v2.y() * v1.z();
1227  }
1228  return fabs(volume) / 6.0;
1229 }
1230 
1231 bool bodies::ConvexMesh::intersectsRay(const Eigen::Vector3d& origin, const Eigen::Vector3d& dir,
1232  EigenSTL::vector_Vector3d* intersections, unsigned int count) const
1233 {
1234  // this is faster than always calling dir.normalized() in case the vector is already unit
1235  const Eigen::Vector3d dirNorm = normalize(dir);
1236 
1237  if (!mesh_data_)
1238  return false;
1239  if (detail::distanceSQR(center_, origin, dirNorm) > radiusBSqr_)
1240  return false;
1241  if (!bounding_box_.intersectsRay(origin, dirNorm))
1242  return false;
1243 
1244  // transform the ray into the coordinate frame of the mesh
1245  Eigen::Vector3d orig(i_pose_ * origin);
1246  Eigen::Vector3d dr(i_pose_.linear() * dirNorm);
1247 
1248  std::vector<detail::intersc> ipts;
1249 
1250  bool result = false;
1251 
1252  // for each triangle
1253  const auto nt = mesh_data_->triangles_.size() / 3;
1254  for (size_t i = 0; i < nt; ++i)
1255  {
1256  Eigen::Vector3d vec(mesh_data_->planes_[mesh_data_->plane_for_triangle_[i]].x(),
1257  mesh_data_->planes_[mesh_data_->plane_for_triangle_[i]].y(),
1258  mesh_data_->planes_[mesh_data_->plane_for_triangle_[i]].z());
1259 
1260  const double tmp = vec.dot(dr);
1261  if (fabs(tmp) > detail::ZERO)
1262  {
1263  // planes_[...].w() corresponds to the unscaled mesh, so we need to compute it ourselves
1264  const double w_scaled_padded = vec.dot(scaled_vertices_->at(mesh_data_->triangles_[3 * i]));
1265  const double t = -(vec.dot(orig) + w_scaled_padded) / tmp;
1266  if (t > 0.0)
1267  {
1268  const auto i3 = 3 * i;
1269  const auto v1 = mesh_data_->triangles_[i3 + 0];
1270  const auto v2 = mesh_data_->triangles_[i3 + 1];
1271  const auto v3 = mesh_data_->triangles_[i3 + 2];
1272 
1273  const Eigen::Vector3d& a = scaled_vertices_->at(v1);
1274  const Eigen::Vector3d& b = scaled_vertices_->at(v2);
1275  const Eigen::Vector3d& c = scaled_vertices_->at(v3);
1276 
1277  Eigen::Vector3d cb(c - b);
1278  Eigen::Vector3d ab(a - b);
1279 
1280  // intersection of the plane defined by the triangle and the ray
1281  Eigen::Vector3d P(orig + dr * t);
1282 
1283  // check if it is inside the triangle
1284  Eigen::Vector3d pb(P - b);
1285  Eigen::Vector3d c1(cb.cross(pb));
1286  Eigen::Vector3d c2(cb.cross(ab));
1287  if (c1.dot(c2) < 0.0)
1288  continue;
1289 
1290  Eigen::Vector3d ca(c - a);
1291  Eigen::Vector3d pa(P - a);
1292  Eigen::Vector3d ba(-ab);
1293 
1294  c1 = ca.cross(pa);
1295  c2 = ca.cross(ba);
1296  if (c1.dot(c2) < 0.0)
1297  continue;
1298 
1299  c1 = ba.cross(pa);
1300  c2 = ba.cross(ca);
1301 
1302  if (c1.dot(c2) < 0.0)
1303  continue;
1304 
1305  result = true;
1306  if (intersections)
1307  {
1308  detail::intersc ip(origin + dirNorm * t, t);
1309  ipts.push_back(ip);
1310  }
1311  else
1312  break;
1313  }
1314  }
1315  }
1316 
1317  if (result && intersections)
1318  {
1319  // If a ray hits exactly the boundary between two triangles, it is reported twice;
1320  // We only want return the intersection once; thus we need to filter them.
1321  detail::filterIntersections(ipts, intersections, count);
1322  }
1323 
1324  return result;
1325 }
1326 
1328 {
1329  // HACK: only needed for ABI compatibility with melodic
1330  {
1331  std::lock_guard<std::mutex> lock(detail::g_triangle_for_plane_mutex);
1332  detail::g_triangle_for_plane_.erase(this);
1333  }
1334 }
1335 
1337 {
1338 }
1339 
1340 bodies::BodyVector::BodyVector(const std::vector<shapes::Shape*>& shapes, const EigenSTL::vector_Isometry3d& poses,
1341  double padding)
1342 {
1343  for (unsigned int i = 0; i < shapes.size(); i++)
1344  addBody(shapes[i], poses[i], padding);
1345 }
1346 
1348 {
1349  clear();
1350 }
1351 
1353 {
1354  for (auto& body : bodies_)
1355  delete body;
1356  bodies_.clear();
1357 }
1358 
1360 {
1361  bodies_.push_back(body);
1362  BoundingSphere sphere;
1363  body->computeBoundingSphere(sphere);
1364 }
1365 
1366 void bodies::BodyVector::addBody(const shapes::Shape* shape, const Eigen::Isometry3d& pose, double padding)
1367 {
1369  body->setPoseDirty(pose);
1370  body->setPaddingDirty(padding);
1371  body->updateInternalData();
1372  addBody(body);
1373 }
1374 
1375 std::size_t bodies::BodyVector::getCount() const
1376 {
1377  return bodies_.size();
1378 }
1379 
1380 void bodies::BodyVector::setPose(unsigned int i, const Eigen::Isometry3d& pose)
1381 {
1382  if (i >= bodies_.size())
1383  {
1384  CONSOLE_BRIDGE_logError("There is no body at index %u", i);
1385  return;
1386  }
1387 
1388  bodies_[i]->setPose(pose);
1389 }
1390 
1391 const bodies::Body* bodies::BodyVector::getBody(unsigned int i) const
1392 {
1393  if (i >= bodies_.size())
1394  {
1395  CONSOLE_BRIDGE_logError("There is no body at index %u", i);
1396  return nullptr;
1397  }
1398  else
1399  return bodies_[i];
1400 }
1401 
1402 bool bodies::BodyVector::containsPoint(const Eigen::Vector3d& p, std::size_t& index, bool verbose) const
1403 {
1404  for (std::size_t i = 0; i < bodies_.size(); ++i)
1405  if (bodies_[i]->containsPoint(p, verbose))
1406  {
1407  index = i;
1408  return true;
1409  }
1410  return false;
1411 }
1412 
1413 bool bodies::BodyVector::containsPoint(const Eigen::Vector3d& p, bool verbose) const
1414 {
1415  std::size_t dummy;
1416  return containsPoint(p, dummy, verbose);
1417 }
1418 
1419 bool bodies::BodyVector::intersectsRay(const Eigen::Vector3d& origin, const Eigen::Vector3d& dir, std::size_t& index,
1420  EigenSTL::vector_Vector3d* intersections, unsigned int count) const
1421 {
1422  for (std::size_t i = 0; i < bodies_.size(); ++i)
1423  if (bodies_[i]->intersectsRay(origin, dir, intersections, count))
1424  {
1425  index = i;
1426  return true;
1427  }
1428  return false;
1429 }
d
Definition of various shapes. No properties such as position are included. These are simply the descr...
bool containsPoint(const Eigen::Vector3d &p, bool verbose=false) const override
Check if a point is inside the body. Surface points are included.
Definition: bodies.cpp:592
bool containsPoint(const Eigen::Vector3d &p, bool verbose=false) const override
Check if a point is inside the body. Surface points are included.
Definition: bodies.cpp:184
BodyPtr cloneAt(const Eigen::Isometry3d &pose, double padding, double scale) const override
Get a clone of this body, but one that is located at the pose pose and has possibly different passing...
Definition: bodies.cpp:1141
bool intersectsRay(const Eigen::Vector3d &origin, const Eigen::Vector3d &dir, EigenSTL::vector_Vector3d *intersections=nullptr, unsigned int count=0) const override
Check if a ray intersects the body, and find the set of intersections, in order, along the ray...
Definition: bodies.cpp:714
virtual bool samplePointInside(random_numbers::RandomNumberGenerator &rng, unsigned int max_attempts, Eigen::Vector3d &result) const
Sample a point that is included in the body using a given random number generator.
Definition: bodies.cpp:168
void updateInternalData() override
This function is called every time a change to the body is made, so that intermediate values stored f...
Definition: bodies.cpp:204
void computeBoundingSphere(const std::vector< const Body *> &bodies, BoundingSphere &mergedSphere)
Compute the bounding sphere for a set of bodies and store the resulting sphere in mergedSphere...
Definition of a cylinder Length is along z axis. Origin is at center of mass.
Definition: shapes.h:127
std::vector< double > getScaledDimensions() const override
Get the dimensions associated to this body (scaled and padded)
Definition: bodies.cpp:199
std::vector< Eigen::Vector3d, Eigen::aligned_allocator< Eigen::Vector3d > > vector_Vector3d
f
void filterIntersections(std::vector< detail::intersc > &ipts, EigenSTL::vector_Vector3d *intersections, const size_t count)
Take intersections points in ipts and add them to intersections, filtering duplicates.
Definition: bodies.cpp:117
bool intersectsRay(const Eigen::Vector3d &origin, const Eigen::Vector3d &dir, EigenSTL::vector_Vector3d *intersections=nullptr, unsigned int count=0) const override
Check if a ray intersects the body, and find the set of intersections, in order, along the ray...
Definition: bodies.cpp:1231
void computeBoundingBox(AABB &bbox) const override
Compute the axis-aligned bounding box for the body, in its current pose. Scaling and padding are acco...
Definition: bodies.cpp:1170
void computeBoundingCylinder(BoundingCylinder &cylinder) const override
Compute the bounding cylinder for the body, in its current pose. Scaling and padding are accounted fo...
Definition: bodies.cpp:1159
bool isPointInsidePlanes(const Eigen::Vector3d &point) const
Check if the point is inside all halfspaces this mesh consists of (mesh_data_->planes_).
Definition: bodies.cpp:1182
void updateInternalData() override
This function is called every time a change to the body is made, so that intermediate values stored f...
Definition: bodies.cpp:616
static std::map< size_t, size_t > & getTriangleForPlane(const ConvexMesh *mesh)
Definition: bodies.cpp:140
BodyPtr cloneAt(const Eigen::Isometry3d &pose, double padding, double scale) const override
Get a clone of this body, but one that is located at the pose pose and has possibly different passing...
Definition: bodies.cpp:425
const Body * getBody(unsigned int i) const
Get the ith body in the vector.
Definition: bodies.cpp:1391
static double distanceSQR(const Eigen::Vector3d &p, const Eigen::Vector3d &origin, const Eigen::Vector3d &dir)
Compute the square of the distance between a ray and a point Note: this requires &#39;dir&#39; to be normaliz...
Definition: bodies.cpp:82
intersc(const Eigen::Vector3d &_pt, const double _tm)
Definition: bodies.cpp:92
void computeBoundingBox(AABB &bbox) const override
Compute the axis-aligned bounding box for the body, in its current pose. Scaling and padding are acco...
Definition: bodies.cpp:702
const EigenSTL::vector_Vector3d & getScaledVertices() const
Definition: bodies.cpp:1130
double computeVolume() const override
Compute the volume of the body. This method includes changes induced by scaling and padding...
Definition: bodies.cpp:438
bool samplePointInside(random_numbers::RandomNumberGenerator &rng, unsigned int max_attempts, Eigen::Vector3d &result) const override
Sample a point that is included in the body using a given random number generator.
Definition: bodies.cpp:263
#define M_PI
BodyPtr cloneAt(const Eigen::Isometry3d &pose, double padding, double scale) const override
Get a clone of this body, but one that is located at the pose pose and has possibly different passing...
Definition: bodies.cpp:214
const EigenSTL::vector_Vector4d & getPlanes() const
Get the planes that define the convex shape.
Definition: bodies.cpp:1135
bool intersectsRay(const Eigen::Vector3d &origin, const Eigen::Vector3d &dir, std::size_t &index, EigenSTL::vector_Vector3d *intersections=nullptr, unsigned int count=0) const
Check if any of the bodies intersects the ray defined by origin and dir. When the first intersection ...
Definition: bodies.cpp:1419
bool samplePointInside(random_numbers::RandomNumberGenerator &rng, unsigned int max_attempts, Eigen::Vector3d &result) const override
Sample a point that is included in the body using a given random number generator.
Definition: bodies.cpp:409
BodyPtr cloneAt(const Eigen::Isometry3d &pose, double padding, double scale) const override
Get a clone of this body, but one that is located at the pose pose and has possibly different passing...
Definition: bodies.cpp:647
Eigen::Vector3d center
Definition: bodies.h:63
double y
Represents an oriented bounding box.
Definition: obb.h:53
void useDimensions(const shapes::Shape *shape) override
Depending on the shape, this function copies the relevant data to the body.
Definition: bodies.cpp:367
std::vector< double > getDimensions() const override
Get the radius of the sphere.
Definition: bodies.cpp:194
Definition of a sphere that bounds another object.
Definition: bodies.h:61
void setPoseAndExtents(const Eigen::Isometry3d &pose, const Eigen::Vector3d &extents)
Set both the pose and extents of the OBB.
Definition: obb.cpp:77
std::vector< double > getScaledDimensions() const override
Get the dimensions associated to this body (scaled and padded)
Definition: bodies.cpp:611
unsigned int countVerticesBehindPlane(const Eigen::Vector4f &planeNormal) const
(Used mainly for debugging) Count the number of vertices behind a plane
Definition: bodies.cpp:1202
unsigned int vertex_count
The number of available vertices.
Definition: shapes.h:343
unsigned int index
A basic definition of a shape. Shapes are considered centered at origin.
Definition: shapes.h:77
Definition of a convex mesh. Convex hull is computed for a given shape::Mesh.
Definition: bodies.h:504
virtual void updateInternalData()=0
This function is called every time a change to the body is made, so that intermediate values stored f...
void computeBoundingBox(AABB &bbox) const override
Compute the axis-aligned bounding box for the body, in its current pose. Scaling and padding are acco...
Definition: bodies.cpp:456
void computeBoundingSphere(BoundingSphere &sphere) const override
Compute the bounding radius for the body, in its current pose. Scaling and padding are accounted for...
Definition: bodies.cpp:665
Definition of a cylinder.
Definition: bodies.h:70
bool containsPoint(const Eigen::Vector3d &p, bool verbose=false) const
Check if any body in the vector contains the input point.
Definition: bodies.cpp:1413
Body * createBodyFromShape(const shapes::Shape *shape)
Create a body from a given shape.
void setPose(unsigned int i, const Eigen::Isometry3d &pose)
Set the pose of a particular body in the vector of bodies.
Definition: bodies.cpp:1380
#define ASSERT_ISOMETRY(transform)
Assert that the given transform is an isometry.
std::vector< double > getDimensions() const override
Returns an empty vector.
Definition: bodies.cpp:1005
void computeBoundingSphere(BoundingSphere &sphere) const override
Compute the bounding radius for the body, in its current pose. Scaling and padding are accounted for...
Definition: bodies.cpp:1153
double z
void useDimensions(const shapes::Shape *shape) override
Depending on the shape, this function copies the relevant data to the body.
Definition: bodies.cpp:825
double * vertices
The position for each vertex vertex k has values at index (3k, 3k+1, 3k+2) = (x,y,z)
Definition: shapes.h:347
void computeBoundingCylinder(BoundingCylinder &cylinder) const override
Compute the bounding cylinder for the body, in its current pose. Scaling and padding are accounted fo...
Definition: bodies.cpp:449
void clear()
Clear all bodies from the vector.
Definition: bodies.cpp:1352
std::size_t getCount() const
Get the number of bodies in this vector.
Definition: bodies.cpp:1375
double uniformReal(double lower_bound, double upper_bound)
Eigen::Vector3d normalize(const Eigen::Vector3d &dir)
Definition: bodies.cpp:151
void updateInternalData() override
This function is called every time a change to the body is made, so that intermediate values stored f...
Definition: bodies.cpp:383
void computeBoundingSphere(BoundingSphere &sphere) const override
Compute the bounding radius for the body, in its current pose. Scaling and padding are accounted for...
Definition: bodies.cpp:230
std::unordered_map< const ConvexMesh *, std::map< size_t, size_t > > g_triangle_for_plane_
Definition: bodies.cpp:138
std::vector< Eigen::Vector4d, Eigen::aligned_allocator< Eigen::Vector4d > > vector_Vector4d
bool intersectsRay(const Eigen::Vector3d &origin, const Eigen::Vector3d &dir, EigenSTL::vector_Vector3d *intersections=nullptr, unsigned int count=0) const override
Check if a ray intersects the body, and find the set of intersections, in order, along the ray...
Definition: bodies.cpp:478
Definition of a sphere.
Definition: shapes.h:106
void useDimensions(const shapes::Shape *shape) override
Depending on the shape, this function copies the relevant data to the body.
Definition: bodies.cpp:189
void computeBoundingBox(AABB &bbox) const override
Compute the axis-aligned bounding box for the body, in its current pose. Scaling and padding are acco...
Definition: bodies.cpp:243
void setPaddingDirty(double padd)
If the dimension of the body should be padded, this method sets the pading.
Definition: bodies.h:142
double computeVolume() const override
Compute the volume of the body. This method includes changes induced by scaling and padding...
Definition: bodies.cpp:660
void computeBoundingSphere(BoundingSphere &sphere) const override
Compute the bounding radius for the body, in its current pose. Scaling and padding are accounted for...
Definition: bodies.cpp:443
void correctVertexOrderFromPlanes()
Definition: bodies.cpp:802
void computeBoundingCylinder(BoundingCylinder &cylinder) const override
Compute the bounding cylinder for the body, in its current pose. Scaling and padding are accounted fo...
Definition: bodies.cpp:236
void extendWithTransformedBox(const Eigen::Isometry3d &transform, const Eigen::Vector3d &box)
Extend with a box transformed by the given transform.
Definition: aabb.cpp:44
virtual void computeBoundingSphere(BoundingSphere &sphere) const =0
Compute the bounding radius for the body, in its current pose. Scaling and padding are accounted for...
Definition of a triangle mesh By convention the "center" of the shape is at the origin. For a mesh this implies that the AABB of the mesh is centered at the origin. Some methods may not work with arbitrary meshes whose AABB is not centered at the origin. Padding is not applied to vertices plainly coordinate-wise, but instead the padding value is added to the length of the direction vector between centroid and each vertex.
Definition: shapes.h:281
Eigen::Isometry3d pose
Definition: bodies.h:72
Definition of a box Aligned with the XYZ axes.
Definition: shapes.h:226
bool containsPoint(const Eigen::Vector3d &p, bool verbose=false) const override
Check if a point is inside the body. Surface points are included.
Definition: bodies.cpp:788
void setDimensions(const shapes::Shape *shape)
Set the dimensions of the body (from corresponding shape)
Definition: bodies.cpp:162
void computeBoundingCylinder(BoundingCylinder &cylinder) const override
Compute the bounding cylinder for the body, in its current pose. Scaling and padding are accounted fo...
Definition: bodies.cpp:671
#define CONSOLE_BRIDGE_logError(fmt,...)
void setPoseDirty(const Eigen::Isometry3d &pose)
Set the pose of the body.
Definition: bodies.h:169
std::vector< double > getScaledDimensions() const override
Returns an empty vector.
Definition: bodies.cpp:1010
static const double ZERO
Definition: bodies.cpp:78
Represents an axis-aligned bounding box.
Definition: aabb.h:45
std::vector< Eigen::Isometry3d, Eigen::aligned_allocator< Eigen::Isometry3d > > vector_Isometry3d
bool samplePointInside(random_numbers::RandomNumberGenerator &rng, unsigned int max_attempts, Eigen::Vector3d &result) const override
Sample a point that is included in the body using a given random number generator.
Definition: bodies.cpp:584
std::mutex g_triangle_for_plane_mutex
Lock this mutex every time you work with g_triangle_for_plane_.
Definition: bodies.cpp:139
void addBody(Body *body)
Add a body.
Definition: bodies.cpp:1359
void updateInternalData() override
This function is called every time a change to the body is made, so that intermediate values stored f...
Definition: bodies.cpp:1079
bool intersectsRay(const Eigen::Vector3d &origin, const Eigen::Vector3d &dir, EigenSTL::vector_Vector3d *intersections=nullptr, unsigned int count=0) const override
Check if a ray intersects the body, and find the set of intersections, in order, along the ray...
Definition: bodies.cpp:286
~ConvexMesh() override
Definition: bodies.cpp:1327
double computeVolume() const override
Compute the volume of the body. This method includes changes induced by scaling and padding...
Definition: bodies.cpp:1216
double computeVolume() const override
Compute the volume of the body. This method includes changes induced by scaling and padding...
Definition: bodies.cpp:225
double x
r
const std::vector< unsigned int > & getTriangles() const
Definition: bodies.cpp:1118
bool containsPoint(const Eigen::Vector3d &p, bool verbose=false) const override
Check if a point is inside the body. Surface points are included.
Definition: bodies.cpp:347
bool operator()(const intersc &a, const intersc &b) const
Definition: bodies.cpp:105
A body is a shape + its pose. Point inclusion, ray intersection can be tested, volumes and bounding s...
Definition: bodies.h:89
std::vector< double > getDimensions() const override
Get the radius & length of the cylinder.
Definition: bodies.cpp:373
std::vector< double > getDimensions() const override
Get the length & width & height (x, y, z) of the box.
Definition: bodies.cpp:606
void computeScaledVerticesFromPlaneProjections()
Project the original vertex to the scaled and padded planes and average.
Definition: bodies.cpp:1015
void useDimensions(const shapes::Shape *shape) override
Depending on the shape, this function copies the relevant data to the body.
Definition: bodies.cpp:598
const EigenSTL::vector_Vector3d & getVertices() const
Definition: bodies.cpp:1124
std::vector< double > getScaledDimensions() const override
Get the dimensions associated to this body (scaled and padded)
Definition: bodies.cpp:378
This set of classes allows quickly detecting whether a given point is inside an object or not...
Definition: aabb.h:42
Eigen::Vector3d pt
Definition: bodies.cpp:96


geometric_shapes
Author(s): Ioan Sucan , Gil Jones
autogenerated on Fri Apr 14 2023 02:14:40