From fefc641a4ac560173e341a1f259791ae7d1c2cd7 Mon Sep 17 00:00:00 2001
From: Mansard <nmansard@laas.fr>
Date: Tue, 23 Sep 2014 15:06:27 +0200
Subject: [PATCH] Partial commit while building model interface.

---
 CMakeLists.txt                        |   2 +
 python/bindings.py                    |   4 +-
 python/test_model.py                  |  12 ++
 src/multibody/joint/joint-variant.hpp |   4 +-
 src/multibody/model.hpp               |  12 +-
 src/python/__init__.py                |   4 +-
 src/python/data.hpp                   |  95 +++++++++++
 src/python/force.hpp                  |   2 +
 src/python/inertia.hpp                |   1 +
 src/python/model.hpp                  | 229 ++++++++++++++++++++++++++
 src/python/module.cpp                 |   2 +
 src/python/motion.hpp                 |   1 +
 src/python/python.cpp                 |  11 +-
 src/python/python.hpp                 |   2 +
 src/python/se3.hpp                    |   3 +
 src/spatial/inertia.hpp               |   3 +
 src/spatial/symmetric3.hpp            |   3 +
 17 files changed, 376 insertions(+), 14 deletions(-)
 create mode 100644 python/test_model.py
 create mode 100644 src/python/data.hpp
 create mode 100644 src/python/model.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ef510c34c..edd3d06a1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -72,6 +72,8 @@ SET(HEADERS
   python/force.hpp
   python/motion.hpp
   python/inertia.hpp
+  python/model.hpp
+  python/data.hpp
 )
 
 MAKE_DIRECTORY("${${PROJECT_NAME}_BINARY_DIR}/include/pinocchio")
diff --git a/python/bindings.py b/python/bindings.py
index 8f37b9e4a..4580368c2 100644
--- a/python/bindings.py
+++ b/python/bindings.py
@@ -9,7 +9,9 @@ X = np.vstack( [ np.hstack([ R, skew(p)*R ]), np.hstack([ zero([3,3]), R ]) ])
 assert( isapprox(m.action(),X))
 M = np.vstack( [ np.hstack([R,p]), np.matrix('0 0 0 1',np.double) ] )
 assert( isapprox(m.homogeneous(),M) )
-
+m2 = se3.SE3.Random()
+assert(isapprox( (m*m2).homogeneous(),m.homogeneous()*m2.homogeneous() ))
+assert(isapprox( (~m).homogeneous(),npl.inv(m.homogeneous()) ))
 
 p = rand(3)
 assert(isapprox(m*p,m.rotation*p+m.translation))
diff --git a/python/test_model.py b/python/test_model.py
new file mode 100644
index 000000000..36cf3e675
--- /dev/null
+++ b/python/test_model.py
@@ -0,0 +1,12 @@
+import pinocchio as se3
+from pinocchio.utils import *
+
+model = se3.Model.BuildEmptyModel()
+print model
+model = se3.Model.BuildHumanoidSimple()
+print model
+print "Bye bye"
+
+data = model.createData()
+
+print model.inertias()
diff --git a/src/multibody/joint/joint-variant.hpp b/src/multibody/joint/joint-variant.hpp
index f41945ad4..2ce0f8cee 100644
--- a/src/multibody/joint/joint-variant.hpp
+++ b/src/multibody/joint/joint-variant.hpp
@@ -74,7 +74,7 @@ namespace se3
 
 } // namespace se3
 
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointModelVariant);
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointDataVariant);
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointModelVariant)
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::JointDataVariant)
 
 #endif // ifndef __se3_joint_variant_hpp__
