GCC Code Coverage Report


Directory: ./
File: include/coal/contact_patch/contact_patch_solver.hxx
Date: 2025-04-01 09:23:31
Exec Total Coverage
Lines: 158 182 86.8%
Branches: 137 310 44.2%

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 INRIA 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_CONTACT_PATCH_SOLVER_HXX
38 #define COAL_CONTACT_PATCH_SOLVER_HXX
39
40 #include "coal/data_types.h"
41 #include "coal/shape/geometric_shapes_traits.h"
42
43 #include "coal/tracy.hh"
44
45 namespace coal {
46
47 // ============================================================================
48 24 inline void ContactPatchSolver::set(const ContactPatchRequest& request) {
49 // Note: it's important for the number of pre-allocated Vec3s in
50 // `m_clipping_sets` to be larger than `request.max_size_patch`
51 // because we don't know in advance how many supports will be discarded to
52 // form the convex-hulls of the shapes supports which will serve as the
53 // input of the Sutherland-Hodgman algorithm.
54 24 size_t num_preallocated_supports = default_num_preallocated_supports;
55
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 if (num_preallocated_supports < 2 * request.getNumSamplesCurvedShapes()) {
56 24 num_preallocated_supports = 2 * request.getNumSamplesCurvedShapes();
57 }
58
59 // Used for support set computation of shape1 and for the first iterate of the
60 // Sutherland-Hodgman algo.
61 24 this->support_set_shape1.points().reserve(num_preallocated_supports);
62 24 this->support_set_shape1.direction = SupportSetDirection::DEFAULT;
63
64 // Used for computing the next iterate of the Sutherland-Hodgman algo.
65 24 this->support_set_buffer.points().reserve(num_preallocated_supports);
66
67 // Used for support set computation of shape2 and acts as the "clipper" set in
68 // the Sutherland-Hodgman algo.
69 24 this->support_set_shape2.points().reserve(num_preallocated_supports);
70 24 this->support_set_shape2.direction = SupportSetDirection::INVERTED;
71
72 24 this->num_samples_curved_shapes = request.getNumSamplesCurvedShapes();
73 24 this->patch_tolerance = request.getPatchTolerance();
74 24 }
75
76 // ============================================================================
77 template <typename ShapeType1, typename ShapeType2>
78 24 void ContactPatchSolver::computePatch(const ShapeType1& s1,
79 const Transform3s& tf1,
80 const ShapeType2& s2,
81 const Transform3s& tf2,
82 const Contact& contact,
83 ContactPatch& contact_patch) const {
84 COAL_TRACY_ZONE_SCOPED_N("coal::ContactPatchSolver::computePatch");
85 // Note: `ContactPatch` is an alias for `SupportSet`.
86 // Step 1
87 24 constructContactPatchFrameFromContact(contact, contact_patch);
88 24 contact_patch.points().clear();
89 if ((bool)(shape_traits<ShapeType1>::IsStrictlyConvex) ||
90 (bool)(shape_traits<ShapeType2>::IsStrictlyConvex)) {
91 // If a shape is strictly convex, the support set in any direction is
92 // reduced to a single point. Thus, the contact point `contact.pos` is the
93 // only point belonging to the contact patch, and it has already been
94 // computed.
95 // TODO(louis): even for strictly convex shapes, we can sample the support
96 // function around the normal and return a pseudo support set. This would
97 // allow spheres and ellipsoids to have a contact surface, which does make
98 // sense in certain physics simulation cases.
99 // Do the same for strictly convex regions of non-strictly convex shapes
100 // like the ends of capsules.
101 2 contact_patch.addPoint(contact.pos);
102 2 return;
103 }
104
105 // Step 2 - Compute support set of each shape, in the direction of
106 // the contact's normal.
107 // The first shape's support set is called "current"; it will be the first
108 // iterate of the Sutherland-Hodgman algorithm. The second shape's support set
109 // is called "clipper"; it will be used to clip "current". The support set
110 // computation step computes a convex polygon; its vertices are ordered
111 // counter-clockwise. This is important as the Sutherland-Hodgman algorithm
112 // expects points to be ranked counter-clockwise.
113 22 this->reset(s1, tf1, s2, tf2, contact_patch);
114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
22 assert(this->num_samples_curved_shapes > 3);
115
116 22 this->supportFuncShape1(&s1, this->support_set_shape1, this->support_guess[0],
117 this->supports_data[0],
118 22 this->num_samples_curved_shapes,
119 22 this->patch_tolerance);
120
121 22 this->supportFuncShape2(&s2, this->support_set_shape2, this->support_guess[1],
122 this->supports_data[1],
123 22 this->num_samples_curved_shapes,
124 22 this->patch_tolerance);
125
126 // We can immediatly return if one of the support set has only
127 // one point.
128
3/6
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
44 if (this->support_set_shape1.size() <= 1 ||
129 22 this->support_set_shape2.size() <= 1) {
130 contact_patch.addPoint(contact.pos);
131 return;
132 }
133
134 // `eps` is be used to check strict positivity of determinants.
135 22 const Scalar eps = Eigen::NumTraits<Scalar>::dummy_precision();
136 using Polygon = SupportSet::Polygon;
137
138
6/6
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 10 times.
28 if ((this->support_set_shape1.size() == 2) &&
139 6 (this->support_set_shape2.size() == 2)) {
140 // Segment-Segment case
141 // We compute the determinant; if it is non-zero, the intersection
142 // has already been computed: it's `Contact::pos`.
143 2 const Polygon& pts1 = this->support_set_shape1.points();
144 2 const Vec2s& a = pts1[0];
145 2 const Vec2s& b = pts1[1];
146
147 2 const Polygon& pts2 = this->support_set_shape2.points();
148 2 const Vec2s& c = pts2[0];
149 2 const Vec2s& d = pts2[1];
150
151 2 const Scalar det =
152
9/18
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 1 times.
✗ Branch 25 not taken.
2 (b(0) - a(0)) * (d(1) - c(1)) >= (b(1) - a(1)) * (d(0) - c(0));
153
1/8
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
2 if ((std::abs(det) > eps) || ((c - d).squaredNorm() < eps) ||
154
1/8
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
2 ((b - a).squaredNorm() < eps)) {
155
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 contact_patch.addPoint(contact.pos);
156 2 return;
157 }
158
159 const Vec2s cd = (d - c);
160 const Scalar l = cd.squaredNorm();
161 Polygon& patch = contact_patch.points();
162
163 // Project a onto [c, d]
164 Scalar t1 = (a - c).dot(cd);
165 t1 = (t1 >= l) ? 1.0 : ((t1 <= 0) ? 0.0 : (t1 / l));
166 const Vec2s p1 = c + t1 * cd;
167 patch.emplace_back(p1);
168
169 // Project b onto [c, d]
170 Scalar t2 = (b - c).dot(cd);
171 t2 = (t2 >= l) ? 1.0 : ((t2 <= 0) ? 0.0 : (t2 / l));
172 const Vec2s p2 = c + t2 * cd;
173 if ((p1 - p2).squaredNorm() >= eps) {
174 patch.emplace_back(p2);
175 }
176 return;
177 }
178
179 //
180 // Step 3 - Main loop of the algorithm: use the "clipper" polygon to clip the
181 // "current" polygon. The resulting intersection is the contact patch of the
182 // contact between s1 and s2. "clipper" and "current" are the support sets of
183 // shape1 and shape2 (they can be swapped, i.e. clipper can be assigned to
184 // shape1 and current to shape2, depending on which case we are). Currently,
185 // to clip one polygon with the other, we use the Sutherland-Hodgman
186 // algorithm:
187 // https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm
188 // In the general case, Sutherland-Hodgman clips one polygon of size >=3 using
189 // another polygon of size >=3. However, it can be easily extended to handle
190 // the segment-polygon case.
191 //
192 // The maximum size of the output of the Sutherland-Hodgman algorithm is n1 +
193 // n2 where n1 and n2 are the sizes of the first and second polygon.
194 20 const size_t max_result_size =
195 20 this->support_set_shape1.size() + this->support_set_shape2.size();
196
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 if (this->added_to_patch.size() < max_result_size) {
197
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 this->added_to_patch.assign(max_result_size, false);
198 }
199
200 20 const Polygon* clipper_ptr = nullptr;
201 20 Polygon* current_ptr = nullptr;
202 20 Polygon* previous_ptr = &(this->support_set_buffer.points());
203
204 // Let the clipper set be the one with the most vertices, to make sure it is
205 // at least a triangle.
206
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
20 if (this->support_set_shape1.size() < this->support_set_shape2.size()) {
207 4 current_ptr = &(this->support_set_shape1.points());
208 4 clipper_ptr = &(this->support_set_shape2.points());
209 } else {
210 16 current_ptr = &(this->support_set_shape2.points());
211 16 clipper_ptr = &(this->support_set_shape1.points());
212 }
213
214 20 const Polygon& clipper = *(clipper_ptr);
215 20 const size_t clipper_size = clipper.size();
216
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 8 times.
82 for (size_t i = 0; i < clipper_size; ++i) {
217 // Swap `current` and `previous`.
218 // `previous` tracks the last iteration of the algorithm; `current` is
219 // filled by clipping `current` using `clipper`.
220 66 Polygon* tmp_ptr = previous_ptr;
221 66 previous_ptr = current_ptr;
222 66 current_ptr = tmp_ptr;
223
224 66 const Polygon& previous = *(previous_ptr);
225 66 Polygon& current = *(current_ptr);
226 66 current.clear();
227
228 66 const Vec2s& a = clipper[i];
229 66 const Vec2s& b = clipper[(i + 1) % clipper_size];
230
2/4
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
66 const Vec2s ab = b - a;
231
232
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 18 times.
66 if (previous.size() == 2) {
233 //
234 // Segment-Polygon case
235 //
236 30 const Vec2s& p1 = previous[0];
237 30 const Vec2s& p2 = previous[1];
238
239
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
30 const Vec2s ap1 = p1 - a;
240
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
30 const Vec2s ap2 = p2 - a;
241
242
4/8
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 15 times.
✗ Branch 11 not taken.
30 const Scalar det1 = ab(0) * ap1(1) - ab(1) * ap1(0);
243
4/8
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 15 times.
✗ Branch 11 not taken.
30 const Scalar det2 = ab(0) * ap2(1) - ab(1) * ap2(0);
244
245
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
30 if (det1 < 0 && det2 < 0) {
246 // Both p1 and p2 are outside the clipping polygon, i.e. there is no
247 // intersection. The algorithm can stop.
248 4 break;
249 }
250
251
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 3 times.
30 if (det1 >= 0 && det2 >= 0) {
252 // Both p1 and p2 are inside the clipping polygon, there is nothing to
253 // do; move to the next iteration.
254
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 current = previous;
255 26 continue;
256 }
257
258 // Compute the intersection between the line (a, b) and the segment
259 // [p1, p2].
260
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
12 if (det1 >= 0) {
261
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
6 if (det1 > eps) {
262
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 const Vec2s p = computeLineSegmentIntersection(a, b, p1, p2);
263
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 current.emplace_back(p1);
264
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 current.emplace_back(p);
265 4 continue;
266 4 } else {
267 // p1 is the only point of current which is also a point of the
268 // clipper. We can exit.
269
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 current.emplace_back(p1);
270 2 break;
271 }
272 } else {
273
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
6 if (det2 > eps) {
274
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 const Vec2s p = computeLineSegmentIntersection(a, b, p1, p2);
275
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 current.emplace_back(p2);
276
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 current.emplace_back(p);
277 4 continue;
278 4 } else {
279 // p2 is the only point of current which is also a point of the
280 // clipper. We can exit.
281
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 current.emplace_back(p2);
282 2 break;
283 }
284 }
285 } else {
286 //
287 // Polygon-Polygon case.
288 //
289
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
36 std::fill(this->added_to_patch.begin(), //
290 this->added_to_patch.end(), //
291 36 false);
292
293 36 const size_t previous_size = previous.size();
294
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 18 times.
176 for (size_t j = 0; j < previous_size; ++j) {
295 140 const Vec2s& p1 = previous[j];
296 140 const Vec2s& p2 = previous[(j + 1) % previous_size];
297
298
2/4
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
140 const Vec2s ap1 = p1 - a;
299
2/4
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
140 const Vec2s ap2 = p2 - a;
300
301
4/8
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 70 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 70 times.
✗ Branch 11 not taken.
140 const Scalar det1 = ab(0) * ap1(1) - ab(1) * ap1(0);
302
4/8
✓ Branch 1 taken 70 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 70 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 70 times.
✗ Branch 11 not taken.
140 const Scalar det2 = ab(0) * ap2(1) - ab(1) * ap2(0);
303
304
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
140 if (det1 < 0 && det2 < 0) {
305 // No intersection. Continue to next segment of previous.
306 132 continue;
307 }
308
309
4/4
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 2 times.
140 if (det1 >= 0 && det2 >= 0) {
310 // Both p1 and p2 are inside the clipping polygon, add p1 to current
311 // (only if it has not already been added).
312
3/4
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 64 times.
✓ Branch 5 taken 2 times.
132 if (!this->added_to_patch[j]) {
313
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
128 current.emplace_back(p1);
314
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
128 this->added_to_patch[j] = true;
315 }
316 // Continue to next segment of previous.
317 132 continue;
318 }
319
320
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
8 if (det1 >= 0) {
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if (det1 > eps) {
322 if (!this->added_to_patch[j]) {
323 current.emplace_back(p1);
324 this->added_to_patch[j] = true;
325 }
326 const Vec2s p = computeLineSegmentIntersection(a, b, p1, p2);
327 current.emplace_back(p);
328 } else {
329 // a, b and p1 are colinear; we add only p1.
330
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 if (!this->added_to_patch[j]) {
331
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 current.emplace_back(p1);
332
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 this->added_to_patch[j] = true;
333 }
334 }
335 } else {
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
4 if (det2 > eps) {
337 const Vec2s p = computeLineSegmentIntersection(a, b, p1, p2);
338 current.emplace_back(p);
339 } else {
340
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
4 if (!this->added_to_patch[(j + 1) % previous.size()]) {
341
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 current.emplace_back(p2);
342
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 this->added_to_patch[(j + 1) % previous.size()] = true;
343 }
344 }
345 }
346 }
347 }
348 //
349 // End of iteration i of Sutherland-Hodgman.
350
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
36 if (current.size() <= 1) {
351 // No intersection or one point found, the algo can early stop.
352 break;
353 }
354 }
355
356 // Transfer the result of the Sutherland-Hodgman algorithm to the contact
357 // patch.
358 20 this->getResult(contact, current_ptr, contact_patch);
359 }
360
361 // ============================================================================
362 22 inline void ContactPatchSolver::getResult(
363 const Contact& contact, const ContactPatch::Polygon* result_ptr,
364 ContactPatch& contact_patch) const {
365
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 16 times.
22 if (result_ptr->size() <= 1) {
366 6 contact_patch.addPoint(contact.pos);
367 6 return;
368 }
369
370 16 const ContactPatch::Polygon& result = *(result_ptr);
371 16 ContactPatch::Polygon& patch = contact_patch.points();
372 16 patch = result;
373 }
374
375 // ============================================================================
376 template <typename ShapeType1, typename ShapeType2>
377 22 inline void ContactPatchSolver::reset(const ShapeType1& shape1,
378 const Transform3s& tf1,
379 const ShapeType2& shape2,
380 const Transform3s& tf2,
381 const ContactPatch& contact_patch) const {
382 // Reset internal quantities
383 22 this->support_set_shape1.clear();
384 22 this->support_set_shape2.clear();
385 22 this->support_set_buffer.clear();
386
387 // Get the support function of each shape
388 22 const Transform3s& tfc = contact_patch.tf;
389
390 22 this->support_set_shape1.direction = SupportSetDirection::DEFAULT;
391 // Set the reference frame of the support set of the first shape to be the
392 // local frame of shape 1.
393 22 Transform3s& tf1c = this->support_set_shape1.tf;
394
3/6
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 11 times.
✗ Branch 12 not taken.
22 tf1c.rotation().noalias() = tf1.rotation().transpose() * tfc.rotation();
395
3/6
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 11 times.
✗ Branch 10 not taken.
44 tf1c.translation().noalias() =
396
1/2
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
44 tf1.rotation().transpose() * (tfc.translation() - tf1.translation());
397 22 this->supportFuncShape1 =
398 22 this->makeSupportSetFunction(&shape1, this->supports_data[0]);
399
400 22 this->support_set_shape2.direction = SupportSetDirection::INVERTED;
401 // Set the reference frame of the support set of the second shape to be the
402 // local frame of shape 2.
403 22 Transform3s& tf2c = this->support_set_shape2.tf;
404
3/6
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 11 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 11 times.
✗ Branch 12 not taken.
22 tf2c.rotation().noalias() = tf2.rotation().transpose() * tfc.rotation();
405
3/6
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 11 times.
✗ Branch 10 not taken.
44 tf2c.translation().noalias() =
406
1/2
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
44 tf2.rotation().transpose() * (tfc.translation() - tf2.translation());
407 22 this->supportFuncShape2 =
408 22 this->makeSupportSetFunction(&shape2, this->supports_data[1]);
409 }
410
411 // ==========================================================================
412 4 inline Vec2s ContactPatchSolver::computeLineSegmentIntersection(
413 const Vec2s& a, const Vec2s& b, const Vec2s& c, const Vec2s& d) {
414
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 const Vec2s ab = b - a;
415
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
4 const Vec2s n(-ab(1), ab(0));
416
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 const Scalar denominator = n.dot(c - d);
417
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
4 if (std::abs(denominator) < std::numeric_limits<Scalar>::epsilon()) {
418 return d;
419 }
420
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 const Scalar nominator = n.dot(a - d);
421 4 Scalar alpha = nominator / denominator;
422 4 alpha = std::min<Scalar>(1.0, std::max<Scalar>(0.0, alpha));
423
4/8
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
8 return alpha * c + (1 - alpha) * d;
424 }
425
426 } // namespace coal
427
428 #endif // COAL_CONTACT_PATCH_SOLVER_HXX
429