Re: [eigen] Eigen and rigid body simulation

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


On Friday 15 January 2010 18:44:13 Mathieu Gautier wrote:
> >>>> We keep the initial concept map approach, and to ease the writing of
> >>>> algorithms we add a trivial and generic LieWrapper<>  class allowing
> >>>> to temporarily use a Lie element with a nicer API:
> >>>> 
> >>>> template<class G>
> >>>> struct LieWrapper {
> >>>> 
> >>>>     typedef Lie<G>  L;
> >>>>     
> >>>>     G inverse() const { return L::inverse(m_element); }
> >>>>     G operator*(const G&  other) const { return
> >>>> 
> >>>> L::comp(m_element,other.element()); }
> >>>> 
> >>>>     G exp() const { return L::exp()(m_element); }
> >>>>     
> >>>>     // ...
> >>>>    
> >>>>    protected:
> >>>>      G&  m_element;
> >>>> 
> >>>> };
> > 
> > After trying to implement this, it seems we don't need both a concept map
> > and a wrapper since one is going to copy the other anyway. So only the
> > wrapper seems fine. It's quite convenient to use so far.
> > 
> > Also I came across a (minor) problem with this approach: what if you need
> > to modify the wrapped element ?
> > 
> > We cannot only wrap lvalues, so we have to have two flavours of
> > LieWrapper depending on constness and well, this gets complicated. Or am
> > I missing something ?
> > 
> > If not, this means we can only specialize for Lie groups when parameters
> > are const (otherwise we need a G&  reference). Does not look like a big
> > deal to me.
> > 
> > Best regards,
> > 
> > 
> > 
> > max.
> 
> Hi,
> 
> Excuse me for the pause, but I didn't have enough time to work on the
> Lie module since last month and I'm a bit rusty :)

Hi, 

Same for me :)

> 
> My main concern about the concept map and the wrapper is that we have to
> use three classes to handle an element of a Lie group, the concept map
> class, the wrapper class and the wrapped class. Maxime had tried an
> implementation only with a wrapper but he couldn't wrap lvalues (btw,
> I'm interesting in seeing this implementation). 

Actually you can, see below. To keep it simple for both users and developpers, 
i kept the concept map approach (developpers), and implemented a lightweight 
wrapper around it (users).

> In any cases, a final
> user have to use, at least two classes, the wrapper and the wrapped
> class, since the wrapper return an instance of the wrapped class.
> 
>   I think it's two complicated in my case, mainly beacause of my end
> users :) . I aim to have only one class, let's say for an element of
> SE(3), something like LieGroup<Matrix<double, 1, 7> > which I could
> typedef with a name more approriate for physic simulation. I also don't
> want to use staitc function so a s to keep writing a=b*c.
> 
> Just to resume, Maxime concerns were :
> 1/ not modifying base classes
> 2/ building new groups from a existing ones (through pairs, tuple, etc.)
> 
> So, I try the following design, where the Wrapper own not an reference
> but an instance of G. I use only LieGroup<G> in order to perform
> operation on the group.
> 

Ok i see but now you need to copy the wrapped element all around. Besides, you 
can't modify an existing G element through the wrapper with your approach.


> template<class G> struct LieGroup{
>    LieGroup<G> inverse() const { return LieGroup<G> m_element.inverse()); }
>    template<class H> LieGroup<G> operator*(const LieGroup<H>& other)
> const { return LieGroup<G>(m_element* other.get()); }
> 
>    template<class H> LieGroup& operator=(const LieGroup<H>& other){
>      m_element = other.get();
>      return *this;
>    }
>    template<class H> LieGroup(const H& el) : m_element(el){};
>    LieGroup(const double* data) : m_element(data){};
>    LieGroup(){};
> 
> protected:
>    G m_element;
> };
> 
> the templated method, such as operator*, allows operation between
> LieGroup<G> and LieGroup<Map<G>> classes. Thus, I can interface my code
> with other libraries returning array of double. This first proposal has
> to be refined, for example the constructors are different for
> LieGroup<G> and LieGroup<Map<G>>, I am working on these issues.
> 
> I think that with these implementation we can achieve both Maxime and my
> requirements.
> 
> I can now define new type to ease the notation in my field of interest :
> 
> typedef LieGroup<Quaterniond> Rotation3D;
> typedef LieGroup<Map<Quaterniond, 0> > Rotation3DMapd;
> typedef LieGroup<Matrix<double,1,7> > Displacement;
> etc.
> 
> I can then write something like  :
> 
> double data[4] = {1,3,4,5};
> 
> Rotd d, dr;
> RotMapd dm(data);
> dr = d *dm;


Yes it's definitely easier to write when using it, but in the end there's 
still a lot of machinery to simply avoid using free functions. In your case, 
the wrapper also involves performance issues since values are copied all 
around.


Since you asked for my implementation, here is the best i could come up with 
so far:

namespace lie {

  // the main concept
  template<class A>
  struct group;

  // some helper templates
  namespace impl {

    template<class A>
    struct remove_const {
      typedef A type;
    };

    template<class A>
    struct remove_const<const A> {
      typedef A type;
    };

    // always returns void, but the argument may trigger SFINAE
    template<class> 
    struct enable_if {
      typedef void type;
    };

  }


  // a wrapper for OO-sexiness
  template<class A, class = void>
  class wrap;

  // we implement it only when typename group<A>::algebra exists
  template<class A>
  class wrap<A, typename impl::enable_if< typename group<  typename 
impl::remove_const<A>::type   >::algebra >::type >{
    typedef typename impl::remove_const<A>::type space;
    typedef group<space> structure;
  public:
  
    A& get;

    wrap(A& ref) : get(ref) { }
  
    space operator*( const space& other) const { 
      return structure::compose(get, other);
    }
    
    space inverse() const {
      return structure::inverse( get );
    }
    
  };



  // One can inherit this to get wrapper creation members
  template<class Derived>
  struct interface {

    lie::wrap<Derived> lie() { return lie::wrap<Derived>( derived() ); }
    lie::wrap<const Derived> lie() const { return lie::wrap<const Derived>( 
derived() ); }

  private:
    const Derived& derived() const { return static_cast< const Derived& > 
(*this ); } 
    Derived& derived() { return static_cast< Derived& > (*this ); } 
  };

}


// to use my Foo class with this, i can do:

struct Foo;

namespace lie {

  //implementation of the concept map for Foo
  template<>
  struct group< Foo > {
    typedef some_type algebra;

    static Foo compose(const Foo& a, const Foo& b) { // ... }
    static Foo inverse(const Foo& a) { // ... }
    
    static Foo identity() { // ... }

    // and so on
  };

}


So from now on, either i use lie::wrap<Foo> to manually construct wrappers 
where needed (maybe through a template function), or i can use the 
lie::interface in the Foo definition for convenience: 

struct Foo : public lie::interface<Foo>{ 
  
 // blah.

};


and then: 


Foo a, b, c;
c = a.lie() * b.lie().inverse();

or ( with a helper lie_wrap template function ) :
c = lie_wrap(a) * lie_wrap(b).inverse()

(which is only a matter of taste)


If i'm bothered with all those pesky "lie" calls, i can always do:
lie::wrap<Foo> aa(a), bb(b);

c = aa * bb.inverse();

I can wrap const lvalues or rvalues with lie::wrap<const Foo>. 

hope this helps :-) tell me what you think.


Best wishes,



-- 
Maxime Tournier

PhD Student - EVASION Team
Grenoble Universities / LJK / INRIA Rhône-Alpes
FRANCE
Tel: +33 4 76 61 54 45



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/