Eigen2Support/Geometry/Transform.h
Go to the documentation of this file.
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
5 // Copyright (C) 2009 Benoit Jacob <jacob.benoit.1@gmail.com>
6 //
7 // This Source Code Form is subject to the terms of the Mozilla
8 // Public License v. 2.0. If a copy of the MPL was not distributed
9 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 
11 // no include guard, we'll include this twice from All.h from Eigen2Support, and it's internal anyway
12 
13 namespace Eigen {
14 
15 // Note that we have to pass Dim and HDim because it is not allowed to use a template
16 // parameter to define a template specialization. To be more precise, in the following
17 // specializations, it is not allowed to use Dim+1 instead of HDim.
18 template< typename Other,
19  int Dim,
20  int HDim,
21  int OtherRows=Other::RowsAtCompileTime,
22  int OtherCols=Other::ColsAtCompileTime>
24 
42 template<typename _Scalar, int _Dim>
43 class Transform
44 {
45 public:
47  enum {
48  Dim = _Dim,
49  HDim = _Dim+1
50  };
52  typedef _Scalar Scalar;
71 
72 protected:
73 
74  MatrixType m_matrix;
75 
76 public:
77 
79  inline Transform() { }
80 
81  inline Transform(const Transform& other)
82  {
83  m_matrix = other.m_matrix;
84  }
85 
86  inline explicit Transform(const TranslationType& t) { *this = t; }
87  inline explicit Transform(const ScalingType& s) { *this = s; }
88  template<typename Derived>
89  inline explicit Transform(const RotationBase<Derived, Dim>& r) { *this = r; }
90 
91  inline Transform& operator=(const Transform& other)
92  { m_matrix = other.m_matrix; return *this; }
93 
94  template<typename OtherDerived, bool BigMatrix> // MSVC 2005 will commit suicide if BigMatrix has a default value
96  {
97  static inline void run(Transform *transform, const MatrixBase<OtherDerived>& other)
98  {
99  transform->matrix() = other;
100  }
101  };
102 
103  template<typename OtherDerived> struct construct_from_matrix<OtherDerived, true>
104  {
105  static inline void run(Transform *transform, const MatrixBase<OtherDerived>& other)
106  {
107  transform->linear() = other;
108  transform->translation().setZero();
109  transform->matrix()(Dim,Dim) = Scalar(1);
110  transform->matrix().template block<1,Dim>(Dim,0).setZero();
111  }
112  };
113 
115  template<typename OtherDerived>
116  inline explicit Transform(const MatrixBase<OtherDerived>& other)
117  {
119  }
120 
122  template<typename OtherDerived>
124  { m_matrix = other; return *this; }
125 
126  #ifdef EIGEN_QT_SUPPORT
127  inline Transform(const QMatrix& other);
128  inline Transform& operator=(const QMatrix& other);
129  inline QMatrix toQMatrix(void) const;
130  inline Transform(const QTransform& other);
131  inline Transform& operator=(const QTransform& other);
132  inline QTransform toQTransform(void) const;
133  #endif
134 
137  inline Scalar operator() (int row, int col) const { return m_matrix(row,col); }
140  inline Scalar& operator() (int row, int col) { return m_matrix(row,col); }
141 
143  inline const MatrixType& matrix() const { return m_matrix; }
145  inline MatrixType& matrix() { return m_matrix; }
146 
148  inline ConstLinearPart linear() const { return m_matrix.template block<Dim,Dim>(0,0); }
150  inline LinearPart linear() { return m_matrix.template block<Dim,Dim>(0,0); }
151 
153  inline ConstTranslationPart translation() const { return m_matrix.template block<Dim,1>(0,Dim); }
155  inline TranslationPart translation() { return m_matrix.template block<Dim,1>(0,Dim); }
156 
164  // note: this function is defined here because some compilers cannot find the respective declaration
165  template<typename OtherDerived>
168  { return ei_transform_product_impl<OtherDerived,Dim,HDim>::run(*this,other.derived()); }
169 
172  template<typename OtherDerived>
173  friend inline const typename ProductReturnType<OtherDerived,MatrixType>::Type
175  { return a.derived() * b.matrix(); }
176 
178  inline const Transform
179  operator * (const Transform& other) const
180  { return Transform(m_matrix * other.matrix()); }
181 
183  void setIdentity() { m_matrix.setIdentity(); }
184  static const typename MatrixType::IdentityReturnType Identity()
185  {
186  return MatrixType::Identity();
187  }
188 
189  template<typename OtherDerived>
190  inline Transform& scale(const MatrixBase<OtherDerived> &other);
191 
192  template<typename OtherDerived>
193  inline Transform& prescale(const MatrixBase<OtherDerived> &other);
194 
195  inline Transform& scale(Scalar s);
196  inline Transform& prescale(Scalar s);
197 
198  template<typename OtherDerived>
199  inline Transform& translate(const MatrixBase<OtherDerived> &other);
200 
201  template<typename OtherDerived>
202  inline Transform& pretranslate(const MatrixBase<OtherDerived> &other);
203 
204  template<typename RotationType>
205  inline Transform& rotate(const RotationType& rotation);
206 
207  template<typename RotationType>
208  inline Transform& prerotate(const RotationType& rotation);
209 
210  Transform& shear(Scalar sx, Scalar sy);
211  Transform& preshear(Scalar sx, Scalar sy);
212 
213  inline Transform& operator=(const TranslationType& t);
214  inline Transform& operator*=(const TranslationType& t) { return translate(t.vector()); }
215  inline Transform operator*(const TranslationType& t) const;
216 
217  inline Transform& operator=(const ScalingType& t);
218  inline Transform& operator*=(const ScalingType& s) { return scale(s.coeffs()); }
219  inline Transform operator*(const ScalingType& s) const;
220  friend inline Transform operator*(const LinearMatrixType& mat, const Transform& t)
221  {
222  Transform res = t;
223  res.matrix().row(Dim) = t.matrix().row(Dim);
224  res.matrix().template block<Dim,HDim>(0,0) = (mat * t.matrix().template block<Dim,HDim>(0,0)).lazy();
225  return res;
226  }
227 
228  template<typename Derived>
230  template<typename Derived>
232  template<typename Derived>
233  inline Transform operator*(const RotationBase<Derived,Dim>& r) const;
234 
235  LinearMatrixType rotation() const;
236  template<typename RotationMatrixType, typename ScalingMatrixType>
237  void computeRotationScaling(RotationMatrixType *rotation, ScalingMatrixType *scaling) const;
238  template<typename ScalingMatrixType, typename RotationMatrixType>
239  void computeScalingRotation(ScalingMatrixType *scaling, RotationMatrixType *rotation) const;
240 
241  template<typename PositionDerived, typename OrientationType, typename ScaleDerived>
243  const OrientationType& orientation, const MatrixBase<ScaleDerived> &scale);
244 
245  inline const MatrixType inverse(TransformTraits traits = Affine) const;
246 
248  const Scalar* data() const { return m_matrix.data(); }
250  Scalar* data() { return m_matrix.data(); }
251 
257  template<typename NewScalarType>
260 
262  template<typename OtherScalarType>
263  inline explicit Transform(const Transform<OtherScalarType,Dim>& other)
264  { m_matrix = other.matrix().template cast<Scalar>(); }
265 
270  bool isApprox(const Transform& other, typename NumTraits<Scalar>::Real prec = precision<Scalar>()) const
271  { return m_matrix.isApprox(other.m_matrix, prec); }
272 
273  #ifdef EIGEN_TRANSFORM_PLUGIN
274  #include EIGEN_TRANSFORM_PLUGIN
275  #endif
276 
277 protected:
278 
279 };
280 
289 
290 /**************************
291 *** Optional QT support ***
292 **************************/
293 
294 #ifdef EIGEN_QT_SUPPORT
295 
299 template<typename Scalar, int Dim>
300 Transform<Scalar,Dim>::Transform(const QMatrix& other)
301 {
302  *this = other;
303 }
304 
309 template<typename Scalar, int Dim>
311 {
312  EIGEN_STATIC_ASSERT(Dim==2, YOU_MADE_A_PROGRAMMING_MISTAKE)
313  m_matrix << other.m11(), other.m21(), other.dx(),
314  other.m12(), other.m22(), other.dy(),
315  0, 0, 1;
316  return *this;
317 }
318 
325 template<typename Scalar, int Dim>
326 QMatrix Transform<Scalar,Dim>::toQMatrix(void) const
327 {
328  EIGEN_STATIC_ASSERT(Dim==2, YOU_MADE_A_PROGRAMMING_MISTAKE)
329  return QMatrix(m_matrix.coeff(0,0), m_matrix.coeff(1,0),
330  m_matrix.coeff(0,1), m_matrix.coeff(1,1),
331  m_matrix.coeff(0,2), m_matrix.coeff(1,2));
332 }
333 
338 template<typename Scalar, int Dim>
339 Transform<Scalar,Dim>::Transform(const QTransform& other)
340 {
341  *this = other;
342 }
343 
348 template<typename Scalar, int Dim>
350 {
351  EIGEN_STATIC_ASSERT(Dim==2, YOU_MADE_A_PROGRAMMING_MISTAKE)
352  m_matrix << other.m11(), other.m21(), other.dx(),
353  other.m12(), other.m22(), other.dy(),
354  other.m13(), other.m23(), other.m33();
355  return *this;
356 }
357 
362 template<typename Scalar, int Dim>
363 QTransform Transform<Scalar,Dim>::toQTransform(void) const
364 {
365  EIGEN_STATIC_ASSERT(Dim==2, YOU_MADE_A_PROGRAMMING_MISTAKE)
366  return QTransform(m_matrix.coeff(0,0), m_matrix.coeff(1,0), m_matrix.coeff(2,0),
367  m_matrix.coeff(0,1), m_matrix.coeff(1,1), m_matrix.coeff(2,1),
368  m_matrix.coeff(0,2), m_matrix.coeff(1,2), m_matrix.coeff(2,2));
369 }
370 #endif
371 
372 /*********************
373 *** Procedural API ***
374 *********************/
375 
380 template<typename Scalar, int Dim>
381 template<typename OtherDerived>
384 {
385  EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(OtherDerived,int(Dim))
386  linear() = (linear() * other.asDiagonal()).lazy();
387  return *this;
388 }
389 
394 template<typename Scalar, int Dim>
396 {
397  linear() *= s;
398  return *this;
399 }
400 
405 template<typename Scalar, int Dim>
406 template<typename OtherDerived>
409 {
410  EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(OtherDerived,int(Dim))
411  m_matrix.template block<Dim,HDim>(0,0) = (other.asDiagonal() * m_matrix.template block<Dim,HDim>(0,0)).lazy();
412  return *this;
413 }
414 
419 template<typename Scalar, int Dim>
421 {
422  m_matrix.template corner<Dim,HDim>(TopLeft) *= s;
423  return *this;
424 }
425 
430 template<typename Scalar, int Dim>
431 template<typename OtherDerived>
434 {
435  EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(OtherDerived,int(Dim))
436  translation() += linear() * other;
437  return *this;
438 }
439 
444 template<typename Scalar, int Dim>
445 template<typename OtherDerived>
448 {
449  EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(OtherDerived,int(Dim))
450  translation() += other;
451  return *this;
452 }
453 
471 template<typename Scalar, int Dim>
472 template<typename RotationType>
474 Transform<Scalar,Dim>::rotate(const RotationType& rotation)
475 {
476  linear() *= ei_toRotationMatrix<Scalar,Dim>(rotation);
477  return *this;
478 }
479 
487 template<typename Scalar, int Dim>
488 template<typename RotationType>
490 Transform<Scalar,Dim>::prerotate(const RotationType& rotation)
491 {
492  m_matrix.template block<Dim,HDim>(0,0) = ei_toRotationMatrix<Scalar,Dim>(rotation)
493  * m_matrix.template block<Dim,HDim>(0,0);
494  return *this;
495 }
496 
502 template<typename Scalar, int Dim>
504 Transform<Scalar,Dim>::shear(Scalar sx, Scalar sy)
505 {
506  EIGEN_STATIC_ASSERT(int(Dim)==2, YOU_MADE_A_PROGRAMMING_MISTAKE)
507  VectorType tmp = linear().col(0)*sy + linear().col(1);
508  linear() << linear().col(0) + linear().col(1)*sx, tmp;
509  return *this;
510 }
511 
517 template<typename Scalar, int Dim>
519 Transform<Scalar,Dim>::preshear(Scalar sx, Scalar sy)
520 {
521  EIGEN_STATIC_ASSERT(int(Dim)==2, YOU_MADE_A_PROGRAMMING_MISTAKE)
522  m_matrix.template block<Dim,HDim>(0,0) = LinearMatrixType(1, sx, sy, 1) * m_matrix.template block<Dim,HDim>(0,0);
523  return *this;
524 }
525 
526 /******************************************************
527 *** Scaling, Translation and Rotation compatibility ***
528 ******************************************************/
529 
530 template<typename Scalar, int Dim>
532 {
533  linear().setIdentity();
534  translation() = t.vector();
535  m_matrix.template block<1,Dim>(Dim,0).setZero();
536  m_matrix(Dim,Dim) = Scalar(1);
537  return *this;
538 }
539 
540 template<typename Scalar, int Dim>
541 inline Transform<Scalar,Dim> Transform<Scalar,Dim>::operator*(const TranslationType& t) const
542 {
543  Transform res = *this;
544  res.translate(t.vector());
545  return res;
546 }
547 
548 template<typename Scalar, int Dim>
550 {
551  m_matrix.setZero();
552  linear().diagonal() = s.coeffs();
553  m_matrix.coeffRef(Dim,Dim) = Scalar(1);
554  return *this;
555 }
556 
557 template<typename Scalar, int Dim>
558 inline Transform<Scalar,Dim> Transform<Scalar,Dim>::operator*(const ScalingType& s) const
559 {
560  Transform res = *this;
561  res.scale(s.coeffs());
562  return res;
563 }
564 
565 template<typename Scalar, int Dim>
566 template<typename Derived>
568 {
569  linear() = ei_toRotationMatrix<Scalar,Dim>(r);
570  translation().setZero();
571  m_matrix.template block<1,Dim>(Dim,0).setZero();
572  m_matrix.coeffRef(Dim,Dim) = Scalar(1);
573  return *this;
574 }
575 
576 template<typename Scalar, int Dim>
577 template<typename Derived>
579 {
580  Transform res = *this;
581  res.rotate(r.derived());
582  return res;
583 }
584 
585 /************************
586 *** Special functions ***
587 ************************/
588 
596 template<typename Scalar, int Dim>
599 {
600  LinearMatrixType result;
601  computeRotationScaling(&result, (LinearMatrixType*)0);
602  return result;
603 }
604 
605 
617 template<typename Scalar, int Dim>
618 template<typename RotationMatrixType, typename ScalingMatrixType>
619 void Transform<Scalar,Dim>::computeRotationScaling(RotationMatrixType *rotation, ScalingMatrixType *scaling) const
620 {
622  Scalar x = (svd.matrixU() * svd.matrixV().adjoint()).determinant(); // so x has absolute value 1
624  sv.coeffRef(0) *= x;
625  if(scaling)
626  {
627  scaling->noalias() = svd.matrixV() * sv.asDiagonal() * svd.matrixV().adjoint();
628  }
629  if(rotation)
630  {
631  LinearMatrixType m(svd.matrixU());
632  m.col(0) /= x;
633  rotation->noalias() = m * svd.matrixV().adjoint();
634  }
635 }
636 
648 template<typename Scalar, int Dim>
649 template<typename ScalingMatrixType, typename RotationMatrixType>
650 void Transform<Scalar,Dim>::computeScalingRotation(ScalingMatrixType *scaling, RotationMatrixType *rotation) const
651 {
653  Scalar x = (svd.matrixU() * svd.matrixV().adjoint()).determinant(); // so x has absolute value 1
655  sv.coeffRef(0) *= x;
656  if(scaling)
657  {
658  scaling->noalias() = svd.matrixU() * sv.asDiagonal() * svd.matrixU().adjoint();
659  }
660  if(rotation)
661  {
662  LinearMatrixType m(svd.matrixU());
663  m.col(0) /= x;
664  rotation->noalias() = m * svd.matrixV().adjoint();
665  }
666 }
667 
671 template<typename Scalar, int Dim>
672 template<typename PositionDerived, typename OrientationType, typename ScaleDerived>
675  const OrientationType& orientation, const MatrixBase<ScaleDerived> &scale)
676 {
677  linear() = ei_toRotationMatrix<Scalar,Dim>(orientation);
678  linear() *= scale.asDiagonal();
679  translation() = position;
680  m_matrix.template block<1,Dim>(Dim,0).setZero();
681  m_matrix(Dim,Dim) = Scalar(1);
682  return *this;
683 }
684 
704 template<typename Scalar, int Dim>
705 inline const typename Transform<Scalar,Dim>::MatrixType
707 {
708  if (traits == Projective)
709  {
710  return m_matrix.inverse();
711  }
712  else
713  {
714  MatrixType res;
715  if (traits == Affine)
716  {
717  res.template corner<Dim,Dim>(TopLeft) = linear().inverse();
718  }
719  else if (traits == Isometry)
720  {
721  res.template corner<Dim,Dim>(TopLeft) = linear().transpose();
722  }
723  else
724  {
725  ei_assert("invalid traits value in Transform::inverse()");
726  }
727  // translation and remaining parts
728  res.template corner<Dim,1>(TopRight) = - res.template corner<Dim,Dim>(TopLeft) * translation();
729  res.template corner<1,Dim>(BottomLeft).setZero();
730  res.coeffRef(Dim,Dim) = Scalar(1);
731  return res;
732  }
733 }
734 
735 /*****************************************************
736 *** Specializations of operator* with a MatrixBase ***
737 *****************************************************/
738 
739 template<typename Other, int Dim, int HDim>
740 struct ei_transform_product_impl<Other,Dim,HDim, HDim,HDim>
741 {
745  static ResultType run(const TransformType& tr, const Other& other)
746  { return tr.matrix() * other; }
747 };
748 
749 template<typename Other, int Dim, int HDim>
750 struct ei_transform_product_impl<Other,Dim,HDim, Dim,Dim>
751 {
754  typedef TransformType ResultType;
755  static ResultType run(const TransformType& tr, const Other& other)
756  {
757  TransformType res;
758  res.translation() = tr.translation();
759  res.matrix().row(Dim) = tr.matrix().row(Dim);
760  res.linear() = (tr.linear() * other).lazy();
761  return res;
762  }
763 };
764 
765 template<typename Other, int Dim, int HDim>
766 struct ei_transform_product_impl<Other,Dim,HDim, HDim,1>
767 {
771  static ResultType run(const TransformType& tr, const Other& other)
772  { return tr.matrix() * other; }
773 };
774 
775 template<typename Other, int Dim, int HDim>
776 struct ei_transform_product_impl<Other,Dim,HDim, Dim,1>
777 {
778  typedef typename Other::Scalar Scalar;
781  static ResultType run(const TransformType& tr, const Other& other)
782  { return ((tr.linear() * other) + tr.translation())
783  * (Scalar(1) / ( (tr.matrix().template block<1,Dim>(Dim,0) * other).coeff(0) + tr.matrix().coeff(Dim,Dim))); }
784 };
785 
786 } // end namespace Eigen
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar, _Dim==Dynamic?Dynamic:(_Dim+1)*(_Dim+1)) enum
Expression of the product of two general matrices or vectors.
Transform & operator*=(const RotationBase< Derived, Dim > &r)
static ResultType run(const TransformType &tr, const Other &other)
internal::cast_return_type< Transform, Transform< NewScalarType, Dim > >::type cast() const
Represents a possibly non uniform scaling transformation.
Transform(const TranslationType &t)
void computeRotationScaling(RotationMatrixType *rotation, ScalingMatrixType *scaling) const
Transform(const RotationBase< Derived, Dim > &r)
Transform(const MatrixBase< OtherDerived > &other)
Translation< Scalar, Dim > TranslationType
const VectorType & vector() const
#define ei_assert
Scaling< Scalar, Dim > ScalingType
Block< MatrixType, Dim, 1 > TranslationPart
Transform & operator*=(const TranslationType &t)
Transform & operator=(const Transform &other)
iterative scaling algorithm to equilibrate rows and column norms in matrices
Definition: matrix.hpp:471
Holds information about the various numeric (i.e. scalar) types allowed by Eigen. ...
Definition: NumTraits.h:88
Matrix< Scalar, Dim, Dim > LinearMatrixType
friend Transform operator*(const LinearMatrixType &mat, const Transform &t)
#define EIGEN_STATIC_ASSERT(CONDITION, MSG)
Definition: StaticAssert.h:111
const SingularValuesType & singularValues() const
Definition: JacobiSVD.h:630
static const MatrixType::IdentityReturnType Identity()
Transform & prerotate(const RotationType &rotation)
Transform & operator=(const MatrixBase< OtherDerived > &other)
Transform & shear(Scalar sx, Scalar sy)
Represents a translation transformation.
Scalar operator()(int row, int col) const
Transform< float, 2 > Transform2f
const VectorType & coeffs() const
Transform & pretranslate(const MatrixBase< OtherDerived > &other)
Transform & preshear(Scalar sx, Scalar sy)
static ResultType run(const TransformType &tr, const Other &other)
EIGEN_STRONG_INLINE const Scalar & coeff(Index rowId, Index colId) const
Block< MatrixType, Dim, Dim > LinearPart
static ResultType run(const TransformType &tr, const Other &other)
EIGEN_STRONG_INLINE Scalar & coeffRef(Index rowId, Index colId)
Matrix< Scalar, Dim, 1 > VectorType
static void run(Transform *transform, const MatrixBase< OtherDerived > &other)
Common base class for compact rotation representations.
Transform(const Transform< OtherScalarType, Dim > &other)
const Scalar * data() const
void computeScalingRotation(ScalingMatrixType *scaling, RotationMatrixType *rotation) const
Transform< float, 3 > Transform3f
static void run(Transform *transform, const MatrixBase< OtherDerived > &other)
Transform & translate(const MatrixBase< OtherDerived > &other)
Derived & setZero(Index size)
ConstTranslationPart translation() const
static ResultType run(const TransformType &tr, const Other &other)
Transform & prescale(const MatrixBase< OtherDerived > &other)
EIGEN_STRONG_INLINE const Scalar * data() const
Transform & rotate(const RotationType &rotation)
const ei_transform_product_impl< OtherDerived, _Dim, _Dim+1 >::ResultType operator*(const MatrixBase< OtherDerived > &other) const
Transform & operator*=(const ScalingType &s)
const MatrixType & matrix() const
Expression of a fixed-size or dynamic-size block.
Definition: Core/Block.h:102
RowXpr row(Index i)
Definition: BlockMethods.h:725
const Block< const MatrixType, Dim, Dim > ConstLinearPart
Transform & scale(const MatrixBase< OtherDerived > &other)
Two-sided Jacobi SVD decomposition of a rectangular matrix.
Transform(const Transform &other)
ConstLinearPart linear() const
const MatrixUType & matrixU() const
Definition: JacobiSVD.h:602
LinearMatrixType rotation() const
const MatrixType inverse(TransformTraits traits=Affine) const
bool isApprox(const Transform &other, typename NumTraits< Scalar >::Real prec=precision< Scalar >()) const
Transform & fromPositionOrientationScale(const MatrixBase< PositionDerived > &position, const OrientationType &orientation, const MatrixBase< ScaleDerived > &scale)
const Block< const MatrixType, Dim, 1 > ConstTranslationPart
ColXpr col(Index i)
Definition: BlockMethods.h:708
const DiagonalWrapper< const Derived > asDiagonal() const
Transform< double, 2 > Transform2d
Transform(const ScalingType &s)
Base class for all dense matrices, vectors, and expressions.
Definition: MatrixBase.h:48
Represents an homogeneous transformation in a N dimensional space.
const MatrixVType & matrixV() const
Definition: JacobiSVD.h:618
RotationMatrixType toRotationMatrix() const
#define EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(TYPE, SIZE)
Definition: StaticAssert.h:141
Matrix< Scalar, HDim, HDim > MatrixType
Transform< double, 3 > Transform3d


acado
Author(s): Milan Vukov, Rien Quirynen
autogenerated on Mon Jun 10 2019 12:35:13