GCC Code Coverage Report


Directory: ./
File: include/pinocchio/bindings/python/spatial/inertia.hpp
Date: 2024-08-27 18:20:05
Exec Total Coverage
Lines: 70 100 70.0%
Branches: 92 284 32.4%

Line Branch Exec Source
1 //
2 // Copyright (c) 2015-2024 CNRS INRIA
3 // Copyright (c) 2016 Wandercraft, 86 rue de Paris 91400 Orsay, France.
4 //
5
6 #ifndef __pinocchio_python_spatial_inertia_hpp__
7 #define __pinocchio_python_spatial_inertia_hpp__
8
9 #include <eigenpy/exception.hpp>
10 #include <eigenpy/eigenpy.hpp>
11 #include <eigenpy/memory.hpp>
12 #include <boost/python/tuple.hpp>
13
14 #include "pinocchio/spatial/inertia.hpp"
15
16 #include "pinocchio/bindings/python/utils/cast.hpp"
17 #include "pinocchio/bindings/python/utils/copyable.hpp"
18 #include "pinocchio/bindings/python/utils/printable.hpp"
19
20 #if EIGENPY_VERSION_AT_MOST(2, 8, 1)
21 EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(pinocchio::Inertia)
22 #endif
23
24 namespace pinocchio
25 {
26 namespace python
27 {
28 namespace bp = boost::python;
29
30 template<typename Inertia>
31 struct InertiaPythonVisitor : public boost::python::def_visitor<InertiaPythonVisitor<Inertia>>
32 {
33 enum
34 {
35 Options = Inertia::Options
36 };
37 typedef typename Inertia::Scalar Scalar;
38 typedef typename Inertia::Vector3 Vector3;
39 typedef typename Inertia::Matrix3 Matrix3;
40 typedef typename Inertia::Vector6 Vector6;
41 typedef typename Inertia::Matrix6 Matrix6;
42
43 typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
44 typedef MotionTpl<Scalar, Options> Motion;
45 typedef ForceTpl<Scalar, Options> Force;
46
47 public:
48 template<class PyClass>
49 20 void visit(PyClass & cl) const
50 {
51 static const Scalar dummy_precision = Eigen::NumTraits<Scalar>::dummy_precision();
52 PINOCCHIO_COMPILER_DIAGNOSTIC_PUSH
53 PINOCCHIO_COMPILER_DIAGNOSTIC_IGNORED_SELF_ASSIGN_OVERLOADED
54
2/4
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
20 cl.def(
55 "__init__",
56 bp::make_constructor(
57 20 &InertiaPythonVisitor::makeFromMCI, bp::default_call_policies(),
58 bp::args("mass", "lever", "inertia")),
59 "Initialize from mass, lever and 3d inertia.")
60
61
3/6
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
40 .def(bp::init<>(bp::arg("self"), "Default constructor."))
62
5/10
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 20 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 20 times.
✗ Branch 14 not taken.
40 .def(bp::init<const Inertia &>((bp::arg("self"), bp::arg("clone")), "Copy constructor"))
63
64
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .add_property(
65 "mass", &InertiaPythonVisitor::getMass, &InertiaPythonVisitor::setMass,
66 "Mass of the Spatial Inertia.")
67
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 .add_property(
68 "lever",
69 bp::make_function(
70 (typename Inertia::Vector3 & (Inertia::*)()) & Inertia::lever,
71 20 bp::return_internal_reference<>()),
72 &InertiaPythonVisitor::setLever,
73 "Center of mass location of the Spatial Inertia. It corresponds to the location of the "
74 "center of mass regarding to the frame where the Spatial Inertia is expressed.")
75
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .add_property(
76 "inertia", &InertiaPythonVisitor::getInertia, &InertiaPythonVisitor::setInertia,
77 "Rotational part of the Spatial Inertia, i.e. a symmetric matrix "
78 "representing the rotational inertia around the center of mass.")
79
80
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 .def("matrix", (Matrix6(Inertia::*)() const) & Inertia::matrix, bp::arg("self"))
81
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def("inverse", (Matrix6(Inertia::*)() const) & Inertia::inverse, bp::arg("self"))
82
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
83 "se3Action", &Inertia::template se3Action<Scalar, Options>, bp::args("self", "M"),
84 "Returns the result of the action of M on *this.")
85
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
86 "se3ActionInverse", &Inertia::template se3ActionInverse<Scalar, Options>,
87 bp::args("self", "M"), "Returns the result of the action of the inverse of M on *this.")
88
89
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(
90
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 "setIdentity", &Inertia::setIdentity, bp::arg("self"),
91 "Set *this to be the Identity inertia.")
92
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(
93
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 "setZero", &Inertia::setZero, bp::arg("self"),
94 "Set all the components of *this to zero.")
95
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(
96
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 "setRandom", &Inertia::setRandom, bp::arg("self"),
97 "Set all the components of *this to random values.")
98
99
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
20 .def(bp::self + bp::self)
100
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 .def(bp::self += bp::self)
101
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 .def(bp::self - bp::self)
102
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 .def(bp::self -= bp::self)
103
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 .def(bp::self * bp::other<Motion>())
104
105 40 .add_property("np", (Matrix6(Inertia::*)() const) & Inertia::matrix)
106
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
107 "vxiv", &Inertia::template vxiv<Motion>, bp::args("self", "v"),
108 "Returns the result of v x Iv.")
109
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
110 "vtiv", &Inertia::template vtiv<Motion>, bp::args("self", "v"),
111 "Returns the result of v.T * Iv.")
112
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
113 "vxi",
114 (Matrix6(Inertia::*)(const MotionDense<Motion> &) const)
115 & Inertia::template vxi<Motion>,
116 bp::args("self", "v"), "Returns the result of v x* I, a 6x6 matrix.")
117
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
118 "ivx",
119 (Matrix6(Inertia::*)(const MotionDense<Motion> &) const)
120 & Inertia::template ivx<Motion>,
121 bp::args("self", "v"), "Returns the result of I vx, a 6x6 matrix.")
122
3/6
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
40 .def(
123 "variation",
124 (Matrix6(Inertia::*)(const MotionDense<Motion> &) const)
125 & Inertia::template variation<Motion>,
126 bp::args("self", "v"), "Returns the time derivative of the inertia.")
127
128 #ifndef PINOCCHIO_PYTHON_SKIP_COMPARISON_OPERATIONS
129
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 .def(bp::self == bp::self)
130
1/2
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
20 .def(bp::self != bp::self)
131 #endif
132
133 #ifndef PINOCCHIO_PYTHON_SKIP_COMPARISON_OPERATIONS
134
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(
135 "isApprox", &Inertia::isApprox,
136
5/10
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 20 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 20 times.
✗ Branch 14 not taken.
60 (bp::arg("self"), bp::arg("other"), bp::arg("prec") = dummy_precision),
137 "Returns true if *this is approximately equal to other, within the precision given "
138 "by prec.")
139
140
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(
141
4/8
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 20 times.
✗ Branch 11 not taken.
40 "isZero", &Inertia::isZero, (bp::arg("self"), bp::arg("prec") = dummy_precision),
142 "Returns true if *this is approximately equal to the zero Inertia, within the "
143 "precision given by prec.")
144 #endif
145
146
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 .def("Identity", &Inertia::Identity, "Returns the identity Inertia.")
147
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("Identity")
148
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def("Zero", &Inertia::Zero, "Returns the zero Inertia.")
149
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("Zero")
150
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def("Random", &Inertia::Random, "Returns a random Inertia.")
151
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("Random")
152
153
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(
154 "toDynamicParameters", &InertiaPythonVisitor::toDynamicParameters_proxy,
155
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
40 bp::arg("self"),
156 "Returns the representation of the matrix as a vector of dynamic parameters."
157 "\nThe parameters are given as v = [m, mc_x, mc_y, mc_z, I_{xx}, I_{xy}, I_{yy}, "
158 "I_{xz}, I_{yz}, I_{zz}]^T "
159 "where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter")
160
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
161 "FromDynamicParameters", &Inertia::template FromDynamicParameters<VectorXs>,
162 bp::args("dynamic_parameters"),
163 "Builds and inertia matrix from a vector of dynamic parameters."
164 "\nThe parameters are given as dynamic_parameters = [m, mc_x, mc_y, mc_z, I_{xx}, "
165 "I_{xy}, I_{yy}, I_{xz}, I_{yz}, I_{zz}]^T "
166 "where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter.")
167
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("FromDynamicParameters")
168
169
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
170 "FromSphere", &Inertia::FromSphere, bp::args("mass", "radius"),
171 "Returns the Inertia of a sphere defined by a given mass and radius.")
172
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("FromSphere")
173
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
174 "FromEllipsoid", &Inertia::FromEllipsoid,
175 bp::args("mass", "length_x", "length_y", "length_z"),
176 "Returns the Inertia of an ellipsoid shape defined by a mass and given dimensions "
177 "the semi-axis of values length_{x,y,z}.")
178
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("FromEllipsoid")
179
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
180 "FromCylinder", &Inertia::FromCylinder, bp::args("mass", "radius", "length"),
181 "Returns the Inertia of a cylinder defined by its mass, radius and length along the "
182 "Z axis.")
183
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("FromCylinder")
184
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
185 "FromBox", &Inertia::FromBox, bp::args("mass", "length_x", "length_y", "length_z"),
186 "Returns the Inertia of a box shape with a mass and of dimension the semi axis of "
187 "length_{x,y,z}.")
188
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("FromBox")
189
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
40 .def(
190 "FromCapsule", &Inertia::FromCapsule, bp::args("mass", "radius", "height"),
191 "Computes the Inertia of a capsule defined by its mass, radius and length along the "
192 "Z axis. Assumes a uniform density.")
193
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .staticmethod("FromCapsule")
194
195 40 .def("__array__", (Matrix6(Inertia::*)() const) & Inertia::matrix)
196
2/4
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
20 .def("__array__", &__array__)
197 #ifndef PINOCCHIO_PYTHON_NO_SERIALIZATION
198
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def_pickle(Pickle())
199 #endif
200 ;
201 PINOCCHIO_COMPILER_DIAGNOSTIC_POP
202 20 }
203
204 static Scalar getMass(const Inertia & self)
205 {
206 return self.mass();
207 }
208 static void setMass(Inertia & self, Scalar mass)
209 {
210 self.mass() = mass;
211 }
212
213 static void setLever(Inertia & self, const Vector3 & lever)
214 {
215 self.lever() = lever;
216 }
217
218 static Matrix3 getInertia(const Inertia & self)
219 {
220 return self.inertia().matrix();
221 }
222 // static void setInertia(Inertia & self, const Vector6 & minimal_inertia) {
223 // self.inertia().data() = minimal_inertia; }
224 static void setInertia(Inertia & self, const Matrix3 & symmetric_inertia)
225 {
226 if (!check_expression_if_real<Scalar>(
227 isZero(symmetric_inertia - symmetric_inertia.transpose())))
228 throw eigenpy::Exception("The 3d inertia should be symmetric.");
229 self.inertia().data() << symmetric_inertia(0, 0), symmetric_inertia(1, 0),
230 symmetric_inertia(1, 1), symmetric_inertia(0, 2), symmetric_inertia(1, 2),
231 symmetric_inertia(2, 2);
232 }
233
234 static VectorXs toDynamicParameters_proxy(const Inertia & self)
235 {
236 return self.toDynamicParameters();
237 }
238
239 static Inertia *
240 makeFromMCI(const Scalar & mass, const Vector3 & lever, const Matrix3 & inertia)
241 {
242 #ifndef PINOCCHIO_PYTHON_SKIP_COMPARISON_OPERATIONS
243 if (!inertia.isApprox(inertia.transpose()))
244 throw eigenpy::Exception("The 3d inertia should be symmetric.");
245 if (
246 (Vector3::UnitX().transpose() * inertia * Vector3::UnitX() < 0)
247 || (Vector3::UnitY().transpose() * inertia * Vector3::UnitY() < 0)
248 || (Vector3::UnitZ().transpose() * inertia * Vector3::UnitZ() < 0))
249 throw eigenpy::Exception("The 3d inertia should be positive.");
250 #endif
251 return new Inertia(mass, lever, inertia);
252 }
253
254 20 static void expose()
255 {
256 #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 6 && EIGENPY_VERSION_AT_LEAST(2, 9, 0)
257 typedef PINOCCHIO_SHARED_PTR_HOLDER_TYPE(Inertia) HolderType;
258 #else
259 typedef ::boost::python::detail::not_specified HolderType;
260 #endif
261 20 bp::class_<Inertia, HolderType>(
262 "Inertia",
263 "This class represenses a sparse version of a Spatial Inertia and its is defined by its "
264 "mass, its center of mass location and the rotational inertia expressed around this "
265 "center of mass.\n\n"
266 "Supported operations ...",
267 bp::no_init)
268
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(InertiaPythonVisitor<Inertia>())
269
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(CastVisitor<Inertia>())
270
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(ExposeConstructorByCastVisitor<Inertia, ::pinocchio::Inertia>())
271
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(CopyableVisitor<Inertia>())
272
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 .def(PrintableVisitor<Inertia>());
273 20 }
274
275 private:
276 static Matrix6 __array__(const Inertia & self, bp::object)
277 {
278 return self.matrix();
279 }
280
281 struct Pickle : bp::pickle_suite
282 {
283 static boost::python::tuple getinitargs(const Inertia & I)
284 {
285 return bp::make_tuple(I.mass(), (Vector3)I.lever(), I.inertia().matrix());
286 }
287
288 20 static bool getstate_manages_dict()
289 {
290 20 return true;
291 }
292 };
293
294 }; // struct InertiaPythonVisitor
295
296 } // namespace python
297 } // namespace pinocchio
298
299 #endif // ifndef __pinocchio_python_spatial_inertia_hpp__
300