diff --git a/src/multibody/model.hpp b/src/multibody/model.hpp
index ea09740b8..5196c4f8e 100644
--- a/src/multibody/model.hpp
+++ b/src/multibody/model.hpp
@@ -11,11 +11,11 @@
 #include "pinocchio/multibody/force-set.hpp"
 #include <iostream>
 
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::SE3);
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Inertia);
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Force);
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Motion);
-EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Matrix<double,6,Eigen::Dynamic>);
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::SE3)
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Inertia)
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Force)
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(se3::Motion)
+EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Matrix<double,6,Eigen::Dynamic>)
 
 namespace se3
 {
@@ -53,6 +53,7 @@ namespace se3
     {
       names[0] = "universe";
     }
+    ~Model() { std::cout << "Destroy model" << std::endl; }
     template<typename D>
     Index addBody( Index parent,const JointModelBase<D> & j,const SE3 & placement,
 		   const Inertia & Y,const std::string & name = "" );
@@ -98,6 +99,7 @@ namespace se3
 
     Data( const Model& ref );
 
+  private:
     void computeLastChild(const Model& model);
     void computeParents_fromRow(const Model& model);
   };
diff --git a/src/python/__init__.py b/src/python/__init__.py
index 03bbca1e6..2facd72d7 100644
--- a/src/python/__init__.py
+++ b/src/python/__init__.py
@@ -16,7 +16,9 @@ def SE3act(m,x):
     elif 'se3Action' in x.__class__.__dict__:
         return x.se3Action(m)
     else:
-        print 'Error: SE3 cannot act on the given object'
+        #print 'Error: SE3 cannot act on the given object'
+        return m.oldmult(x)
+setattr(se3.SE3,'oldmult',se3.SE3.__mul__)
 setattr(se3.SE3,'__mul__',SE3act)
 setattr(se3.SE3,'act',SE3act)
 
diff --git a/src/python/data.hpp b/src/python/data.hpp
new file mode 100644
index 000000000..f553cd9f7
--- /dev/null
+++ b/src/python/data.hpp
@@ -0,0 +1,95 @@
+#ifndef __se3_python_data_hpp__
+#define __se3_python_data_hpp__
+
+#include <eigenpy/exception.hpp>
+#include <eigenpy/eigenpy.hpp>
+#include "pinocchio/multibody/model.hpp"
+
+#include <boost/shared_ptr.hpp>
+
+namespace se3
+{
+  namespace python
+  {
+
+    /* See the strategy applied for handling the Model object. The same applies
+     * here. */
+    struct DataHandler
+    {
+      typedef boost::shared_ptr<Data> SmartPtr_t;
+      typedef Data * Ptr_t;
+
+      SmartPtr_t smptr;
+      Ptr_t rawptr;
+      bool smart;
+
+      DataHandler(Data * data,bool transmitOwnership=false)
+	: smptr( transmitOwnership ? data : NULL )
+	, rawptr( data )
+	, smart( transmitOwnership ) {}
+      DataHandler( SmartPtr_t data )
+	: smptr(data), rawptr(NULL), smart(true) {}
+      ~DataHandler()
+      {    
+	std::cout << "Destroy data handler " << std::endl;
+	if( (!smart) && (rawptr!=NULL) ) delete rawptr;
+      }
+
+      Data *       ptr()              { return smart ?  smptr.get() :  rawptr; }
+      const Data * ptr()        const { return smart ?  smptr.get() :  rawptr; }
+      Data *       operator->()       { return ptr(); }
+      const Data * operator->() const { return ptr(); }
+
+      Data &       get()              { return smart ? *smptr       : *rawptr; }
+      const Data & get()        const { return smart ? *smptr       : *rawptr; }
+      Data &       operator*()        { return get(); }
+      const Data&  operator*()  const { return get(); }
+    };
+
+
+    namespace bp = boost::python;
+
+    struct DataPythonVisitor
+      : public boost::python::def_visitor< DataPythonVisitor >
+    {
+
+    public:
+
+      /* --- Convert From C++ to Python ------------------------------------- */
+      static PyObject* convert(DataHandler::SmartPtr_t const& ptr)
+      {
+	return boost::python::incref(boost::python::object(DataHandler(ptr)).ptr());
+      }
+
+      /* --- Exposing C++ API to python through the handler ----------------- */
+      template<class PyClass>
+      void visit(PyClass& cl) const 
+      {
+	cl
+	  ;
+      }
+
+
+      /* --- Expose --------------------------------------------------------- */
+      static void expose()
+      {
+	bp::class_<DataHandler>("Data",
+				 "Articulated rigid body data (const)",
+				 bp::no_init)
+	  .def(DataPythonVisitor());
+    
+	/* Not sure if it is a good idea to enable automatic
+	 * conversion. Prevent it for now */
+	//bp::to_python_converter< Data,DataPythonVisitor >();
+	bp::to_python_converter< DataHandler::SmartPtr_t,DataPythonVisitor >();
+      }
+
+
+    };
+    
+
+
+  }} // namespace se3::python
+
+#endif // ifndef __se3_python_data_hpp__
+
diff --git a/src/python/force.hpp b/src/python/force.hpp
index 13eb0b61f..94b8533d0 100644
--- a/src/python/force.hpp
+++ b/src/python/force.hpp
@@ -58,7 +58,9 @@ namespace se3
 	  .def("vector",&Force_fx::toVector)
 	  .def("se3Action",&Force_fx::se3Action)
 	  .def("se3ActionInverse",&Force_fx::se3ActionInverse)
