GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/pinocchio/bindings/python/spatial/inertia.hpp Lines: 49 81 60.5 %
Date: 2024-01-23 21:41:47 Branches: 72 240 30.0 %

Line Branch Exec Source
1
//
2
// Copyright (c) 2015-2023 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
#include "pinocchio/bindings/python/fwd.hpp"
16
#include "pinocchio/bindings/python/utils/copyable.hpp"
17
#include "pinocchio/bindings/python/utils/printable.hpp"
18
19
EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(pinocchio::Inertia)
20
21
namespace pinocchio
22
{
23
  namespace python
24
  {
25
    namespace bp = boost::python;
26
27
    template<typename T> struct call;
28
29
    template<typename Scalar, int Options>
30
    struct call< InertiaTpl<Scalar,Options> >
31
    {
32
      typedef InertiaTpl<Scalar,Options> Inertia;
33
34
      static bool isApprox(const Inertia & self, const Inertia & other,
35
                           const Scalar & prec = Eigen::NumTraits<Scalar>::dummy_precision())
36
      {
37
        return self.isApprox(other,prec);
38
      }
39
40
      static bool isZero(const Inertia & self,
41
                         const Scalar & prec = Eigen::NumTraits<Scalar>::dummy_precision())
42
      {
43
        return self.isZero(prec);
44
      }
45
    };
46
47
38
    BOOST_PYTHON_FUNCTION_OVERLOADS(isApproxInertia_overload,call<Inertia>::isApprox,2,3)
48
    BOOST_PYTHON_FUNCTION_OVERLOADS(isZero_overload,call<Inertia>::isZero,1,2)
49
50
    template<typename Inertia>
51
    struct InertiaPythonVisitor
52
      : public boost::python::def_visitor< InertiaPythonVisitor<Inertia> >
53
    {
54
55
      typedef typename Inertia::Scalar Scalar;
56
      typedef typename Inertia::Vector3 Vector3;
57
      typedef typename Inertia::Matrix3 Matrix3;
58
      typedef typename Inertia::Vector6 Vector6;
59
      typedef typename Inertia::Matrix6 Matrix6;
60
61
    public:
62
63
      template<class PyClass>
64
19
      void visit(PyClass& cl) const
65
      {
66
        cl
67
        .def("__init__",
68
             bp::make_constructor(&InertiaPythonVisitor::makeFromMCI,
69
                                  bp::default_call_policies(),
70
                                  bp::args("mass","lever","inertia")),
71
             "Initialize from mass, lever and 3d inertia.")
72

38
        .def(bp::init<Inertia>(bp::args("self","other"),"Copy constructor."))
73
74

38
        .add_property("mass",
75
                      &InertiaPythonVisitor::getMass,
76
                      &InertiaPythonVisitor::setMass,
77
                      "Mass of the Spatial Inertia.")
78
19
        .add_property("lever",
79
                      bp::make_function((typename Inertia::Vector3 & (Inertia::*)())&Inertia::lever,
80
                                        bp::return_internal_reference<>()),
81
                      &InertiaPythonVisitor::setLever,
82
                      "Center of mass location of the Spatial Inertia. It corresponds to the location of the center of mass regarding to the frame where the Spatial Inertia is expressed.")
83

38
        .add_property("inertia",
84
                      &InertiaPythonVisitor::getInertia,
85
                      &InertiaPythonVisitor::setInertia,
86
                      "Rotational part of the Spatial Inertia, i.e. a symmetric matrix representing the rotational inertia around the center of mass.")
87
88
19
        .def("matrix",&Inertia::matrix,bp::arg("self"))
89
38
        .def("se3Action",&Inertia::se3Action,
90
             bp::args("self","M"),"Returns the result of the action of M on *this.")
91

38
        .def("se3ActionInverse",&Inertia::se3ActionInverse,
92
             bp::args("self","M"),"Returns the result of the action of the inverse of M on *this.")
93
94

38
        .def("setIdentity",&Inertia::setIdentity,bp::arg("self"),
95
             "Set *this to be the Identity inertia.")
96

38
        .def("setZero",&Inertia::setZero,bp::arg("self"),
97
             "Set all the components of *this to zero.")
98

38
        .def("setRandom",&Inertia::setRandom,bp::arg("self"),
99
             "Set all the components of *this to random values.")
100
101

57
        .def(bp::self + bp::self)
102

38
        .def(bp::self * bp::other<Motion>() )
103
19
        .add_property("np",&Inertia::matrix)
104
38
        .def("vxiv",&Inertia::vxiv,bp::args("self","v"),"Returns the result of v x Iv.")
105

38
        .def("vtiv",&Inertia::vtiv,bp::args("self","v"),"Returns the result of v.T * Iv.")
106

38
        .def("vxi",(Matrix6 (Inertia::*)(const Motion &) const)&Inertia::vxi,
107
             bp::args("self","v"),
108
             "Returns the result of v x* I, a 6x6 matrix.")
109

38
        .def("ivx",(Matrix6 (Inertia::*)(const Motion &) const)&Inertia::ivx,
110
             bp::args("self","v"),
111
             "Returns the result of I vx, a 6x6 matrix.")
112

38
        .def("variation",(Matrix6 (Inertia::*)(const Motion &) const)&Inertia::variation,
113
             bp::args("self","v"),
114
             "Returns the time derivative of the inertia.")
115
116

38
        .def(bp::self == bp::self)
117
19
        .def(bp::self != bp::self)
118
119
19
        .def("isApprox",
120
             call<Inertia>::isApprox,
121
             isApproxInertia_overload(bp::args("self","other","prec"),
122
                                      "Returns true if *this is approximately equal to other, within the precision given by prec."))
123
124

38
        .def("isZero",
125
             call<Inertia>::isZero,
126
             isZero_overload(bp::args("self","prec"),
127
                             "Returns true if *this is approximately equal to the zero Inertia, within the precision given by prec."))
128
129

38
        .def("Identity",&Inertia::Identity,"Returns the identity Inertia.")
130
19
        .staticmethod("Identity")
131
19
        .def("Zero",&Inertia::Zero,"Returns the null Inertia.")
132
19
        .staticmethod("Zero")
133
19
        .def("Random",&Inertia::Random,"Returns a random Inertia.")
134
19
        .staticmethod("Random")
135
136
19
        .def("toDynamicParameters",&InertiaPythonVisitor::toDynamicParameters_proxy,bp::arg("self"),
137
             "Returns the representation of the matrix as a vector of dynamic parameters."
138
              "\nThe parameters are given as v = [m, mc_x, mc_y, mc_z, I_{xx}, I_{xy}, I_{yy}, I_{xz}, I_{yz}, I_{zz}]^T "
139
              "where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter"
140
        )
141

38
        .def("FromDynamicParameters",&Inertia::template FromDynamicParameters<Eigen::VectorXd>,
142
              bp::args("dynamic_parameters"),
143
              "Builds and inertia matrix from a vector of dynamic parameters."
144
              "\nThe parameters are given as dynamic_parameters = [m, mc_x, mc_y, mc_z, I_{xx}, I_{xy}, I_{yy}, I_{xz}, I_{yz}, I_{zz}]^T "
145
              "where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter."
146
        )
147

38
        .staticmethod("FromDynamicParameters")
148
149
19
        .def("FromSphere", &Inertia::FromSphere,
150
             bp::args("mass","radius"),
151
             "Returns the Inertia of a sphere defined by a given mass and radius.")
152

38
        .staticmethod("FromSphere")
153
19
        .def("FromEllipsoid", &Inertia::FromEllipsoid,
154
             bp::args("mass","length_x","length_y","length_z"),
155
             "Returns the Inertia of an ellipsoid shape defined by a mass and given dimensions the semi-axis of values length_{x,y,z}.")
156

38
        .staticmethod("FromEllipsoid")
157
19
        .def("FromCylinder", &Inertia::FromCylinder,
158
             bp::args("mass","radius","length"),
159
             "Returns the Inertia of a cylinder defined by its mass, radius and length along the Z axis.")
160

38
        .staticmethod("FromCylinder")
161
19
        .def("FromBox", &Inertia::FromBox,
162
             bp::args("mass","length_x","length_y","length_z"),
163
             "Returns the Inertia of a box shape with a mass and of dimension the semi axis of length_{x,y,z}.")
164

38
        .staticmethod("FromBox")
165
166
19
        .def("__array__",&Inertia::matrix)
167
168

19
        .def_pickle(Pickle())
169
        ;
170
19
      }
171
172
      static Scalar getMass( const Inertia & self ) { return self.mass(); }
173
      static void setMass( Inertia & self, Scalar mass ) { self.mass() = mass; }
174
175
      static void setLever( Inertia & self, const Vector3 & lever ) { self.lever() = lever; }
176
177
      static Matrix3 getInertia( const Inertia & self ) { return self.inertia().matrix(); }
178
//      static void setInertia(Inertia & self, const Vector6 & minimal_inertia) { self.inertia().data() = minimal_inertia; }
179
      static void setInertia(Inertia & self, const Matrix3 & symmetric_inertia)
180
      {
181
        assert(symmetric_inertia.isApprox(symmetric_inertia.transpose()));
182
        self.inertia().data() <<
183
        symmetric_inertia(0,0),
184
        symmetric_inertia(1,0),
185
        symmetric_inertia(1,1),
186
        symmetric_inertia(0,2),
187
        symmetric_inertia(1,2),
188
        symmetric_inertia(2,2);
189
      }
190
191
      static Eigen::VectorXd toDynamicParameters_proxy(const Inertia & self)
192
      {
193
        return self.toDynamicParameters();
194
      }
195
196
      static Inertia* makeFromMCI(const double & mass,
197
                                  const Vector3 & lever,
198
                                  const Matrix3 & inertia)
199
      {
200
        if(! inertia.isApprox(inertia.transpose()) )
201
          throw eigenpy::Exception("The 3d inertia should be symmetric.");
202
        if( (Eigen::Vector3d::UnitX().transpose()*inertia*Eigen::Vector3d::UnitX()<0)
203
           || (Eigen::Vector3d::UnitY().transpose()*inertia*Eigen::Vector3d::UnitY()<0)
204
           || (Eigen::Vector3d::UnitZ().transpose()*inertia*Eigen::Vector3d::UnitZ()<0) )
205
          throw eigenpy::Exception("The 3d inertia should be positive.");
206
        return new Inertia(mass,lever,inertia);
207
           }
208
209
19
      static void expose()
210
      {
211
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 6 && EIGENPY_VERSION_AT_LEAST(2,9,0)
212
    typedef PINOCCHIO_SHARED_PTR_HOLDER_TYPE(Inertia) HolderType;
213
#else
214
    typedef ::boost::python::detail::not_specified HolderType;
215
#endif
216
        bp::class_<Inertia,HolderType>("Inertia",
217
                            "This class represenses a sparse version of a Spatial Inertia and its is defined by its mass, its center of mass location and the rotational inertia expressed around this center of mass.\n\n"
218
                            "Supported operations ...",
219
                            bp::init<>(bp::arg("self"),"Default constructor."))
220
        .def(InertiaPythonVisitor<Inertia>())
221

38
        .def(CopyableVisitor<Inertia>())
222

19
        .def(PrintableVisitor<Inertia>())
223
        ;
224
225
19
      }
226
227
    private:
228
229
      struct Pickle : bp::pickle_suite
230
      {
231
        static
232
        boost::python::tuple
233
        getinitargs(const Inertia & I)
234
        { return bp::make_tuple(I.mass(),(Vector3)I.lever(),I.inertia().matrix()); }
235
236
19
        static bool getstate_manages_dict() { return true; }
237
      };
238
239
240
    }; // struct InertiaPythonVisitor
241
242
  } // namespace python
243
} // namespace pinocchio
244
245
#endif // ifndef __pinocchio_python_spatial_inertia_hpp__