| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Software License Agreement (BSD License) | ||
| 3 | * Copyright (c) 2015-2021, CNRS-LAAS INRIA | ||
| 4 | * All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above | ||
| 13 | * copyright notice, this list of conditions and the following | ||
| 14 | * disclaimer in the documentation and/or other materials provided | ||
| 15 | * with the distribution. | ||
| 16 | * * Neither the name of Open Source Robotics Foundation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
| 23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
| 24 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
| 26 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
| 27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 28 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | ||
| 30 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 31 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 32 | */ | ||
| 33 | |||
| 34 | #include "coal/math/transform.h" | ||
| 35 | #include "coal/shape/geometric_shapes.h" | ||
| 36 | #include "coal/internal/shape_shape_func.h" | ||
| 37 | |||
| 38 | #include "coal/tracy.hh" | ||
| 39 | |||
| 40 | // Note that partial specialization of template functions is not allowed. | ||
| 41 | // Therefore, two implementations with the default narrow phase solvers are | ||
| 42 | // provided. If another narrow phase solver were to be used, the default | ||
| 43 | // template implementation would be called, unless the function is also | ||
| 44 | // specialized for this new type. | ||
| 45 | // | ||
| 46 | // One solution would be to make narrow phase solvers derive from an abstract | ||
| 47 | // class and specialize the template for this abstract class. | ||
| 48 | namespace coal { | ||
| 49 | struct GJKSolver; | ||
| 50 | |||
| 51 | namespace internal { | ||
| 52 | /// Clamp num / denom in [0, 1] | ||
| 53 | 834 | Scalar clamp(const Scalar& num, const Scalar& denom) { | |
| 54 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 834 times.
|
834 | assert(denom >= 0.); |
| 55 |
2/2✓ Branch 0 taken 366 times.
✓ Branch 1 taken 468 times.
|
834 | if (num <= 0.) |
| 56 | 366 | return 0.; | |
| 57 |
2/2✓ Branch 0 taken 338 times.
✓ Branch 1 taken 130 times.
|
468 | else if (num >= denom) |
| 58 | 338 | return 1.; | |
| 59 | else | ||
| 60 | 130 | return num / denom; | |
| 61 | } | ||
| 62 | |||
| 63 | /// Clamp s=s_n/s_d in [0, 1] and stores a + s * d in a_sd | ||
| 64 | 3653 | void clamped_linear(Vec3s& a_sd, const Vec3s& a, const Scalar& s_n, | |
| 65 | const Scalar& s_d, const Vec3s& d) { | ||
| 66 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3653 times.
|
3653 | assert(s_d >= 0.); |
| 67 |
2/2✓ Branch 0 taken 292 times.
✓ Branch 1 taken 3361 times.
|
3653 | if (s_n <= 0.) |
| 68 | 292 | a_sd = a; | |
| 69 |
2/2✓ Branch 0 taken 1309 times.
✓ Branch 1 taken 2052 times.
|
3361 | else if (s_n >= s_d) |
| 70 |
2/4✓ Branch 1 taken 1309 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1309 times.
✗ Branch 5 not taken.
|
1309 | a_sd = a + d; |
| 71 | else | ||
| 72 |
3/6✓ Branch 1 taken 2052 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2052 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2052 times.
✗ Branch 8 not taken.
|
2052 | a_sd = a + s_n / s_d * d; |
| 73 | 3653 | } | |
| 74 | |||
| 75 | // Compute the distance between C1 and C2 by computing the distance | ||
| 76 | // between the two segments supporting the capsules. | ||
| 77 | // Match algorithm of Real-Time Collision Detection, Christer Ericson - Closest | ||
| 78 | // Point of Two Line Segments | ||
| 79 | /// @param wp1, wp2: witness points on the capsules | ||
| 80 | /// @param normal: normal pointing from capsule1 to capsule2 | ||
| 81 | template <> | ||
| 82 | 5844 | Scalar ShapeShapeDistance<Capsule, Capsule>( | |
| 83 | const CollisionGeometry* o1, const Transform3s& tf1, | ||
| 84 | const CollisionGeometry* o2, const Transform3s& tf2, const GJKSolver*, | ||
| 85 | const bool, Vec3s& wp1, Vec3s& wp2, Vec3s& normal) { | ||
| 86 | COAL_TRACY_ZONE_SCOPED_N( | ||
| 87 | "coal::internal::ShapeShapeDistance<Capsule, Capsule>"); | ||
| 88 | 5844 | const Capsule* capsule1 = static_cast<const Capsule*>(o1); | |
| 89 | 5844 | const Capsule* capsule2 = static_cast<const Capsule*>(o2); | |
| 90 | |||
| 91 | 5844 | Scalar EPSILON = std::numeric_limits<Scalar>::epsilon() * 100; | |
| 92 | |||
| 93 | // We assume that capsules are centered at the origin. | ||
| 94 | 5844 | const coal::Vec3s& c1 = tf1.getTranslation(); | |
| 95 | 5844 | const coal::Vec3s& c2 = tf2.getTranslation(); | |
| 96 | // We assume that capsules are oriented along z-axis. | ||
| 97 | 5844 | Scalar halfLength1 = capsule1->halfLength; | |
| 98 | 5844 | Scalar halfLength2 = capsule2->halfLength; | |
| 99 | 5844 | Scalar radius1 = (capsule1->radius + capsule1->getSweptSphereRadius()); | |
| 100 | 5844 | Scalar radius2 = (capsule2->radius + capsule2->getSweptSphereRadius()); | |
| 101 | // direction of capsules | ||
| 102 | // ||d1|| = 2 * halfLength1 | ||
| 103 |
3/6✓ Branch 2 taken 5844 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5844 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 5844 times.
✗ Branch 9 not taken.
|
5844 | const coal::Vec3s d1 = 2 * halfLength1 * tf1.getRotation().col(2); |
| 104 |
3/6✓ Branch 2 taken 5844 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5844 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 5844 times.
✗ Branch 9 not taken.
|
5844 | const coal::Vec3s d2 = 2 * halfLength2 * tf2.getRotation().col(2); |
| 105 | |||
| 106 | // Starting point of the segments | ||
| 107 | // p1 + d1 is the end point of the segment | ||
| 108 |
3/6✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5844 times.
✗ Branch 8 not taken.
|
5844 | const coal::Vec3s p1 = c1 - d1 / 2; |
| 109 |
3/6✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5844 times.
✗ Branch 8 not taken.
|
5844 | const coal::Vec3s p2 = c2 - d2 / 2; |
| 110 |
2/4✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
|
5844 | const coal::Vec3s r = p1 - p2; |
| 111 |
1/2✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
|
5844 | Scalar a = d1.dot(d1); |
| 112 |
1/2✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
|
5844 | Scalar b = d1.dot(d2); |
| 113 |
1/2✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
|
5844 | Scalar c = d1.dot(r); |
| 114 |
1/2✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
|
5844 | Scalar e = d2.dot(d2); |
| 115 |
1/2✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
|
5844 | Scalar f = d2.dot(r); |
| 116 | // S1 is parametrized by the equation p1 + s * d1 | ||
| 117 | // S2 is parametrized by the equation p2 + t * d2 | ||
| 118 | |||
| 119 |
2/4✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
|
5844 | Vec3s w1, w2; |
| 120 |
2/2✓ Branch 0 taken 1000 times.
✓ Branch 1 taken 4844 times.
|
5844 | if (a <= EPSILON) { |
| 121 |
1/2✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
|
1000 | w1 = p1; |
| 122 |
1/2✓ Branch 0 taken 1000 times.
✗ Branch 1 not taken.
|
1000 | if (e <= EPSILON) |
| 123 | // Check if the segments degenerate into points | ||
| 124 |
1/2✓ Branch 1 taken 1000 times.
✗ Branch 2 not taken.
|
1000 | w2 = p2; |
| 125 | else | ||
| 126 | // First segment is degenerated | ||
| 127 | ✗ | clamped_linear(w2, p2, f, e, d2); | |
| 128 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4844 times.
|
4844 | } else if (e <= EPSILON) { |
| 129 | // Second segment is degenerated | ||
| 130 | ✗ | clamped_linear(w1, p1, -c, a, d1); | |
| 131 | ✗ | w2 = p2; | |
| 132 | } else { | ||
| 133 | // Always non-negative, equal 0 if the segments are colinear | ||
| 134 | 4844 | Scalar denom = Scalar(fmax(a * e - b * b, 0)); | |
| 135 | |||
| 136 | Scalar s; | ||
| 137 | Scalar t; | ||
| 138 |
2/2✓ Branch 0 taken 834 times.
✓ Branch 1 taken 4010 times.
|
4844 | if (denom > EPSILON) { |
| 139 |
1/2✓ Branch 1 taken 834 times.
✗ Branch 2 not taken.
|
834 | s = clamp((b * f - c * e), denom); |
| 140 | 834 | t = b * s + f; | |
| 141 | } else { | ||
| 142 | 4010 | s = 0.; | |
| 143 | 4010 | t = f; | |
| 144 | } | ||
| 145 | |||
| 146 |
2/2✓ Branch 0 taken 3261 times.
✓ Branch 1 taken 1583 times.
|
4844 | if (t <= 0.0) { |
| 147 |
1/2✓ Branch 1 taken 3261 times.
✗ Branch 2 not taken.
|
3261 | w2 = p2; |
| 148 |
1/2✓ Branch 1 taken 3261 times.
✗ Branch 2 not taken.
|
3261 | clamped_linear(w1, p1, -c, a, d1); |
| 149 |
2/2✓ Branch 0 taken 392 times.
✓ Branch 1 taken 1191 times.
|
1583 | } else if (t >= e) { |
| 150 |
1/2✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
|
392 | clamped_linear(w1, p1, (b - c), a, d1); |
| 151 |
2/4✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 392 times.
✗ Branch 5 not taken.
|
392 | w2 = p2 + d2; |
| 152 | } else { | ||
| 153 |
3/6✓ Branch 1 taken 1191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1191 times.
✗ Branch 8 not taken.
|
1191 | w1 = p1 + s * d1; |
| 154 |
3/6✓ Branch 1 taken 1191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1191 times.
✗ Branch 8 not taken.
|
1191 | w2 = p2 + t / e * d2; |
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | // witness points achieving the distance between the two segments | ||
| 159 |
2/4✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
|
5844 | Scalar distance = (w1 - w2).norm(); |
| 160 | |||
| 161 | // capsule spcecific distance computation | ||
| 162 | 5844 | distance = distance - (radius1 + radius2); | |
| 163 | |||
| 164 | // Normal points from o1 to o2 | ||
| 165 |
3/6✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5844 times.
✗ Branch 8 not taken.
|
5844 | normal = (w2 - w1).normalized(); |
| 166 |
3/6✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5844 times.
✗ Branch 8 not taken.
|
5844 | wp1 = w1 + radius1 * normal; |
| 167 |
3/6✓ Branch 1 taken 5844 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5844 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5844 times.
✗ Branch 8 not taken.
|
5844 | wp2 = w2 - radius2 * normal; |
| 168 | |||
| 169 | 5844 | return distance; | |
| 170 | } | ||
| 171 | } // namespace internal | ||
| 172 | |||
| 173 | } // namespace coal | ||
| 174 |