+
 	  .def("__str__",&ForcePythonVisitor::toString)
+	  .add_property("np",&Force_fx::toVector)
 	  
 	  .def("Random",&Force_fx::Random)
 	  .staticmethod("Random")
diff --git a/src/python/inertia.hpp b/src/python/inertia.hpp
index f0d96275d..d16542698 100644
--- a/src/python/inertia.hpp
+++ b/src/python/inertia.hpp
@@ -66,6 +66,7 @@ namespace se3
 	  .def("__str__",&InertiaPythonVisitor::toString)
 	  .def( bp::self + bp::self)
 	  .def( bp::self * bp::other<Motion_fx>() )
+	  .add_property("np",&Inertia_fx::matrix)
 
 	  .def("Identity",&Inertia_fx::Identity)
 	  .staticmethod("Identity")
diff --git a/src/python/model.hpp b/src/python/model.hpp
new file mode 100644
index 000000000..6d8a689c9
--- /dev/null
+++ b/src/python/model.hpp
@@ -0,0 +1,229 @@
+#ifndef __se3_python_model_hpp__
+#define __se3_python_model_hpp__
+
+#include <eigenpy/exception.hpp>
+#include <eigenpy/eigenpy.hpp>
+#include "pinocchio/multibody/model.hpp"
+#include "pinocchio/multibody/parser/sample-models.hpp"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
+#include <boost/python/return_internal_reference.hpp>
+
+namespace se3
+{
+  namespace python
+  {
+
+    /* There might be several way of building and owning a model object:
+
+     *   - build from C++ (as static or dynamic, wathever), and owned by
+     * C++. In that case, use a ModelHandler with no ownership ModelHandler(
+     * Model*,false).
+     *   - build from C++ but owned by python. Python would be in charge of
+     * destroying it when it is done. In that case, use
+     * ModelHandler(Model*,true). Take care not to destroy the object by
+     * yourself, otherwise there might have access to deallocated memory and
+     * double destruction.
+     *   - build and managed by shared_ptr. It is the best way, in the sense
+     * that the object is manage both by C++ and python. It will be released
+     * only when both are done with it.  In that case, simply give Python the
+     * shared_ptr ModelHandler( shared_ptr<Model> ).
+     * 
+     * It is not possible to construct a Model from python, because of Eigen
+     * memory alignement.  The python visitor does not define any
+     * constructor. Instead, some static makers (function allocating memory and
+     * returning a pointer Model *) are implemented.  When Python is making
+     * such a Model, it is stored in a shared_ptr and can be access directly
+     * from C++. By default, when passing a Model to python, it is kept as a
+     * raw pointer. Access is then done from python to the C++ memory without
+     * any guarantee. If you want to enforce memory access, prefer transmitting
+     * a shared_ptr to python.
+     */
+    struct ModelHandler
+    {
+      typedef boost::shared_ptr<Model> SmartPtr_t;
+      typedef Model * Ptr_t;
+
+      SmartPtr_t smptr;
+      Ptr_t rawptr;
+      bool smart;
+
+      ModelHandler(Model * model,bool transmitOwnership=false)
+	: smptr( transmitOwnership ? model : NULL )
+	, rawptr( model )
+	, smart( transmitOwnership ) {}
+      ModelHandler( SmartPtr_t model )
+	: smptr(model), rawptr(NULL), smart(true) {}
+      ~ModelHandler()
+      {    
+	std::cout << "Destroy model handler " << std::endl;
+	if( (!smart) && (rawptr!=NULL) ) delete rawptr;
+      }
+
+      Model *       ptr()              { return smart ?  smptr.get() :  rawptr; }
+      const Model * ptr()        const { return smart ?  smptr.get() :  rawptr; }
+      Model *       operator->()       { return ptr(); }
+      const Model * operator->() const { return ptr(); }
+
+      Model &       get()              { return smart ? *smptr       : *rawptr; }
+      const Model & get()        const { return smart ? *smptr       : *rawptr; }
+      Model &       operator*()        { return get(); }
+      const Model&  operator*()  const { return get(); }
+    };
+
+
+    namespace bp = boost::python;
+
+    struct InertiasVisitor
+      : public boost::python::def_visitor< InertiasVisitor >
+    {
+      typedef typename eigenpy::UnalignedEquivalent<Inertia>::type Inertia_fx;
+      typedef std::vector<Inertia> Inertias;
+      
+      template<class PyClass>
+      void visit(PyClass& cl) const 
+      {
+	cl
+	  .def("__getitem__", &InertiasVisitor::getItem)
+	  .def("__setitem__", &InertiasVisitor::setItem)
+	  .def("__len__",&InertiasVisitor::length)
+	  ;
+      }
+
+      static Inertia getItem( const Inertias & Ys,int i) { return Ys[i]; }
+      static void setItem( Inertias & Ys,int i,const Inertia_fx & Y)
+      { 
+	std::cout << "Y = " << Y << std::endl;
+	Ys[i] = Y; 
+      }
+      static int length( const Inertias & Ys ) { return Ys.size(); }
+
+    };
+
+
+    struct ParentsVisitor
+     : public boost::python::def_visitor< ParentsVisitor >
+    {
+      typedef Model::Index Index;
+      typedef std::vector<Index> Parents;
+      
+      template<class PyClass>
+      void visit(PyClass& cl) const 
+      {
+	cl
+	  .def("__getitem__", &ParentsVisitor::getItem)
+	  .def("__setitem__", &ParentsVisitor::setItem)
+	  .def("__len__",&ParentsVisitor::length)
+	  ;
+      }
+
+      static Index getItem( const Parents & Ys,int i) { return Ys[i]; }
+      static void setItem( Parents & Ys,int i,const Index & Y)
+      { 
+	std::cout << "p = " << Y << std::endl;
+	Ys[i] = Y; 
+      }
+      static int length( const Parents & Ys ) { return Ys.size(); }
+    };
+
+
+    struct ModelPythonVisitor
+      : public boost::python::def_visitor< ModelPythonVisitor >
+    {
+
+    public:
+
+      /* --- Convert From C++ to Python ------------------------------------- */
+      // static PyObject* convert(Model const& modelConstRef)
+      // {
+      // 	Model * ptr = const_cast<Model*>(&modelConstRef);
+      // 	return boost::python::incref(boost::python::object(ModelHandler(ptr)).ptr());
+      // }
+      static PyObject* convert(ModelHandler::SmartPtr_t const& ptr)
+      {
+	return boost::python::incref(boost::python::object(ModelHandler(ptr)).ptr());
+      }
+
+      /* --- Exposing C++ API to python through the handler ----------------- */
+      template<class PyClass>
+      void visit(PyClass& cl) const 
+      {
+	cl
+	  .def("getBodyId",&ModelPythonVisitor::getBodyId)
+	  .def("createData",&ModelPythonVisitor::createData)
+
+	  .def("__str__",&ModelPythonVisitor::toString)
+
+	  .add_property("inertias",
+			bp::make_function(&ModelPythonVisitor::inertias,
+					  bp::return_internal_reference<>())  )
+
+	  .add_property("parents", 
+			bp::make_function(&ModelPythonVisitor::parents,
+					  bp::return_internal_reference<>())  )
+
+	  .def("BuildEmptyModel",&ModelPythonVisitor::maker_empty)
+	  .staticmethod("BuildEmptyModel")
+	  .def("BuildHumanoidSimple",&ModelPythonVisitor::maker_humanoidSimple)
+	  .staticmethod("BuildHumanoidSimple")
+	  ;
+      }
+
+      static Model::Index getBodyId( const ModelHandler & modelPtr, const std::string & name )
+      { return  modelPtr->getBodyId(name); }
+      static boost::shared_ptr<Data> createData(const ModelHandler& m )
+      {	return boost::shared_ptr<Data>( new Data(*m) );      } 
+      
+      typedef std::vector<Inertia> Inertias_t;
+      static Inertias_t & inertias( ModelHandler & m ) { return m->inertias; }
+      static void set_inertias( ModelHandler & m,const Inertias_t & Ys )
+      { m->inertias = Ys; }
+      static ParentsVisitor::Parents & parents( ModelHandler & m ) { return m->parents; }
+
+      
+
+      static ModelHandler maker_empty()
+      {
+	return ModelHandler( new Model(),true );
+      }
+      static ModelHandler maker_humanoidSimple()
+      {
+	Model * model = new Model();
+	buildModels::humanoidSimple(*model);
+	return ModelHandler( model,true );
+      }
+
+      static std::string toString(const ModelHandler& m) 
+      {	  std::ostringstream s; s << *m; return s.str();       }
+
+      /* --- Expose --------------------------------------------------------- */
+      static void expose()
+      {
+	bp::class_<InertiasVisitor::Inertias>("ListInertia")
+	  .def(InertiasVisitor());
+
+	bp::class_<ParentsVisitor::Parents>("ListInertia")
+	  .def(ParentsVisitor());
+	  //.def(bp::vector_indexing_suite<Inertias_t>());
+
+	bp::class_<ModelHandler>("Model",
+				 "Articulated rigid body model (const)",
+				 bp::no_init)
+	  .def(ModelPythonVisitor());
+    
+	/* Not sure if it is a good idea to enable automatic
+	 * conversion. Prevent it for now */
+	//bp::to_python_converter< Model,ModelPythonVisitor >();
+	bp::to_python_converter< ModelHandler::SmartPtr_t,ModelPythonVisitor >();
+      }
+
+
+    };
+    
+
+
+  }} // namespace se3::python
+
+#endif // ifndef __se3_python_model_hpp__
+
diff --git a/src/python/module.cpp b/src/python/module.cpp
index 2e089477a..b621f2a60 100644
--- a/src/python/module.cpp
+++ b/src/python/module.cpp
@@ -22,5 +22,7 @@ BOOST_PYTHON_MODULE(libpinocchio_pywrap)
   se3::python::exposeForce();
   se3::python::exposeMotion();
   se3::python::exposeInertia();
