From 1f11ac4bd3fd03e5394611742b759f1501fc3f2d Mon Sep 17 00:00:00 2001 From: Florent Lamiraux <florent@laas.fr> Date: Sat, 15 Dec 2018 17:01:14 +0100 Subject: [PATCH] Migrate functions computing interactions between geometric shapes in a local header so that they can be called from another file. --- src/CMakeLists.txt | 1 + src/narrowphase/details.h | 2550 +++++++++++++++++++++++++++++++ src/narrowphase/narrowphase.cpp | 2498 +----------------------------- 3 files changed, 2571 insertions(+), 2478 deletions(-) create mode 100644 src/narrowphase/details.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e1605e5..bad2cde1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,7 @@ set(${LIBRARY_NAME}_SOURCES BV/OBB.cpp narrowphase/narrowphase.cpp narrowphase/gjk.cpp + narrowphase/details.h shape/geometric_shapes.cpp shape/geometric_shapes_utility.cpp distance_capsule_capsule.cpp diff --git a/src/narrowphase/details.h b/src/narrowphase/details.h new file mode 100644 index 00000000..7c50b733 --- /dev/null +++ b/src/narrowphase/details.h @@ -0,0 +1,2550 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2011-2014, Willow Garage, Inc. + * Copyright (c) 2014-2015, Open Source Robotics Foundation + * Copyright (c) 2018-2019, Centre National de la Recherche Scientifique + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Open Source Robotics Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** \author Jia Pan, Florent Lamiraux */ + +#ifndef HPP_FCL_SRC_NARROWPHASE_DETAILS_H +# define HPP_FCL_SRC_NARROWPHASE_DETAILS_H + +#include <hpp/fcl/collision_node.h> +#include <hpp/fcl/traversal/traversal_node_setup.h> +#include <hpp/fcl/narrowphase/narrowphase.h> + +namespace fcl { + namespace details + { + // Compute the point on a line segment that is the closest point on the + // segment to to another point. The code is inspired by the explanation + // given by Dan Sunday's page: + // http://geomalgorithms.com/a02-_lines.html + static inline void lineSegmentPointClosestToPoint (const Vec3f &p, const Vec3f &s1, const Vec3f &s2, Vec3f &sp) { + Vec3f v = s2 - s1; + Vec3f w = p - s1; + + FCL_REAL c1 = w.dot(v); + FCL_REAL c2 = v.dot(v); + + if (c1 <= 0) { + sp = s1; + } else if (c2 <= c1) { + sp = s2; + } else { + FCL_REAL b = c1/c2; + Vec3f Pb = s1 + v * b; + sp = Pb; + } + } + + inline bool sphereCapsuleIntersect + (const Sphere& s1, const Transform3f& tf1, + const Capsule& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal_) + { + Transform3f tf2_inv (tf2); + tf2_inv.inverse(); + + Vec3f pos1 (tf2.transform (Vec3f (0., 0., 0.5 * s2.lz))); // from distance function + Vec3f pos2 (tf2.transform (Vec3f (0., 0., -0.5 * s2.lz))); + Vec3f s_c = tf1.getTranslation (); + + Vec3f segment_point; + + lineSegmentPointClosestToPoint (s_c, pos1, pos2, segment_point); + Vec3f diff = s_c - segment_point; + + FCL_REAL diffN = diff.norm(); + FCL_REAL distance = diffN - s1.radius - s2.radius; + + if (distance > 0) + return false; + + // Vec3f normal (-diff.normalized()); + + if (distance < 0 && penetration_depth) + *penetration_depth = -distance; + + if (normal_) + *normal_ = -diff / diffN; + + if (contact_points) { + *contact_points = segment_point + diff * s2.radius; + } + + return true; + } + + inline bool sphereCapsuleDistance + (const Sphere& s1, const Transform3f& tf1, + const Capsule& s2, const Transform3f& tf2, + FCL_REAL& dist, Vec3f& p1, Vec3f& p2, Vec3f& normal) + { + Transform3f tf2_inv(tf2); + tf2_inv.inverse(); + + Vec3f pos1 (tf2.transform (Vec3f (0., 0., 0.5 * s2.lz))); + Vec3f pos2 (tf2.transform (Vec3f (0., 0., -0.5 * s2.lz))); + Vec3f s_c = tf1.getTranslation (); + + Vec3f segment_point; + + lineSegmentPointClosestToPoint (s_c, pos1, pos2, segment_point); + normal = segment_point - s_c; + FCL_REAL norm (normal.norm()); + dist = norm - s1.radius - s2.radius; + + FCL_REAL eps (std::numeric_limits<FCL_REAL>::epsilon()); + if (norm > eps) { + normal.normalize(); + } else { + normal << 1,0,0; + } + p1 = s_c + normal * s1.radius; + p2 = segment_point - normal * s2.radius; + + if(dist <= 0) { + p1 = p2 = .5 * (p1+p2); + return false; + } + return true; + } + + inline bool sphereSphereIntersect + (const Sphere& s1, const Transform3f& tf1, + const Sphere& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Vec3f diff = tf2.getTranslation() - tf1.getTranslation(); + FCL_REAL len = diff.norm(); + if(len > s1.radius + s2.radius) + return false; + + if(penetration_depth) + *penetration_depth = s1.radius + s2.radius - len; + + // If the centers of two sphere are at the same position, the normal is (0, 0, 0). + // Otherwise, normal is pointing from center of object 1 to center of object 2 + if(normal) + { + if(len > 0) + *normal = diff / len; + else + *normal = diff; + } + + if(contact_points) + *contact_points = tf1.getTranslation() + diff * s1.radius / (s1.radius + s2.radius); + + return true; + } + + + inline bool sphereSphereDistance(const Sphere& s1, const Transform3f& tf1, + const Sphere& s2, const Transform3f& tf2, + FCL_REAL& dist, Vec3f& p1, Vec3f& p2, + Vec3f& normal) + { + Vec3f o1 = tf1.getTranslation(); + Vec3f o2 = tf2.getTranslation(); + Vec3f diff = o1 - o2; + FCL_REAL len = diff.norm(); + normal = -diff/len; + dist = len - s1.radius - s2.radius; + + p1 = o1 - diff * (s1.radius / len); + p2 = o2 + diff * (s2.radius / len); + + return (dist >=0); + } + + /** \brief the minimum distance from a point to a line */ + inline FCL_REAL segmentSqrDistance + (const Vec3f& from, const Vec3f& to,const Vec3f& p, Vec3f& nearest) + { + Vec3f diff = p - from; + Vec3f v = to - from; + FCL_REAL t = v.dot(diff); + + if(t > 0) + { + FCL_REAL dotVV = v.dot(v); + if(t < dotVV) + { + t /= dotVV; + diff -= v * t; + } + else + { + t = 1; + diff -= v; + } + } + else + t = 0; + + nearest = from + v * t; + return diff.dot(diff); + } + + /// @brief Whether a point's projection is in a triangle + inline bool projectInTriangle (const Vec3f& p1, const Vec3f& p2, + const Vec3f& p3, const Vec3f& normal, + const Vec3f& p) + { + Vec3f edge1(p2 - p1); + Vec3f edge2(p3 - p2); + Vec3f edge3(p1 - p3); + + Vec3f p1_to_p(p - p1); + Vec3f p2_to_p(p - p2); + Vec3f p3_to_p(p - p3); + + Vec3f edge1_normal(edge1.cross(normal)); + Vec3f edge2_normal(edge2.cross(normal)); + Vec3f edge3_normal(edge3.cross(normal)); + + FCL_REAL r1, r2, r3; + r1 = edge1_normal.dot(p1_to_p); + r2 = edge2_normal.dot(p2_to_p); + r3 = edge3_normal.dot(p3_to_p); + if ( ( r1 > 0 && r2 > 0 && r3 > 0 ) || + ( r1 <= 0 && r2 <= 0 && r3 <= 0 ) ) + return true; + return false; + } + + // Intersection between sphere and triangle + // Sphere is in position tf1, Triangle is expressed in global frame + inline bool sphereTriangleIntersect + (const Sphere& s, const Transform3f& tf1, + const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, + FCL_REAL& distance, Vec3f& p1, Vec3f& p2, + Vec3f& normal_) + { + Vec3f normal = (P2 - P1).cross(P3 - P1); + normal.normalize(); + const Vec3f& center = tf1.getTranslation(); + const FCL_REAL& radius = s.radius; + assert (radius >= 0); + FCL_REAL eps (std::numeric_limits<FCL_REAL>::epsilon()); + FCL_REAL radius_with_threshold = radius + eps; + Vec3f p1_to_center = center - P1; + FCL_REAL distance_from_plane = p1_to_center.dot(normal); + + if(distance_from_plane < 0) + { + distance_from_plane *= -1; + normal *= -1; + } + + bool is_inside_contact_plane = (distance_from_plane < radius_with_threshold); + + bool has_contact = false; + Vec3f contact_point; + if(is_inside_contact_plane) + { + if(projectInTriangle(P1, P2, P3, normal, center)) + { + has_contact = true; + contact_point = center - normal * distance_from_plane; + } + else + { + FCL_REAL contact_capsule_radius_sqr = radius_with_threshold * radius_with_threshold; + Vec3f nearest_on_edge; + FCL_REAL distance_sqr; + distance_sqr = segmentSqrDistance(P1, P2, center, nearest_on_edge); + if(distance_sqr < contact_capsule_radius_sqr) + { + has_contact = true; + contact_point = nearest_on_edge; + } + + distance_sqr = segmentSqrDistance(P2, P3, center, nearest_on_edge); + if(distance_sqr < contact_capsule_radius_sqr) + { + has_contact = true; + contact_point = nearest_on_edge; + } + + distance_sqr = segmentSqrDistance(P3, P1, center, nearest_on_edge); + if(distance_sqr < contact_capsule_radius_sqr) + { + has_contact = true; + contact_point = nearest_on_edge; + } + } + } + + Vec3f center_to_contact = contact_point - center; + if(has_contact) + { + FCL_REAL distance_sqr = center_to_contact.squaredNorm(); + + if(distance_sqr < radius_with_threshold * radius_with_threshold) + { + if(distance_sqr > eps) + { + FCL_REAL dist = std::sqrt(distance_sqr); + normal_ = center_to_contact.normalized(); + p1 = p2 = contact_point; + distance = dist - radius; + } + else + { + normal_ = -normal; + p1 = p2 = contact_point; + distance = - radius; + } + } + } + else + { + Vec3f unit (center_to_contact.normalized ()); + p1 = center + radius * unit; + p2 = contact_point; + } + return has_contact; + } + + + inline bool sphereTriangleDistance + (const Sphere& sp, const Transform3f& tf, + const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, FCL_REAL* dist) + { + // from geometric tools, very different from the collision code. + + const Vec3f& center = tf.getTranslation(); + FCL_REAL radius = sp.radius; + Vec3f diff = P1 - center; + Vec3f edge0 = P2 - P1; + Vec3f edge1 = P3 - P1; + FCL_REAL a00 = edge0.squaredNorm(); + FCL_REAL a01 = edge0.dot(edge1); + FCL_REAL a11 = edge1.squaredNorm(); + FCL_REAL b0 = diff.dot(edge0); + FCL_REAL b1 = diff.dot(edge1); + FCL_REAL c = diff.squaredNorm(); + FCL_REAL det = fabs(a00*a11 - a01*a01); + FCL_REAL s = a01*b1 - a11*b0; + FCL_REAL t = a01*b0 - a00*b1; + + FCL_REAL sqr_dist; + + if(s + t <= det) + { + if(s < 0) + { + if(t < 0) // region 4 + { + if(b0 < 0) + { + t = 0; + if(-b0 >= a00) + { + s = 1; + sqr_dist = a00 + 2*b0 + c; + } + else + { + s = -b0/a00; + sqr_dist = b0*s + c; + } + } + else + { + s = 0; + if(b1 >= 0) + { + t = 0; + sqr_dist = c; + } + else if(-b1 >= a11) + { + t = 1; + sqr_dist = a11 + 2*b1 + c; + } + else + { + t = -b1/a11; + sqr_dist = b1*t + c; + } + } + } + else // region 3 + { + s = 0; + if(b1 >= 0) + { + t = 0; + sqr_dist = c; + } + else if(-b1 >= a11) + { + t = 1; + sqr_dist = a11 + 2*b1 + c; + } + else + { + t = -b1/a11; + sqr_dist = b1*t + c; + } + } + } + else if(t < 0) // region 5 + { + t = 0; + if(b0 >= 0) + { + s = 0; + sqr_dist = c; + } + else if(-b0 >= a00) + { + s = 1; + sqr_dist = a00 + 2*b0 + c; + } + else + { + s = -b0/a00; + sqr_dist = b0*s + c; + } + } + else // region 0 + { + // minimum at interior point + FCL_REAL inv_det = (1)/det; + s *= inv_det; + t *= inv_det; + sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; + } + } + else + { + FCL_REAL tmp0, tmp1, numer, denom; + + if(s < 0) // region 2 + { + tmp0 = a01 + b0; + tmp1 = a11 + b1; + if(tmp1 > tmp0) + { + numer = tmp1 - tmp0; + denom = a00 - 2*a01 + a11; + if(numer >= denom) + { + s = 1; + t = 0; + sqr_dist = a00 + 2*b0 + c; + } + else + { + s = numer/denom; + t = 1 - s; + sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; + } + } + else + { + s = 0; + if(tmp1 <= 0) + { + t = 1; + sqr_dist = a11 + 2*b1 + c; + } + else if(b1 >= 0) + { + t = 0; + sqr_dist = c; + } + else + { + t = -b1/a11; + sqr_dist = b1*t + c; + } + } + } + else if(t < 0) // region 6 + { + tmp0 = a01 + b1; + tmp1 = a00 + b0; + if(tmp1 > tmp0) + { + numer = tmp1 - tmp0; + denom = a00 - 2*a01 + a11; + if(numer >= denom) + { + t = 1; + s = 0; + sqr_dist = a11 + 2*b1 + c; + } + else + { + t = numer/denom; + s = 1 - t; + sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; + } + } + else + { + t = 0; + if(tmp1 <= 0) + { + s = 1; + sqr_dist = a00 + 2*b0 + c; + } + else if(b0 >= 0) + { + s = 0; + sqr_dist = c; + } + else + { + s = -b0/a00; + sqr_dist = b0*s + c; + } + } + } + else // region 1 + { + numer = a11 + b1 - a01 - b0; + if(numer <= 0) + { + s = 0; + t = 1; + sqr_dist = a11 + 2*b1 + c; + } + else + { + denom = a00 - 2*a01 + a11; + if(numer >= denom) + { + s = 1; + t = 0; + sqr_dist = a00 + 2*b0 + c; + } + else + { + s = numer/denom; + t = 1 - s; + sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; + } + } + } + } + + // Account for numerical round-off error. + if(sqr_dist < 0) + sqr_dist = 0; + + if(sqr_dist > radius * radius) + { + if(dist) *dist = std::sqrt(sqr_dist) - radius; + return true; + } + else + { + if(dist) *dist = -1; + return false; + } + } + + + inline bool sphereTriangleDistance + (const Sphere& sp, const Transform3f& tf, const Vec3f& P1, + const Vec3f& P2, const Vec3f& P3, FCL_REAL* dist, Vec3f* p1, Vec3f* p2) + { + if(p1 || p2) + { + Vec3f o = tf.getTranslation(); + Project::ProjectResult result; + result = Project::projectTriangle(P1, P2, P3, o); + if(result.sqr_distance > sp.radius * sp.radius) + { + if(dist) *dist = std::sqrt(result.sqr_distance) - sp.radius; + Vec3f project_p = P1 * result.parameterization[0] + P2 * result.parameterization[1] + P3 * result.parameterization[2]; + Vec3f dir = o - project_p; + dir.normalize(); + if(p1) { *p1 = o - dir * sp.radius; } + if(p2) *p2 = project_p; + return true; + } + else + return false; + } + else + { + return sphereTriangleDistance(sp, tf, P1, P2, P3, dist); + } + } + + + inline bool sphereTriangleDistance + (const Sphere& sp, const Transform3f& tf1, const Vec3f& P1, + const Vec3f& P2, const Vec3f& P3, const Transform3f& tf2, + FCL_REAL* dist, Vec3f* p1, Vec3f* p2) + { + bool res = details::sphereTriangleDistance(sp, tf1, tf2.transform(P1), tf2.transform(P2), tf2.transform(P3), dist, p1, p2); + return res; + } + + + + struct ContactPoint + { + Vec3f normal; + Vec3f point; + FCL_REAL depth; + ContactPoint(const Vec3f& n, const Vec3f& p, FCL_REAL d) : normal(n), point(p), depth(d) {} + }; + + + static inline void lineClosestApproach(const Vec3f& pa, const Vec3f& ua, + const Vec3f& pb, const Vec3f& ub, + FCL_REAL* alpha, FCL_REAL* beta) + { + Vec3f p = pb - pa; + FCL_REAL uaub = ua.dot(ub); + FCL_REAL q1 = ua.dot(p); + FCL_REAL q2 = -ub.dot(p); + FCL_REAL d = 1 - uaub * uaub; + if(d <= (FCL_REAL)(0.0001f)) + { + *alpha = 0; + *beta = 0; + } + else + { + d = 1 / d; + *alpha = (q1 + uaub * q2) * d; + *beta = (uaub * q1 + q2) * d; + } + } + + // find all the intersection points between the 2D rectangle with vertices + // at (+/-h[0],+/-h[1]) and the 2D quadrilateral with vertices (p[0],p[1]), + // (p[2],p[3]),(p[4],p[5]),(p[6],p[7]). + // + // the intersection points are returned as x,y pairs in the 'ret' array. + // the number of intersection points is returned by the function (this will + // be in the range 0 to 8). + static int intersectRectQuad2(FCL_REAL h[2], FCL_REAL p[8], FCL_REAL ret[16]) + { + // q (and r) contain nq (and nr) coordinate points for the current (and + // chopped) polygons + int nq = 4, nr = 0; + FCL_REAL buffer[16]; + FCL_REAL* q = p; + FCL_REAL* r = ret; + for(int dir = 0; dir <= 1; ++dir) + { + // direction notation: xy[0] = x axis, xy[1] = y axis + for(int sign = -1; sign <= 1; sign += 2) + { + // chop q along the line xy[dir] = sign*h[dir] + FCL_REAL* pq = q; + FCL_REAL* pr = r; + nr = 0; + for(int i = nq; i > 0; --i) + { + // go through all points in q and all lines between adjacent points + if(sign * pq[dir] < h[dir]) + { + // this point is inside the chopping line + pr[0] = pq[0]; + pr[1] = pq[1]; + pr += 2; + nr++; + if(nr & 8) + { + q = r; + goto done; + } + } + FCL_REAL* nextq = (i > 1) ? pq+2 : q; + if((sign*pq[dir] < h[dir]) ^ (sign*nextq[dir] < h[dir])) + { + // this line crosses the chopping line + pr[1-dir] = pq[1-dir] + (nextq[1-dir]-pq[1-dir]) / + (nextq[dir]-pq[dir]) * (sign*h[dir]-pq[dir]); + pr[dir] = sign*h[dir]; + pr += 2; + nr++; + if(nr & 8) + { + q = r; + goto done; + } + } + pq += 2; + } + q = r; + r = (q == ret) ? buffer : ret; + nq = nr; + } + } + + done: + if(q != ret) memcpy(ret, q, nr*2*sizeof(FCL_REAL)); + return nr; + } + + // given n points in the plane (array p, of size 2*n), generate m points that + // best represent the whole set. the definition of 'best' here is not + // predetermined - the idea is to select points that give good box-box + // collision detection behavior. the chosen point indexes are returned in the + // array iret (of size m). 'i0' is always the first entry in the array. + // n must be in the range [1..8]. m must be in the range [1..n]. i0 must be + // in the range [0..n-1]. + static inline void cullPoints2(int n, FCL_REAL p[], int m, int i0, int iret[]) + { + // compute the centroid of the polygon in cx,cy + FCL_REAL a, cx, cy, q; + switch(n) + { + case 1: + cx = p[0]; + cy = p[1]; + break; + case 2: + cx = 0.5 * (p[0] + p[2]); + cy = 0.5 * (p[1] + p[3]); + break; + default: + a = 0; + cx = 0; + cy = 0; + for(int i = 0; i < n-1; ++i) + { + q = p[i*2]*p[i*2+3] - p[i*2+2]*p[i*2+1]; + a += q; + cx += q*(p[i*2]+p[i*2+2]); + cy += q*(p[i*2+1]+p[i*2+3]); + } + q = p[n*2-2]*p[1] - p[0]*p[n*2-1]; + if(std::abs(a+q) > std::numeric_limits<FCL_REAL>::epsilon()) + a = 1/(3*(a+q)); + else + a= 1e18f; + + cx = a*(cx + q*(p[n*2-2]+p[0])); + cy = a*(cy + q*(p[n*2-1]+p[1])); + } + + + // compute the angle of each point w.r.t. the centroid + FCL_REAL A[8]; + for(int i = 0; i < n; ++i) + A[i] = atan2(p[i*2+1]-cy,p[i*2]-cx); + + // search for points that have angles closest to A[i0] + i*(2*pi/m). + int avail[8]; + for(int i = 0; i < n; ++i) avail[i] = 1; + avail[i0] = 0; + iret[0] = i0; + iret++; + const double pi = boost::math::constants::pi<FCL_REAL>(); + for(int j = 1; j < m; ++j) + { + a = j*(2*pi/m) + A[i0]; + if (a > pi) a -= 2*pi; + FCL_REAL maxdiff= 1e9, diff; + + *iret = i0; // iret is not allowed to keep this value, but it sometimes does, when diff=#QNAN0 + for(int i = 0; i < n; ++i) + { + if(avail[i]) + { + diff = std::abs(A[i]-a); + if(diff > pi) diff = 2*pi - diff; + if(diff < maxdiff) + { + maxdiff = diff; + *iret = i; + } + } + } + avail[*iret] = 0; + iret++; + } + } + + + + inline int boxBox2(const Vec3f& side1, const Matrix3f& R1, const Vec3f& T1, + const Vec3f& side2, const Matrix3f& R2, const Vec3f& T2, + Vec3f& normal, FCL_REAL* depth, int* return_code, + int maxc, std::vector<ContactPoint>& contacts) + { + const FCL_REAL fudge_factor = FCL_REAL(1.05); + Vec3f normalC; + FCL_REAL s, s2, l; + int invert_normal, code; + + Vec3f p (T2 - T1); // get vector from centers of box 1 to box 2, relative to box 1 + Vec3f pp (R1.transpose() * p); // get pp = p relative to body 1 + + // get side lengths / 2 + Vec3f A (side1 * 0.5); + Vec3f B (side2 * 0.5); + + // Rij is R1'*R2, i.e. the relative rotation between R1 and R2 + Matrix3f R (R1.transpose() * R2); + Matrix3f Q (R.cwiseAbs()); + + + // for all 15 possible separating axes: + // * see if the axis separates the boxes. if so, return 0. + // * find the depth of the penetration along the separating axis (s2) + // * if this is the largest depth so far, record it. + // the normal vector will be set to the separating axis with the smallest + // depth. note: normalR is set to point to a column of R1 or R2 if that is + // the smallest depth normal so far. otherwise normalR is 0 and normalC is + // set to a vector relative to body 1. invert_normal is 1 if the sign of + // the normal should be flipped. + + int best_col_id = -1; + const Matrix3f* normalR = 0; + FCL_REAL tmp = 0; + + s = - std::numeric_limits<FCL_REAL>::max(); + invert_normal = 0; + code = 0; + + // separating axis = u1, u2, u3 + tmp = pp[0]; + s2 = std::abs(tmp) - (Q.row(0).dot(B) + A[0]); + if(s2 > 0) { *return_code = 0; return 0; } + if(s2 > s) + { + s = s2; + best_col_id = 0; + normalR = &R1; + invert_normal = (tmp < 0); + code = 1; + } + + tmp = pp[1]; + s2 = std::abs(tmp) - (Q.row(1).dot(B) + A[1]); + if(s2 > 0) { *return_code = 0; return 0; } + if(s2 > s) + { + s = s2; + best_col_id = 1; + normalR = &R1; + invert_normal = (tmp < 0); + code = 2; + } + + tmp = pp[2]; + s2 = std::abs(tmp) - (Q.row(2).dot(B) + A[2]); + if(s2 > 0) { *return_code = 0; return 0; } + if(s2 > s) + { + s = s2; + best_col_id = 2; + normalR = &R1; + invert_normal = (tmp < 0); + code = 3; + } + + // separating axis = v1, v2, v3 + tmp = R2.col(0).dot(p); + s2 = std::abs(tmp) - (Q.col(0).dot(A) + B[0]); + if(s2 > 0) { *return_code = 0; return 0; } + if(s2 > s) + { + s = s2; + best_col_id = 0; + normalR = &R2; + invert_normal = (tmp < 0); + code = 4; + } + + tmp = R2.col(1).dot(p); + s2 = std::abs(tmp) - (Q.col(1).dot(A) + B[1]); + if(s2 > 0) { *return_code = 0; return 0; } + if(s2 > s) + { + s = s2; + best_col_id = 1; + normalR = &R2; + invert_normal = (tmp < 0); + code = 5; + } + + tmp = R2.col(2).dot(p); + s2 = std::abs(tmp) - (Q.col(2).dot(A) + B[2]); + if(s2 > 0) { *return_code = 0; return 0; } + if(s2 > s) + { + s = s2; + best_col_id = 2; + normalR = &R2; + invert_normal = (tmp < 0); + code = 6; + } + + + FCL_REAL fudge2(1.0e-6); + Q.array() += fudge2; + + Vec3f n; + FCL_REAL eps = std::numeric_limits<FCL_REAL>::epsilon(); + + // separating axis = u1 x (v1,v2,v3) + tmp = pp[2] * R(1, 0) - pp[1] * R(2, 0); + s2 = std::abs(tmp) - (A[1] * Q(2, 0) + A[2] * Q(1, 0) + B[1] * Q(0, 2) + B[2] * Q(0, 1)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(0, -R(2, 0), R(1, 0)); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 7; + } + } + + tmp = pp[2] * R(1, 1) - pp[1] * R(2, 1); + s2 = std::abs(tmp) - (A[1] * Q(2, 1) + A[2] * Q(1, 1) + B[0] * Q(0, 2) + B[2] * Q(0, 0)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(0, -R(2, 1), R(1, 1)); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 8; + } + } + + tmp = pp[2] * R(1, 2) - pp[1] * R(2, 2); + s2 = std::abs(tmp) - (A[1] * Q(2, 2) + A[2] * Q(1, 2) + B[0] * Q(0, 1) + B[1] * Q(0, 0)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(0, -R(2, 2), R(1, 2)); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 9; + } + } + + // separating axis = u2 x (v1,v2,v3) + tmp = pp[0] * R(2, 0) - pp[2] * R(0, 0); + s2 = std::abs(tmp) - (A[0] * Q(2, 0) + A[2] * Q(0, 0) + B[1] * Q(1, 2) + B[2] * Q(1, 1)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(R(2, 0), 0, -R(0, 0)); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 10; + } + } + + tmp = pp[0] * R(2, 1) - pp[2] * R(0, 1); + s2 = std::abs(tmp) - (A[0] * Q(2, 1) + A[2] * Q(0, 1) + B[0] * Q(1, 2) + B[2] * Q(1, 0)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(R(2, 1), 0, -R(0, 1)); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 11; + } + } + + tmp = pp[0] * R(2, 2) - pp[2] * R(0, 2); + s2 = std::abs(tmp) - (A[0] * Q(2, 2) + A[2] * Q(0, 2) + B[0] * Q(1, 1) + B[1] * Q(1, 0)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(R(2, 2), 0, -R(0, 2)); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 12; + } + } + + // separating axis = u3 x (v1,v2,v3) + tmp = pp[1] * R(0, 0) - pp[0] * R(1, 0); + s2 = std::abs(tmp) - (A[0] * Q(1, 0) + A[1] * Q(0, 0) + B[1] * Q(2, 2) + B[2] * Q(2, 1)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(-R(1, 0), R(0, 0), 0); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 13; + } + } + + tmp = pp[1] * R(0, 1) - pp[0] * R(1, 1); + s2 = std::abs(tmp) - (A[0] * Q(1, 1) + A[1] * Q(0, 1) + B[0] * Q(2, 2) + B[2] * Q(2, 0)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(-R(1, 1), R(0, 1), 0); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 14; + } + } + + tmp = pp[1] * R(0, 2) - pp[0] * R(1, 2); + s2 = std::abs(tmp) - (A[0] * Q(1, 2) + A[1] * Q(0, 2) + B[0] * Q(2, 1) + B[1] * Q(2, 0)); + if(s2 > 0) { *return_code = 0; return 0; } + n = Vec3f(-R(1, 2), R(0, 2), 0); + l = n.norm(); + if(l > eps) + { + s2 /= l; + if(s2 * fudge_factor > s) + { + s = s2; + best_col_id = -1; + normalC = n / l; + invert_normal = (tmp < 0); + code = 15; + } + } + + + + if (!code) { *return_code = code; return 0; } + + // if we get to this point, the boxes interpenetrate. compute the normal + // in global coordinates. + if(best_col_id != -1) + normal = normalR->col(best_col_id); + else + normal = R1 * normalC; + + if(invert_normal) + normal = -normal; + + *depth = -s; // s is negative when the boxes are in collision + + // compute contact point(s) + + if(code > 6) + { + // an edge from box 1 touches an edge from box 2. + // find a point pa on the intersecting edge of box 1 + Vec3f pa(T1); + FCL_REAL sign; + + for(int j = 0; j < 3; ++j) + { + sign = (R1.col(j).dot(normal) > 0) ? 1 : -1; + pa += R1.col(j) * (A[j] * sign); + } + + // find a point pb on the intersecting edge of box 2 + Vec3f pb(T2); + + for(int j = 0; j < 3; ++j) + { + sign = (R2.col(j).dot(normal) > 0) ? -1 : 1; + pb += R2.col(j) * (B[j] * sign); + } + + FCL_REAL alpha, beta; + Vec3f ua(R1.col((code-7)/3)); + Vec3f ub(R2.col((code-7)%3)); + + lineClosestApproach(pa, ua, pb, ub, &alpha, &beta); + pa += ua * alpha; + pb += ub * beta; + + + // Vec3f pointInWorld((pa + pb) * 0.5); + // contacts.push_back(ContactPoint(-normal, pointInWorld, -*depth)); + contacts.push_back(ContactPoint(-normal,pb,-*depth)); + *return_code = code; + + return 1; + } + + // okay, we have a face-something intersection (because the separating + // axis is perpendicular to a face). define face 'a' to be the reference + // face (i.e. the normal vector is perpendicular to this) and face 'b' to be + // the incident face (the closest face of the other box). + + const Matrix3f *Ra, *Rb; + const Vec3f *pa, *pb, *Sa, *Sb; + + if(code <= 3) + { + Ra = &R1; + Rb = &R2; + pa = &T1; + pb = &T2; + Sa = &A; + Sb = &B; + } + else + { + Ra = &R2; + Rb = &R1; + pa = &T2; + pb = &T1; + Sa = &B; + Sb = &A; + } + + // nr = normal vector of reference face dotted with axes of incident box. + // anr = absolute values of nr. + Vec3f normal2, nr, anr; + if(code <= 3) + normal2 = normal; + else + normal2 = -normal; + + nr = Rb->transpose() * normal2; + anr = nr.cwiseAbs(); + + // find the largest compontent of anr: this corresponds to the normal + // for the indident face. the other axis numbers of the indicent face + // are stored in a1,a2. + int lanr, a1, a2; + if(anr[1] > anr[0]) + { + if(anr[1] > anr[2]) + { + a1 = 0; + lanr = 1; + a2 = 2; + } + else + { + a1 = 0; + a2 = 1; + lanr = 2; + } + } + else + { + if(anr[0] > anr[2]) + { + lanr = 0; + a1 = 1; + a2 = 2; + } + else + { + a1 = 0; + a2 = 1; + lanr = 2; + } + } + + // compute center point of incident face, in reference-face coordinates + Vec3f center; + if(nr[lanr] < 0) + center = (*pb) - (*pa) + Rb->col(lanr) * ((*Sb)[lanr]); + else + center = (*pb) - (*pa) - Rb->col(lanr) * ((*Sb)[lanr]); + + // find the normal and non-normal axis numbers of the reference box + int codeN, code1, code2; + if(code <= 3) + codeN = code-1; + else codeN = code-4; + + if(codeN == 0) + { + code1 = 1; + code2 = 2; + } + else if(codeN == 1) + { + code1 = 0; + code2 = 2; + } + else + { + code1 = 0; + code2 = 1; + } + + // find the four corners of the incident face, in reference-face coordinates + FCL_REAL quad[8]; // 2D coordinate of incident face (x,y pairs) + FCL_REAL c1, c2, m11, m12, m21, m22; + c1 = Ra->col(code1).dot(center); + c2 = Ra->col(code2).dot(center); + // optimize this? - we have already computed this data above, but it is not + // stored in an easy-to-index format. for now it's quicker just to recompute + // the four dot products. + Vec3f tempRac = Ra->col(code1); + m11 = Rb->col(a1).dot(tempRac); + m12 = Rb->col(a2).dot(tempRac); + tempRac = Ra->col(code2); + m21 = Rb->col(a1).dot(tempRac); + m22 = Rb->col(a2).dot(tempRac); + + FCL_REAL k1 = m11 * (*Sb)[a1]; + FCL_REAL k2 = m21 * (*Sb)[a1]; + FCL_REAL k3 = m12 * (*Sb)[a2]; + FCL_REAL k4 = m22 * (*Sb)[a2]; + quad[0] = c1 - k1 - k3; + quad[1] = c2 - k2 - k4; + quad[2] = c1 - k1 + k3; + quad[3] = c2 - k2 + k4; + quad[4] = c1 + k1 + k3; + quad[5] = c2 + k2 + k4; + quad[6] = c1 + k1 - k3; + quad[7] = c2 + k2 - k4; + + // find the size of the reference face + FCL_REAL rect[2]; + rect[0] = (*Sa)[code1]; + rect[1] = (*Sa)[code2]; + + // intersect the incident and reference faces + FCL_REAL ret[16]; + int n_intersect = intersectRectQuad2(rect, quad, ret); + if(n_intersect < 1) { *return_code = code; return 0; } // this should never happen + + // convert the intersection points into reference-face coordinates, + // and compute the contact position and depth for each point. only keep + // those points that have a positive (penetrating) depth. delete points in + // the 'ret' array as necessary so that 'point' and 'ret' correspond. + Vec3f points[8]; // penetrating contact points + FCL_REAL dep[8]; // depths for those points + FCL_REAL det1 = 1.f/(m11*m22 - m12*m21); + m11 *= det1; + m12 *= det1; + m21 *= det1; + m22 *= det1; + int cnum = 0; // number of penetrating contact points found + for(int j = 0; j < n_intersect; ++j) + { + FCL_REAL k1 = m22*(ret[j*2]-c1) - m12*(ret[j*2+1]-c2); + FCL_REAL k2 = -m21*(ret[j*2]-c1) + m11*(ret[j*2+1]-c2); + points[cnum] = center + Rb->col(a1) * k1 + Rb->col(a2) * k2; + dep[cnum] = (*Sa)[codeN] - normal2.dot(points[cnum]); + if(dep[cnum] >= 0) + { + ret[cnum*2] = ret[j*2]; + ret[cnum*2+1] = ret[j*2+1]; + cnum++; + } + } + if(cnum < 1) { *return_code = code; return 0; } // this should never happen + + // we can't generate more contacts than we actually have + if(maxc > cnum) maxc = cnum; + if(maxc < 1) maxc = 1; + + if(cnum <= maxc) + { + if(code<4) + { + // we have less contacts than we need, so we use them all + for(int j = 0; j < cnum; ++j) + { + Vec3f pointInWorld = points[j] + (*pa); + contacts.push_back(ContactPoint(-normal, pointInWorld, -dep[j])); + } + } + else + { + // we have less contacts than we need, so we use them all + for(int j = 0; j < cnum; ++j) + { + Vec3f pointInWorld = points[j] + (*pa) - normal * dep[j]; + contacts.push_back(ContactPoint(-normal, pointInWorld, -dep[j])); + } + } + } + else + { + // we have more contacts than are wanted, some of them must be culled. + // find the deepest point, it is always the first contact. + int i1 = 0; + FCL_REAL maxdepth = dep[0]; + for(int i = 1; i < cnum; ++i) + { + if(dep[i] > maxdepth) + { + maxdepth = dep[i]; + i1 = i; + } + } + + int iret[8]; + cullPoints2(cnum, ret, maxc, i1, iret); + + for(int j = 0; j < maxc; ++j) + { + Vec3f posInWorld = points[iret[j]] + (*pa); + if(code < 4) + contacts.push_back(ContactPoint(-normal, posInWorld, -dep[iret[j]])); + else + contacts.push_back(ContactPoint(-normal, posInWorld - normal * dep[iret[j]], -dep[iret[j]])); + } + cnum = maxc; + } + + *return_code = code; + return cnum; + } + + inline bool compareContactPoints + (const ContactPoint& c1,const ContactPoint& c2) + { + return c1.depth < c2.depth; + } // Accending order, assuming penetration depth is a negative number! + + inline bool boxBoxIntersect(const Box& s1, const Transform3f& tf1, + const Box& s2, const Transform3f& tf2, + Vec3f* contact_points, + FCL_REAL* penetration_depth_, Vec3f* normal_) + { + std::vector<ContactPoint> contacts; + int return_code; + Vec3f normal; + FCL_REAL depth; + /* int cnum = */ boxBox2(s1.side, tf1.getRotation(), tf1.getTranslation(), + s2.side, tf2.getRotation(), tf2.getTranslation(), + normal, &depth, &return_code, + 4, contacts); + + if(normal_) *normal_ = normal; + if(penetration_depth_) *penetration_depth_ = depth; + + if(contact_points) + { + if(contacts.size() > 0) + { + std::sort(contacts.begin(), contacts.end(), compareContactPoints); + *contact_points = contacts[0].point; + if(penetration_depth_) *penetration_depth_ = -contacts[0].depth; + } + } + + return return_code != 0; + } + + template<typename T> + inline T halfspaceIntersectTolerance() + { + return 0; + } + + template<> + inline float halfspaceIntersectTolerance() + { + return 0.0001f; + } + + template<> + inline double halfspaceIntersectTolerance() + { + return 0.0000001; + } + + inline bool sphereHalfspaceIntersect + (const Sphere& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + FCL_REAL& distance, Vec3f& p1, Vec3f& p2, + Vec3f& normal) + { + Halfspace new_s2 = transform(s2, tf2); + const Vec3f& center = tf1.getTranslation(); + distance = new_s2.signedDistance(center) - s1.radius; + if(distance <= 0) + { + normal = -new_s2.n; // pointing from s1 to s2 + p1 = p2 = center - new_s2.n * s1.radius - (distance * 0.5) * new_s2.n; + return true; + } + else { + p1 = center - s1.radius * new_s2.n; + p2 = p1 - distance * new_s2.n; + return false; + } + } + + /// @brief box half space, a, b, c = +/- edge size + /// n^T * (R(o + a v1 + b v2 + c v3) + T) <= d + /// so (R^T n) (a v1 + b v2 + c v3) + n * T <= d + /// check whether d - n * T - (R^T n) (a v1 + b v2 + c v3) >= 0 for some a, b, c + /// the max value of left side is d - n * T + |(R^T n) (a v1 + b v2 + c v3)|, check that is enough + inline bool boxHalfspaceIntersect + (const Box& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2) + { + Halfspace new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f Q = R.transpose() * new_s2.n; + Vec3f A(Q[0] * s1.side[0], Q[1] * s1.side[1], Q[2] * s1.side[2]); + Vec3f B = A.cwiseAbs(); + + FCL_REAL depth = 0.5 * (B[0] + B[1] + B[2]) - new_s2.signedDistance(T); + return (depth >= 0); + } + + + inline bool boxHalfspaceIntersect + (const Box& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + if(!contact_points && !penetration_depth && !normal) + return boxHalfspaceIntersect(s1, tf1, s2, tf2); + else + { + Halfspace new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f Q = R.transpose() * new_s2.n; + Vec3f A(Q[0] * s1.side[0], Q[1] * s1.side[1], Q[2] * s1.side[2]); + Vec3f B = A.cwiseAbs(); + + FCL_REAL depth = 0.5 * (B[0] + B[1] + B[2]) - new_s2.signedDistance(T); + if(depth < 0) return false; + + Vec3f axis[3]; + axis[0] = R.col(0); + axis[1] = R.col(1); + axis[2] = R.col(2); + + /// find deepest point + Vec3f p(T); + int sign = 0; + + if(std::abs(Q[0] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(Q[0] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) + { + sign = (A[0] > 0) ? -1 : 1; + p += axis[0] * (0.5 * s1.side[0] * sign); + } + else if(std::abs(Q[1] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(Q[1] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) + { + sign = (A[1] > 0) ? -1 : 1; + p += axis[1] * (0.5 * s1.side[1] * sign); + } + else if(std::abs(Q[2] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(Q[2] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) + { + sign = (A[2] > 0) ? -1 : 1; + p += axis[2] * (0.5 * s1.side[2] * sign); + } + else + { + for(std::size_t i = 0; i < 3; ++i) + { + sign = (A[i] > 0) ? -1 : 1; + p += axis[i] * (0.5 * s1.side[i] * sign); + } + } + + /// compute the contact point from the deepest point + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = p + new_s2.n * (depth * 0.5); + + return true; + } + } + + inline bool capsuleHalfspaceIntersect + (const Capsule& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Halfspace new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + + FCL_REAL cosa = dir_z.dot(new_s2.n); + if(std::abs(cosa) < halfspaceIntersectTolerance<FCL_REAL>()) + { + FCL_REAL signed_dist = new_s2.signedDistance(T); + FCL_REAL depth = s1.radius - signed_dist; + if(depth < 0) return false; + + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = T + new_s2.n * (0.5 * depth - s1.radius); + + return true; + } + else + { + int sign = (cosa > 0) ? -1 : 1; + Vec3f p = T + dir_z * (s1.lz * 0.5 * sign); + + FCL_REAL signed_dist = new_s2.signedDistance(p); + FCL_REAL depth = s1.radius - signed_dist; + if(depth < 0) return false; + + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) + { + // deepest point + Vec3f c = p - new_s2.n * s1.radius; + *contact_points = c + new_s2.n * (0.5 * depth); + } + + return true; + } + } + + + inline bool cylinderHalfspaceIntersect + (const Cylinder& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Halfspace new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + FCL_REAL cosa = dir_z.dot(new_s2.n); + + if(cosa < halfspaceIntersectTolerance<FCL_REAL>()) + { + FCL_REAL signed_dist = new_s2.signedDistance(T); + FCL_REAL depth = s1.radius - signed_dist; + if(depth < 0) return false; + + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = T + new_s2.n * (0.5 * depth - s1.radius); + + return true; + } + else + { + Vec3f C = dir_z * cosa - new_s2.n; + if(std::abs(cosa + 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < halfspaceIntersectTolerance<FCL_REAL>()) + C = Vec3f(0, 0, 0); + else + { + FCL_REAL s = C.norm(); + s = s1.radius / s; + C *= s; + } + + int sign = (cosa > 0) ? -1 : 1; + // deepest point + Vec3f p = T + dir_z * (s1.lz * 0.5 * sign) + C; + FCL_REAL depth = -new_s2.signedDistance(p); + if(depth < 0) return false; + else + { + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = p + new_s2.n * (0.5 * depth); + return true; + } + } + } + + + inline bool coneHalfspaceIntersect + (const Cone& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Halfspace new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + FCL_REAL cosa = dir_z.dot(new_s2.n); + + if(cosa < halfspaceIntersectTolerance<FCL_REAL>()) + { + FCL_REAL signed_dist = new_s2.signedDistance(T); + FCL_REAL depth = s1.radius - signed_dist; + if(depth < 0) return false; + else + { + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = T - dir_z * (s1.lz * 0.5) + new_s2.n * (0.5 * depth - s1.radius); + return true; + } + } + else + { + Vec3f C = dir_z * cosa - new_s2.n; + if(std::abs(cosa + 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < halfspaceIntersectTolerance<FCL_REAL>()) + C = Vec3f(0, 0, 0); + else + { + FCL_REAL s = C.norm(); + s = s1.radius / s; + C *= s; + } + + Vec3f p1 = T + dir_z * (0.5 * s1.lz); + Vec3f p2 = T - dir_z * (0.5 * s1.lz) + C; + + FCL_REAL d1 = new_s2.signedDistance(p1); + FCL_REAL d2 = new_s2.signedDistance(p2); + + if(d1 > 0 && d2 > 0) return false; + else + { + FCL_REAL depth = -std::min(d1, d2); + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = ((d1 < d2) ? p1 : p2) + new_s2.n * (0.5 * depth); + return true; + } + } + } + + inline bool convexHalfspaceIntersect + (const Convex& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Halfspace new_s2 = transform(s2, tf2); + + Vec3f v; + FCL_REAL depth = std::numeric_limits<FCL_REAL>::max(); + + for(int i = 0; i < s1.num_points; ++i) + { + Vec3f p = tf1.transform(s1.points[i]); + + FCL_REAL d = new_s2.signedDistance(p); + if(d < depth) + { + depth = d; + v = p; + } + } + + if(depth <= 0) + { + if(contact_points) *contact_points = v - new_s2.n * (0.5 * depth); + if(penetration_depth) *penetration_depth = depth; + if(normal) *normal = -new_s2.n; + return true; + } + else + return false; + } + + // Intersection between half-space and triangle + // Half-space is in pose tf1, + // Triangle is in pose tf2 + // Computes distance and closest points (p1, p2) if no collision, + // contact point (p1 = p2), normal and penetration depth (-distance) + // if collision + inline bool halfspaceTriangleIntersect + (const Halfspace& s1, const Transform3f& tf1, const Vec3f& P1, + const Vec3f& P2, const Vec3f& P3, const Transform3f& tf2, + FCL_REAL& distance, Vec3f& p1, Vec3f& p2, Vec3f& normal) + { + Halfspace new_s1 = transform(s1, tf1); + + Vec3f v = tf2.transform(P1); + FCL_REAL depth = new_s1.signedDistance(v); + + Vec3f p = tf2.transform(P2); + FCL_REAL d = new_s1.signedDistance(p); + if(d < depth) + { + depth = d; + v = p; + } + + p = tf2.transform(P3); + d = new_s1.signedDistance(p); + if(d < depth) + { + depth = d; + v = p; + } + // v is the vertex with minimal projection abscissa (depth) on normal to + //plane, + distance = depth; + if(depth <= 0) + { + normal = new_s1.n; + p1 = p2 = v - (0.5 * depth) * new_s1.n; + return true; + } + else + { + p1 = v - depth * new_s1.n; + p2 = v; + return false; + } + } + + + /// @brief return whether plane collides with halfspace + /// if the separation plane of the halfspace is parallel with the plane + /// return code 1, if the plane's normal is the same with halfspace's normal and plane is inside halfspace, also return plane in pl + /// return code 2, if the plane's normal is oppositie to the halfspace's normal and plane is inside halfspace, also return plane in pl + /// plane is outside halfspace, collision-free + /// if not parallel + /// return the intersection ray, return code 3. ray origin is p and direction is d + inline bool planeHalfspaceIntersect + (const Plane& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, Plane& pl, Vec3f& p, + Vec3f& d, FCL_REAL& penetration_depth, int& ret) + { + Plane new_s1 = transform(s1, tf1); + Halfspace new_s2 = transform(s2, tf2); + + ret = 0; + + Vec3f dir = (new_s1.n).cross(new_s2.n); + FCL_REAL dir_norm = dir.squaredNorm(); + if(dir_norm < std::numeric_limits<FCL_REAL>::epsilon()) // parallel + { + if((new_s1.n).dot(new_s2.n) > 0) + { + if(new_s1.d < new_s2.d) + { + penetration_depth = new_s2.d - new_s1.d; + ret = 1; + pl = new_s1; + return true; + } + else + return false; + } + else + { + if(new_s1.d + new_s2.d > 0) + return false; + else + { + penetration_depth = -(new_s1.d + new_s2.d); + ret = 2; + pl = new_s1; + return true; + } + } + } + + Vec3f n = new_s2.n * new_s1.d - new_s1.n * new_s2.d; + Vec3f origin = n.cross(dir); + origin *= (1.0 / dir_norm); + + p = origin; + d = dir; + ret = 3; + penetration_depth = std::numeric_limits<FCL_REAL>::max(); + + return true; + } + + ///@ brief return whether two halfspace intersect + /// if the separation planes of the two halfspaces are parallel + /// return code 1, if two halfspaces' normal are same and s1 is in s2, also return s1 in s; + /// return code 2, if two halfspaces' normal are same and s2 is in s1, also return s2 in s; + /// return code 3, if two halfspaces' normal are opposite and s1 and s2 are into each other; + /// collision free, if two halfspaces' are separate; + /// if the separation planes of the two halfspaces are not parallel, return intersection ray, return code 4. ray origin is p and direction is d + /// collision free return code 0 + inline bool halfspaceIntersect(const Halfspace& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f& p, Vec3f& d, + Halfspace& s, + FCL_REAL& penetration_depth, int& ret) + { + Halfspace new_s1 = transform(s1, tf1); + Halfspace new_s2 = transform(s2, tf2); + + ret = 0; + + Vec3f dir = (new_s1.n).cross(new_s2.n); + FCL_REAL dir_norm = dir.squaredNorm(); + if(dir_norm < std::numeric_limits<FCL_REAL>::epsilon()) // parallel + { + if((new_s1.n).dot(new_s2.n) > 0) + { + if(new_s1.d < new_s2.d) // s1 is inside s2 + { + ret = 1; + penetration_depth = std::numeric_limits<FCL_REAL>::max(); + s = new_s1; + } + else // s2 is inside s1 + { + ret = 2; + penetration_depth = std::numeric_limits<FCL_REAL>::max(); + s = new_s2; + } + return true; + } + else + { + if(new_s1.d + new_s2.d > 0) // not collision + return false; + else // in each other + { + ret = 3; + penetration_depth = -(new_s1.d + new_s2.d); + return true; + } + } + } + + Vec3f n = new_s2.n * new_s1.d - new_s1.n * new_s2.d; + Vec3f origin = n.cross(dir); + origin *= (1.0 / dir_norm); + + p = origin; + d = dir; + ret = 4; + penetration_depth = std::numeric_limits<FCL_REAL>::max(); + + return true; + } + + template<typename T> + inline T planeIntersectTolerance() + { + return 0; + } + + template<> + inline double planeIntersectTolerance<double>() + { + return 0.0000001; + } + + template<> + float inline planeIntersectTolerance<float>() + { + return 0.0001f; + } + + inline bool spherePlaneIntersect + (const Sphere& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Plane new_s2 = transform(s2, tf2); + + const Vec3f& center = tf1.getTranslation(); + FCL_REAL signed_dist = new_s2.signedDistance(center); + FCL_REAL depth = - std::abs(signed_dist) + s1.radius; + if(depth >= 0) + { + if(normal) { if (signed_dist > 0) *normal = -new_s2.n; else *normal = new_s2.n; } + if(penetration_depth) *penetration_depth = depth; + if(contact_points) *contact_points = center - new_s2.n * signed_dist; + + return true; + } + else + return false; + } + + /// @brief box half space, a, b, c = +/- edge size + /// n^T * (R(o + a v1 + b v2 + c v3) + T) ~ d + /// so (R^T n) (a v1 + b v2 + c v3) + n * T ~ d + /// check whether d - n * T - (R^T n) (a v1 + b v2 + c v3) >= 0 for some a, b, c and <=0 for some a, b, c + /// so need to check whether |d - n * T| <= |(R^T n)(a v1 + b v2 + c v3)|, the reason is as follows: + /// (R^T n) (a v1 + b v2 + c v3) can get |(R^T n) (a v1 + b v2 + c v3)| for one a, b, c. + /// if |d - n * T| <= |(R^T n)(a v1 + b v2 + c v3)| then can get both positive and negative value on the right side. + inline bool boxPlaneIntersect(const Box& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* contact_points, + FCL_REAL* penetration_depth, Vec3f* normal) + { + Plane new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f Q = R.transpose() * new_s2.n; + Vec3f A(Q[0] * s1.side[0], Q[1] * s1.side[1], Q[2] * s1.side[2]); + Vec3f B = A.cwiseAbs(); + + FCL_REAL signed_dist = new_s2.signedDistance(T); + FCL_REAL depth = 0.5 * (B[0] + B[1] + B[2]) - std::abs(signed_dist); + if(depth < 0) return false; + + Vec3f axis[3]; + axis[0] = R.col(0); + axis[1] = R.col(1); + axis[2] = R.col(2); + + // find the deepest point + Vec3f p = T; + + // when center is on the positive side of the plane, use a, b, c make (R^T n) (a v1 + b v2 + c v3) the minimum + // otherwise, use a, b, c make (R^T n) (a v1 + b v2 + c v3) the maximum + int sign = (signed_dist > 0) ? 1 : -1; + + if(std::abs(Q[0] - 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(Q[0] + 1) < planeIntersectTolerance<FCL_REAL>()) + { + int sign2 = (A[0] > 0) ? -1 : 1; + sign2 *= sign; + p += axis[0] * (0.5 * s1.side[0] * sign2); + } + else if(std::abs(Q[1] - 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(Q[1] + 1) < planeIntersectTolerance<FCL_REAL>()) + { + int sign2 = (A[1] > 0) ? -1 : 1; + sign2 *= sign; + p += axis[1] * (0.5 * s1.side[1] * sign2); + } + else if(std::abs(Q[2] - 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(Q[2] + 1) < planeIntersectTolerance<FCL_REAL>()) + { + int sign2 = (A[2] > 0) ? -1 : 1; + sign2 *= sign; + p += axis[2] * (0.5 * s1.side[2] * sign2); + } + else + { + for(std::size_t i = 0; i < 3; ++i) + { + int sign2 = (A[i] > 0) ? -1 : 1; + sign2 *= sign; + p += axis[i] * (0.5 * s1.side[i] * sign2); + } + } + + // compute the contact point by project the deepest point onto the plane + if(penetration_depth) *penetration_depth = depth; + if(normal) { if (signed_dist > 0) *normal = -new_s2.n; else *normal = new_s2.n; } + if(contact_points) *contact_points = p - new_s2.n * new_s2.signedDistance(p); + + return true; + } + + + inline bool capsulePlaneIntersect(const Capsule& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2) + { + Plane new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + Vec3f p1 = T + dir_z * (0.5 * s1.lz); + Vec3f p2 = T - dir_z * (0.5 * s1.lz); + + FCL_REAL d1 = new_s2.signedDistance(p1); + FCL_REAL d2 = new_s2.signedDistance(p2); + + // two end points on different side of the plane + if(d1 * d2 <= 0) + return true; + + // two end points on the same side of the plane, but the end point spheres might intersect the plane + return (std::abs(d1) <= s1.radius) || (std::abs(d2) <= s1.radius); + } + + inline bool capsulePlaneIntersect + (const Capsule& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Plane new_s2 = transform(s2, tf2); + + if(!contact_points && !penetration_depth && !normal) + return capsulePlaneIntersect(s1, tf1, s2, tf2); + else + { + Plane new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + + + Vec3f p1 = T + dir_z * (0.5 * s1.lz); + Vec3f p2 = T - dir_z * (0.5 * s1.lz); + + FCL_REAL d1 = new_s2.signedDistance(p1); + FCL_REAL d2 = new_s2.signedDistance(p2); + + FCL_REAL abs_d1 = std::abs(d1); + FCL_REAL abs_d2 = std::abs(d2); + + // two end points on different side of the plane + // the contact point is the intersect of axis with the plane + // the normal is the direction to avoid intersection + // the depth is the minimum distance to resolve the collision + if(d1 * d2 < -planeIntersectTolerance<FCL_REAL>()) + { + if(abs_d1 < abs_d2) + { + if(penetration_depth) *penetration_depth = abs_d1 + s1.radius; + if(contact_points) *contact_points = p1 * (abs_d2 / (abs_d1 + abs_d2)) + p2 * (abs_d1 / (abs_d1 + abs_d2)); + if(normal) { if (d1 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } + } + else + { + if(penetration_depth) *penetration_depth = abs_d2 + s1.radius; + if(contact_points) *contact_points = p1 * (abs_d2 / (abs_d1 + abs_d2)) + p2 * (abs_d1 / (abs_d1 + abs_d2)); + if(normal) { if (d2 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } + } + return true; + } + + if(abs_d1 > s1.radius && abs_d2 > s1.radius) + return false; + else + { + if(penetration_depth) *penetration_depth = s1.radius - std::min(abs_d1, abs_d2); + + if(contact_points) + { + if(abs_d1 <= s1.radius && abs_d2 <= s1.radius) + { + Vec3f c1 = p1 - new_s2.n * d2; + Vec3f c2 = p2 - new_s2.n * d1; + *contact_points = (c1 + c2) * 0.5; + } + else if(abs_d1 <= s1.radius) + { + Vec3f c = p1 - new_s2.n * d1; + *contact_points = c; + } + else if(abs_d2 <= s1.radius) + { + Vec3f c = p2 - new_s2.n * d2; + *contact_points = c; + } + } + + if(normal) { if (d1 < 0) *normal = new_s2.n; else *normal = -new_s2.n; } + return true; + } + } + } + + /// @brief cylinder-plane intersect + /// n^T (R (r * cosa * v1 + r * sina * v2 + h * v3) + T) ~ d + /// need one point to be positive and one to be negative + /// (n^T * v3) * h + n * T -d + r * (cosa * (n^T * R * v1) + sina * (n^T * R * v2)) ~ 0 + /// (n^T * v3) * h + r * (cosa * (n^T * R * v1) + sina * (n^T * R * v2)) + n * T - d ~ 0 + inline bool cylinderPlaneIntersect + (const Cylinder& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2) + { + Plane new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f Q = R.transpose() * new_s2.n; + + FCL_REAL term = std::abs(Q[2]) * s1.lz + s1.radius * std::sqrt(Q[0] * Q[0] + Q[1] * Q[1]); + FCL_REAL dist = new_s2.distance(T); + FCL_REAL depth = term - dist; + + if(depth < 0) + return false; + else + return true; + } + + inline bool cylinderPlaneIntersect + (const Cylinder& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + if(!contact_points && !penetration_depth && !normal) + return cylinderPlaneIntersect(s1, tf1, s2, tf2); + else + { + Plane new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + FCL_REAL cosa = dir_z.dot(new_s2.n); + + if(std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) + { + FCL_REAL d = new_s2.signedDistance(T); + FCL_REAL depth = s1.radius - std::abs(d); + if(depth < 0) return false; + else + { + if(penetration_depth) *penetration_depth = depth; + if(normal) { if (d < 0) *normal = new_s2.n; else *normal = -new_s2.n; } + if(contact_points) *contact_points = T - new_s2.n * d; + return true; + } + } + else + { + Vec3f C = dir_z * cosa - new_s2.n; + if(std::abs(cosa + 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < planeIntersectTolerance<FCL_REAL>()) + C = Vec3f(0, 0, 0); + else + { + FCL_REAL s = C.norm(); + s = s1.radius / s; + C *= s; + } + + Vec3f p1 = T + dir_z * (0.5 * s1.lz); + Vec3f p2 = T - dir_z * (0.5 * s1.lz); + + Vec3f c1, c2; + if(cosa > 0) + { + c1 = p1 - C; + c2 = p2 + C; + } + else + { + c1 = p1 + C; + c2 = p2 - C; + } + + FCL_REAL d1 = new_s2.signedDistance(c1); + FCL_REAL d2 = new_s2.signedDistance(c2); + + if(d1 * d2 <= 0) + { + FCL_REAL abs_d1 = std::abs(d1); + FCL_REAL abs_d2 = std::abs(d2); + + if(abs_d1 > abs_d2) + { + if(penetration_depth) *penetration_depth = abs_d2; + if(contact_points) *contact_points = c2 - new_s2.n * d2; + if(normal) { if (d2 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } + } + else + { + if(penetration_depth) *penetration_depth = abs_d1; + if(contact_points) *contact_points = c1 - new_s2.n * d1; + if(normal) { if (d1 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } + } + return true; + } + else + return false; + } + } + } + + inline bool conePlaneIntersect + (const Cone& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Plane new_s2 = transform(s2, tf2); + + const Matrix3f& R = tf1.getRotation(); + const Vec3f& T = tf1.getTranslation(); + + Vec3f dir_z = R.col(2); + FCL_REAL cosa = dir_z.dot(new_s2.n); + + if(std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) + { + FCL_REAL d = new_s2.signedDistance(T); + FCL_REAL depth = s1.radius - std::abs(d); + if(depth < 0) return false; + else + { + if(penetration_depth) *penetration_depth = depth; + if(normal) { if (d < 0) *normal = new_s2.n; else *normal = -new_s2.n; } + if(contact_points) *contact_points = T - dir_z * (0.5 * s1.lz) + dir_z * (0.5 * depth / s1.radius * s1.lz) - new_s2.n * d; + return true; + } + } + else + { + Vec3f C = dir_z * cosa - new_s2.n; + if(std::abs(cosa + 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < planeIntersectTolerance<FCL_REAL>()) + C = Vec3f(0, 0, 0); + else + { + FCL_REAL s = C.norm(); + s = s1.radius / s; + C *= s; + } + + Vec3f c[3]; + c[0] = T + dir_z * (0.5 * s1.lz); + c[1] = T - dir_z * (0.5 * s1.lz) + C; + c[2] = T - dir_z * (0.5 * s1.lz) - C; + + FCL_REAL d[3]; + d[0] = new_s2.signedDistance(c[0]); + d[1] = new_s2.signedDistance(c[1]); + d[2] = new_s2.signedDistance(c[2]); + + if((d[0] >= 0 && d[1] >= 0 && d[2] >= 0) || (d[0] <= 0 && d[1] <= 0 && d[2] <= 0)) + return false; + else + { + bool positive[3]; + for(std::size_t i = 0; i < 3; ++i) + positive[i] = (d[i] >= 0); + + int n_positive = 0; + FCL_REAL d_positive = 0, d_negative = 0; + for(std::size_t i = 0; i < 3; ++i) + { + if(positive[i]) + { + n_positive++; + if(d_positive <= d[i]) d_positive = d[i]; + } + else + { + if(d_negative <= -d[i]) d_negative = -d[i]; + } + } + + if(penetration_depth) *penetration_depth = std::min(d_positive, d_negative); + if(normal) { if (d_positive > d_negative) *normal = -new_s2.n; else *normal = new_s2.n; } + if(contact_points) + { + Vec3f p[2]; + Vec3f q; + + FCL_REAL p_d[2]; + FCL_REAL q_d(0); + + if(n_positive == 2) + { + for(std::size_t i = 0, j = 0; i < 3; ++i) + { + if(positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } + else { q = c[i]; q_d = d[i]; } + } + + Vec3f t1 = (-p[0] * q_d + q * p_d[0]) / (-q_d + p_d[0]); + Vec3f t2 = (-p[1] * q_d + q * p_d[1]) / (-q_d + p_d[1]); + *contact_points = (t1 + t2) * 0.5; + } + else + { + for(std::size_t i = 0, j = 0; i < 3; ++i) + { + if(!positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } + else { q = c[i]; q_d = d[i]; } + } + + Vec3f t1 = (p[0] * q_d - q * p_d[0]) / (q_d - p_d[0]); + Vec3f t2 = (p[1] * q_d - q * p_d[1]) / (q_d - p_d[1]); + *contact_points = (t1 + t2) * 0.5; + } + } + return true; + } + } + } + + inline bool convexPlaneIntersect + (const Convex& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) + { + Plane new_s2 = transform(s2, tf2); + + Vec3f v_min, v_max; + FCL_REAL d_min = std::numeric_limits<FCL_REAL>::max(), d_max = -std::numeric_limits<FCL_REAL>::max(); + + for(int i = 0; i < s1.num_points; ++i) + { + Vec3f p = tf1.transform(s1.points[i]); + + FCL_REAL d = new_s2.signedDistance(p); + + if(d < d_min) { d_min = d; v_min = p; } + if(d > d_max) { d_max = d; v_max = p; } + } + + if(d_min * d_max > 0) return false; + else + { + if(d_min + d_max > 0) + { + if(penetration_depth) *penetration_depth = -d_min; + if(normal) *normal = -new_s2.n; + if(contact_points) *contact_points = v_min - new_s2.n * d_min; + } + else + { + if(penetration_depth) *penetration_depth = d_max; + if(normal) *normal = new_s2.n; + if(contact_points) *contact_points = v_max - new_s2.n * d_max; + } + return true; + } + } + + + + inline bool planeTriangleIntersect + (const Plane& s1, const Transform3f& tf1, const Vec3f& P1, + const Vec3f& P2, const Vec3f& P3, const Transform3f& tf2, + FCL_REAL distance, Vec3f& p1, Vec3f& p2, Vec3f& normal) + { + Plane new_s1 = transform(s1, tf1); + + Vec3f c[3]; + c[0] = tf2.transform(P1); + c[1] = tf2.transform(P2); + c[2] = tf2.transform(P3); + + FCL_REAL d[3]; + d[0] = new_s1.signedDistance(c[0]); + d[1] = new_s1.signedDistance(c[1]); + d[2] = new_s1.signedDistance(c[2]); + int imin; + if (d[0] >= 0 && d[1] >= 0 && d[2] >= 0) + { + if (d[0] < d[1]) + if (d[0] < d[2]) { + imin = 0; + } + else { // d [2] <= d[0] < d [1] + imin = 2; + } + else { // d[1] <= d[0] + if (d[2] < d[1]) { + imin = 2; + } else { // d[1] <= d[2] + imin = 1; + } + } + distance = d[imin]; + p2 = c[imin]; + p1 = c[imin] - d[imin] * new_s1.n; + return false; + } + if (d[0] <= 0 && d[1] <= 0 && d[2] <= 0) + { + if (d[0] > d[1]) + if (d[0] > d[2]) { + imin = 0; + } + else { // d [2] >= d[0] > d [1] + imin = 2; + } + else { // d[1] >= d[0] + if (d[2] > d[1]) { + imin = 2; + } else { // d[1] >= d[2] + imin = 1; + } + } + distance = -d[imin]; + p2 = c[imin]; + p1 = c[imin] - d[imin] * new_s1.n; + return false; + } + bool positive[3]; + for(std::size_t i = 0; i < 3; ++i) + positive[i] = (d[i] > 0); + + int n_positive = 0; + FCL_REAL d_positive = 0, d_negative = 0; + for(std::size_t i = 0; i < 3; ++i) + { + if(positive[i]) + { + n_positive++; + if(d_positive <= d[i]) d_positive = d[i]; + } + else + { + if(d_negative <= -d[i]) d_negative = -d[i]; + } + } + + distance = -std::min(d_positive, d_negative); + if (d_positive > d_negative) + { + normal = new_s1.n; + } else + { + normal = -new_s1.n; + } + Vec3f p[2]; + Vec3f q; + + FCL_REAL p_d[2]; + FCL_REAL q_d(0); + + if(n_positive == 2) + { + for(std::size_t i = 0, j = 0; i < 3; ++i) + { + if(positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } + else { q = c[i]; q_d = d[i]; } + } + + Vec3f t1 = (-p[0] * q_d + q * p_d[0]) / (-q_d + p_d[0]); + Vec3f t2 = (-p[1] * q_d + q * p_d[1]) / (-q_d + p_d[1]); + p1 = p2 = (t1 + t2) * 0.5; + } + else + { + for(std::size_t i = 0, j = 0; i < 3; ++i) + { + if(!positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } + else { q = c[i]; q_d = d[i]; } + } + + Vec3f t1 = (p[0] * q_d - q * p_d[0]) / (q_d - p_d[0]); + Vec3f t2 = (p[1] * q_d - q * p_d[1]) / (q_d - p_d[1]); + p1 = p2 = (t1 + t2) * 0.5; + } + return true; + } + + inline bool halfspacePlaneIntersect + (const Halfspace& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Plane& pl, Vec3f& p, Vec3f& d, FCL_REAL& penetration_depth, int& ret) + { + return planeHalfspaceIntersect(s2, tf2, s1, tf1, pl, p, d, penetration_depth, ret); + } + + inline bool planeIntersect + (const Plane& s1, const Transform3f& tf1, + const Plane& s2, const Transform3f& tf2, + Vec3f* /*contact_points*/, FCL_REAL* /*penetration_depth*/, Vec3f* /*normal*/) + { + Plane new_s1 = transform(s1, tf1); + Plane new_s2 = transform(s2, tf2); + + FCL_REAL a = (new_s1.n).dot(new_s2.n); + if(a == 1 && new_s1.d != new_s2.d) + return false; + if(a == -1 && new_s1.d != -new_s2.d) + return false; + + return true; + } + // Compute penetration distance and normal of two triangles in collision + // Normal is normal of triangle 1 (P1, P2, P3), penetration depth is the + // minimal distance (Q1, Q2, Q3) should be translated along the normal so + // that the triangles are collision free. + // + // Note that we compute here an upper bound of the penetration distance, + // not the exact value. + static FCL_REAL computePenetration + (const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, + const Vec3f& Q1, const Vec3f& Q2, const Vec3f& Q3, const Vec3f& p1, + const Transform3f& tf1, const Transform3f& tf2, Vec3f& normal) + { + Vec3f globalP1 (tf1.transform (P1)); + Vec3f globalP2 (tf1.transform (P2)); + Vec3f globalP3 (tf1.transform (P3)); + Vec3f globalQ1 (tf2.transform (Q1)); + Vec3f globalQ2 (tf2.transform (Q2)); + Vec3f globalQ3 (tf2.transform (Q3)); + Vec3f u ((globalP2-globalP1).cross (globalP3-globalP1)); + normal = u.normalized (); + FCL_REAL depth; + FCL_REAL depth1 ((globalP1-globalQ1).dot (normal)); + FCL_REAL depth2 ((globalP1-globalQ2).dot (normal)); + FCL_REAL depth3 ((globalP1-globalQ3).dot (normal)); + depth = std::max (depth1, std::max (depth2, depth3)); + return depth; + } + } // details +} // namespace fcl +#endif // HPP_FCL_SRC_NARROWPHASE_DETAILS_H diff --git a/src/narrowphase/narrowphase.cpp b/src/narrowphase/narrowphase.cpp index d7fdea15..8fe4bb7c 100644 --- a/src/narrowphase/narrowphase.cpp +++ b/src/narrowphase/narrowphase.cpp @@ -40,2480 +40,10 @@ #include <hpp/fcl/intersect.h> #include <boost/math/constants/constants.hpp> #include <vector> +#include "../src/narrowphase/details.h" namespace fcl { - -namespace details -{ -// Compute the point on a line segment that is the closest point on the -// segment to to another point. The code is inspired by the explanation -// given by Dan Sunday's page: -// http://geomalgorithms.com/a02-_lines.html -static inline void lineSegmentPointClosestToPoint (const Vec3f &p, const Vec3f &s1, const Vec3f &s2, Vec3f &sp) { - Vec3f v = s2 - s1; - Vec3f w = p - s1; - - FCL_REAL c1 = w.dot(v); - FCL_REAL c2 = v.dot(v); - - if (c1 <= 0) { - sp = s1; - } else if (c2 <= c1) { - sp = s2; - } else { - FCL_REAL b = c1/c2; - Vec3f Pb = s1 + v * b; - sp = Pb; - } -} - -bool sphereCapsuleIntersect(const Sphere& s1, const Transform3f& tf1, - const Capsule& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal_) -{ - Transform3f tf2_inv (tf2); - tf2_inv.inverse(); - - Vec3f pos1 (tf2.transform (Vec3f (0., 0., 0.5 * s2.lz))); // from distance function - Vec3f pos2 (tf2.transform (Vec3f (0., 0., -0.5 * s2.lz))); - Vec3f s_c = tf1.getTranslation (); - - Vec3f segment_point; - - lineSegmentPointClosestToPoint (s_c, pos1, pos2, segment_point); - Vec3f diff = s_c - segment_point; - - FCL_REAL diffN = diff.norm(); - FCL_REAL distance = diffN - s1.radius - s2.radius; - - if (distance > 0) - return false; - - // Vec3f normal (-diff.normalized()); - - if (distance < 0 && penetration_depth) - *penetration_depth = -distance; - - if (normal_) - *normal_ = -diff / diffN; - - if (contact_points) { - *contact_points = segment_point + diff * s2.radius; - } - - return true; -} - -bool sphereCapsuleDistance(const Sphere& s1, const Transform3f& tf1, - const Capsule& s2, const Transform3f& tf2, - FCL_REAL& dist, Vec3f& p1, Vec3f& p2, Vec3f& normal) -{ - Transform3f tf2_inv(tf2); - tf2_inv.inverse(); - - Vec3f pos1 (tf2.transform (Vec3f (0., 0., 0.5 * s2.lz))); - Vec3f pos2 (tf2.transform (Vec3f (0., 0., -0.5 * s2.lz))); - Vec3f s_c = tf1.getTranslation (); - - Vec3f segment_point; - - lineSegmentPointClosestToPoint (s_c, pos1, pos2, segment_point); - normal = s_c - segment_point; - - dist = normal.norm() - s1.radius - s2.radius; - - normal.normalize(); - p1 = s_c - normal * s1.radius; - p2 = segment_point + normal * s1.radius; - - if(dist <= 0) - return false; - - return true; -} - -bool sphereSphereIntersect(const Sphere& s1, const Transform3f& tf1, - const Sphere& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Vec3f diff = tf2.getTranslation() - tf1.getTranslation(); - FCL_REAL len = diff.norm(); - if(len > s1.radius + s2.radius) - return false; - - if(penetration_depth) - *penetration_depth = s1.radius + s2.radius - len; - - // If the centers of two sphere are at the same position, the normal is (0, 0, 0). - // Otherwise, normal is pointing from center of object 1 to center of object 2 - if(normal) - { - if(len > 0) - *normal = diff / len; - else - *normal = diff; - } - - if(contact_points) - *contact_points = tf1.getTranslation() + diff * s1.radius / (s1.radius + s2.radius); - - return true; -} - - -bool sphereSphereDistance(const Sphere& s1, const Transform3f& tf1, - const Sphere& s2, const Transform3f& tf2, - FCL_REAL& dist, Vec3f& p1, Vec3f& p2, Vec3f& normal) -{ - Vec3f o1 = tf1.getTranslation(); - Vec3f o2 = tf2.getTranslation(); - Vec3f diff = o1 - o2; - FCL_REAL len = diff.norm(); - normal = -diff/len; - dist = len - s1.radius - s2.radius; - - p1 = o1 - diff * (s1.radius / len); - p2 = o2 + diff * (s2.radius / len); - - return (dist >=0); -} - -/** \brief the minimum distance from a point to a line */ -FCL_REAL segmentSqrDistance(const Vec3f& from, const Vec3f& to,const Vec3f& p, Vec3f& nearest) -{ - Vec3f diff = p - from; - Vec3f v = to - from; - FCL_REAL t = v.dot(diff); - - if(t > 0) - { - FCL_REAL dotVV = v.dot(v); - if(t < dotVV) - { - t /= dotVV; - diff -= v * t; - } - else - { - t = 1; - diff -= v; - } - } - else - t = 0; - - nearest = from + v * t; - return diff.dot(diff); -} - -/// @brief Whether a point's projection is in a triangle -bool projectInTriangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3, const Vec3f& normal, const Vec3f& p) -{ - Vec3f edge1(p2 - p1); - Vec3f edge2(p3 - p2); - Vec3f edge3(p1 - p3); - - Vec3f p1_to_p(p - p1); - Vec3f p2_to_p(p - p2); - Vec3f p3_to_p(p - p3); - - Vec3f edge1_normal(edge1.cross(normal)); - Vec3f edge2_normal(edge2.cross(normal)); - Vec3f edge3_normal(edge3.cross(normal)); - - FCL_REAL r1, r2, r3; - r1 = edge1_normal.dot(p1_to_p); - r2 = edge2_normal.dot(p2_to_p); - r3 = edge3_normal.dot(p3_to_p); - if ( ( r1 > 0 && r2 > 0 && r3 > 0 ) || - ( r1 <= 0 && r2 <= 0 && r3 <= 0 ) ) - return true; - return false; -} - -// Intersection between sphere and triangle -// Sphere is in position tf1, Triangle is expressed in global frame -bool sphereTriangleIntersect(const Sphere& s, const Transform3f& tf1, - const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, - FCL_REAL& distance, Vec3f& p1, Vec3f& p2, - Vec3f& normal_) -{ - Vec3f normal = (P2 - P1).cross(P3 - P1); - normal.normalize(); - const Vec3f& center = tf1.getTranslation(); - const FCL_REAL& radius = s.radius; - assert (radius >= 0); - FCL_REAL eps (std::numeric_limits<FCL_REAL>::epsilon()); - FCL_REAL radius_with_threshold = radius + eps; - Vec3f p1_to_center = center - P1; - FCL_REAL distance_from_plane = p1_to_center.dot(normal); - - if(distance_from_plane < 0) - { - distance_from_plane *= -1; - normal *= -1; - } - - bool is_inside_contact_plane = (distance_from_plane < radius_with_threshold); - - bool has_contact = false; - Vec3f contact_point; - if(is_inside_contact_plane) - { - if(projectInTriangle(P1, P2, P3, normal, center)) - { - has_contact = true; - contact_point = center - normal * distance_from_plane; - } - else - { - FCL_REAL contact_capsule_radius_sqr = radius_with_threshold * radius_with_threshold; - Vec3f nearest_on_edge; - FCL_REAL distance_sqr; - distance_sqr = segmentSqrDistance(P1, P2, center, nearest_on_edge); - if(distance_sqr < contact_capsule_radius_sqr) - { - has_contact = true; - contact_point = nearest_on_edge; - } - - distance_sqr = segmentSqrDistance(P2, P3, center, nearest_on_edge); - if(distance_sqr < contact_capsule_radius_sqr) - { - has_contact = true; - contact_point = nearest_on_edge; - } - - distance_sqr = segmentSqrDistance(P3, P1, center, nearest_on_edge); - if(distance_sqr < contact_capsule_radius_sqr) - { - has_contact = true; - contact_point = nearest_on_edge; - } - } - } - - Vec3f center_to_contact = contact_point - center; - if(has_contact) - { - FCL_REAL distance_sqr = center_to_contact.squaredNorm(); - - if(distance_sqr < radius_with_threshold * radius_with_threshold) - { - if(distance_sqr > eps) - { - FCL_REAL dist = std::sqrt(distance_sqr); - normal_ = center_to_contact.normalized(); - p1 = p2 = contact_point; - distance = dist - radius; - } - else - { - normal_ = -normal; - p1 = p2 = contact_point; - distance = - radius; - } - } - } - else - { - Vec3f unit (center_to_contact.normalized ()); - p1 = center + radius * unit; - p2 = contact_point; - } - return has_contact; -} - - -bool sphereTriangleDistance(const Sphere& sp, const Transform3f& tf, - const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, - FCL_REAL* dist) -{ - // from geometric tools, very different from the collision code. - - const Vec3f& center = tf.getTranslation(); - FCL_REAL radius = sp.radius; - Vec3f diff = P1 - center; - Vec3f edge0 = P2 - P1; - Vec3f edge1 = P3 - P1; - FCL_REAL a00 = edge0.squaredNorm(); - FCL_REAL a01 = edge0.dot(edge1); - FCL_REAL a11 = edge1.squaredNorm(); - FCL_REAL b0 = diff.dot(edge0); - FCL_REAL b1 = diff.dot(edge1); - FCL_REAL c = diff.squaredNorm(); - FCL_REAL det = fabs(a00*a11 - a01*a01); - FCL_REAL s = a01*b1 - a11*b0; - FCL_REAL t = a01*b0 - a00*b1; - - FCL_REAL sqr_dist; - - if(s + t <= det) - { - if(s < 0) - { - if(t < 0) // region 4 - { - if(b0 < 0) - { - t = 0; - if(-b0 >= a00) - { - s = 1; - sqr_dist = a00 + 2*b0 + c; - } - else - { - s = -b0/a00; - sqr_dist = b0*s + c; - } - } - else - { - s = 0; - if(b1 >= 0) - { - t = 0; - sqr_dist = c; - } - else if(-b1 >= a11) - { - t = 1; - sqr_dist = a11 + 2*b1 + c; - } - else - { - t = -b1/a11; - sqr_dist = b1*t + c; - } - } - } - else // region 3 - { - s = 0; - if(b1 >= 0) - { - t = 0; - sqr_dist = c; - } - else if(-b1 >= a11) - { - t = 1; - sqr_dist = a11 + 2*b1 + c; - } - else - { - t = -b1/a11; - sqr_dist = b1*t + c; - } - } - } - else if(t < 0) // region 5 - { - t = 0; - if(b0 >= 0) - { - s = 0; - sqr_dist = c; - } - else if(-b0 >= a00) - { - s = 1; - sqr_dist = a00 + 2*b0 + c; - } - else - { - s = -b0/a00; - sqr_dist = b0*s + c; - } - } - else // region 0 - { - // minimum at interior point - FCL_REAL inv_det = (1)/det; - s *= inv_det; - t *= inv_det; - sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; - } - } - else - { - FCL_REAL tmp0, tmp1, numer, denom; - - if(s < 0) // region 2 - { - tmp0 = a01 + b0; - tmp1 = a11 + b1; - if(tmp1 > tmp0) - { - numer = tmp1 - tmp0; - denom = a00 - 2*a01 + a11; - if(numer >= denom) - { - s = 1; - t = 0; - sqr_dist = a00 + 2*b0 + c; - } - else - { - s = numer/denom; - t = 1 - s; - sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; - } - } - else - { - s = 0; - if(tmp1 <= 0) - { - t = 1; - sqr_dist = a11 + 2*b1 + c; - } - else if(b1 >= 0) - { - t = 0; - sqr_dist = c; - } - else - { - t = -b1/a11; - sqr_dist = b1*t + c; - } - } - } - else if(t < 0) // region 6 - { - tmp0 = a01 + b1; - tmp1 = a00 + b0; - if(tmp1 > tmp0) - { - numer = tmp1 - tmp0; - denom = a00 - 2*a01 + a11; - if(numer >= denom) - { - t = 1; - s = 0; - sqr_dist = a11 + 2*b1 + c; - } - else - { - t = numer/denom; - s = 1 - t; - sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; - } - } - else - { - t = 0; - if(tmp1 <= 0) - { - s = 1; - sqr_dist = a00 + 2*b0 + c; - } - else if(b0 >= 0) - { - s = 0; - sqr_dist = c; - } - else - { - s = -b0/a00; - sqr_dist = b0*s + c; - } - } - } - else // region 1 - { - numer = a11 + b1 - a01 - b0; - if(numer <= 0) - { - s = 0; - t = 1; - sqr_dist = a11 + 2*b1 + c; - } - else - { - denom = a00 - 2*a01 + a11; - if(numer >= denom) - { - s = 1; - t = 0; - sqr_dist = a00 + 2*b0 + c; - } - else - { - s = numer/denom; - t = 1 - s; - sqr_dist = s*(a00*s + a01*t + 2*b0) + t*(a01*s + a11*t + 2*b1) + c; - } - } - } - } - - // Account for numerical round-off error. - if(sqr_dist < 0) - sqr_dist = 0; - - if(sqr_dist > radius * radius) - { - if(dist) *dist = std::sqrt(sqr_dist) - radius; - return true; - } - else - { - if(dist) *dist = -1; - return false; - } -} - - -bool sphereTriangleDistance(const Sphere& sp, const Transform3f& tf, - const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, - FCL_REAL* dist, Vec3f* p1, Vec3f* p2) -{ - if(p1 || p2) - { - Vec3f o = tf.getTranslation(); - Project::ProjectResult result; - result = Project::projectTriangle(P1, P2, P3, o); - if(result.sqr_distance > sp.radius * sp.radius) - { - if(dist) *dist = std::sqrt(result.sqr_distance) - sp.radius; - Vec3f project_p = P1 * result.parameterization[0] + P2 * result.parameterization[1] + P3 * result.parameterization[2]; - Vec3f dir = o - project_p; - dir.normalize(); - if(p1) { *p1 = o - dir * sp.radius; } - if(p2) *p2 = project_p; - return true; - } - else - return false; - } - else - { - return sphereTriangleDistance(sp, tf, P1, P2, P3, dist); - } -} - - -bool sphereTriangleDistance(const Sphere& sp, const Transform3f& tf1, - const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, const Transform3f& tf2, - FCL_REAL* dist, Vec3f* p1, Vec3f* p2) -{ - bool res = details::sphereTriangleDistance(sp, tf1, tf2.transform(P1), tf2.transform(P2), tf2.transform(P3), dist, p1, p2); - return res; -} - - - -struct ContactPoint -{ - Vec3f normal; - Vec3f point; - FCL_REAL depth; - ContactPoint(const Vec3f& n, const Vec3f& p, FCL_REAL d) : normal(n), point(p), depth(d) {} -}; - - -static inline void lineClosestApproach(const Vec3f& pa, const Vec3f& ua, - const Vec3f& pb, const Vec3f& ub, - FCL_REAL* alpha, FCL_REAL* beta) -{ - Vec3f p = pb - pa; - FCL_REAL uaub = ua.dot(ub); - FCL_REAL q1 = ua.dot(p); - FCL_REAL q2 = -ub.dot(p); - FCL_REAL d = 1 - uaub * uaub; - if(d <= (FCL_REAL)(0.0001f)) - { - *alpha = 0; - *beta = 0; - } - else - { - d = 1 / d; - *alpha = (q1 + uaub * q2) * d; - *beta = (uaub * q1 + q2) * d; - } -} - -// find all the intersection points between the 2D rectangle with vertices -// at (+/-h[0],+/-h[1]) and the 2D quadrilateral with vertices (p[0],p[1]), -// (p[2],p[3]),(p[4],p[5]),(p[6],p[7]). -// -// the intersection points are returned as x,y pairs in the 'ret' array. -// the number of intersection points is returned by the function (this will -// be in the range 0 to 8). -static int intersectRectQuad2(FCL_REAL h[2], FCL_REAL p[8], FCL_REAL ret[16]) -{ - // q (and r) contain nq (and nr) coordinate points for the current (and - // chopped) polygons - int nq = 4, nr = 0; - FCL_REAL buffer[16]; - FCL_REAL* q = p; - FCL_REAL* r = ret; - for(int dir = 0; dir <= 1; ++dir) - { - // direction notation: xy[0] = x axis, xy[1] = y axis - for(int sign = -1; sign <= 1; sign += 2) - { - // chop q along the line xy[dir] = sign*h[dir] - FCL_REAL* pq = q; - FCL_REAL* pr = r; - nr = 0; - for(int i = nq; i > 0; --i) - { - // go through all points in q and all lines between adjacent points - if(sign * pq[dir] < h[dir]) - { - // this point is inside the chopping line - pr[0] = pq[0]; - pr[1] = pq[1]; - pr += 2; - nr++; - if(nr & 8) - { - q = r; - goto done; - } - } - FCL_REAL* nextq = (i > 1) ? pq+2 : q; - if((sign*pq[dir] < h[dir]) ^ (sign*nextq[dir] < h[dir])) - { - // this line crosses the chopping line - pr[1-dir] = pq[1-dir] + (nextq[1-dir]-pq[1-dir]) / - (nextq[dir]-pq[dir]) * (sign*h[dir]-pq[dir]); - pr[dir] = sign*h[dir]; - pr += 2; - nr++; - if(nr & 8) - { - q = r; - goto done; - } - } - pq += 2; - } - q = r; - r = (q == ret) ? buffer : ret; - nq = nr; - } - } - - done: - if(q != ret) memcpy(ret, q, nr*2*sizeof(FCL_REAL)); - return nr; -} - -// given n points in the plane (array p, of size 2*n), generate m points that -// best represent the whole set. the definition of 'best' here is not -// predetermined - the idea is to select points that give good box-box -// collision detection behavior. the chosen point indexes are returned in the -// array iret (of size m). 'i0' is always the first entry in the array. -// n must be in the range [1..8]. m must be in the range [1..n]. i0 must be -// in the range [0..n-1]. -static inline void cullPoints2(int n, FCL_REAL p[], int m, int i0, int iret[]) -{ - // compute the centroid of the polygon in cx,cy - FCL_REAL a, cx, cy, q; - switch(n) - { - case 1: - cx = p[0]; - cy = p[1]; - break; - case 2: - cx = 0.5 * (p[0] + p[2]); - cy = 0.5 * (p[1] + p[3]); - break; - default: - a = 0; - cx = 0; - cy = 0; - for(int i = 0; i < n-1; ++i) - { - q = p[i*2]*p[i*2+3] - p[i*2+2]*p[i*2+1]; - a += q; - cx += q*(p[i*2]+p[i*2+2]); - cy += q*(p[i*2+1]+p[i*2+3]); - } - q = p[n*2-2]*p[1] - p[0]*p[n*2-1]; - if(std::abs(a+q) > std::numeric_limits<FCL_REAL>::epsilon()) - a = 1/(3*(a+q)); - else - a= 1e18f; - - cx = a*(cx + q*(p[n*2-2]+p[0])); - cy = a*(cy + q*(p[n*2-1]+p[1])); - } - - - // compute the angle of each point w.r.t. the centroid - FCL_REAL A[8]; - for(int i = 0; i < n; ++i) - A[i] = atan2(p[i*2+1]-cy,p[i*2]-cx); - - // search for points that have angles closest to A[i0] + i*(2*pi/m). - int avail[8]; - for(int i = 0; i < n; ++i) avail[i] = 1; - avail[i0] = 0; - iret[0] = i0; - iret++; - const double pi = boost::math::constants::pi<FCL_REAL>(); - for(int j = 1; j < m; ++j) - { - a = j*(2*pi/m) + A[i0]; - if (a > pi) a -= 2*pi; - FCL_REAL maxdiff= 1e9, diff; - - *iret = i0; // iret is not allowed to keep this value, but it sometimes does, when diff=#QNAN0 - for(int i = 0; i < n; ++i) - { - if(avail[i]) - { - diff = std::abs(A[i]-a); - if(diff > pi) diff = 2*pi - diff; - if(diff < maxdiff) - { - maxdiff = diff; - *iret = i; - } - } - } - avail[*iret] = 0; - iret++; - } -} - - - -int boxBox2(const Vec3f& side1, const Matrix3f& R1, const Vec3f& T1, - const Vec3f& side2, const Matrix3f& R2, const Vec3f& T2, - Vec3f& normal, FCL_REAL* depth, int* return_code, - int maxc, std::vector<ContactPoint>& contacts) -{ - const FCL_REAL fudge_factor = FCL_REAL(1.05); - Vec3f normalC; - FCL_REAL s, s2, l; - int invert_normal, code; - - Vec3f p (T2 - T1); // get vector from centers of box 1 to box 2, relative to box 1 - Vec3f pp (R1.transpose() * p); // get pp = p relative to body 1 - - // get side lengths / 2 - Vec3f A (side1 * 0.5); - Vec3f B (side2 * 0.5); - - // Rij is R1'*R2, i.e. the relative rotation between R1 and R2 - Matrix3f R (R1.transpose() * R2); - Matrix3f Q (R.cwiseAbs()); - - - // for all 15 possible separating axes: - // * see if the axis separates the boxes. if so, return 0. - // * find the depth of the penetration along the separating axis (s2) - // * if this is the largest depth so far, record it. - // the normal vector will be set to the separating axis with the smallest - // depth. note: normalR is set to point to a column of R1 or R2 if that is - // the smallest depth normal so far. otherwise normalR is 0 and normalC is - // set to a vector relative to body 1. invert_normal is 1 if the sign of - // the normal should be flipped. - - int best_col_id = -1; - const Matrix3f* normalR = 0; - FCL_REAL tmp = 0; - - s = - std::numeric_limits<FCL_REAL>::max(); - invert_normal = 0; - code = 0; - - // separating axis = u1, u2, u3 - tmp = pp[0]; - s2 = std::abs(tmp) - (Q.row(0).dot(B) + A[0]); - if(s2 > 0) { *return_code = 0; return 0; } - if(s2 > s) - { - s = s2; - best_col_id = 0; - normalR = &R1; - invert_normal = (tmp < 0); - code = 1; - } - - tmp = pp[1]; - s2 = std::abs(tmp) - (Q.row(1).dot(B) + A[1]); - if(s2 > 0) { *return_code = 0; return 0; } - if(s2 > s) - { - s = s2; - best_col_id = 1; - normalR = &R1; - invert_normal = (tmp < 0); - code = 2; - } - - tmp = pp[2]; - s2 = std::abs(tmp) - (Q.row(2).dot(B) + A[2]); - if(s2 > 0) { *return_code = 0; return 0; } - if(s2 > s) - { - s = s2; - best_col_id = 2; - normalR = &R1; - invert_normal = (tmp < 0); - code = 3; - } - - // separating axis = v1, v2, v3 - tmp = R2.col(0).dot(p); - s2 = std::abs(tmp) - (Q.col(0).dot(A) + B[0]); - if(s2 > 0) { *return_code = 0; return 0; } - if(s2 > s) - { - s = s2; - best_col_id = 0; - normalR = &R2; - invert_normal = (tmp < 0); - code = 4; - } - - tmp = R2.col(1).dot(p); - s2 = std::abs(tmp) - (Q.col(1).dot(A) + B[1]); - if(s2 > 0) { *return_code = 0; return 0; } - if(s2 > s) - { - s = s2; - best_col_id = 1; - normalR = &R2; - invert_normal = (tmp < 0); - code = 5; - } - - tmp = R2.col(2).dot(p); - s2 = std::abs(tmp) - (Q.col(2).dot(A) + B[2]); - if(s2 > 0) { *return_code = 0; return 0; } - if(s2 > s) - { - s = s2; - best_col_id = 2; - normalR = &R2; - invert_normal = (tmp < 0); - code = 6; - } - - - FCL_REAL fudge2(1.0e-6); - Q.array() += fudge2; - - Vec3f n; - FCL_REAL eps = std::numeric_limits<FCL_REAL>::epsilon(); - - // separating axis = u1 x (v1,v2,v3) - tmp = pp[2] * R(1, 0) - pp[1] * R(2, 0); - s2 = std::abs(tmp) - (A[1] * Q(2, 0) + A[2] * Q(1, 0) + B[1] * Q(0, 2) + B[2] * Q(0, 1)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(0, -R(2, 0), R(1, 0)); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 7; - } - } - - tmp = pp[2] * R(1, 1) - pp[1] * R(2, 1); - s2 = std::abs(tmp) - (A[1] * Q(2, 1) + A[2] * Q(1, 1) + B[0] * Q(0, 2) + B[2] * Q(0, 0)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(0, -R(2, 1), R(1, 1)); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 8; - } - } - - tmp = pp[2] * R(1, 2) - pp[1] * R(2, 2); - s2 = std::abs(tmp) - (A[1] * Q(2, 2) + A[2] * Q(1, 2) + B[0] * Q(0, 1) + B[1] * Q(0, 0)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(0, -R(2, 2), R(1, 2)); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 9; - } - } - - // separating axis = u2 x (v1,v2,v3) - tmp = pp[0] * R(2, 0) - pp[2] * R(0, 0); - s2 = std::abs(tmp) - (A[0] * Q(2, 0) + A[2] * Q(0, 0) + B[1] * Q(1, 2) + B[2] * Q(1, 1)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(R(2, 0), 0, -R(0, 0)); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 10; - } - } - - tmp = pp[0] * R(2, 1) - pp[2] * R(0, 1); - s2 = std::abs(tmp) - (A[0] * Q(2, 1) + A[2] * Q(0, 1) + B[0] * Q(1, 2) + B[2] * Q(1, 0)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(R(2, 1), 0, -R(0, 1)); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 11; - } - } - - tmp = pp[0] * R(2, 2) - pp[2] * R(0, 2); - s2 = std::abs(tmp) - (A[0] * Q(2, 2) + A[2] * Q(0, 2) + B[0] * Q(1, 1) + B[1] * Q(1, 0)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(R(2, 2), 0, -R(0, 2)); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 12; - } - } - - // separating axis = u3 x (v1,v2,v3) - tmp = pp[1] * R(0, 0) - pp[0] * R(1, 0); - s2 = std::abs(tmp) - (A[0] * Q(1, 0) + A[1] * Q(0, 0) + B[1] * Q(2, 2) + B[2] * Q(2, 1)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(-R(1, 0), R(0, 0), 0); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 13; - } - } - - tmp = pp[1] * R(0, 1) - pp[0] * R(1, 1); - s2 = std::abs(tmp) - (A[0] * Q(1, 1) + A[1] * Q(0, 1) + B[0] * Q(2, 2) + B[2] * Q(2, 0)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(-R(1, 1), R(0, 1), 0); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 14; - } - } - - tmp = pp[1] * R(0, 2) - pp[0] * R(1, 2); - s2 = std::abs(tmp) - (A[0] * Q(1, 2) + A[1] * Q(0, 2) + B[0] * Q(2, 1) + B[1] * Q(2, 0)); - if(s2 > 0) { *return_code = 0; return 0; } - n = Vec3f(-R(1, 2), R(0, 2), 0); - l = n.norm(); - if(l > eps) - { - s2 /= l; - if(s2 * fudge_factor > s) - { - s = s2; - best_col_id = -1; - normalC = n / l; - invert_normal = (tmp < 0); - code = 15; - } - } - - - - if (!code) { *return_code = code; return 0; } - - // if we get to this point, the boxes interpenetrate. compute the normal - // in global coordinates. - if(best_col_id != -1) - normal = normalR->col(best_col_id); - else - normal = R1 * normalC; - - if(invert_normal) - normal = -normal; - - *depth = -s; // s is negative when the boxes are in collision - - // compute contact point(s) - - if(code > 6) - { - // an edge from box 1 touches an edge from box 2. - // find a point pa on the intersecting edge of box 1 - Vec3f pa(T1); - FCL_REAL sign; - - for(int j = 0; j < 3; ++j) - { - sign = (R1.col(j).dot(normal) > 0) ? 1 : -1; - pa += R1.col(j) * (A[j] * sign); - } - - // find a point pb on the intersecting edge of box 2 - Vec3f pb(T2); - - for(int j = 0; j < 3; ++j) - { - sign = (R2.col(j).dot(normal) > 0) ? -1 : 1; - pb += R2.col(j) * (B[j] * sign); - } - - FCL_REAL alpha, beta; - Vec3f ua(R1.col((code-7)/3)); - Vec3f ub(R2.col((code-7)%3)); - - lineClosestApproach(pa, ua, pb, ub, &alpha, &beta); - pa += ua * alpha; - pb += ub * beta; - - - // Vec3f pointInWorld((pa + pb) * 0.5); - // contacts.push_back(ContactPoint(-normal, pointInWorld, -*depth)); - contacts.push_back(ContactPoint(-normal,pb,-*depth)); - *return_code = code; - - return 1; - } - - // okay, we have a face-something intersection (because the separating - // axis is perpendicular to a face). define face 'a' to be the reference - // face (i.e. the normal vector is perpendicular to this) and face 'b' to be - // the incident face (the closest face of the other box). - - const Matrix3f *Ra, *Rb; - const Vec3f *pa, *pb, *Sa, *Sb; - - if(code <= 3) - { - Ra = &R1; - Rb = &R2; - pa = &T1; - pb = &T2; - Sa = &A; - Sb = &B; - } - else - { - Ra = &R2; - Rb = &R1; - pa = &T2; - pb = &T1; - Sa = &B; - Sb = &A; - } - - // nr = normal vector of reference face dotted with axes of incident box. - // anr = absolute values of nr. - Vec3f normal2, nr, anr; - if(code <= 3) - normal2 = normal; - else - normal2 = -normal; - - nr = Rb->transpose() * normal2; - anr = nr.cwiseAbs(); - - // find the largest compontent of anr: this corresponds to the normal - // for the indident face. the other axis numbers of the indicent face - // are stored in a1,a2. - int lanr, a1, a2; - if(anr[1] > anr[0]) - { - if(anr[1] > anr[2]) - { - a1 = 0; - lanr = 1; - a2 = 2; - } - else - { - a1 = 0; - a2 = 1; - lanr = 2; - } - } - else - { - if(anr[0] > anr[2]) - { - lanr = 0; - a1 = 1; - a2 = 2; - } - else - { - a1 = 0; - a2 = 1; - lanr = 2; - } - } - - // compute center point of incident face, in reference-face coordinates - Vec3f center; - if(nr[lanr] < 0) - center = (*pb) - (*pa) + Rb->col(lanr) * ((*Sb)[lanr]); - else - center = (*pb) - (*pa) - Rb->col(lanr) * ((*Sb)[lanr]); - - // find the normal and non-normal axis numbers of the reference box - int codeN, code1, code2; - if(code <= 3) - codeN = code-1; - else codeN = code-4; - - if(codeN == 0) - { - code1 = 1; - code2 = 2; - } - else if(codeN == 1) - { - code1 = 0; - code2 = 2; - } - else - { - code1 = 0; - code2 = 1; - } - - // find the four corners of the incident face, in reference-face coordinates - FCL_REAL quad[8]; // 2D coordinate of incident face (x,y pairs) - FCL_REAL c1, c2, m11, m12, m21, m22; - c1 = Ra->col(code1).dot(center); - c2 = Ra->col(code2).dot(center); - // optimize this? - we have already computed this data above, but it is not - // stored in an easy-to-index format. for now it's quicker just to recompute - // the four dot products. - Vec3f tempRac = Ra->col(code1); - m11 = Rb->col(a1).dot(tempRac); - m12 = Rb->col(a2).dot(tempRac); - tempRac = Ra->col(code2); - m21 = Rb->col(a1).dot(tempRac); - m22 = Rb->col(a2).dot(tempRac); - - FCL_REAL k1 = m11 * (*Sb)[a1]; - FCL_REAL k2 = m21 * (*Sb)[a1]; - FCL_REAL k3 = m12 * (*Sb)[a2]; - FCL_REAL k4 = m22 * (*Sb)[a2]; - quad[0] = c1 - k1 - k3; - quad[1] = c2 - k2 - k4; - quad[2] = c1 - k1 + k3; - quad[3] = c2 - k2 + k4; - quad[4] = c1 + k1 + k3; - quad[5] = c2 + k2 + k4; - quad[6] = c1 + k1 - k3; - quad[7] = c2 + k2 - k4; - - // find the size of the reference face - FCL_REAL rect[2]; - rect[0] = (*Sa)[code1]; - rect[1] = (*Sa)[code2]; - - // intersect the incident and reference faces - FCL_REAL ret[16]; - int n_intersect = intersectRectQuad2(rect, quad, ret); - if(n_intersect < 1) { *return_code = code; return 0; } // this should never happen - - // convert the intersection points into reference-face coordinates, - // and compute the contact position and depth for each point. only keep - // those points that have a positive (penetrating) depth. delete points in - // the 'ret' array as necessary so that 'point' and 'ret' correspond. - Vec3f points[8]; // penetrating contact points - FCL_REAL dep[8]; // depths for those points - FCL_REAL det1 = 1.f/(m11*m22 - m12*m21); - m11 *= det1; - m12 *= det1; - m21 *= det1; - m22 *= det1; - int cnum = 0; // number of penetrating contact points found - for(int j = 0; j < n_intersect; ++j) - { - FCL_REAL k1 = m22*(ret[j*2]-c1) - m12*(ret[j*2+1]-c2); - FCL_REAL k2 = -m21*(ret[j*2]-c1) + m11*(ret[j*2+1]-c2); - points[cnum] = center + Rb->col(a1) * k1 + Rb->col(a2) * k2; - dep[cnum] = (*Sa)[codeN] - normal2.dot(points[cnum]); - if(dep[cnum] >= 0) - { - ret[cnum*2] = ret[j*2]; - ret[cnum*2+1] = ret[j*2+1]; - cnum++; - } - } - if(cnum < 1) { *return_code = code; return 0; } // this should never happen - - // we can't generate more contacts than we actually have - if(maxc > cnum) maxc = cnum; - if(maxc < 1) maxc = 1; - - if(cnum <= maxc) - { - if(code<4) - { - // we have less contacts than we need, so we use them all - for(int j = 0; j < cnum; ++j) - { - Vec3f pointInWorld = points[j] + (*pa); - contacts.push_back(ContactPoint(-normal, pointInWorld, -dep[j])); - } - } - else - { - // we have less contacts than we need, so we use them all - for(int j = 0; j < cnum; ++j) - { - Vec3f pointInWorld = points[j] + (*pa) - normal * dep[j]; - contacts.push_back(ContactPoint(-normal, pointInWorld, -dep[j])); - } - } - } - else - { - // we have more contacts than are wanted, some of them must be culled. - // find the deepest point, it is always the first contact. - int i1 = 0; - FCL_REAL maxdepth = dep[0]; - for(int i = 1; i < cnum; ++i) - { - if(dep[i] > maxdepth) - { - maxdepth = dep[i]; - i1 = i; - } - } - - int iret[8]; - cullPoints2(cnum, ret, maxc, i1, iret); - - for(int j = 0; j < maxc; ++j) - { - Vec3f posInWorld = points[iret[j]] + (*pa); - if(code < 4) - contacts.push_back(ContactPoint(-normal, posInWorld, -dep[iret[j]])); - else - contacts.push_back(ContactPoint(-normal, posInWorld - normal * dep[iret[j]], -dep[iret[j]])); - } - cnum = maxc; - } - - *return_code = code; - return cnum; -} - -bool compareContactPoints(const ContactPoint& c1,const ContactPoint& c2) { return c1.depth < c2.depth; } // Accending order, assuming penetration depth is a negative number! - -bool boxBoxIntersect(const Box& s1, const Transform3f& tf1, - const Box& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth_, Vec3f* normal_) -{ - std::vector<ContactPoint> contacts; - int return_code; - Vec3f normal; - FCL_REAL depth; - /* int cnum = */ boxBox2(s1.side, tf1.getRotation(), tf1.getTranslation(), - s2.side, tf2.getRotation(), tf2.getTranslation(), - normal, &depth, &return_code, - 4, contacts); - - if(normal_) *normal_ = normal; - if(penetration_depth_) *penetration_depth_ = depth; - - if(contact_points) - { - if(contacts.size() > 0) - { - std::sort(contacts.begin(), contacts.end(), compareContactPoints); - *contact_points = contacts[0].point; - if(penetration_depth_) *penetration_depth_ = -contacts[0].depth; - } - } - - return return_code != 0; -} - -template<typename T> -T halfspaceIntersectTolerance() -{ - return 0; -} - -template<> -float halfspaceIntersectTolerance() -{ - return 0.0001f; -} - -template<> -double halfspaceIntersectTolerance() -{ - return 0.0000001; -} - -bool sphereHalfspaceIntersect(const Sphere& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Halfspace new_s2 = transform(s2, tf2); - const Vec3f& center = tf1.getTranslation(); - FCL_REAL depth = s1.radius - new_s2.signedDistance(center); - if(depth >= 0) - { - if(normal) *normal = -new_s2.n; // pointing from s1 to s2 - if(penetration_depth) *penetration_depth = depth; - if(contact_points) *contact_points = center - new_s2.n * s1.radius + new_s2.n * (depth * 0.5); - return true; - } - else - return false; -} - -/// @brief box half space, a, b, c = +/- edge size -/// n^T * (R(o + a v1 + b v2 + c v3) + T) <= d -/// so (R^T n) (a v1 + b v2 + c v3) + n * T <= d -/// check whether d - n * T - (R^T n) (a v1 + b v2 + c v3) >= 0 for some a, b, c -/// the max value of left side is d - n * T + |(R^T n) (a v1 + b v2 + c v3)|, check that is enough -bool boxHalfspaceIntersect(const Box& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2) -{ - Halfspace new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f Q = R.transpose() * new_s2.n; - Vec3f A(Q[0] * s1.side[0], Q[1] * s1.side[1], Q[2] * s1.side[2]); - Vec3f B = A.cwiseAbs(); - - FCL_REAL depth = 0.5 * (B[0] + B[1] + B[2]) - new_s2.signedDistance(T); - return (depth >= 0); -} - - -bool boxHalfspaceIntersect(const Box& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - if(!contact_points && !penetration_depth && !normal) - return boxHalfspaceIntersect(s1, tf1, s2, tf2); - else - { - Halfspace new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f Q = R.transpose() * new_s2.n; - Vec3f A(Q[0] * s1.side[0], Q[1] * s1.side[1], Q[2] * s1.side[2]); - Vec3f B = A.cwiseAbs(); - - FCL_REAL depth = 0.5 * (B[0] + B[1] + B[2]) - new_s2.signedDistance(T); - if(depth < 0) return false; - - Vec3f axis[3]; - axis[0] = R.col(0); - axis[1] = R.col(1); - axis[2] = R.col(2); - - /// find deepest point - Vec3f p(T); - int sign = 0; - - if(std::abs(Q[0] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(Q[0] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) - { - sign = (A[0] > 0) ? -1 : 1; - p += axis[0] * (0.5 * s1.side[0] * sign); - } - else if(std::abs(Q[1] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(Q[1] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) - { - sign = (A[1] > 0) ? -1 : 1; - p += axis[1] * (0.5 * s1.side[1] * sign); - } - else if(std::abs(Q[2] - 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(Q[2] + 1) < halfspaceIntersectTolerance<FCL_REAL>()) - { - sign = (A[2] > 0) ? -1 : 1; - p += axis[2] * (0.5 * s1.side[2] * sign); - } - else - { - for(std::size_t i = 0; i < 3; ++i) - { - sign = (A[i] > 0) ? -1 : 1; - p += axis[i] * (0.5 * s1.side[i] * sign); - } - } - - /// compute the contact point from the deepest point - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = p + new_s2.n * (depth * 0.5); - - return true; - } -} - -bool capsuleHalfspaceIntersect(const Capsule& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Halfspace new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - - FCL_REAL cosa = dir_z.dot(new_s2.n); - if(std::abs(cosa) < halfspaceIntersectTolerance<FCL_REAL>()) - { - FCL_REAL signed_dist = new_s2.signedDistance(T); - FCL_REAL depth = s1.radius - signed_dist; - if(depth < 0) return false; - - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = T + new_s2.n * (0.5 * depth - s1.radius); - - return true; - } - else - { - int sign = (cosa > 0) ? -1 : 1; - Vec3f p = T + dir_z * (s1.lz * 0.5 * sign); - - FCL_REAL signed_dist = new_s2.signedDistance(p); - FCL_REAL depth = s1.radius - signed_dist; - if(depth < 0) return false; - - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) - { - // deepest point - Vec3f c = p - new_s2.n * s1.radius; - *contact_points = c + new_s2.n * (0.5 * depth); - } - - return true; - } -} - - -bool cylinderHalfspaceIntersect(const Cylinder& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Halfspace new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - FCL_REAL cosa = dir_z.dot(new_s2.n); - - if(cosa < halfspaceIntersectTolerance<FCL_REAL>()) - { - FCL_REAL signed_dist = new_s2.signedDistance(T); - FCL_REAL depth = s1.radius - signed_dist; - if(depth < 0) return false; - - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = T + new_s2.n * (0.5 * depth - s1.radius); - - return true; - } - else - { - Vec3f C = dir_z * cosa - new_s2.n; - if(std::abs(cosa + 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < halfspaceIntersectTolerance<FCL_REAL>()) - C = Vec3f(0, 0, 0); - else - { - FCL_REAL s = C.norm(); - s = s1.radius / s; - C *= s; - } - - int sign = (cosa > 0) ? -1 : 1; - // deepest point - Vec3f p = T + dir_z * (s1.lz * 0.5 * sign) + C; - FCL_REAL depth = -new_s2.signedDistance(p); - if(depth < 0) return false; - else - { - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = p + new_s2.n * (0.5 * depth); - return true; - } - } -} - - -bool coneHalfspaceIntersect(const Cone& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Halfspace new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - FCL_REAL cosa = dir_z.dot(new_s2.n); - - if(cosa < halfspaceIntersectTolerance<FCL_REAL>()) - { - FCL_REAL signed_dist = new_s2.signedDistance(T); - FCL_REAL depth = s1.radius - signed_dist; - if(depth < 0) return false; - else - { - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = T - dir_z * (s1.lz * 0.5) + new_s2.n * (0.5 * depth - s1.radius); - return true; - } - } - else - { - Vec3f C = dir_z * cosa - new_s2.n; - if(std::abs(cosa + 1) < halfspaceIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < halfspaceIntersectTolerance<FCL_REAL>()) - C = Vec3f(0, 0, 0); - else - { - FCL_REAL s = C.norm(); - s = s1.radius / s; - C *= s; - } - - Vec3f p1 = T + dir_z * (0.5 * s1.lz); - Vec3f p2 = T - dir_z * (0.5 * s1.lz) + C; - - FCL_REAL d1 = new_s2.signedDistance(p1); - FCL_REAL d2 = new_s2.signedDistance(p2); - - if(d1 > 0 && d2 > 0) return false; - else - { - FCL_REAL depth = -std::min(d1, d2); - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = ((d1 < d2) ? p1 : p2) + new_s2.n * (0.5 * depth); - return true; - } - } -} - -bool convexHalfspaceIntersect(const Convex& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Halfspace new_s2 = transform(s2, tf2); - - Vec3f v; - FCL_REAL depth = std::numeric_limits<FCL_REAL>::max(); - - for(int i = 0; i < s1.num_points; ++i) - { - Vec3f p = tf1.transform(s1.points[i]); - - FCL_REAL d = new_s2.signedDistance(p); - if(d < depth) - { - depth = d; - v = p; - } - } - - if(depth <= 0) - { - if(contact_points) *contact_points = v - new_s2.n * (0.5 * depth); - if(penetration_depth) *penetration_depth = depth; - if(normal) *normal = -new_s2.n; - return true; - } - else - return false; -} - -// Intersection between half-space and triangle -// Half-space is in pose tf1, -// Triangle is in pose tf2 -// Computes distance and closest points (p1, p2) if no collision, -// contact point (p1 = p2), normal and penetration depth (-distance) -// if collision -bool halfspaceTriangleIntersect -(const Halfspace& s1, const Transform3f& tf1, const Vec3f& P1, const Vec3f& P2, - const Vec3f& P3, const Transform3f& tf2, FCL_REAL& distance, Vec3f& p1, - Vec3f& p2, Vec3f& normal) -{ - Halfspace new_s1 = transform(s1, tf1); - - Vec3f v = tf2.transform(P1); - FCL_REAL depth = new_s1.signedDistance(v); - - Vec3f p = tf2.transform(P2); - FCL_REAL d = new_s1.signedDistance(p); - if(d < depth) - { - depth = d; - v = p; - } - - p = tf2.transform(P3); - d = new_s1.signedDistance(p); - if(d < depth) - { - depth = d; - v = p; - } - // v is the vertex with minimal projection abscissa (depth) on normal to - //plane, - distance = depth; - if(depth <= 0) - { - normal = new_s1.n; - p1 = p2 = v - (0.5 * depth) * new_s1.n; - return true; - } - else - { - p1 = v - depth * new_s1.n; - p2 = v; - return false; - } -} - - -/// @brief return whether plane collides with halfspace -/// if the separation plane of the halfspace is parallel with the plane -/// return code 1, if the plane's normal is the same with halfspace's normal and plane is inside halfspace, also return plane in pl -/// return code 2, if the plane's normal is oppositie to the halfspace's normal and plane is inside halfspace, also return plane in pl -/// plane is outside halfspace, collision-free -/// if not parallel -/// return the intersection ray, return code 3. ray origin is p and direction is d -bool planeHalfspaceIntersect(const Plane& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Plane& pl, - Vec3f& p, Vec3f& d, - FCL_REAL& penetration_depth, - int& ret) -{ - Plane new_s1 = transform(s1, tf1); - Halfspace new_s2 = transform(s2, tf2); - - ret = 0; - - Vec3f dir = (new_s1.n).cross(new_s2.n); - FCL_REAL dir_norm = dir.squaredNorm(); - if(dir_norm < std::numeric_limits<FCL_REAL>::epsilon()) // parallel - { - if((new_s1.n).dot(new_s2.n) > 0) - { - if(new_s1.d < new_s2.d) - { - penetration_depth = new_s2.d - new_s1.d; - ret = 1; - pl = new_s1; - return true; - } - else - return false; - } - else - { - if(new_s1.d + new_s2.d > 0) - return false; - else - { - penetration_depth = -(new_s1.d + new_s2.d); - ret = 2; - pl = new_s1; - return true; - } - } - } - - Vec3f n = new_s2.n * new_s1.d - new_s1.n * new_s2.d; - Vec3f origin = n.cross(dir); - origin *= (1.0 / dir_norm); - - p = origin; - d = dir; - ret = 3; - penetration_depth = std::numeric_limits<FCL_REAL>::max(); - - return true; -} - -///@ brief return whether two halfspace intersect -/// if the separation planes of the two halfspaces are parallel -/// return code 1, if two halfspaces' normal are same and s1 is in s2, also return s1 in s; -/// return code 2, if two halfspaces' normal are same and s2 is in s1, also return s2 in s; -/// return code 3, if two halfspaces' normal are opposite and s1 and s2 are into each other; -/// collision free, if two halfspaces' are separate; -/// if the separation planes of the two halfspaces are not parallel, return intersection ray, return code 4. ray origin is p and direction is d -/// collision free return code 0 -bool halfspaceIntersect(const Halfspace& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f& p, Vec3f& d, - Halfspace& s, - FCL_REAL& penetration_depth, - int& ret) -{ - Halfspace new_s1 = transform(s1, tf1); - Halfspace new_s2 = transform(s2, tf2); - - ret = 0; - - Vec3f dir = (new_s1.n).cross(new_s2.n); - FCL_REAL dir_norm = dir.squaredNorm(); - if(dir_norm < std::numeric_limits<FCL_REAL>::epsilon()) // parallel - { - if((new_s1.n).dot(new_s2.n) > 0) - { - if(new_s1.d < new_s2.d) // s1 is inside s2 - { - ret = 1; - penetration_depth = std::numeric_limits<FCL_REAL>::max(); - s = new_s1; - } - else // s2 is inside s1 - { - ret = 2; - penetration_depth = std::numeric_limits<FCL_REAL>::max(); - s = new_s2; - } - return true; - } - else - { - if(new_s1.d + new_s2.d > 0) // not collision - return false; - else // in each other - { - ret = 3; - penetration_depth = -(new_s1.d + new_s2.d); - return true; - } - } - } - - Vec3f n = new_s2.n * new_s1.d - new_s1.n * new_s2.d; - Vec3f origin = n.cross(dir); - origin *= (1.0 / dir_norm); - - p = origin; - d = dir; - ret = 4; - penetration_depth = std::numeric_limits<FCL_REAL>::max(); - - return true; -} - -template<typename T> -T planeIntersectTolerance() -{ - return 0; -} - -template<> -double planeIntersectTolerance<double>() -{ - return 0.0000001; -} - -template<> -float planeIntersectTolerance<float>() -{ - return 0.0001f; -} - -bool spherePlaneIntersect(const Sphere& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Plane new_s2 = transform(s2, tf2); - - const Vec3f& center = tf1.getTranslation(); - FCL_REAL signed_dist = new_s2.signedDistance(center); - FCL_REAL depth = - std::abs(signed_dist) + s1.radius; - if(depth >= 0) - { - if(normal) { if (signed_dist > 0) *normal = -new_s2.n; else *normal = new_s2.n; } - if(penetration_depth) *penetration_depth = depth; - if(contact_points) *contact_points = center - new_s2.n * signed_dist; - - return true; - } - else - return false; -} - -/// @brief box half space, a, b, c = +/- edge size -/// n^T * (R(o + a v1 + b v2 + c v3) + T) ~ d -/// so (R^T n) (a v1 + b v2 + c v3) + n * T ~ d -/// check whether d - n * T - (R^T n) (a v1 + b v2 + c v3) >= 0 for some a, b, c and <=0 for some a, b, c -/// so need to check whether |d - n * T| <= |(R^T n)(a v1 + b v2 + c v3)|, the reason is as follows: -/// (R^T n) (a v1 + b v2 + c v3) can get |(R^T n) (a v1 + b v2 + c v3)| for one a, b, c. -/// if |d - n * T| <= |(R^T n)(a v1 + b v2 + c v3)| then can get both positive and negative value on the right side. -bool boxPlaneIntersect(const Box& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Plane new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f Q = R.transpose() * new_s2.n; - Vec3f A(Q[0] * s1.side[0], Q[1] * s1.side[1], Q[2] * s1.side[2]); - Vec3f B = A.cwiseAbs(); - - FCL_REAL signed_dist = new_s2.signedDistance(T); - FCL_REAL depth = 0.5 * (B[0] + B[1] + B[2]) - std::abs(signed_dist); - if(depth < 0) return false; - - Vec3f axis[3]; - axis[0] = R.col(0); - axis[1] = R.col(1); - axis[2] = R.col(2); - - // find the deepest point - Vec3f p = T; - - // when center is on the positive side of the plane, use a, b, c make (R^T n) (a v1 + b v2 + c v3) the minimum - // otherwise, use a, b, c make (R^T n) (a v1 + b v2 + c v3) the maximum - int sign = (signed_dist > 0) ? 1 : -1; - - if(std::abs(Q[0] - 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(Q[0] + 1) < planeIntersectTolerance<FCL_REAL>()) - { - int sign2 = (A[0] > 0) ? -1 : 1; - sign2 *= sign; - p += axis[0] * (0.5 * s1.side[0] * sign2); - } - else if(std::abs(Q[1] - 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(Q[1] + 1) < planeIntersectTolerance<FCL_REAL>()) - { - int sign2 = (A[1] > 0) ? -1 : 1; - sign2 *= sign; - p += axis[1] * (0.5 * s1.side[1] * sign2); - } - else if(std::abs(Q[2] - 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(Q[2] + 1) < planeIntersectTolerance<FCL_REAL>()) - { - int sign2 = (A[2] > 0) ? -1 : 1; - sign2 *= sign; - p += axis[2] * (0.5 * s1.side[2] * sign2); - } - else - { - for(std::size_t i = 0; i < 3; ++i) - { - int sign2 = (A[i] > 0) ? -1 : 1; - sign2 *= sign; - p += axis[i] * (0.5 * s1.side[i] * sign2); - } - } - - // compute the contact point by project the deepest point onto the plane - if(penetration_depth) *penetration_depth = depth; - if(normal) { if (signed_dist > 0) *normal = -new_s2.n; else *normal = new_s2.n; } - if(contact_points) *contact_points = p - new_s2.n * new_s2.signedDistance(p); - - return true; -} - - -bool capsulePlaneIntersect(const Capsule& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2) -{ - Plane new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - Vec3f p1 = T + dir_z * (0.5 * s1.lz); - Vec3f p2 = T - dir_z * (0.5 * s1.lz); - - FCL_REAL d1 = new_s2.signedDistance(p1); - FCL_REAL d2 = new_s2.signedDistance(p2); - - // two end points on different side of the plane - if(d1 * d2 <= 0) - return true; - - // two end points on the same side of the plane, but the end point spheres might intersect the plane - return (std::abs(d1) <= s1.radius) || (std::abs(d2) <= s1.radius); -} - -bool capsulePlaneIntersect(const Capsule& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Plane new_s2 = transform(s2, tf2); - - if(!contact_points && !penetration_depth && !normal) - return capsulePlaneIntersect(s1, tf1, s2, tf2); - else - { - Plane new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - - - Vec3f p1 = T + dir_z * (0.5 * s1.lz); - Vec3f p2 = T - dir_z * (0.5 * s1.lz); - - FCL_REAL d1 = new_s2.signedDistance(p1); - FCL_REAL d2 = new_s2.signedDistance(p2); - - FCL_REAL abs_d1 = std::abs(d1); - FCL_REAL abs_d2 = std::abs(d2); - - // two end points on different side of the plane - // the contact point is the intersect of axis with the plane - // the normal is the direction to avoid intersection - // the depth is the minimum distance to resolve the collision - if(d1 * d2 < -planeIntersectTolerance<FCL_REAL>()) - { - if(abs_d1 < abs_d2) - { - if(penetration_depth) *penetration_depth = abs_d1 + s1.radius; - if(contact_points) *contact_points = p1 * (abs_d2 / (abs_d1 + abs_d2)) + p2 * (abs_d1 / (abs_d1 + abs_d2)); - if(normal) { if (d1 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } - } - else - { - if(penetration_depth) *penetration_depth = abs_d2 + s1.radius; - if(contact_points) *contact_points = p1 * (abs_d2 / (abs_d1 + abs_d2)) + p2 * (abs_d1 / (abs_d1 + abs_d2)); - if(normal) { if (d2 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } - } - return true; - } - - if(abs_d1 > s1.radius && abs_d2 > s1.radius) - return false; - else - { - if(penetration_depth) *penetration_depth = s1.radius - std::min(abs_d1, abs_d2); - - if(contact_points) - { - if(abs_d1 <= s1.radius && abs_d2 <= s1.radius) - { - Vec3f c1 = p1 - new_s2.n * d2; - Vec3f c2 = p2 - new_s2.n * d1; - *contact_points = (c1 + c2) * 0.5; - } - else if(abs_d1 <= s1.radius) - { - Vec3f c = p1 - new_s2.n * d1; - *contact_points = c; - } - else if(abs_d2 <= s1.radius) - { - Vec3f c = p2 - new_s2.n * d2; - *contact_points = c; - } - } - - if(normal) { if (d1 < 0) *normal = new_s2.n; else *normal = -new_s2.n; } - return true; - } - } -} - -/// @brief cylinder-plane intersect -/// n^T (R (r * cosa * v1 + r * sina * v2 + h * v3) + T) ~ d -/// need one point to be positive and one to be negative -/// (n^T * v3) * h + n * T -d + r * (cosa * (n^T * R * v1) + sina * (n^T * R * v2)) ~ 0 -/// (n^T * v3) * h + r * (cosa * (n^T * R * v1) + sina * (n^T * R * v2)) + n * T - d ~ 0 -bool cylinderPlaneIntersect(const Cylinder& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2) -{ - Plane new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f Q = R.transpose() * new_s2.n; - - FCL_REAL term = std::abs(Q[2]) * s1.lz + s1.radius * std::sqrt(Q[0] * Q[0] + Q[1] * Q[1]); - FCL_REAL dist = new_s2.distance(T); - FCL_REAL depth = term - dist; - - if(depth < 0) - return false; - else - return true; -} - -bool cylinderPlaneIntersect(const Cylinder& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - if(!contact_points && !penetration_depth && !normal) - return cylinderPlaneIntersect(s1, tf1, s2, tf2); - else - { - Plane new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - FCL_REAL cosa = dir_z.dot(new_s2.n); - - if(std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) - { - FCL_REAL d = new_s2.signedDistance(T); - FCL_REAL depth = s1.radius - std::abs(d); - if(depth < 0) return false; - else - { - if(penetration_depth) *penetration_depth = depth; - if(normal) { if (d < 0) *normal = new_s2.n; else *normal = -new_s2.n; } - if(contact_points) *contact_points = T - new_s2.n * d; - return true; - } - } - else - { - Vec3f C = dir_z * cosa - new_s2.n; - if(std::abs(cosa + 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < planeIntersectTolerance<FCL_REAL>()) - C = Vec3f(0, 0, 0); - else - { - FCL_REAL s = C.norm(); - s = s1.radius / s; - C *= s; - } - - Vec3f p1 = T + dir_z * (0.5 * s1.lz); - Vec3f p2 = T - dir_z * (0.5 * s1.lz); - - Vec3f c1, c2; - if(cosa > 0) - { - c1 = p1 - C; - c2 = p2 + C; - } - else - { - c1 = p1 + C; - c2 = p2 - C; - } - - FCL_REAL d1 = new_s2.signedDistance(c1); - FCL_REAL d2 = new_s2.signedDistance(c2); - - if(d1 * d2 <= 0) - { - FCL_REAL abs_d1 = std::abs(d1); - FCL_REAL abs_d2 = std::abs(d2); - - if(abs_d1 > abs_d2) - { - if(penetration_depth) *penetration_depth = abs_d2; - if(contact_points) *contact_points = c2 - new_s2.n * d2; - if(normal) { if (d2 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } - } - else - { - if(penetration_depth) *penetration_depth = abs_d1; - if(contact_points) *contact_points = c1 - new_s2.n * d1; - if(normal) { if (d1 < 0) *normal = -new_s2.n; else *normal = new_s2.n; } - } - return true; - } - else - return false; - } - } -} - -bool conePlaneIntersect(const Cone& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Plane new_s2 = transform(s2, tf2); - - const Matrix3f& R = tf1.getRotation(); - const Vec3f& T = tf1.getTranslation(); - - Vec3f dir_z = R.col(2); - FCL_REAL cosa = dir_z.dot(new_s2.n); - - if(std::abs(cosa) < planeIntersectTolerance<FCL_REAL>()) - { - FCL_REAL d = new_s2.signedDistance(T); - FCL_REAL depth = s1.radius - std::abs(d); - if(depth < 0) return false; - else - { - if(penetration_depth) *penetration_depth = depth; - if(normal) { if (d < 0) *normal = new_s2.n; else *normal = -new_s2.n; } - if(contact_points) *contact_points = T - dir_z * (0.5 * s1.lz) + dir_z * (0.5 * depth / s1.radius * s1.lz) - new_s2.n * d; - return true; - } - } - else - { - Vec3f C = dir_z * cosa - new_s2.n; - if(std::abs(cosa + 1) < planeIntersectTolerance<FCL_REAL>() || std::abs(cosa - 1) < planeIntersectTolerance<FCL_REAL>()) - C = Vec3f(0, 0, 0); - else - { - FCL_REAL s = C.norm(); - s = s1.radius / s; - C *= s; - } - - Vec3f c[3]; - c[0] = T + dir_z * (0.5 * s1.lz); - c[1] = T - dir_z * (0.5 * s1.lz) + C; - c[2] = T - dir_z * (0.5 * s1.lz) - C; - - FCL_REAL d[3]; - d[0] = new_s2.signedDistance(c[0]); - d[1] = new_s2.signedDistance(c[1]); - d[2] = new_s2.signedDistance(c[2]); - - if((d[0] >= 0 && d[1] >= 0 && d[2] >= 0) || (d[0] <= 0 && d[1] <= 0 && d[2] <= 0)) - return false; - else - { - bool positive[3]; - for(std::size_t i = 0; i < 3; ++i) - positive[i] = (d[i] >= 0); - - int n_positive = 0; - FCL_REAL d_positive = 0, d_negative = 0; - for(std::size_t i = 0; i < 3; ++i) - { - if(positive[i]) - { - n_positive++; - if(d_positive <= d[i]) d_positive = d[i]; - } - else - { - if(d_negative <= -d[i]) d_negative = -d[i]; - } - } - - if(penetration_depth) *penetration_depth = std::min(d_positive, d_negative); - if(normal) { if (d_positive > d_negative) *normal = -new_s2.n; else *normal = new_s2.n; } - if(contact_points) - { - Vec3f p[2]; - Vec3f q; - - FCL_REAL p_d[2]; - FCL_REAL q_d(0); - - if(n_positive == 2) - { - for(std::size_t i = 0, j = 0; i < 3; ++i) - { - if(positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } - else { q = c[i]; q_d = d[i]; } - } - - Vec3f t1 = (-p[0] * q_d + q * p_d[0]) / (-q_d + p_d[0]); - Vec3f t2 = (-p[1] * q_d + q * p_d[1]) / (-q_d + p_d[1]); - *contact_points = (t1 + t2) * 0.5; - } - else - { - for(std::size_t i = 0, j = 0; i < 3; ++i) - { - if(!positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } - else { q = c[i]; q_d = d[i]; } - } - - Vec3f t1 = (p[0] * q_d - q * p_d[0]) / (q_d - p_d[0]); - Vec3f t2 = (p[1] * q_d - q * p_d[1]) / (q_d - p_d[1]); - *contact_points = (t1 + t2) * 0.5; - } - } - return true; - } - } -} - -bool convexPlaneIntersect(const Convex& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) -{ - Plane new_s2 = transform(s2, tf2); - - Vec3f v_min, v_max; - FCL_REAL d_min = std::numeric_limits<FCL_REAL>::max(), d_max = -std::numeric_limits<FCL_REAL>::max(); - - for(int i = 0; i < s1.num_points; ++i) - { - Vec3f p = tf1.transform(s1.points[i]); - - FCL_REAL d = new_s2.signedDistance(p); - - if(d < d_min) { d_min = d; v_min = p; } - if(d > d_max) { d_max = d; v_max = p; } - } - - if(d_min * d_max > 0) return false; - else - { - if(d_min + d_max > 0) - { - if(penetration_depth) *penetration_depth = -d_min; - if(normal) *normal = -new_s2.n; - if(contact_points) *contact_points = v_min - new_s2.n * d_min; - } - else - { - if(penetration_depth) *penetration_depth = d_max; - if(normal) *normal = new_s2.n; - if(contact_points) *contact_points = v_max - new_s2.n * d_max; - } - return true; - } -} - - - -bool planeTriangleIntersect -(const Plane& s1, const Transform3f& tf1, const Vec3f& P1, const Vec3f& P2, - const Vec3f& P3, const Transform3f& tf2, FCL_REAL distance, - Vec3f& p1, Vec3f& p2, Vec3f& normal) -{ - Plane new_s1 = transform(s1, tf1); - - Vec3f c[3]; - c[0] = tf2.transform(P1); - c[1] = tf2.transform(P2); - c[2] = tf2.transform(P3); - - FCL_REAL d[3]; - d[0] = new_s1.signedDistance(c[0]); - d[1] = new_s1.signedDistance(c[1]); - d[2] = new_s1.signedDistance(c[2]); - int imin; - if (d[0] >= 0 && d[1] >= 0 && d[2] >= 0) - { - if (d[0] < d[1]) - if (d[0] < d[2]) { - imin = 0; - } - else { // d [2] <= d[0] < d [1] - imin = 2; - } - else { // d[1] <= d[0] - if (d[2] < d[1]) { - imin = 2; - } else { // d[1] <= d[2] - imin = 1; - } - } - distance = d[imin]; - p2 = c[imin]; - p1 = c[imin] - d[imin] * new_s1.n; - return false; - } - if (d[0] <= 0 && d[1] <= 0 && d[2] <= 0) - { - if (d[0] > d[1]) - if (d[0] > d[2]) { - imin = 0; - } - else { // d [2] >= d[0] > d [1] - imin = 2; - } - else { // d[1] >= d[0] - if (d[2] > d[1]) { - imin = 2; - } else { // d[1] >= d[2] - imin = 1; - } - } - distance = -d[imin]; - p2 = c[imin]; - p1 = c[imin] - d[imin] * new_s1.n; - return false; - } - bool positive[3]; - for(std::size_t i = 0; i < 3; ++i) - positive[i] = (d[i] > 0); - - int n_positive = 0; - FCL_REAL d_positive = 0, d_negative = 0; - for(std::size_t i = 0; i < 3; ++i) - { - if(positive[i]) - { - n_positive++; - if(d_positive <= d[i]) d_positive = d[i]; - } - else - { - if(d_negative <= -d[i]) d_negative = -d[i]; - } - } - - distance = -std::min(d_positive, d_negative); - if (d_positive > d_negative) - { - normal = new_s1.n; - } else - { - normal = -new_s1.n; - } - Vec3f p[2]; - Vec3f q; - - FCL_REAL p_d[2]; - FCL_REAL q_d(0); - - if(n_positive == 2) - { - for(std::size_t i = 0, j = 0; i < 3; ++i) - { - if(positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } - else { q = c[i]; q_d = d[i]; } - } - - Vec3f t1 = (-p[0] * q_d + q * p_d[0]) / (-q_d + p_d[0]); - Vec3f t2 = (-p[1] * q_d + q * p_d[1]) / (-q_d + p_d[1]); - p1 = p2 = (t1 + t2) * 0.5; - } - else - { - for(std::size_t i = 0, j = 0; i < 3; ++i) - { - if(!positive[i]) { p[j] = c[i]; p_d[j] = d[i]; j++; } - else { q = c[i]; q_d = d[i]; } - } - - Vec3f t1 = (p[0] * q_d - q * p_d[0]) / (q_d - p_d[0]); - Vec3f t2 = (p[1] * q_d - q * p_d[1]) / (q_d - p_d[1]); - p1 = p2 = (t1 + t2) * 0.5; - } - return true; -} - -bool halfspacePlaneIntersect(const Halfspace& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Plane& pl, Vec3f& p, Vec3f& d, - FCL_REAL& penetration_depth, - int& ret) -{ - return planeHalfspaceIntersect(s2, tf2, s1, tf1, pl, p, d, penetration_depth, ret); -} - -bool planeIntersect(const Plane& s1, const Transform3f& tf1, - const Plane& s2, const Transform3f& tf2, - Vec3f* /*contact_points*/, FCL_REAL* /*penetration_depth*/, Vec3f* /*normal*/) -{ - Plane new_s1 = transform(s1, tf1); - Plane new_s2 = transform(s2, tf2); - - FCL_REAL a = (new_s1.n).dot(new_s2.n); - if(a == 1 && new_s1.d != new_s2.d) - return false; - if(a == -1 && new_s1.d != -new_s2.d) - return false; - - return true; -} - // Compute penetration distance and normal of two triangles in collision - // Normal is normal of triangle 1 (P1, P2, P3), penetration depth is the - // minimal distance (Q1, Q2, Q3) should be translated along the normal so - // that the triangles are collision free. - // - // Note that we compute here an upper bound of the penetration distance, - // not the exact value. - static FCL_REAL computePenetration - (const Vec3f& P1, const Vec3f& P2, const Vec3f& P3, - const Vec3f& Q1, const Vec3f& Q2, const Vec3f& Q3, const Vec3f& p1, - const Transform3f& tf1, const Transform3f& tf2, Vec3f& normal) - { - Vec3f globalP1 (tf1.transform (P1)); - Vec3f globalP2 (tf1.transform (P2)); - Vec3f globalP3 (tf1.transform (P3)); - Vec3f globalQ1 (tf2.transform (Q1)); - Vec3f globalQ2 (tf2.transform (Q2)); - Vec3f globalQ3 (tf2.transform (Q3)); - Vec3f u ((globalP2-globalP1).cross (globalP3-globalP1)); - normal = u.normalized (); - FCL_REAL depth; - FCL_REAL depth1 ((globalP1-globalQ1).dot (normal)); - FCL_REAL depth2 ((globalP1-globalQ2).dot (normal)); - FCL_REAL depth3 ((globalP1-globalQ3).dot (normal)); - depth = std::max (depth1, std::max (depth2, depth3)); - return depth; - } -} // details - // Shape intersect algorithms not using libccd // // +------------+-----+--------+---------+------+----------+-------+------------+----------+ @@ -2615,11 +145,18 @@ bool GJKSolver_indep::shapeIntersect<Box, Box>(const Box& s1, const Transform3f& } template<> -bool GJKSolver_indep::shapeIntersect<Sphere, Halfspace>(const Sphere& s1, const Transform3f& tf1, - const Halfspace& s2, const Transform3f& tf2, - Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) const +bool GJKSolver_indep::shapeIntersect<Sphere, Halfspace> +(const Sphere& s1, const Transform3f& tf1, + const Halfspace& s2, const Transform3f& tf2, + Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) const { - return details::sphereHalfspaceIntersect(s1, tf1, s2, tf2, contact_points, penetration_depth, normal); + FCL_REAL distance; + Vec3f p1, p2; + bool res = details::sphereHalfspaceIntersect(s1, tf1, s2, tf2, distance, p1, + p2, *normal); + *contact_points = p1; + *penetration_depth = -distance; + return res; } template<> @@ -2627,9 +164,14 @@ bool GJKSolver_indep::shapeIntersect<Halfspace, Sphere>(const Halfspace& s1, con const Sphere& s2, const Transform3f& tf2, Vec3f* contact_points, FCL_REAL* penetration_depth, Vec3f* normal) const { - const bool res = details::sphereHalfspaceIntersect(s2, tf2, s1, tf1, contact_points, penetration_depth, normal); - if (normal) (*normal) *= -1.0; - return res; + FCL_REAL distance; + Vec3f p1, p2; + bool res = details::sphereHalfspaceIntersect(s2, tf2, s1, tf1, distance, p1, + p2, *normal); + *contact_points = p1; + *penetration_depth = -distance; + (*normal) *= -1.0; + return res; } template<> -- GitLab