GCC Code Coverage Report


Directory: ./
File: include/hpp/constraints/convex-shape.hh
Date: 2025-05-05 12:19:30
Exec Total Coverage
Lines: 111 145 76.6%
Branches: 94 204 46.1%

Line Branch Exec Source
1 // Copyright (c) 2015, LAAS-CNRS
2 // Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
3 //
4
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // 1. Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27 // DAMAGE.
28
29 #ifndef HPP_CONSTRAINTS_CONVEX_SHAPE_HH
30 #define HPP_CONSTRAINTS_CONVEX_SHAPE_HH
31
32 #include <coal/shape/geometric_shapes.h>
33
34 #include <vector>
35
36 // Only for specialization of vector3_t. This is a bad design of Pinocchio.
37 #include <hpp/constraints/config.hh>
38 #include <hpp/constraints/fwd.hh>
39 #include <hpp/pinocchio/joint.hh>
40 #include <pinocchio/multibody/model.hpp>
41
42 namespace hpp {
43 namespace constraints {
44 /// Return the closest point to point P, on a segment line \f$ A + t*v, t \in
45 /// [0,1] \f$. \param P PA where P is the point to \param A the origin the
46 /// segment line \param v vector presenting the segment line \param[out] B the
47 /// closest point
48 inline void closestPointToSegment(const vector3_t& P, const vector3_t& A,
49 const vector3_t& v, vector3_t& B) {
50 vector3_t w = A - P;
51 value_type c1, c2;
52 c1 = v.dot(w);
53 c2 = v.dot(v);
54 if (c1 <= 0)
55 B = A;
56 else if (c2 <= c1)
57 B = A + v;
58 else
59 B = A + c1 / c2 * v;
60 }
61
62 /// \param A, u point and vector defining the line \f$ A + t*u \f$
63 /// \param P, n point and normal vector defining the plane \f$ Q \in plane
64 /// \Rightleftarrow (P - Q) . n = 0 \f$ \return the intesection point. \warning
65 /// \c u and \c n are expected not to be orthogonal. \todo make this function
66 /// robust to orthogonal inputs.
67 152329 inline vector3_t linePlaneIntersection(const vector3_t& A, const vector3_t& u,
68 const vector3_t& P, const vector3_t& n) {
69
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 152334 times.
152329 assert(std::abs(n.dot(u)) > 1e-8);
70
6/12
✓ Branch 2 taken 152339 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 152328 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 152337 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 152336 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 152339 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 152330 times.
✗ Branch 18 not taken.
304664 return A + u * (n.dot(P - A)) / n.dot(u);
71 }
72
73 class HPP_CONSTRAINTS_DLLAPI ConvexShape {
74 public:
75 /// Represent a convex shape
76 /// \param pts a sequence of points lying in a plane. The convex shape is
77 /// obtained by connecting consecutive points (in a circular way)
78 ///
79 /// \note There is no convexity check yet. The order is important:
80 /// The normal is parallel to (pts[1] - pts[0]).cross (pts[2] - pts[1])
81 /// The normal to the segment in the plane are directed outward.
82 /// (pts[i+1] - pts[i]).cross (normalToConvexShape)
83 26 ConvexShape(const std::vector<vector3_t>& pts,
84 JointPtr_t joint = JointPtr_t())
85
4/8
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 26 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 26 times.
✗ Branch 14 not taken.
26 : Pts_(pts), joint_(joint) {
86
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 init();
87 26 }
88
89 ConvexShape(const coal::TriangleP& t, const JointPtr_t& joint = JointPtr_t())
90 : Pts_(triangleToPoints(t)), joint_(joint) {
91 init();
92 }
93
94 /// This constructor is required for compatibility with deprecated
95 /// Triangle constructor.
96 1 ConvexShape(const vector3_t& p0, const vector3_t& p1, const vector3_t& p2,
97 const JointPtr_t& joint = JointPtr_t())
98
4/8
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
1 : Pts_(points(p0, p1, p2)), joint_(joint) {
99
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 init();
100 1 }
101
102 // Copy constructor
103
5/10
✓ Branch 2 taken 38601 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38601 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 38601 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 38600 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 38600 times.
✗ Branch 18 not taken.
38594 ConvexShape(const ConvexShape& t) : Pts_(t.Pts_), joint_(t.joint_) { init(); }
104
105 14 void reverse() {
106 14 std::reverse(Pts_.begin(), Pts_.end());
107 14 init();
108 14 }
109
110 /// Intersection with a line defined by a point and a vector.
111 /// \param A, u point and vector expressed in the local frame.
112 inline vector3_t intersectionLocal(const vector3_t& A,
113 const vector3_t& u) const {
114 return linePlaneIntersection(A, u, C_, N_);
115 }
116
117 /// As isInside but consider A as expressed in joint frame.
118 23 inline bool isInsideLocal(const vector3_t& Ap) const {
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
23 assert(shapeDimension_ > 2);
120
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 16 times.
76 for (std::size_t i = 0; i < shapeDimension_; ++i) {
121
3/4
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 53 times.
60 if (Ns_[i].dot(Ap - Pts_[i]) > 0) return false;
122 }
123 16 return true;
124 }
125
126 /// Return the shortest distance from a point to the shape
127 /// A negative value means the point is inside the shape
128 /// \param a a point already in the plane containing the convex shape,
129 /// and expressed in the local frame.
130 152370 inline value_type distanceLocal(const vector3_t& a) const {
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152370 times.
152370 assert(shapeDimension_ > 1);
132 152370 const value_type inf = std::numeric_limits<value_type>::infinity();
133 152370 value_type minPosDist = inf, maxNegDist = -inf;
134 152370 bool outside = false;
135
2/2
✓ Branch 0 taken 610420 times.
✓ Branch 1 taken 152381 times.
762801 for (std::size_t i = 0; i < shapeDimension_; ++i) {
136
2/4
✓ Branch 6 taken 610432 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 610431 times.
✗ Branch 10 not taken.
610420 value_type d = dist(a - Pts_[i], Ls_[i], Us_[i], Ns_[i]);
137
2/2
✓ Branch 0 taken 97986 times.
✓ Branch 1 taken 512445 times.
610431 if (d > 0) {
138 97986 outside = true;
139
2/2
✓ Branch 0 taken 87129 times.
✓ Branch 1 taken 10857 times.
97986 if (d < minPosDist) minPosDist = d;
140 }
141
4/4
✓ Branch 0 taken 512443 times.
✓ Branch 1 taken 97988 times.
✓ Branch 2 taken 307399 times.
✓ Branch 3 taken 205044 times.
610431 if (d <= 0 && d > maxNegDist) maxNegDist = d;
142 }
143
2/2
✓ Branch 0 taken 86415 times.
✓ Branch 1 taken 65966 times.
152381 if (outside) return minPosDist;
144 65966 return maxNegDist;
145 }
146
147 /// Return the X axis of the plane in the joint frame
148 2 inline const vector3_t& planeXaxis() const {
149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(shapeDimension_ > 2);
150 2 return Ns_[0];
151 }
152 /// Return the Y axis of the plane in the joint frame
153 /// The Y axis is aligned with \f$ Pts_[1] - Pts_[0] \f$
154 2 inline const vector3_t& planeYaxis() const {
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 assert(shapeDimension_ > 2);
156 2 return Us_[0];
157 }
158
159 /// Transform of the shape in the joint frame
160 77112 inline const Transform3s& positionInJoint() const { return MinJoint_; }
161
162 bool operator==(ConvexShape const& other) const {
163 if (Pts_ != other.Pts_) return false;
164 if (shapeDimension_ != other.shapeDimension_) return false;
165 if (C_ != other.C_) return false;
166 if (N_ != other.N_) return false;
167 if (Ns_ != other.Ns_) return false;
168 if (Us_ != other.Us_) return false;
169 if (Ls_ != other.Ls_) return false;
170 if (MinJoint_ != other.MinJoint_) return false;
171 if (joint_ != other.joint_) return false;
172 return true;
173 }
174 bool operator!=(ConvexShape const& other) const {
175 return !(this->operator==(other));
176 }
177
178 /// The points in the joint frame. It is constant.
179 std::vector<vector3_t> Pts_;
180 size_t shapeDimension_;
181 /// the center in the joint frame. It is constant.
182 vector3_t C_;
183 /// the normal to the shape in the joint frame. It is constant.
184 vector3_t N_;
185 /// Ns_ and Us_ are unit vector, in the plane containing the shape,
186 /// expressed in the joint frame.
187 /// Ns_[i] is normal to edge i, pointing inside.
188 /// Ns_[i] is a vector director of edge i.
189 std::vector<vector3_t> Ns_, Us_;
190 vector_t Ls_;
191 Transform3s MinJoint_;
192 JointPtr_t joint_;
193
194 private:
195 /// Return the distance between the point A and the segment
196 /// [P, c2*v] oriented by u.
197 /// w = PA.
198 610427 inline value_type dist(const vector3_t& w, const value_type& c2,
199 const vector3_t& v, const vector3_t& u) const {
200 value_type c1;
201 610427 c1 = v.dot(w);
202
4/4
✓ Branch 0 taken 98146 times.
✓ Branch 1 taken 512288 times.
✓ Branch 3 taken 11569 times.
✓ Branch 4 taken 86576 times.
610434 if (c1 <= 0) return (u.dot(w) > 0) ? (w.norm()) : (-w.norm());
203
2/2
✓ Branch 0 taken 98736 times.
✓ Branch 1 taken 413552 times.
512288 if (c2 <= c1)
204 // TODO: (w - c2 * v).norm() == sqrt((u.dot(w)**2 + (c1 - c2)**2)
205 // second should be cheaper.
206
8/14
✓ Branch 1 taken 11569 times.
✓ Branch 2 taken 87169 times.
✓ Branch 4 taken 11569 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11569 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 11569 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 87168 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 87168 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 87159 times.
✗ Branch 20 not taken.
98736 return (u.dot(w) > 0) ? ((w - c2 * v).norm()) : (-(w - c2 * v).norm());
207 413552 return u.dot(w);
208 }
209
210 static std::vector<vector3_t> triangleToPoints(const coal::TriangleP& t) {
211 // TODO
212 // return points (t.a, t.b, t.c);
213 std::vector<vector3_t> ret(3);
214 ret[0] = t.a;
215 ret[1] = t.b;
216 ret[2] = t.c;
217 return ret;
218 }
219 1 static std::vector<vector3_t> points(const vector3_t& p0, const vector3_t& p1,
220 const vector3_t& p2) {
221
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 std::vector<vector3_t> ret(3);
222
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 ret[0] = p0;
223
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 ret[1] = p1;
224
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 ret[2] = p2;
225 1 return ret;
226 }
227
228 38642 void init() {
229 38642 shapeDimension_ = Pts_.size();
230
231
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38638 times.
38642 switch (shapeDimension_) {
232 case 0:
233 throw std::logic_error("Cannot represent an empty shape.");
234 break;
235 4 case 1:
236 4 C_ = Pts_[0];
237 // The transformation will be (N_, Ns_[0], Us_[0])
238 // Fill vectors so as to be consistent
239
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 N_ = vector3_t(1, 0, 0);
240
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 Ns_.push_back(vector3_t(0, 1, 0));
241
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 Us_.push_back(vector3_t(0, 0, 1));
242 4 break;
243 case 2:
244 Ls_ = vector_t(1);
245 C_ = (Pts_[0] + Pts_[1]) / 2;
246 // The transformation will be (N_, Ns_[0], Us_[0])
247 // Fill vectors so as to be consistent
248 Us_.push_back(Pts_[1] - Pts_[0]);
249 Ls_[0] = Us_[0].norm();
250 Us_[0].normalize();
251 if (Us_[0][0] != 0)
252 N_ = vector3_t(-Us_[0][1], Us_[0][0], 0);
253 else
254 N_ = vector3_t(0, -Us_[0][2], Us_[0][1]);
255 N_.normalize();
256 Ns_.push_back(Us_[0].cross(N_));
257 Ns_[0].normalize(); // Should be unnecessary
258 break;
259 38638 default:
260 38638 Ls_ = vector_t(shapeDimension_);
261 38638 C_.setZero();
262
2/2
✓ Branch 2 taken 155312 times.
✓ Branch 3 taken 38638 times.
193950 for (std::size_t i = 0; i < shapeDimension_; ++i) C_ += Pts_[i];
263 // TODO This is very ugly. Why Eigen does not have the operator/=(int)
264 // ...
265
1/2
✓ Branch 2 taken 38638 times.
✗ Branch 3 not taken.
38638 C_ /= (value_type)Pts_.size();
266
2/4
✓ Branch 6 taken 38638 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 38638 times.
✗ Branch 10 not taken.
38638 N_ = (Pts_[1] - Pts_[0]).cross(Pts_[2] - Pts_[1]);
267
2/4
✓ Branch 2 taken 38638 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 38638 times.
38638 assert(!N_.isZero());
268 38638 N_.normalize();
269
270 38638 Us_.resize(Pts_.size());
271 38638 Ns_.resize(Pts_.size());
272
2/2
✓ Branch 0 taken 155312 times.
✓ Branch 1 taken 38638 times.
193950 for (std::size_t i = 0; i < shapeDimension_; ++i) {
273
1/2
✓ Branch 5 taken 155312 times.
✗ Branch 6 not taken.
155312 Us_[i] = Pts_[(i + 1) % shapeDimension_] - Pts_[i];
274 155312 Ls_[i] = Us_[i].norm();
275 155312 Us_[i].normalize();
276 155312 Ns_[i] = Us_[i].cross(N_);
277 155312 Ns_[i].normalize();
278 }
279
2/2
✓ Branch 0 taken 155312 times.
✓ Branch 1 taken 38638 times.
193950 for (std::size_t i = 0; i < shapeDimension_; ++i) {
280
1/2
✓ Branch 3 taken 155312 times.
✗ Branch 4 not taken.
155312 assert(Us_[(i + 1) % shapeDimension_].dot(Ns_[i]) < 0 &&
281 "The sequence does not define a convex surface");
282 }
283 38638 break;
284 }
285
286 38642 MinJoint_.translation() = C_;
287
1/2
✓ Branch 3 taken 38642 times.
✗ Branch 4 not taken.
38642 MinJoint_.rotation().col(0) = N_;
288
1/2
✓ Branch 4 taken 38642 times.
✗ Branch 5 not taken.
38642 MinJoint_.rotation().col(1) = Ns_[0];
289
1/2
✓ Branch 4 taken 38642 times.
✗ Branch 5 not taken.
38642 MinJoint_.rotation().col(2) = Us_[0];
290 38642 }
291 };
292
293 struct HPP_CONSTRAINTS_DLLAPI ConvexShapeData {
294 // normal in the world frame
295 vector3_t normal_;
296 // center in the world frame
297 vector3_t center_;
298 // Current joint position
299 Transform3s oMj_;
300
301 /// Compute center and normal in world frame
302 6 inline void updateToCurrentTransform(const ConvexShape& cs) {
303
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (cs.joint_ == NULL) {
304 6 oMj_.setIdentity();
305 6 _recompute<true>(cs);
306 } else {
307 oMj_ = cs.joint_->currentTransformation();
308 _recompute<false>(cs);
309 }
310 6 }
311
312 /// Compute center and normal in world frame
313 /// Thread safe version.
314 229432 inline void updateToCurrentTransform(const ConvexShape& cs,
315 const pinocchio::DeviceData& d) {
316
2/2
✓ Branch 1 taken 1879 times.
✓ Branch 2 taken 227553 times.
229432 if (cs.joint_ == NULL) {
317 1879 oMj_.setIdentity();
318 1885 _recompute<true>(cs);
319 } else {
320 227553 oMj_ = cs.joint_->currentTransformation(d);
321 227561 _recompute<false>(cs);
322 }
323 229439 }
324
325 template <bool WorldFrame>
326 458884 inline void _recompute(const ConvexShape& cs) {
327 if (WorldFrame) {
328 3768 center_ = cs.C_;
329 3788 normal_ = cs.N_;
330 } else {
331 455116 center_ = oMj_.act(cs.C_);
332
1/2
✓ Branch 3 taken 227556 times.
✗ Branch 4 not taken.
455124 normal_ = oMj_.rotation() * cs.N_;
333 }
334 458900 }
335
336 /// Intersection with a line defined by a point and a vector.
337 /// \param A, u point and vector expressed in the world frame.
338 152327 inline vector3_t intersection(const vector3_t& A, const vector3_t& u) const {
339 152327 return linePlaneIntersection(A, u, center_, normal_);
340 }
341
342 /// Check whether the intersection of the line defined by A and u
343 /// onto the plane containing the triangle is inside the triangle.
344 /// \param A, u point and vector in world frame defining the line \f$ A + t*u
345 /// \f$
346 inline bool isInside(const ConvexShape& cs, const vector3_t& A,
347 const vector3_t& u) const {
348 return isInside(cs, intersection(A, u));
349 }
350 /// Check whether the point As in world frame is inside the triangle.
351 18 inline bool isInside(const ConvexShape& cs, const vector3_t& Ap) const {
352
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
18 if (cs.joint_ == NULL) return cs.isInsideLocal(Ap);
353 vector3_t Ap_loc = oMj_.actInv(Ap);
354 return cs.isInsideLocal(Ap_loc);
355 }
356
357 /// \param yaxis vector in world frame to which we should try to align
358 inline Transform3s alignedPositionInJoint(const ConvexShape& cs,
359 vector3_t yaxis) const {
360 assert(cs.shapeDimension_ > 2);
361 // Project vector onto the plane
362 yaxis = oMj_.actInv(yaxis);
363 vector3_t yproj = yaxis - yaxis.dot(cs.N_) * cs.N_;
364 if (yproj.isZero())
365 return cs.MinJoint_;
366 else {
367 Transform3s M;
368 M.translation() = cs.C_;
369 M.rotation().col(0) = cs.N_;
370 M.rotation().col(1) = yaxis;
371 M.rotation().col(2) = cs.N_.cross(yaxis);
372 return M;
373 }
374 }
375
376 /// See ConvexShape::distanceLocal
377 /// \param a a point already in the plane containing the convex shape,
378 /// and expressed in the global frame.
379 152368 inline value_type distance(const ConvexShape& cs, vector3_t a) const {
380
2/2
✓ Branch 1 taken 150448 times.
✓ Branch 2 taken 1917 times.
152368 if (cs.joint_ != NULL) a = oMj_.actInv(a);
381 152365 return cs.distanceLocal(a);
382 }
383 };
384 } // namespace constraints
385 } // namespace hpp
386
387 #endif // HPP_CONSTRAINTS_CONVEX_SHAPE_HH
388