| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Software License Agreement (BSD License) | ||
| 3 | * | ||
| 4 | * Copyright (c) 2024, INRIA | ||
| 5 | * All rights reserved. | ||
| 6 | * | ||
| 7 | * Redistribution and use in source and binary forms, with or without | ||
| 8 | * modification, are permitted provided that the following conditions | ||
| 9 | * are met: | ||
| 10 | * | ||
| 11 | * * Redistributions of source code must retain the above copyright | ||
| 12 | * notice, this list of conditions and the following disclaimer. | ||
| 13 | * * Redistributions in binary form must reproduce the above | ||
| 14 | * copyright notice, this list of conditions and the following | ||
| 15 | * disclaimer in the documentation and/or other materials provided | ||
| 16 | * with the distribution. | ||
| 17 | * * Neither the name of Willow Garage, Inc. nor the names of its | ||
| 18 | * contributors may be used to endorse or promote products derived | ||
| 19 | * from this software without specific prior written permission. | ||
| 20 | * | ||
| 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
| 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
| 25 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
| 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
| 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
| 29 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | ||
| 31 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 32 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 33 | */ | ||
| 34 | |||
| 35 | /** \author Louis Montaut */ | ||
| 36 | |||
| 37 | #ifndef COAL_INTERNAL_SHAPE_SHAPE_CONTACT_PATCH_FUNC_H | ||
| 38 | #define COAL_INTERNAL_SHAPE_SHAPE_CONTACT_PATCH_FUNC_H | ||
| 39 | |||
| 40 | #include "coal/collision_data.h" | ||
| 41 | #include "coal/collision_utility.h" | ||
| 42 | #include "coal/narrowphase/narrowphase.h" | ||
| 43 | #include "coal/contact_patch/contact_patch_solver.h" | ||
| 44 | #include "coal/shape/geometric_shapes_traits.h" | ||
| 45 | |||
| 46 | #include "coal/tracy.hh" | ||
| 47 | |||
| 48 | namespace coal { | ||
| 49 | |||
| 50 | /// @brief Shape-shape contact patch computation. | ||
| 51 | /// Assumes that `csolver` and the `ContactPatchResult` have already been set up | ||
| 52 | /// by the `ContactPatchRequest`. | ||
| 53 | template <typename ShapeType1, typename ShapeType2> | ||
| 54 | struct ComputeShapeShapeContactPatch { | ||
| 55 | 24 | static void run(const CollisionGeometry* o1, const Transform3s& tf1, | |
| 56 | const CollisionGeometry* o2, const Transform3s& tf2, | ||
| 57 | const CollisionResult& collision_result, | ||
| 58 | const ContactPatchSolver* csolver, | ||
| 59 | const ContactPatchRequest& request, | ||
| 60 | ContactPatchResult& result) { | ||
| 61 | // Note: see specializations for Plane and Halfspace below. | ||
| 62 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
|
24 | if (!collision_result.isCollision()) { |
| 63 | ✗ | return; | |
| 64 | } | ||
| 65 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | COAL_ASSERT( |
| 66 | result.check(request), | ||
| 67 | "The contact patch result and request are incompatible (issue of " | ||
| 68 | "contact patch size or maximum number of contact patches). Make sure " | ||
| 69 | "result is initialized with request.", | ||
| 70 | std::logic_error); | ||
| 71 | |||
| 72 | 24 | const ShapeType1& s1 = static_cast<const ShapeType1&>(*o1); | |
| 73 | 24 | const ShapeType2& s2 = static_cast<const ShapeType2&>(*o2); | |
| 74 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 12 times.
|
48 | for (size_t i = 0; i < collision_result.numContacts(); ++i) { |
| 75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
24 | if (i >= request.max_num_patch) { |
| 76 | ✗ | break; | |
| 77 | } | ||
| 78 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
24 | csolver->setSupportGuess(collision_result.cached_support_func_guess); |
| 79 | 24 | const Contact& contact = collision_result.getContact(i); | |
| 80 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | ContactPatch& contact_patch = result.getUnusedContactPatch(); |
| 81 | 24 | csolver->computePatch(s1, tf1, s2, tf2, contact, contact_patch); | |
| 82 | } | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | /// @brief Computes the contact patch between a Plane/Halfspace and another | ||
| 87 | /// shape. | ||
| 88 | /// @tparam InvertShapes set to true if the first shape of the collision pair | ||
| 89 | /// is s2 and not s1 (if you had to invert (s1, tf1) and (s2, tf2) when calling | ||
| 90 | /// this function). | ||
| 91 | template <bool InvertShapes, typename OtherShapeType, typename PlaneOrHalfspace> | ||
| 92 | 24 | void computePatchPlaneOrHalfspace(const OtherShapeType& s1, | |
| 93 | const Transform3s& tf1, | ||
| 94 | const PlaneOrHalfspace& s2, | ||
| 95 | const Transform3s& tf2, | ||
| 96 | const ContactPatchSolver* csolver, | ||
| 97 | const Contact& contact, | ||
| 98 | ContactPatch& contact_patch) { | ||
| 99 | COAL_UNUSED_VARIABLE(s2); | ||
| 100 | COAL_UNUSED_VARIABLE(tf2); | ||
| 101 | 24 | constructContactPatchFrameFromContact(contact, contact_patch); | |
| 102 | if ((bool)(shape_traits<OtherShapeType>::IsStrictlyConvex)) { | ||
| 103 | // Only one point of contact; it has already been computed. | ||
| 104 | ✗ | contact_patch.addPoint(contact.pos); | |
| 105 | ✗ | return; | |
| 106 | } | ||
| 107 | |||
| 108 | // We only need to compute the support set in the direction of the normal. | ||
| 109 | // We need to temporarily express the patch in the local frame of shape1. | ||
| 110 | 24 | SupportSet& support_set = csolver->support_set_shape1; | |
| 111 |
2/4✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
|
24 | support_set.tf.rotation().noalias() = |
| 112 |
2/4✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
|
24 | tf1.rotation().transpose() * contact_patch.tf.rotation(); |
| 113 |
4/8✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 12 times.
✗ Branch 12 not taken.
|
48 | support_set.tf.translation().noalias() = |
| 114 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
48 | tf1.rotation().transpose() * |
| 115 | 24 | (contact_patch.tf.translation() - tf1.translation()); | |
| 116 | |||
| 117 | // Note: for now, taking into account swept-sphere radius does not change | ||
| 118 | // anything to the support set computations. However it will be used in the | ||
| 119 | // future if we want to store the offsets to the support plane for each point | ||
| 120 | // in a support set. | ||
| 121 | using SupportOptions = details::SupportOptions; | ||
| 122 | if (InvertShapes) { | ||
| 123 | 24 | support_set.direction = ContactPatch::PatchDirection::INVERTED; | |
| 124 | 24 | details::getShapeSupportSet<SupportOptions::WithSweptSphere>( | |
| 125 | 24 | &s1, support_set, csolver->support_guess[1], csolver->supports_data[1], | |
| 126 | 24 | csolver->num_samples_curved_shapes, csolver->patch_tolerance); | |
| 127 | } else { | ||
| 128 | ✗ | support_set.direction = ContactPatch::PatchDirection::DEFAULT; | |
| 129 | ✗ | details::getShapeSupportSet<SupportOptions::WithSweptSphere>( | |
| 130 | ✗ | &s1, support_set, csolver->support_guess[0], csolver->supports_data[0], | |
| 131 | ✗ | csolver->num_samples_curved_shapes, csolver->patch_tolerance); | |
| 132 | } | ||
| 133 | 24 | csolver->getResult(contact, &(support_set.points()), contact_patch); | |
| 134 | } | ||
| 135 | |||
| 136 | #define PLANE_OR_HSPACE_AND_OTHER_SHAPE_CONTACT_PATCH(PlaneOrHspace) \ | ||
| 137 | template <typename OtherShapeType> \ | ||
| 138 | struct ComputeShapeShapeContactPatch<OtherShapeType, PlaneOrHspace> { \ | ||
| 139 | static void run(const CollisionGeometry* o1, const Transform3s& tf1, \ | ||
| 140 | const CollisionGeometry* o2, const Transform3s& tf2, \ | ||
| 141 | const CollisionResult& collision_result, \ | ||
| 142 | const ContactPatchSolver* csolver, \ | ||
| 143 | const ContactPatchRequest& request, \ | ||
| 144 | ContactPatchResult& result) { \ | ||
| 145 | COAL_TRACY_ZONE_SCOPED_N( \ | ||
| 146 | "coal::ComputeShapeShapeContactPatch<OtherShapeType, " \ | ||
| 147 | "PlaneOrHspace>::run"); \ | ||
| 148 | if (!collision_result.isCollision()) { \ | ||
| 149 | return; \ | ||
| 150 | } \ | ||
| 151 | COAL_ASSERT( \ | ||
| 152 | result.check(request), \ | ||
| 153 | "The contact patch result and request are incompatible (issue of " \ | ||
| 154 | "contact patch size or maximum number of contact patches). Make " \ | ||
| 155 | "sure " \ | ||
| 156 | "result is initialized with request.", \ | ||
| 157 | std::logic_error); \ | ||
| 158 | \ | ||
| 159 | const OtherShapeType& s1 = static_cast<const OtherShapeType&>(*o1); \ | ||
| 160 | const PlaneOrHspace& s2 = static_cast<const PlaneOrHspace&>(*o2); \ | ||
| 161 | for (size_t i = 0; i < collision_result.numContacts(); ++i) { \ | ||
| 162 | if (i >= request.max_num_patch) { \ | ||
| 163 | break; \ | ||
| 164 | } \ | ||
| 165 | csolver->setSupportGuess(collision_result.cached_support_func_guess); \ | ||
| 166 | const Contact& contact = collision_result.getContact(i); \ | ||
| 167 | ContactPatch& contact_patch = result.getUnusedContactPatch(); \ | ||
| 168 | computePatchPlaneOrHalfspace<false, OtherShapeType, PlaneOrHspace>( \ | ||
| 169 | s1, tf1, s2, tf2, csolver, contact, contact_patch); \ | ||
| 170 | } \ | ||
| 171 | } \ | ||
| 172 | }; \ | ||
| 173 | \ | ||
| 174 | template <typename OtherShapeType> \ | ||
| 175 | struct ComputeShapeShapeContactPatch<PlaneOrHspace, OtherShapeType> { \ | ||
| 176 | static void run(const CollisionGeometry* o1, const Transform3s& tf1, \ | ||
| 177 | const CollisionGeometry* o2, const Transform3s& tf2, \ | ||
| 178 | const CollisionResult& collision_result, \ | ||
| 179 | const ContactPatchSolver* csolver, \ | ||
| 180 | const ContactPatchRequest& request, \ | ||
| 181 | ContactPatchResult& result) { \ | ||
| 182 | COAL_TRACY_ZONE_SCOPED_N( \ | ||
| 183 | "coal::ComputeShapeShapeContactPatch<PlaneOrHspace, " \ | ||
| 184 | "OtherShapeType>::run"); \ | ||
| 185 | if (!collision_result.isCollision()) { \ | ||
| 186 | return; \ | ||
| 187 | } \ | ||
| 188 | COAL_ASSERT( \ | ||
| 189 | result.check(request), \ | ||
| 190 | "The contact patch result and request are incompatible (issue of " \ | ||
| 191 | "contact patch size or maximum number of contact patches). Make " \ | ||
| 192 | "sure " \ | ||
| 193 | "result is initialized with request.", \ | ||
| 194 | std::logic_error); \ | ||
| 195 | \ | ||
| 196 | const PlaneOrHspace& s1 = static_cast<const PlaneOrHspace&>(*o1); \ | ||
| 197 | const OtherShapeType& s2 = static_cast<const OtherShapeType&>(*o2); \ | ||
| 198 | for (size_t i = 0; i < collision_result.numContacts(); ++i) { \ | ||
| 199 | if (i >= request.max_num_patch) { \ | ||
| 200 | break; \ | ||
| 201 | } \ | ||
| 202 | csolver->setSupportGuess(collision_result.cached_support_func_guess); \ | ||
| 203 | const Contact& contact = collision_result.getContact(i); \ | ||
| 204 | ContactPatch& contact_patch = result.getUnusedContactPatch(); \ | ||
| 205 | computePatchPlaneOrHalfspace<true, OtherShapeType, PlaneOrHspace>( \ | ||
| 206 | s2, tf2, s1, tf1, csolver, contact, contact_patch); \ | ||
| 207 | } \ | ||
| 208 | } \ | ||
| 209 | }; | ||
| 210 | |||
| 211 | ✗ | PLANE_OR_HSPACE_AND_OTHER_SHAPE_CONTACT_PATCH(Plane) | |
| 212 |
8/14✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 12 times.
✓ Branch 10 taken 12 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 12 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 12 times.
✗ Branch 18 not taken.
✓ Branch 22 taken 12 times.
✓ Branch 23 taken 12 times.
|
48 | PLANE_OR_HSPACE_AND_OTHER_SHAPE_CONTACT_PATCH(Halfspace) |
| 213 | |||
| 214 | #define PLANE_HSPACE_CONTACT_PATCH(PlaneOrHspace1, PlaneOrHspace2) \ | ||
| 215 | template <> \ | ||
| 216 | struct ComputeShapeShapeContactPatch<PlaneOrHspace1, PlaneOrHspace2> { \ | ||
| 217 | static void run(const CollisionGeometry* o1, const Transform3s& tf1, \ | ||
| 218 | const CollisionGeometry* o2, const Transform3s& tf2, \ | ||
| 219 | const CollisionResult& collision_result, \ | ||
| 220 | const ContactPatchSolver* csolver, \ | ||
| 221 | const ContactPatchRequest& request, \ | ||
| 222 | ContactPatchResult& result) { \ | ||
| 223 | COAL_UNUSED_VARIABLE(o1); \ | ||
| 224 | COAL_UNUSED_VARIABLE(tf1); \ | ||
| 225 | COAL_UNUSED_VARIABLE(o2); \ | ||
| 226 | COAL_UNUSED_VARIABLE(tf2); \ | ||
| 227 | COAL_UNUSED_VARIABLE(csolver); \ | ||
| 228 | if (!collision_result.isCollision()) { \ | ||
| 229 | return; \ | ||
| 230 | } \ | ||
| 231 | COAL_ASSERT( \ | ||
| 232 | result.check(request), \ | ||
| 233 | "The contact patch result and request are incompatible (issue of " \ | ||
| 234 | "contact patch size or maximum number of contact patches). Make " \ | ||
| 235 | "sure " \ | ||
| 236 | "result is initialized with request.", \ | ||
| 237 | std::logic_error); \ | ||
| 238 | \ | ||
| 239 | for (size_t i = 0; i < collision_result.numContacts(); ++i) { \ | ||
| 240 | if (i >= request.max_num_patch) { \ | ||
| 241 | break; \ | ||
| 242 | } \ | ||
| 243 | const Contact& contact = collision_result.getContact(i); \ | ||
| 244 | ContactPatch& contact_patch = result.getUnusedContactPatch(); \ | ||
| 245 | constructContactPatchFrameFromContact(contact, contact_patch); \ | ||
| 246 | contact_patch.addPoint(contact.pos); \ | ||
| 247 | } \ | ||
| 248 | } \ | ||
| 249 | }; | ||
| 250 | |||
| 251 | ✗ | PLANE_HSPACE_CONTACT_PATCH(Plane, Plane) | |
| 252 | ✗ | PLANE_HSPACE_CONTACT_PATCH(Plane, Halfspace) | |
| 253 | ✗ | PLANE_HSPACE_CONTACT_PATCH(Halfspace, Plane) | |
| 254 | ✗ | PLANE_HSPACE_CONTACT_PATCH(Halfspace, Halfspace) | |
| 255 | |||
| 256 | #undef PLANE_OR_HSPACE_AND_OTHER_SHAPE_CONTACT_PATCH | ||
| 257 | #undef PLANE_HSPACE_CONTACT_PATCH | ||
| 258 | |||
| 259 | template <typename ShapeType1, typename ShapeType2> | ||
| 260 | 24 | void ShapeShapeContactPatch(const CollisionGeometry* o1, const Transform3s& tf1, | |
| 261 | const CollisionGeometry* o2, const Transform3s& tf2, | ||
| 262 | const CollisionResult& collision_result, | ||
| 263 | const ContactPatchSolver* csolver, | ||
| 264 | const ContactPatchRequest& request, | ||
| 265 | ContactPatchResult& result) { | ||
| 266 | COAL_TRACY_ZONE_SCOPED_N("coal::ShapeShapeContactPatch"); | ||
| 267 | 24 | return ComputeShapeShapeContactPatch<ShapeType1, ShapeType2>::run( | |
| 268 | 24 | o1, tf1, o2, tf2, collision_result, csolver, request, result); | |
| 269 | } | ||
| 270 | |||
| 271 | } // namespace coal | ||
| 272 | |||
| 273 | #endif | ||
| 274 |