Skip to content

Commit 650f8e1

Browse files
authored
Merge pull request #329 from lmontaut/normal_and_nearest_points
Normal and nearest points uniformization
2 parents d309037 + 169b700 commit 650f8e1

14 files changed

+1213
-596
lines changed

include/hpp/fcl/collision_data.h

+54-11
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#define HPP_FCL_COLLISION_DATA_H
4040

4141
#include <vector>
42+
#include <array>
4243
#include <set>
4344
#include <limits>
4445

@@ -70,9 +71,14 @@ struct HPP_FCL_DLLAPI Contact {
7071
/// if object 2 is octree, it is the id of the cell
7172
int b2;
7273

73-
/// @brief contact normal, pointing from o1 to o2
74+
/// @brief contact normal, pointing from o1 to o2.
75+
/// See DistanceResult::normal for a complete definition of the normal.
7476
Vec3f normal;
7577

78+
/// @brief nearest points associated to this contact.
79+
/// See \ref CollisionResult::nearest_points.
80+
std::array<Vec3f, 2> nearest_points;
81+
7682
/// @brief contact position, in world space
7783
Vec3f pos;
7884

@@ -97,7 +103,24 @@ struct HPP_FCL_DLLAPI Contact {
97103
b2(b2_),
98104
normal(normal_),
99105
pos(pos_),
100-
penetration_depth(depth_) {}
106+
penetration_depth(depth_) {
107+
nearest_points[0] = pos - 0.5 * depth_ * normal_;
108+
nearest_points[1] = pos + 0.5 * depth_ * normal_;
109+
}
110+
111+
Contact(const CollisionGeometry* o1_, const CollisionGeometry* o2_, int b1_,
112+
int b2_, const Vec3f& p1, const Vec3f& p2, const Vec3f& normal_,
113+
FCL_REAL depth_)
114+
: o1(o1_),
115+
o2(o2_),
116+
b1(b1_),
117+
b2(b2_),
118+
normal(normal_),
119+
penetration_depth(depth_) {
120+
nearest_points[0] = p1;
121+
nearest_points[1] = p2;
122+
pos = (p1 + p2) / 2;
123+
}
101124

102125
bool operator<(const Contact& other) const {
103126
if (b1 == other.b1) return b2 < other.b2;
@@ -117,7 +140,7 @@ struct QueryResult;
117140

118141
/// @brief base class for all query requests
119142
struct HPP_FCL_DLLAPI QueryRequest {
120-
// @briefInitial guess to use for the GJK algorithm
143+
// @brief Initial guess to use for the GJK algorithm
121144
GJKInitialGuess gjk_initial_guess;
122145

123146
/// @brief whether enable gjk initial guess
@@ -233,11 +256,13 @@ enum CollisionRequestFlag {
233256

234257
/// @brief request to the collision algorithm
235258
struct HPP_FCL_DLLAPI CollisionRequest : QueryRequest {
236-
/// @brief The maximum number of contacts will return
259+
/// @brief The maximum number of contacts that can be returned
237260
size_t num_max_contacts;
238261

239262
/// @brief whether the contact information (normal, penetration depth and
240263
/// contact position) will return
264+
/// @note Only effective if the collision pair involves an Octree.
265+
/// Otherwise, it is always true.
241266
bool enable_contact;
242267

243268
/// Whether a lower bound on distance is returned when objects are disjoint
@@ -307,13 +332,21 @@ struct HPP_FCL_DLLAPI CollisionResult : QueryResult {
307332
public:
308333
/// Lower bound on distance between objects if they are disjoint.
309334
/// See \ref hpp_fcl_collision_and_distance_lower_bound_computation
310-
/// @note computed only on request (or if it does not add any computational
311-
/// overhead).
335+
/// @note Always computed. If \ref CollisionRequest::distance_upper_bound is
336+
/// set to infinity, distance_lower_bound is the actual distance between the
337+
/// shapes.
312338
FCL_REAL distance_lower_bound;
313339

314340
/// @brief nearest points
315341
/// available only when distance_lower_bound is inferior to
316342
/// CollisionRequest::break_distance.
343+
/// @note Also referred as "witness points" in other collision libraries.
344+
/// The points p1 = nearest_points[0] and p2 = nearest_points[1] verify the
345+
/// property that dist(o1, o2) * (p1 - p2) is the separation vector between o1
346+
/// and o2, with dist(o1, o2) being the **signed** distance separating o1 from
347+
/// o2. See \ref DistanceResult::normal for the definition of the separation
348+
/// vector. If o1 and o2 have multiple contacts, the nearest_points are
349+
/// associated with the contact which has the greatest penetration depth.
317350
Vec3f nearest_points[2];
318351

319352
public:
@@ -419,14 +452,24 @@ struct HPP_FCL_DLLAPI DistanceRequest : QueryRequest {
419452
/// @brief distance result
420453
struct HPP_FCL_DLLAPI DistanceResult : QueryResult {
421454
public:
422-
/// @brief minimum distance between two objects. if two objects are in
455+
/// @brief minimum distance between two objects. If two objects are in
423456
/// collision, min_distance <= 0.
424457
FCL_REAL min_distance;
425458

426-
/// @brief nearest points
427-
Vec3f nearest_points[2];
428-
429-
/// In case both objects are in collision, store the normal
459+
/// @brief nearest points.
460+
/// See CollisionResult::nearest_points.
461+
std::array<Vec3f, 2> nearest_points;
462+
463+
/// Stores the normal, defined as the normalized separation vector:
464+
/// normal = (p2 - p1) / dist(o1, o2), where p1 = nearest_points[0]
465+
/// belongs to o1 and p2 = nearest_points[1] belongs to o2 and dist(o1, o2) is
466+
/// the **signed** distance between o1 and o2. The normal always points from
467+
/// o1 to o2.
468+
/// @note The separation vector is the smallest vector such that if o1 is
469+
/// translated by it, o1 and o2 are in touching contact (they share at least
470+
/// one contact point but have a zero intersection volume). If the shapes
471+
/// overlap, dist(o1, o2) = -((p2-p1).norm()). Otherwise, dist(o1, o2) =
472+
/// (p2-p1).norm().
430473
Vec3f normal;
431474

432475
/// @brief collision object 1

include/hpp/fcl/internal/shape_shape_func.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ struct ShapeShapeCollider {
6666
if (request.isSatisfied(result)) return result.numContacts();
6767

6868
DistanceResult distanceResult;
69+
// (louis) enable_contact not used yet but may be in the future
6970
DistanceRequest distanceRequest(request.enable_contact);
7071
FCL_REAL distance = ShapeShapeDistance<T_SH1, T_SH2>(
7172
o1, tf1, o2, tf2, nsolver, distanceRequest, distanceResult);
@@ -83,10 +84,8 @@ struct ShapeShapeCollider {
8384
const Vec3f& p1 = distanceResult.nearest_points[0];
8485
const Vec3f& p2 = distanceResult.nearest_points[1];
8586

86-
Contact contact(
87-
o1, o2, distanceResult.b1, distanceResult.b2, (p1 + p2) / 2,
88-
(distance <= 0 ? distanceResult.normal : (p2 - p1).normalized()),
89-
-distance);
87+
Contact contact(o1, o2, distanceResult.b1, distanceResult.b2, p1, p2,
88+
distanceResult.normal, distance);
9089

9190
result.addContact(contact);
9291
}
@@ -136,6 +135,11 @@ SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Halfspace);
136135
SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Plane);
137136
SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Sphere);
138137
SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Cylinder);
138+
// TODO
139+
// SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Sphere, Capsule);
140+
// SHAPE_SHAPE_DISTANCE_SPECIALIZATION(TriangleP, TriangleP);
141+
// SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Ellispoids, Plane);
142+
// SHAPE_SHAPE_DISTANCE_SPECIALIZATION(Ellispoids, Halfspace);
139143

140144
SHAPE_SHAPE_DISTANCE_SPECIALIZATION(ConvexBase, Halfspace);
141145
SHAPE_SHAPE_DISTANCE_SPECIALIZATION(TriangleP, Halfspace);

include/hpp/fcl/narrowphase/narrowphase.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ struct HPP_FCL_DLLAPI GJKSolver {
132132
gjk.getClosestPoints(shape, w0, w1);
133133
distance_lower_bound = gjk.distance;
134134
if (normal)
135-
(*normal).noalias() = tf1.getRotation() * (w0 - w1).normalized();
135+
(*normal).noalias() = tf1.getRotation() * (w1 - w0).normalized();
136136
if (contact_points) *contact_points = tf1.transform((w0 + w1) / 2);
137137
return true;
138138
} else {
@@ -206,7 +206,7 @@ struct HPP_FCL_DLLAPI GJKSolver {
206206
if (gjk.hasPenetrationInformation(shape)) {
207207
gjk.getClosestPoints(shape, w0, w1);
208208
distance = gjk.distance;
209-
normal.noalias() = tf1.getRotation() * (w0 - w1).normalized();
209+
normal.noalias() = tf1.getRotation() * (w1 - w0).normalized();
210210
p1 = p2 = tf1.transform((w0 + w1) / 2);
211211
} else {
212212
details::EPA epa(epa_max_face_num, epa_max_vertex_num,
@@ -289,7 +289,7 @@ struct HPP_FCL_DLLAPI GJKSolver {
289289
// assert (distance == (w0 - w1).norm());
290290
distance = gjk.distance;
291291

292-
normal.noalias() = tf1.getRotation() * gjk.ray;
292+
normal.noalias() = -tf1.getRotation() * gjk.ray;
293293
normal.normalize();
294294
p1 = tf1.transform(p1);
295295
p2 = tf1.transform(p2);
@@ -302,7 +302,7 @@ struct HPP_FCL_DLLAPI GJKSolver {
302302
// Return contact points in case of collision
303303
// p1 = tf1.transform (p1);
304304
// p2 = tf1.transform (p2);
305-
normal.noalias() = tf1.getRotation() * (p1 - p2);
305+
normal.noalias() = tf1.getRotation() * (p2 - p1);
306306
normal.normalize();
307307
p1 = tf1.transform(p1);
308308
p2 = tf1.transform(p2);

include/hpp/fcl/serialization/collision_data.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ void save(Archive& ar, const hpp::fcl::Contact& contact,
1717
ar& make_nvp("b1", contact.b1);
1818
ar& make_nvp("b2", contact.b2);
1919
ar& make_nvp("normal", contact.normal);
20+
ar& make_nvp("nearest_points", contact.nearest_points);
2021
ar& make_nvp("pos", contact.pos);
2122
ar& make_nvp("penetration_depth", contact.penetration_depth);
2223
}
@@ -27,6 +28,10 @@ void load(Archive& ar, hpp::fcl::Contact& contact,
2728
ar >> make_nvp("b1", contact.b1);
2829
ar >> make_nvp("b2", contact.b2);
2930
ar >> make_nvp("normal", contact.normal);
31+
std::array<hpp::fcl::Vec3f, 2> nearest_points;
32+
ar >> make_nvp("nearest_points", nearest_points);
33+
contact.nearest_points[0] = nearest_points[0];
34+
contact.nearest_points[1] = nearest_points[1];
3035
ar >> make_nvp("pos", contact.pos);
3136
ar >> make_nvp("penetration_depth", contact.penetration_depth);
3237
contact.o1 = NULL;
@@ -115,7 +120,7 @@ void save(Archive& ar, const hpp::fcl::DistanceResult& distance_result,
115120
ar& make_nvp("base", boost::serialization::base_object<hpp::fcl::QueryResult>(
116121
distance_result));
117122
ar& make_nvp("min_distance", distance_result.min_distance);
118-
ar& make_nvp("nearest_points", make_array(distance_result.nearest_points, 2));
123+
ar& make_nvp("nearest_points", distance_result.nearest_points);
119124
ar& make_nvp("normal", distance_result.normal);
120125
ar& make_nvp("b1", distance_result.b1);
121126
ar& make_nvp("b2", distance_result.b2);
@@ -128,8 +133,10 @@ void load(Archive& ar, hpp::fcl::DistanceResult& distance_result,
128133
make_nvp("base", boost::serialization::base_object<hpp::fcl::QueryResult>(
129134
distance_result));
130135
ar >> make_nvp("min_distance", distance_result.min_distance);
131-
ar >>
132-
make_nvp("nearest_points", make_array(distance_result.nearest_points, 2));
136+
std::array<hpp::fcl::Vec3f, 2> nearest_points;
137+
ar >> make_nvp("nearest_points", nearest_points);
138+
distance_result.nearest_points[0] = nearest_points[0];
139+
distance_result.nearest_points[1] = nearest_points[1];
133140
ar >> make_nvp("normal", distance_result.normal);
134141
ar >> make_nvp("b1", distance_result.b1);
135142
ar >> make_nvp("b2", distance_result.b2);

python/collision.cc

+14
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ const CollisionGeometry* geto(const Contact& c) {
5959
return index == 1 ? c.o1 : c.o2;
6060
}
6161

62+
struct ContactWrapper {
63+
static Vec3f getNearestPoint1(const Contact& contact) {
64+
return contact.nearest_points[0];
65+
}
66+
static Vec3f getNearestPoint2(const Contact& contact) {
67+
return contact.nearest_points[1];
68+
}
69+
};
70+
6271
void exposeCollisionAPI() {
6372
if (!eigenpy::register_symbolic_link_to_registered_type<
6473
CollisionRequestFlag>()) {
@@ -152,9 +161,14 @@ void exposeCollisionAPI() {
152161
make_function(&geto<2>,
153162
return_value_policy<reference_existing_object>()),
154163
doxygen::class_attrib_doc<Contact>("o2"))
164+
.def("getNearestPoint1", &ContactWrapper::getNearestPoint1,
165+
doxygen::class_attrib_doc<Contact>("nearest_points"))
166+
.def("getNearestPoint2", &ContactWrapper::getNearestPoint2,
167+
doxygen::class_attrib_doc<Contact>("nearest_points"))
155168
.DEF_RW_CLASS_ATTRIB(Contact, b1)
156169
.DEF_RW_CLASS_ATTRIB(Contact, b2)
157170
.DEF_RW_CLASS_ATTRIB(Contact, normal)
171+
.DEF_RW_CLASS_ATTRIB(Contact, nearest_points)
158172
.DEF_RW_CLASS_ATTRIB(Contact, pos)
159173
.DEF_RW_CLASS_ATTRIB(Contact, penetration_depth)
160174
.def(self == self)

python/distance.cc

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ void exposeDistanceAPI() {
8888
doxygen::class_attrib_doc<DistanceResult>("nearest_points"))
8989
.def("getNearestPoint2", &DistanceRequestWrapper::getNearestPoint2,
9090
doxygen::class_attrib_doc<DistanceResult>("nearest_points"))
91+
.DEF_RO_CLASS_ATTRIB(DistanceResult, nearest_points)
9192
.DEF_RO_CLASS_ATTRIB(DistanceResult, o1)
9293
.DEF_RO_CLASS_ATTRIB(DistanceResult, o2)
9394
.DEF_RW_CLASS_ATTRIB(DistanceResult, b1)

src/collision.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ std::size_t collide(const CollisionObject* o1, const CollisionObject* o2,
7070
std::size_t collide(const CollisionGeometry* o1, const Transform3f& tf1,
7171
const CollisionGeometry* o2, const Transform3f& tf2,
7272
const CollisionRequest& request, CollisionResult& result) {
73-
// If securit margin is set to -infinity, return that there is no collision
73+
// If security margin is set to -infinity, return that there is no collision
7474
if (request.security_margin == -std::numeric_limits<FCL_REAL>::infinity()) {
7575
result.clear();
7676
return false;

src/distance/capsule_capsule.cpp

+5-8
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ template <>
8080
FCL_REAL ShapeShapeDistance<Capsule, Capsule>(
8181
const CollisionGeometry* o1, const Transform3f& tf1,
8282
const CollisionGeometry* o2, const Transform3f& tf2, const GJKSolver*,
83-
const DistanceRequest& request, DistanceResult& result) {
83+
const DistanceRequest& /*request*/, DistanceResult& result) {
8484
const Capsule* capsule1 = static_cast<const Capsule*>(o1);
8585
const Capsule* capsule2 = static_cast<const Capsule*>(o2);
8686

@@ -153,18 +153,15 @@ FCL_REAL ShapeShapeDistance<Capsule, Capsule>(
153153

154154
// witness points achieving the distance between the two segments
155155
FCL_REAL distance = (w1 - w2).norm();
156-
const Vec3f normal = (w1 - w2) / distance;
157-
result.normal = normal;
158156

159157
// capsule spcecific distance computation
160158
distance = distance - (radius1 + radius2);
161159
result.min_distance = distance;
162160

163-
// witness points for the capsules
164-
if (request.enable_nearest_points) {
165-
result.nearest_points[0] = w1 - radius1 * normal;
166-
result.nearest_points[1] = w2 + radius2 * normal;
167-
}
161+
// Normal points from o1 to o2
162+
result.normal = (w2 - w1).normalized();
163+
result.nearest_points[0] = w1 + radius1 * result.normal;
164+
result.nearest_points[1] = w2 - radius2 * result.normal;
168165

169166
return distance;
170167
}

src/distance/sphere_sphere.cpp

+7-23
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,10 @@ FCL_REAL ShapeShapeDistance<Sphere, Sphere>(
7474
FCL_REAL dist = c1c2.norm();
7575
Vec3f unit(0, 0, 0);
7676
if (dist > epsilon) unit = c1c2 / dist;
77-
FCL_REAL penetrationDepth;
78-
penetrationDepth = r1 + r2 - dist;
79-
bool collision = (penetrationDepth >= 0);
80-
result.min_distance = -penetrationDepth;
81-
if (collision) {
82-
// Take contact point at the middle of intersection between each sphere
83-
// and segment [c1 c2].
84-
FCL_REAL abscissa = .5 * r1 + .5 * (dist - r2);
85-
Vec3f contact = center1 + abscissa * unit;
86-
result.nearest_points[0] = result.nearest_points[1] = contact;
87-
return result.min_distance;
88-
} else {
89-
FCL_REAL abs1(r1), abs2(dist - r2);
90-
result.nearest_points[0] = center1 + abs1 * unit;
91-
result.nearest_points[1] = center1 + abs2 * unit;
92-
}
77+
result.min_distance = dist - (r1 + r2);
78+
result.normal = unit;
79+
result.nearest_points[0] = center1 + r1 * unit;
80+
result.nearest_points[1] = center2 - r2 * unit;
9381
return result.min_distance;
9482
}
9583

@@ -101,7 +89,7 @@ std::size_t ShapeShapeCollider<Sphere, Sphere>::run(
10189
const Sphere* s1 = static_cast<const Sphere*>(o1);
10290
const Sphere* s2 = static_cast<const Sphere*>(o2);
10391

104-
// We assume that capsules are centered at the origin.
92+
// We assume that spheres are centered at the origin.
10593
const fcl::Vec3f& center1 = tf1.getTranslation();
10694
const fcl::Vec3f& center2 = tf2.getTranslation();
10795
FCL_REAL r1 = s1->radius;
@@ -119,12 +107,8 @@ std::size_t ShapeShapeCollider<Sphere, Sphere>::run(
119107
center1 + unit * r1,
120108
center2 - unit * r2);
121109
if (distToCollision <= request.collision_distance_threshold) {
122-
// Take contact point at the middle of intersection between each sphere
123-
// and segment [c1 c2].
124-
FCL_REAL abscissa = .5 * r1 + .5 * (dist - r2);
125-
Vec3f contactPoint = center1 + abscissa * unit;
126-
Contact contact(o1, o2, -1, -1, contactPoint, unit,
127-
-(distToCollision + margin));
110+
Contact contact(o1, o2, -1, -1, center1 + unit * r1, center2 - unit * r2,
111+
unit, distToCollision + margin);
128112
result.addContact(contact);
129113
return 1;
130114
}

0 commit comments

Comments
 (0)