+
+  se3::python::exposeModel();
 }
  
diff --git a/src/python/motion.hpp b/src/python/motion.hpp
index 425e42fbe..4835e8665 100644
--- a/src/python/motion.hpp
+++ b/src/python/motion.hpp
@@ -66,6 +66,7 @@ namespace se3
 	  .def("cross_force",&MotionPythonVisitor::cross_force)
 	  
 	  .def("__str__",&MotionPythonVisitor::toString)
+	  .add_property("np",&Motion_fx::toVector)
 
 	  .def("Random",&Motion_fx::Random)
 	  .staticmethod("Random")
diff --git a/src/python/python.cpp b/src/python/python.cpp
index 0b33429e5..58337655b 100644
--- a/src/python/python.cpp
+++ b/src/python/python.cpp
@@ -4,6 +4,9 @@
 #include "pinocchio/python/motion.hpp"
 #include "pinocchio/python/inertia.hpp"
 
+#include "pinocchio/python/model.hpp"
+#include "pinocchio/python/data.hpp"
+
 namespace se3
 {
   namespace python
@@ -24,12 +27,10 @@ namespace se3
     {
       InertiaPythonVisitor<Inertia>::expose();
     }
-    void exposeStatics()
+    void exposeModel()
     {
- 
-
+      ModelPythonVisitor::expose();
+      DataPythonVisitor::expose();
     }
-    
-    
 
   }} // namespace se3::python
