Directory: | ./ |
---|---|
File: | include/coal/internal/shape_shape_contact_patch_func.h |
Date: | 2025-04-01 09:23:31 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 28 | 41 | 68.3% |
Branches: | 19 | 76 | 25.0% |
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 3 taken 12 times.
✗ Branch 4 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 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
24 | csolver->setSupportGuess(collision_result.cached_support_func_guess); |
79 | 24 | const Contact& contact = collision_result.getContact(i); | |
80 | 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 |
1/2✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
24 | tf1.rotation().transpose() * contact_patch.tf.rotation(); |
113 |
3/6✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 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 |
6/10✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 12 times.
✓ Branch 13 taken 12 times.
✗ Branch 14 not taken.
✓ Branch 20 taken 12 times.
✓ Branch 21 taken 12 times.
|
24 | 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 |