GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/pinocchio/bindings/python/spatial/inertia.hpp Lines: 49 81 60.5 %
Date: 2024-04-26 13:14:21 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
#if EIGENPY_VERSION_AT_MOST(2,8,1)
20
EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(pinocchio::Inertia)
21
#endif
22
23
namespace pinocchio
24
{
25
  namespace python
26
  {
27
    namespace bp = boost::python;
28
29
    template<typename T> struct call;
30
31
    template<typename Scalar, int Options>
32
    struct call< InertiaTpl<Scalar,Options> >
33
    {
34
      typedef InertiaTpl<Scalar,Options> Inertia;
35
36
      static bool isApprox(const Inertia & self, const Inertia & other,
37
                           const Scalar & prec = Eigen::NumTraits<Scalar>::dummy_precision())
38
      {
39
        return self.isApprox(other,prec);
40
      }
41
42
      static bool isZero(const Inertia & self,
43
                         const Scalar & prec = Eigen::NumTraits<Scalar>::dummy_precision())
44
      {
45
        return self.isZero(prec);
46
      }
47
    };
48
49
38
    BOOST_PYTHON_FUNCTION_OVERLOADS(isApproxInertia_overload,call<Inertia>::isApprox,2,3)
50
    BOOST_PYTHON_FUNCTION_OVERLOADS(isZero_overload,call<Inertia>::isZero,1,2)
51
52
    template<typename Inertia>
53
    struct InertiaPythonVisitor
54
      : public boost::python::def_visitor< InertiaPythonVisitor<Inertia> >
55
    {
56
57
      typedef typename Inertia::Scalar Scalar;
58
      typedef typename Inertia::Vector3 Vector3;
59
      typedef typename Inertia::Matrix3 Matrix3;
60
      typedef typename Inertia::Vector6 Vector6;
61
      typedef typename Inertia::Matrix6 Matrix6;
62
63
    public:
64
65
      template<class PyClass>
66
19
      void visit(PyClass& cl) const
67
      {
68
        cl
69
        .def("__init__",
70
             bp::make_constructor(&InertiaPythonVisitor::makeFromMCI,
71
                                  bp::default_call_policies(),
72
                                  bp::args("mass","lever","inertia")),
73
             "Initialize from mass, lever and 3d inertia.")
74

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

38
        .add_property("mass",
77
                      &InertiaPythonVisitor::getMass,
78
                      &InertiaPythonVisitor::setMass,
79
                      "Mass of the Spatial Inertia.")
80
19
        .add_property("lever",
81
                      bp::make_function((typename Inertia::Vector3 & (Inertia::*)())&Inertia::lever,
82
                                        bp::return_internal_reference<>()),
83
                      &InertiaPythonVisitor::setLever,
84
                      "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.")
85

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

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

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

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

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

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

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

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

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

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

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

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

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

38
        .def("Identity",&Inertia::Identity,"Returns the identity Inertia.")
132
19
        .staticmethod("Identity")
133
19
        .def("Zero",&Inertia::Zero,"Returns the null Inertia.")
134
19
        .staticmethod("Zero")
135
19
        .def("Random",&Inertia::Random,"Returns a random Inertia.")
136
19
        .staticmethod("Random")
137
138
19
        .def("toDynamicParameters",&InertiaPythonVisitor::toDynamicParameters_proxy,bp::arg("self"),
139
             "Returns the representation of the matrix as a vector of dynamic parameters."
140
              "\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 "
141
              "where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter"
142
        )
143

38
        .def("FromDynamicParameters",&Inertia::template FromDynamicParameters<Eigen::VectorXd>,
144
              bp::args("dynamic_parameters"),
145
              "Builds and inertia matrix from a vector of dynamic parameters."
146
              "\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 "
147
              "where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter."
148
        )
149

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

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

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

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

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

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

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

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