diff --git a/src/python/python.hpp b/src/python/python.hpp
index e82dec694..dc00aa812 100644
--- a/src/python/python.hpp
+++ b/src/python/python.hpp
@@ -10,6 +10,8 @@ namespace se3
     void exposeMotion();
     void exposeInertia();
 
+    void exposeModel();
+
   }} // namespace se3::python
 
 #endif // ifndef __se3_python_python_hpp__
diff --git a/src/python/se3.hpp b/src/python/se3.hpp
index 08660c838..10e80d29d 100644
--- a/src/python/se3.hpp
+++ b/src/python/se3.hpp
@@ -69,6 +69,9 @@ namespace se3
 	  .def("actInv_se3", &SE3PythonVisitor::actInv_se3)
 	  
 	  .def("__str__",&SE3PythonVisitor::toString)
+	  .def("__invert__",&SE3_fx::inverse)
+	  .def(bp::self * bp::self)
+	  .add_property("np",&SE3_fx::toActionMatrix)
 
 	  .def("Identity",&SE3_fx::Identity)
 	  .staticmethod("Identity")
diff --git a/src/spatial/inertia.hpp b/src/spatial/inertia.hpp
index 4b5976abf..5d6ae4510 100644
--- a/src/spatial/inertia.hpp
+++ b/src/spatial/inertia.hpp
@@ -49,6 +49,9 @@ namespace se3
       m=clone.m; c=clone.c; I=clone.I;
       return *this;
     }
+    /* Requiered by std::vector boost::python bindings. */
+    bool operator==( const InertiaTpl& Y2 ) 
+    { return (m==Y2.m) && (c==Y2.c) && (I==Y2.I); }
     template<typename S2,int O2>
     InertiaTpl( const InertiaTpl<S2,O2> & clone )
       : m(clone.mass()),
diff --git a/src/spatial/symmetric3.hpp b/src/spatial/symmetric3.hpp
index 80a65d789..5bbcd0b5a 100644
--- a/src/spatial/symmetric3.hpp
+++ b/src/spatial/symmetric3.hpp
@@ -58,6 +58,9 @@ namespace se3
     static Symmetric3Tpl Random()   { return Symmetric3Tpl(Vector6::Random().eval());  }
     static Symmetric3Tpl Identity() { return Symmetric3Tpl( 1, 0, 1, 0, 0, 1);  }
 
+    /* Requiered by Inertia::operator== */
+    bool operator== (const Symmetric3Tpl & S2 ) { return data == S2.data; }
+    
     struct SkewSquare
     {
       const Vector3 & v;
-- 
GitLab