GCC Code Coverage Report


Directory: ./
File: include/coal/internal/traversal_node_hfield_shape.h
Date: 2025-04-01 09:23:31
Exec Total Coverage
Lines: 238 248 96.0%
Branches: 196 396 49.5%

Line Branch Exec Source
1 /*
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2021-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 Open Source Robotics Foundation 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 Jia Pan */
36
37 #ifndef COAL_TRAVERSAL_NODE_HFIELD_SHAPE_H
38 #define COAL_TRAVERSAL_NODE_HFIELD_SHAPE_H
39
40 /// @cond INTERNAL
41
42 #include "coal/collision_data.h"
43 #include "coal/shape/geometric_shapes.h"
44 #include "coal/narrowphase/narrowphase.h"
45 #include "coal/shape/geometric_shapes_utility.h"
46 #include "coal/internal/shape_shape_func.h"
47 #include "coal/internal/traversal_node_base.h"
48 #include "coal/internal/traversal.h"
49 #include "coal/internal/intersect.h"
50 #include "coal/hfield.h"
51 #include "coal/shape/convex.h"
52
53 namespace coal {
54
55 /// @addtogroup Traversal_For_Collision
56 /// @{
57
58 namespace details {
59 template <typename BV>
60 Convex<Quadrilateral> buildConvexQuadrilateral(const HFNode<BV>& node,
61 const HeightField<BV>& model) {
62 const MatrixXs& heights = model.getHeights();
63 const VecXs& x_grid = model.getXGrid();
64 const VecXs& y_grid = model.getYGrid();
65
66 const Scalar min_height = model.getMinHeight();
67
68 const Scalar x0 = x_grid[node.x_id], x1 = x_grid[node.x_id + 1],
69 y0 = y_grid[node.y_id], y1 = y_grid[node.y_id + 1];
70 const Eigen::Block<const MatrixXs, 2, 2> cell =
71 heights.block<2, 2>(node.y_id, node.x_id);
72
73 assert(cell.maxCoeff() > min_height &&
74 "max_height is lower than min_height"); // Check whether the geometry
75 // is degenerated
76
77 std::shared_ptr<std::vector<Vec3s>> pts(new std::vector<Vec3s>({
78 Vec3s(x0, y0, min_height),
79 Vec3s(x0, y1, min_height),
80 Vec3s(x1, y1, min_height),
81 Vec3s(x1, y0, min_height),
82 Vec3s(x0, y0, cell(0, 0)),
83 Vec3s(x0, y1, cell(1, 0)),
84 Vec3s(x1, y1, cell(1, 1)),
85 Vec3s(x1, y0, cell(0, 1)),
86 }));
87
88 std::shared_ptr<std::vector<Quadrilateral>> polygons(
89 new std::vector<Quadrilateral>(6));
90 (*polygons)[0].set(0, 3, 2, 1); // x+ side
91 (*polygons)[1].set(0, 1, 5, 4); // y- side
92 (*polygons)[2].set(1, 2, 6, 5); // x- side
93 (*polygons)[3].set(2, 3, 7, 6); // y+ side
94 (*polygons)[4].set(3, 0, 4, 7); // z- side
95 (*polygons)[5].set(4, 5, 6, 7); // z+ side
96
97 return Convex<Quadrilateral>(pts, // points
98 8, // num points
99 polygons,
100 6 // number of polygons
101 );
102 }
103
104 enum class FaceOrientationConvexPart1 {
105 BOTTOM = 0,
106 TOP = 1,
107 WEST = 2,
108 SOUTH_EAST = 4,
109 NORTH = 8,
110 };
111
112 enum class FaceOrientationConvexPart2 {
113 BOTTOM = 0,
114 TOP = 1,
115 SOUTH = 2,
116 NORTH_WEST = 4,
117 EAST = 8,
118 };
119
120 template <typename BV>
121 32204 void buildConvexTriangles(const HFNode<BV>& node, const HeightField<BV>& model,
122 Convex<Triangle>& convex1, int& convex1_active_faces,
123 Convex<Triangle>& convex2,
124 int& convex2_active_faces) {
125 32204 const MatrixXs& heights = model.getHeights();
126 32204 const VecXs& x_grid = model.getXGrid();
127 32204 const VecXs& y_grid = model.getYGrid();
128
129 32204 const Scalar min_height = model.getMinHeight();
130
131
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 const Scalar x0 = x_grid[node.x_id], x1 = x_grid[node.x_id + 1],
132
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 y0 = y_grid[node.y_id], y1 = y_grid[node.y_id + 1];
133 32204 const Scalar max_height = node.max_height;
134 const Eigen::Block<const MatrixXs, 2, 2> cell =
135
1/2
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
32204 heights.block<2, 2>(node.y_id, node.x_id);
136
137 32204 const int contact_active_faces = node.contact_active_faces;
138 32204 convex1_active_faces = 0;
139 32204 convex2_active_faces = 0;
140
141 typedef HFNodeBase::FaceOrientation FaceOrientation;
142
143
1/2
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
32204 if (contact_active_faces & FaceOrientation::TOP) {
144 32204 convex1_active_faces |= int(details::FaceOrientationConvexPart1::TOP);
145 32204 convex2_active_faces |= int(details::FaceOrientationConvexPart2::TOP);
146 }
147
148
1/2
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
32204 if (contact_active_faces & FaceOrientation::BOTTOM) {
149 32204 convex1_active_faces |= int(details::FaceOrientationConvexPart1::BOTTOM);
150 32204 convex2_active_faces |= int(details::FaceOrientationConvexPart2::BOTTOM);
151 }
152
153 // Specific orientation for Convex1
154
2/2
✓ Branch 1 taken 686 times.
✓ Branch 2 taken 29905 times.
32204 if (contact_active_faces & FaceOrientation::WEST) {
155 783 convex1_active_faces |= int(details::FaceOrientationConvexPart1::WEST);
156 }
157
158
2/2
✓ Branch 1 taken 550 times.
✓ Branch 2 taken 30041 times.
32204 if (contact_active_faces & FaceOrientation::NORTH) {
159 617 convex1_active_faces |= int(details::FaceOrientationConvexPart1::NORTH);
160 }
161
162 // Specific orientation for Convex2
163
2/2
✓ Branch 1 taken 417 times.
✓ Branch 2 taken 30174 times.
32204 if (contact_active_faces & FaceOrientation::EAST) {
164 422 convex2_active_faces |= int(details::FaceOrientationConvexPart2::EAST);
165 }
166
167
2/2
✓ Branch 1 taken 474 times.
✓ Branch 2 taken 30117 times.
32204 if (contact_active_faces & FaceOrientation::SOUTH) {
168 485 convex2_active_faces |= int(details::FaceOrientationConvexPart2::SOUTH);
169 }
170
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30591 times.
32204 assert(max_height > min_height &&
172 "max_height is lower than min_height"); // Check whether the geometry
173 // is degenerated
174 COAL_UNUSED_VARIABLE(max_height);
175
176 {
177
6/12
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30591 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 30591 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 30591 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 30591 times.
✗ Branch 18 not taken.
64408 std::shared_ptr<std::vector<Vec3s>> pts(new std::vector<Vec3s>({
178 Vec3s(x0, y0, min_height), // A
179 Vec3s(x0, y1, min_height), // B
180 Vec3s(x1, y0, min_height), // C
181
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 Vec3s(x0, y0, cell(0, 0)), // D
182
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 Vec3s(x0, y1, cell(1, 0)), // E
183
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 Vec3s(x1, y0, cell(0, 1)), // F
184 }));
185
186
1/2
✓ Branch 2 taken 30591 times.
✗ Branch 3 not taken.
64408 std::shared_ptr<std::vector<Triangle>> triangles(
187
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 new std::vector<Triangle>(8));
188 32204 (*triangles)[0].set(0, 2, 1); // bottom
189 32204 (*triangles)[1].set(3, 4, 5); // top
190 32204 (*triangles)[2].set(0, 1, 3); // West 1
191 32204 (*triangles)[3].set(3, 1, 4); // West 2
192 32204 (*triangles)[4].set(1, 2, 5); // South-East 1
193 32204 (*triangles)[5].set(1, 5, 4); // South-East 1
194 32204 (*triangles)[6].set(0, 5, 2); // North 1
195 32204 (*triangles)[7].set(5, 0, 3); // North 2
196
197
1/2
✓ Branch 3 taken 30591 times.
✗ Branch 4 not taken.
32204 convex1.set(pts, // points
198 6, // num points
199 triangles,
200 8 // number of polygons
201 );
202 32204 }
203
204 {
205
6/12
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30591 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 30591 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 30591 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 30591 times.
✗ Branch 18 not taken.
64408 std::shared_ptr<std::vector<Vec3s>> pts(new std::vector<Vec3s>({
206 Vec3s(x0, y1, min_height), // A
207 Vec3s(x1, y1, min_height), // B
208 Vec3s(x1, y0, min_height), // C
209
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 Vec3s(x0, y1, cell(1, 0)), // D
210
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 Vec3s(x1, y1, cell(1, 1)), // E
211
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 Vec3s(x1, y0, cell(0, 1)), // F
212 }));
213
214
1/2
✓ Branch 2 taken 30591 times.
✗ Branch 3 not taken.
64408 std::shared_ptr<std::vector<Triangle>> triangles(
215
2/4
✓ Branch 1 taken 30591 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30591 times.
✗ Branch 5 not taken.
32204 new std::vector<Triangle>(8));
216 32204 (*triangles)[0].set(2, 1, 0); // bottom
217 32204 (*triangles)[1].set(3, 4, 5); // top
218 32204 (*triangles)[2].set(0, 1, 3); // South 1
219 32204 (*triangles)[3].set(3, 1, 4); // South 2
220 32204 (*triangles)[4].set(0, 5, 2); // North West 1
221 32204 (*triangles)[5].set(0, 3, 5); // North West 2
222 32204 (*triangles)[6].set(1, 2, 5); // East 1
223 32204 (*triangles)[7].set(4, 1, 2); // East 2
224
225
1/2
✓ Branch 3 taken 30591 times.
✗ Branch 4 not taken.
32204 convex2.set(pts, // points
226 6, // num points
227 triangles,
228 8 // number of polygons
229 );
230 32204 }
231 32204 }
232
233 122551 inline Vec3s projectTriangle(const Vec3s& pointA, const Vec3s& pointB,
234 const Vec3s& pointC, const Vec3s& point) {
235 const Project<Scalar>::ProjectResult result =
236
1/2
✓ Branch 1 taken 122551 times.
✗ Branch 2 not taken.
122551 Project<Scalar>::projectTriangle(pointA, pointB, pointC, point);
237
2/4
✓ Branch 1 taken 122551 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 122551 times.
✗ Branch 5 not taken.
122551 Vec3s res = result.parameterization[0] * pointA +
238
2/4
✓ Branch 1 taken 122551 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 122551 times.
✗ Branch 5 not taken.
245102 result.parameterization[1] * pointB +
239
2/4
✓ Branch 1 taken 122551 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 122551 times.
✗ Branch 5 not taken.
245102 result.parameterization[2] * pointC;
240
241 245102 return res;
242 }
243
244 inline Vec3s projectTetrahedra(const Vec3s& pointA, const Vec3s& pointB,
245 const Vec3s& pointC, const Vec3s& pointD,
246 const Vec3s& point) {
247 const Project<Scalar>::ProjectResult result =
248 Project<Scalar>::projectTetrahedra(pointA, pointB, pointC, pointD, point);
249 Vec3s res = result.parameterization[0] * pointA +
250 result.parameterization[1] * pointB +
251 result.parameterization[2] * pointC +
252 result.parameterization[3] * pointD;
253
254 return res;
255 }
256
257 87 inline Vec3s computeTriangleNormal(const Triangle& triangle,
258 const std::vector<Vec3s>& points) {
259
1/2
✓ Branch 3 taken 87 times.
✗ Branch 4 not taken.
87 const Vec3s pointA = points[triangle[0]];
260
1/2
✓ Branch 3 taken 87 times.
✗ Branch 4 not taken.
87 const Vec3s pointB = points[triangle[1]];
261
1/2
✓ Branch 3 taken 87 times.
✗ Branch 4 not taken.
87 const Vec3s pointC = points[triangle[2]];
262
263
4/8
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 87 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 87 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 87 times.
✗ Branch 11 not taken.
87 const Vec3s normal = (pointB - pointA).cross(pointC - pointA).normalized();
264
4/8
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 87 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 87 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 87 times.
✗ Branch 10 not taken.
87 assert(!normal.array().isNaN().any() && "normal is ill-defined");
265
266 174 return normal;
267 }
268
269 122551 inline Vec3s projectPointOnTriangle(const Vec3s& contact_point,
270 const Triangle& triangle,
271 const std::vector<Vec3s>& points) {
272
1/2
✓ Branch 3 taken 122551 times.
✗ Branch 4 not taken.
122551 const Vec3s pointA = points[triangle[0]];
273
1/2
✓ Branch 3 taken 122551 times.
✗ Branch 4 not taken.
122551 const Vec3s pointB = points[triangle[1]];
274
1/2
✓ Branch 3 taken 122551 times.
✗ Branch 4 not taken.
122551 const Vec3s pointC = points[triangle[2]];
275
276 const Vec3s contact_point_projected =
277
1/2
✓ Branch 1 taken 122551 times.
✗ Branch 2 not taken.
122551 projectTriangle(pointA, pointB, pointC, contact_point);
278
279 245102 return contact_point_projected;
280 }
281
282 122464 inline Scalar distanceContactPointToTriangle(const Vec3s& contact_point,
283 const Triangle& triangle,
284 const std::vector<Vec3s>& points) {
285 const Vec3s contact_point_projected =
286
1/2
✓ Branch 1 taken 122464 times.
✗ Branch 2 not taken.
122464 projectPointOnTriangle(contact_point, triangle, points);
287
2/4
✓ Branch 1 taken 122464 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 122464 times.
✗ Branch 5 not taken.
122464 return (contact_point_projected - contact_point).norm();
288 }
289
290 122410 inline Scalar distanceContactPointToFace(const size_t face_id,
291 const Vec3s& contact_point,
292 const Convex<Triangle>& convex,
293 size_t& closest_face_id) {
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 122410 times.
122410 assert((face_id >= 0 && face_id < 8) && "face_id should be in [0;7]");
295
296 122410 const std::vector<Vec3s>& points = *(convex.points);
297
2/2
✓ Branch 0 taken 122356 times.
✓ Branch 1 taken 54 times.
122410 if (face_id <= 1) {
298 122356 const Triangle& triangle = (*(convex.polygons))[face_id];
299 122356 closest_face_id = face_id;
300 122356 return distanceContactPointToTriangle(contact_point, triangle, points);
301 } else {
302 54 const Triangle& triangle1 = (*(convex.polygons))[face_id];
303 const Scalar distance_to_triangle1 =
304 54 distanceContactPointToTriangle(contact_point, triangle1, points);
305
306 54 const Triangle& triangle2 = (*(convex.polygons))[face_id + 1];
307 const Scalar distance_to_triangle2 =
308 54 distanceContactPointToTriangle(contact_point, triangle2, points);
309
310
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 34 times.
54 if (distance_to_triangle1 > distance_to_triangle2) {
311 20 closest_face_id = face_id + 1;
312 20 return distance_to_triangle2;
313 } else {
314 34 closest_face_id = face_id;
315 34 return distance_to_triangle1;
316 }
317 }
318 }
319
320 template <typename Polygone, typename Shape>
321 122360 bool binCorrection(const Convex<Polygone>& convex,
322 const int convex_active_faces, const Shape& shape,
323 const Transform3s& shape_pose, Scalar& distance,
324 Vec3s& contact_1, Vec3s& contact_2, Vec3s& normal,
325 Vec3s& face_normal, const bool is_collision) {
326 122360 const Scalar prec = Scalar(1e-12);
327 122360 const std::vector<Vec3s>& points = *(convex.points);
328
329 122360 bool hfield_witness_is_on_bin_side = true;
330
331 // int closest_face_id_bottom_face = -1;
332 // int closest_face_id_top_face = -1;
333
334 122360 std::vector<size_t> active_faces;
335
1/2
✓ Branch 1 taken 61180 times.
✗ Branch 2 not taken.
122360 active_faces.reserve(5);
336
1/2
✓ Branch 1 taken 61180 times.
✗ Branch 2 not taken.
122360 active_faces.push_back(0);
337
1/2
✓ Branch 1 taken 61180 times.
✗ Branch 2 not taken.
122360 active_faces.push_back(1);
338
339
3/4
✓ Branch 0 taken 1158 times.
✓ Branch 1 taken 60022 times.
✓ Branch 3 taken 1158 times.
✗ Branch 4 not taken.
122360 if (convex_active_faces & 2) active_faces.push_back(2);
340
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 61180 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
122360 if (convex_active_faces & 4) active_faces.push_back(4);
341
3/4
✓ Branch 0 taken 965 times.
✓ Branch 1 taken 60215 times.
✓ Branch 3 taken 965 times.
✗ Branch 4 not taken.
122360 if (convex_active_faces & 8) active_faces.push_back(6);
342
343 122360 Triangle face_triangle;
344 122360 Scalar shortest_distance_to_face = (std::numeric_limits<Scalar>::max)();
345
1/2
✓ Branch 1 taken 61180 times.
✗ Branch 2 not taken.
122360 face_normal = normal;
346
2/2
✓ Branch 5 taken 122410 times.
✓ Branch 6 taken 65 times.
244950 for (const size_t active_face : active_faces) {
347 size_t closest_face_id;
348
1/2
✓ Branch 1 taken 122410 times.
✗ Branch 2 not taken.
244820 const Scalar distance_to_face = distanceContactPointToFace(
349 active_face, contact_1, convex, closest_face_id);
350
351 244820 const bool contact_point_is_on_face = distance_to_face <= prec;
352
2/2
✓ Branch 0 taken 61115 times.
✓ Branch 1 taken 61295 times.
244820 if (contact_point_is_on_face) {
353 122230 hfield_witness_is_on_bin_side = false;
354 122230 face_triangle = (*(convex.polygons))[closest_face_id];
355 122230 shortest_distance_to_face = distance_to_face;
356 122230 break;
357
2/2
✓ Branch 0 taken 61241 times.
✓ Branch 1 taken 54 times.
122590 } else if (distance_to_face < shortest_distance_to_face) {
358 122482 face_triangle = (*(convex.polygons))[closest_face_id];
359 122482 shortest_distance_to_face = distance_to_face;
360 }
361 }
362
363 // We correct only if there is a collision with the bin
364
2/2
✓ Branch 0 taken 87 times.
✓ Branch 1 taken 61093 times.
122360 if (is_collision) {
365
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 87 times.
174 if (!face_triangle.isValid())
366 COAL_THROW_PRETTY("face_triangle is not initialized", std::logic_error);
367
368
1/2
✓ Branch 3 taken 87 times.
✗ Branch 4 not taken.
174 const Vec3s face_pointA = points[face_triangle[0]];
369
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
174 face_normal = computeTriangleNormal(face_triangle, points);
370
371 174 int hint = 0;
372 // Since we compute the support manually, we need to take into account the
373 // sphere swept radius of the shape.
374 // TODO: take into account the swept-sphere radius of the bin.
375
4/8
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 87 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 87 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 87 times.
✗ Branch 11 not taken.
174 const Vec3s _support = getSupport<details::SupportOptions::WithSweptSphere>(
376
1/2
✓ Branch 2 taken 87 times.
✗ Branch 3 not taken.
174 &shape, -shape_pose.rotation().transpose() * face_normal, hint);
377
2/4
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 87 times.
✗ Branch 5 not taken.
174 const Vec3s support =
378
1/2
✓ Branch 3 taken 87 times.
✗ Branch 4 not taken.
174 shape_pose.rotation() * _support + shape_pose.translation();
379
380 // Project support into the inclined bin having triangle
381
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
174 const Scalar offset_plane = face_normal.dot(face_pointA);
382
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
174 const Plane projection_plane(face_normal, offset_plane);
383 174 const Scalar distance_support_projection_plane =
384
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
174 projection_plane.signedDistance(support);
385
386
3/6
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 87 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 87 times.
✗ Branch 8 not taken.
174 const Vec3s projected_support =
387 support - distance_support_projection_plane * face_normal;
388
389 // We need now to project the projected in the triangle shape
390
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
174 contact_1 =
391 projectPointOnTriangle(projected_support, face_triangle, points);
392
3/6
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 87 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 87 times.
✗ Branch 8 not taken.
174 contact_2 = contact_1 + distance_support_projection_plane * face_normal;
393
1/2
✓ Branch 1 taken 87 times.
✗ Branch 2 not taken.
174 normal = face_normal;
394 174 distance = -std::fabs(distance_support_projection_plane);
395 }
396
397 122360 return hfield_witness_is_on_bin_side;
398 }
399
400 template <typename Polygone, typename Shape, int Options>
401 61180 bool shapeDistance(const GJKSolver* nsolver, const CollisionRequest& request,
402 const Convex<Polygone>& convex1,
403 const int convex1_active_faces,
404 const Convex<Polygone>& convex2,
405 const int convex2_active_faces, const Transform3s& tf1,
406 const Shape& shape, const Transform3s& tf2, Scalar& distance,
407 Vec3s& c1, Vec3s& c2, Vec3s& normal, Vec3s& normal_top,
408 bool& hfield_witness_is_on_bin_side) {
409 enum { RTIsIdentity = Options & RelativeTransformationIsIdentity };
410
411
1/2
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
61180 const Transform3s Id;
412 // The solver `nsolver` has already been set up by the collision request
413 // `request`. If GJK early stopping is enabled through `request`, it will be
414 // used.
415 // The only thing we need to make sure is that in case of collision, the
416 // penetration information is computed (as we do bins comparison).
417 61180 const bool compute_penetration = true;
418
4/8
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30590 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30590 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30590 times.
✗ Branch 11 not taken.
61180 Vec3s contact1_1, contact1_2, contact2_1, contact2_2;
419
4/8
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30590 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30590 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30590 times.
✗ Branch 11 not taken.
61180 Vec3s normal1, normal1_top, normal2, normal2_top;
420 Scalar distance1, distance2;
421
422 if (RTIsIdentity) {
423 distance1 = internal::ShapeShapeDistance<Convex<Polygone>, Shape>(
424 &convex1, Id, &shape, tf2, nsolver, compute_penetration, contact1_1,
425 contact1_2, normal1);
426 } else {
427
1/2
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
61180 distance1 = internal::ShapeShapeDistance<Convex<Polygone>, Shape>(
428 &convex1, tf1, &shape, tf2, nsolver, compute_penetration, contact1_1,
429 contact1_2, normal1);
430 }
431 61180 bool collision1 = (distance1 - request.security_margin <=
432 61180 request.collision_distance_threshold);
433
434 bool hfield_witness_is_on_bin_side1 =
435
1/2
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
61180 binCorrection(convex1, convex1_active_faces, shape, tf2, distance1,
436 contact1_1, contact1_2, normal1, normal1_top, collision1);
437
438 if (RTIsIdentity) {
439 distance2 = internal::ShapeShapeDistance<Convex<Polygone>, Shape>(
440 &convex2, Id, &shape, tf2, nsolver, compute_penetration, contact2_1,
441 contact2_2, normal2);
442 } else {
443
1/2
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
61180 distance2 = internal::ShapeShapeDistance<Convex<Polygone>, Shape>(
444 &convex2, tf1, &shape, tf2, nsolver, compute_penetration, contact2_1,
445 contact2_2, normal2);
446 }
447 61180 bool collision2 = (distance2 - request.security_margin <=
448 61180 request.collision_distance_threshold);
449
450 bool hfield_witness_is_on_bin_side2 =
451
1/2
✓ Branch 1 taken 30590 times.
✗ Branch 2 not taken.
61180 binCorrection(convex2, convex2_active_faces, shape, tf2, distance2,
452 contact2_1, contact2_2, normal2, normal2_top, collision2);
453
454
4/4
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 30557 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 2 times.
61180 if (collision1 && collision2) {
455
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
62 if (distance1 > distance2) // switch values
456 {
457 distance = distance2;
458 c1 = contact2_1;
459 c2 = contact2_2;
460 normal = normal2;
461 normal_top = normal2_top;
462 hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side2;
463 } else {
464 62 distance = distance1;
465
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 c1 = contact1_1;
466
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 c2 = contact1_2;
467
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 normal = normal1;
468
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
62 normal_top = normal1_top;
469 62 hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side1;
470 }
471 62 return true;
472
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 30557 times.
61118 } else if (collision1) {
473 4 distance = distance1;
474
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 c1 = contact1_1;
475
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 c2 = contact1_2;
476
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 normal = normal1;
477
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 normal_top = normal1_top;
478 4 hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side1;
479 4 return true;
480
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 30534 times.
61114 } else if (collision2) {
481 46 distance = distance2;
482
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
46 c1 = contact2_1;
483
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
46 c2 = contact2_2;
484
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
46 normal = normal2;
485
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
46 normal_top = normal2_top;
486 46 hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side2;
487 46 return true;
488 }
489
490
2/2
✓ Branch 0 taken 12197 times.
✓ Branch 1 taken 18337 times.
61068 if (distance1 > distance2) // switch values
491 {
492 24394 distance = distance2;
493
1/2
✓ Branch 1 taken 12197 times.
✗ Branch 2 not taken.
24394 c1 = contact2_1;
494
1/2
✓ Branch 1 taken 12197 times.
✗ Branch 2 not taken.
24394 c2 = contact2_2;
495
1/2
✓ Branch 1 taken 12197 times.
✗ Branch 2 not taken.
24394 normal = normal2;
496
1/2
✓ Branch 1 taken 12197 times.
✗ Branch 2 not taken.
24394 normal_top = normal2_top;
497 24394 hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side2;
498 } else {
499 36674 distance = distance1;
500
1/2
✓ Branch 1 taken 18337 times.
✗ Branch 2 not taken.
36674 c1 = contact1_1;
501
1/2
✓ Branch 1 taken 18337 times.
✗ Branch 2 not taken.
36674 c2 = contact1_2;
502
1/2
✓ Branch 1 taken 18337 times.
✗ Branch 2 not taken.
36674 normal = normal1;
503
1/2
✓ Branch 1 taken 18337 times.
✗ Branch 2 not taken.
36674 normal_top = normal1_top;
504 36674 hfield_witness_is_on_bin_side = hfield_witness_is_on_bin_side1;
505 }
506 61068 return false;
507 }
508
509 } // namespace details
510
511 /// @brief Traversal node for collision between height field and shape
512 template <typename BV, typename S,
513 int _Options = RelativeTransformationIsIdentity>
514 class HeightFieldShapeCollisionTraversalNode
515 : public CollisionTraversalNodeBase {
516 public:
517 typedef CollisionTraversalNodeBase Base;
518
519 enum {
520 Options = _Options,
521 RTIsIdentity = _Options & RelativeTransformationIsIdentity
522 };
523
524 110 HeightFieldShapeCollisionTraversalNode(const CollisionRequest& request)
525
1/2
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
110 : CollisionTraversalNodeBase(request) {
526 110 model1 = NULL;
527 110 model2 = NULL;
528
529 110 num_bv_tests = 0;
530 110 num_leaf_tests = 0;
531 110 query_time_seconds = 0.0;
532
533 110 nsolver = NULL;
534 110 count = 0;
535 }
536
537 /// @brief Whether the BV node in the first BVH tree is leaf
538 127594 bool isFirstNodeLeaf(unsigned int b) const {
539 127594 return model1->getBV(b).isLeaf();
540 }
541
542 /// @brief Obtain the left child of BV node in the first BVH
543 63822 int getFirstLeftChild(unsigned int b) const {
544 63822 return static_cast<int>(model1->getBV(b).leftChild());
545 }
546
547 /// @brief Obtain the right child of BV node in the first BVH
548 63822 int getFirstRightChild(unsigned int b) const {
549 63822 return static_cast<int>(model1->getBV(b).rightChild());
550 }
551
552 /// test between BV b1 and shape
553 /// @param b1 BV to test,
554 /// @retval sqrDistLowerBound square of a lower bound of the minimal
555 /// distance between bounding volumes.
556 /// @brief BV culling test in one BVTT node
557 66478 bool BVDisjoints(unsigned int b1, unsigned int /*b2*/,
558 Scalar& sqrDistLowerBound) const {
559
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33239 times.
66478 if (this->enable_statistics) this->num_bv_tests++;
560
561 bool disjoint;
562 if (RTIsIdentity) {
563 assert(false && "must never happened");
564 disjoint = !this->model1->getBV(b1).bv.overlap(
565 this->model2_bv, this->request, sqrDistLowerBound);
566 } else {
567 66478 disjoint = !overlap(this->tf1.getRotation(), this->tf1.getTranslation(),
568 66478 this->model1->getBV(b1).bv, this->model2_bv,
569 this->request, sqrDistLowerBound);
570 }
571
572
2/2
✓ Branch 0 taken 1328 times.
✓ Branch 1 taken 31911 times.
66478 if (disjoint)
573 2656 internal::updateDistanceLowerBoundFromBV(this->request, *this->result,
574 sqrDistLowerBound);
575
576
3/4
✓ Branch 0 taken 1328 times.
✓ Branch 1 taken 31911 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1328 times.
66478 assert(!disjoint || sqrDistLowerBound > 0);
577 66478 return disjoint;
578 }
579
580 /// @brief Intersection testing between leaves (one Convex and one shape)
581 61116 void leafCollides(unsigned int b1, unsigned int /*b2*/,
582 Scalar& sqrDistLowerBound) const {
583 61116 count++;
584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30558 times.
61116 if (this->enable_statistics) this->num_leaf_tests++;
585
1/2
✓ Branch 1 taken 30558 times.
✗ Branch 2 not taken.
61116 const HFNode<BV>& node = this->model1->getBV(b1);
586
587 // Split quadrilateral primitives into two convex shapes corresponding to
588 // polyhedron with triangular bases. This is essential to keep the convexity
589
590 // typedef Convex<Quadrilateral> ConvexQuadrilateral;
591 // const ConvexQuadrilateral convex =
592 // details::buildConvexQuadrilateral(node,*this->model1);
593
594 typedef Convex<Triangle> ConvexTriangle;
595
2/4
✓ Branch 1 taken 30558 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30558 times.
✗ Branch 5 not taken.
61116 ConvexTriangle convex1, convex2;
596 int convex1_active_faces, convex2_active_faces;
597 // TODO: inherit from hfield's inflation here
598
1/2
✓ Branch 1 taken 30558 times.
✗ Branch 2 not taken.
61116 details::buildConvexTriangles(node, *this->model1, convex1,
599 convex1_active_faces, convex2,
600 convex2_active_faces);
601
602 // Compute aabb_local for BoundingVolumeGuess case in the GJK solver
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30558 times.
61116 if (nsolver->gjk_initial_guess == GJKInitialGuess::BoundingVolumeGuess) {
604 convex1.computeLocalAABB();
605 convex2.computeLocalAABB();
606 }
607
608 Scalar distance;
609 // Vec3s contact_point, normal;
610
4/8
✓ Branch 1 taken 30558 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 30558 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 30558 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 30558 times.
✗ Branch 11 not taken.
61116 Vec3s c1, c2, normal, normal_face;
611 bool hfield_witness_is_on_bin_side;
612
613 122232 bool collision = details::shapeDistance<Triangle, S, Options>(
614 61116 nsolver, this->request, convex1, convex1_active_faces, convex2,
615
1/2
✓ Branch 1 taken 30558 times.
✗ Branch 2 not taken.
61116 convex2_active_faces, this->tf1, *(this->model2), this->tf2, distance,
616 c1, c2, normal, normal_face, hfield_witness_is_on_bin_side);
617
618 61116 Scalar distToCollision = distance - this->request.security_margin;
619
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 30534 times.
61116 if (distToCollision <= this->request.collision_distance_threshold) {
620 48 sqrDistLowerBound = 0;
621
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
48 if (this->result->numContacts() < this->request.num_max_contacts) {
622
4/8
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
✓ Branch 8 taken 24 times.
✗ Branch 9 not taken.
48 if (normal_face.isApprox(normal) &&
623 (collision || !hfield_witness_is_on_bin_side)) {
624
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
48 this->result->addContact(Contact(this->model1, this->model2, (int)b1,
625 (int)Contact::NONE, c1, c2, normal,
626 distance));
627
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
48 assert(this->result->isCollision());
628 }
629 }
630 } else
631 61068 sqrDistLowerBound = distToCollision * distToCollision;
632
633 // const Vec3s c1 = contact_point - distance * 0.5 * normal;
634 // const Vec3s c2 = contact_point + distance * 0.5 * normal;
635
1/2
✓ Branch 1 taken 30558 times.
✗ Branch 2 not taken.
61116 internal::updateDistanceLowerBoundFromLeaf(this->request, *this->result,
636 distToCollision, c1, c2, normal);
637
638
3/4
✓ Branch 1 taken 30534 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 30534 times.
61116 assert(this->result->isCollision() || sqrDistLowerBound > 0);
639 }
640
641 const GJKSolver* nsolver;
642
643 const HeightField<BV>* model1;
644 const S* model2;
645 BV model2_bv;
646
647 mutable int num_bv_tests;
648 mutable int num_leaf_tests;
649 mutable Scalar query_time_seconds;
650 mutable int count;
651 };
652
653 /// @}
654
655 /// @addtogroup Traversal_For_Distance
656 /// @{
657
658 /// @brief Traversal node for distance between height field and shape
659 template <typename BV, typename S,
660 int _Options = RelativeTransformationIsIdentity>
661 class HeightFieldShapeDistanceTraversalNode : public DistanceTraversalNodeBase {
662 public:
663 typedef DistanceTraversalNodeBase Base;
664
665 enum {
666 Options = _Options,
667 RTIsIdentity = _Options & RelativeTransformationIsIdentity
668 };
669
670 HeightFieldShapeDistanceTraversalNode() : DistanceTraversalNodeBase() {
671 model1 = NULL;
672 model2 = NULL;
673
674 num_leaf_tests = 0;
675 query_time_seconds = 0.0;
676
677 rel_err = 0;
678 abs_err = 0;
679 nsolver = NULL;
680 }
681
682 /// @brief Whether the BV node in the first BVH tree is leaf
683 bool isFirstNodeLeaf(unsigned int b) const {
684 return model1->getBV(b).isLeaf();
685 }
686
687 /// @brief Obtain the left child of BV node in the first BVH
688 int getFirstLeftChild(unsigned int b) const {
689 return model1->getBV(b).leftChild();
690 }
691
692 /// @brief Obtain the right child of BV node in the first BVH
693 int getFirstRightChild(unsigned int b) const {
694 return model1->getBV(b).rightChild();
695 }
696
697 /// @brief BV culling test in one BVTT node
698 Scalar BVDistanceLowerBound(unsigned int b1, unsigned int /*b2*/) const {
699 return model1->getBV(b1).bv.distance(
700 model2_bv); // TODO(jcarpent): tf1 is not taken into account here.
701 }
702
703 /// @brief Distance testing between leaves (one bin of the height field and
704 /// one shape)
705 /// TODO(louis): deal with Hfield-Shape distance just like in Hfield-Shape
706 /// collision (bin correction etc).
707 void leafComputeDistance(unsigned int b1, unsigned int /*b2*/) const {
708 if (this->enable_statistics) this->num_leaf_tests++;
709
710 const BVNode<BV>& node = this->model1->getBV(b1);
711
712 typedef Convex<Quadrilateral> ConvexQuadrilateral;
713 const ConvexQuadrilateral convex =
714 details::buildConvexQuadrilateral(node, *this->model1);
715
716 Vec3s p1, p2, normal;
717 const Scalar distance =
718 internal::ShapeShapeDistance<ConvexQuadrilateral, S>(
719 &convex, this->tf1, this->model2, this->tf2, this->nsolver,
720 this->request.enable_signed_distance, p1, p2, normal);
721
722 this->result->update(distance, this->model1, this->model2, b1,
723 DistanceResult::NONE, p1, p2, normal);
724 }
725
726 /// @brief Whether the traversal process can stop early
727 bool canStop(Scalar c) const {
728 if ((c >= this->result->min_distance - abs_err) &&
729 (c * (1 + rel_err) >= this->result->min_distance))
730 return true;
731 return false;
732 }
733
734 Scalar rel_err;
735 Scalar abs_err;
736
737 const GJKSolver* nsolver;
738
739 const HeightField<BV>* model1;
740 const S* model2;
741 BV model2_bv;
742
743 mutable int num_bv_tests;
744 mutable int num_leaf_tests;
745 mutable Scalar query_time_seconds;
746 };
747
748 /// @}
749
750 } // namespace coal
751
752 /// @endcond
753
754 #endif
755