%OLD VERSION!!! \documentclass[a4paper,11pt,twoside]{article} \usepackage{Lweb} \begin{document} \title{Affine Transformations in d-Space\\ (class Aff\_transformationHd)} \author{M. Seel} \maketitle \tableofcontents \newpage \section{The Manual Page of class Aff\_transformationHd} \input{Aff_transformationHd.man} \section{The Implementation of class Aff\_transformationHd} <>= //--------------------------------------------------------------------- // file generated by notangle from noweb/Aff_transformationHd.lw // please debug or modify noweb file // coding: K. Mehlhorn, M. Seel //--------------------------------------------------------------------- #ifndef CGAL_AFF_TRANSFORMATIONHD_H #define CGAL_AFF_TRANSFORMATIONHD_H #include #include #include #include #include #include #include CGAL_BEGIN_NAMESPACE <> <> CGAL_END_NAMESPACE #endif // CGAL_AFF_TRANSFORMATIONHD_H @ The implementation file contains the following. <>= //--------------------------------------------------------------------- // file generated by notangle from noweb/Aff_transformationHd.lw // please debug or modify noweb file // coding: K. Mehlhorn, M. Seel //--------------------------------------------------------------------- #ifndef CGAL_AFF_TRANSFORMATIONHD_C #define CGAL_AFF_TRANSFORMATIONHD_C CGAL_BEGIN_NAMESPACE <> CGAL_END_NAMESPACE #endif // CGAL_AFF_TRANSFORMATIONHD_C @ \subsection{The Representation Class} First we provide a representation class for Aff\_transformationHd. <>= template class Aff_transformationHd; template class Aff_transformationHd_rep; template class Aff_transformationHd_rep : public Ref_counted { friend class Aff_transformationHd; typedef typename LA::Matrix Matrix; Matrix M_; public: Aff_transformationHd_rep(int d) : M_(d+1) {} Aff_transformationHd_rep(const Matrix& M_init) : M_(M_init) {} ~Aff_transformationHd_rep() {} }; @ \subsection{The Datatype} And now for the class definition. We interleave the prototyping and the implementation. <>= /*{\Moptions outfile=Aff_transformation_d.man}*/ /*{\Manpage{Aff_transformation_d}{R}{Affine Transformations}{t}}*/ /*{\Msubst Hd#_d Aff_transformationHd#Aff_transformation_d Quotient#FT }*/ template class Aff_transformationHd : public Handle_for< Aff_transformationHd_rep<_RT,_LA> > { typedef Aff_transformationHd_rep<_RT,_LA> Rep; typedef Handle_for Base; typedef Aff_transformationHd<_RT,_LA> Self; /*{\Mdefinition An instance of the data type |\Mname| is an affine transformation of $d$-dimensional space. It is specified by a square matrix $M$ of dimension $d + 1$. All entries in the last row of |M| except the diagonal entry must be zero; the diagonal entry must be non-zero. A point $p$ with homogeneous coordinates $(p[0], \ldots, p[d])$ can be transformed into the point |p.transform(A)|, where |A| is an affine transformation created from |M| by the constructors below. }*/ public: /*{\Mtypes 4}*/ typedef _RT RT; /*{\Mtypemember the ring type.}*/ typedef Quotient<_RT> FT; /*{\Mtypemember the field type.}*/ typedef _LA LA; /*{\Mtypemember the linear algebra layer.}*/ typedef typename _LA::Matrix Matrix; /*{\Mtypemember the matrix type.}*/ typedef typename _LA::Vector Vector; /*{\Mcreation 3}*/ Aff_transformationHd(int d = 0) : Base( Rep(d) ) {} /*{\Mcreate introduces a transformation in $d$-dimensional space.}*/ Aff_transformationHd(int d, Identity_transformation) : Base( Rep(d) ) /*{\Mcreate introduces the identity transformation in $d$-dimensional space.}*/ { for (int i = 0; i <= d; ++i) ptr->M_(i,i) = RT(1); } Aff_transformationHd(const Matrix& M) : Base( Rep(M) ) /*{\Mcreate introduces the transformation of $d$ - space specified by matrix $M$. \precond |M| is a square matrix of dimension $d + 1$. }*/ { CGAL_assertion_msg((M.row_dimension()==M.column_dimension()), "Aff_transformationHd::\ construction: initialization matrix is not quadratic."); } #ifndef CGAL_SIMPLE_INTERFACE template Aff_transformationHd(Scaling, Forward_iterator start, Forward_iterator end) : Base( Rep(std::distance(start,end)-1) ) /*{\Mcreate introduces the transformation of $d$-space specified by a diagonal matrix with entries |set [start,end)| on the diagonal (a scaling of the space). \precond |set [start,end)| is a vector of dimension $d+1$.}*/ { int i=0; while (start != end) { ptr->M_(i,i) = *start++;++i; } } #else #define FIXATHD(I) \ Aff_transformationHd(Scaling, I start, I end) : \ Base( Rep(end-start-1) ) \ { int i=0; while (start != end) { ptr->M_(i,i) = *start++;++i; } } FIXATHD(int*) FIXATHD(const int*) FIXATHD(RT*) FIXATHD(const RT*) #undef FIXATHD #endif Aff_transformationHd(Translation, const VectorHd& v) : Base( Rep(v.dimension()) ) /*{\Mcreate introduces the translation by vector $v$.}*/ @ The homogenous representation of the rational vector is put into the last column of the matrix and the rest of the diagonal is filled with the homogenizing element: \begin{displaymath} \left( \begin{array}{ccccc} vec.homogeneous(vec.dimension()) & 0 & \cdots & 0 & vec.homogeneous(0) \\ 0 & vec.homogeneous(vec.dimension()) & & & vec.homogeneous(1) \\ 0 & 0 & \ddots & 0 & \vdots \\ 0 & \cdots & 0& 0 & vec.homogeneous(vec.dimension()) \\ \end{array} \right) \end{displaymath}{ <>= { int d = v.dimension(); for (int i = 0; i < d; ++i) { ptr->M_(i,i) = v.homogeneous(d); ptr->M_(i,d) = v.homogeneous(i); } ptr->M_(d,d) = v.homogeneous(d); } Aff_transformationHd(int d, Scaling, const RT& num, const RT& den) : Base( Rep(d) ) /*{\Mcreate returns a scaling by a scale factor |num/den|.}*/ @ We provide a simple scaling transformation. We put the numerator of the rational scaling factor into the Matrix elements $M_{0,0}$ to $M_{d-1,d-1}$ and the denominator into the lower right corner element $M_{d,d}$: \begin{displaymath} \left( \begin{array}{cccc} num &&& \\ & \ddots && \\ && num & \\ &&& den \\ \end{array} \right) \end{displaymath} <>= { Matrix& M = ptr->M_; for (int i = 0; i < d; ++i) M(i,i) = num; M(d,d) = den; } Aff_transformationHd(int d, Rotation, const RT& sin_num, const RT& cos_num, const RT& den, int e1 = 0, int e2 = 1); /*{\Mcreate returns a planar rotation with sine and cosine values |sin_num/den| and |cos_num/den| in the plane spanned by the base vectors $b_{e1}$ and $b_{e2}$ in $d$-space. Thus the default use delivers a planar rotation in the $x$-$y$ plane. \precond $|sin_num|^2 + |cos_num|^2 = |den|^2$ and $0 \leq e_1 < e_2 < d$}*/ @ To implement a rational rotation we put the standard 2d rotation matrix into the intersection points of the $e_1$-th and $e_2$-th columns and rows and the denominator of the sine and cosine values into the right lower corner. This looks in the default version: \begin{displaymath} \left( \begin{array}{ccc} cos\_num & -sin\_num & \\ sin\_num & cos\_num & \\ & & den \\ \end{array} \right) \end{displaymath} <>= template Aff_transformationHd:: Aff_transformationHd(int d, Rotation, const RT& sin_num, const RT& cos_num, const RT& den, int e1, int e2) : Base( Rep(d) ) { CGAL_assertion_msg((sin_num*sin_num + cos_num*cos_num == den*den), "planar_rotation: rotation parameters disobey precondition."); CGAL_assertion_msg((0<=e1 && e1<=e2 && e2M_; for (int i=0; i>= Aff_transformationHd(int d, Rotation, const DirectionHd& dir, const RT& num, const RT& den, int e1 = 0, int e2 = 1); /*{\Mcreate returns a planar rotation within the plane spanned by the base vectors $b_{e1}$ and $b_{e2}$ in $d$-space. The rotation parameters are given by the $2$-dimensional direction |dir|, such that the difference between the sines and cosines of the rotation given by |dir| and the approximated rotation are at most |num/den| each.\\ \precond |dir.dimension()==2|, |!dir.is_degenerate()| and |num < den| is positive and $0 \leq e_1 < e_2 < d$ }*/ @ Here we implement a special rotation calculation procedure starting from a direction |dir| and a rational error bound |num/den|. We want to find a triple $(sin,cos,denom)$ which obeys the equality $sin^2 + cos^2 = denom^2$ and at the same time approximates the rotation given by direction $dir$, such that the differeces between the sines and cosines of $dir$ and the approximation are at most $num/den$. The code is based on the rational rotation method presented by Canny and Ressler at the 8th SCG 1992. The approximation is based on Farey sequences. To check the quality of the current approximation we have to compare a rational and a (possibly) non-rational number. The implementation used division and modulus operation \% (the division is always exact, that is, it is known that there is no remainder). <>= template Aff_transformationHd:: Aff_transformationHd(int d, Rotation, const DirectionHd& dir, const RT& num, const RT& den, int e1, int e2) : Base( Rep(d) ) { CGAL_assertion_msg((dir.dimension() == 2), "planar_rotation: dir has to be 2 dimensional."); CGAL_assertion_msg((RT(0)<=num && num < den), "planar_rotation: num and den have to be positive."); CGAL_assertion_msg((0<=e1 && e1<=e2 && e2 dx) { RT tmp = dx; dx = dy; dy = tmp; } /* approximate |sin = dy / sqrt(sq_hypotenuse)| \\ |if ( dy / sqrt(sq_hypotenuse) < num/den )| */ if (dy * dy * den * den < sq_hypotenuse * num * num) { cos = denom = 1; sin = 0; } else { RT p,q,p0,q0,p1,q1; p0 = 0; q0 = p1 = q1 = 1; for(;;) { p = p0 + p1; q = q0 + q1; sin = RT(2)*p*q; denom = CGAL_NTS square(p) + CGAL_NTS square(q); // sanity check for approximation // | sin/denom < dy/sqrt(hypotenuse) + num/den| // | && sin/denom > dy/sqrt(hypotenuse) - num/den| // | == sin/denom - num/den < dy/sqrt(sq_hypotenuse)| // | && sin/denom + num/den > dy/sqrt(sq_hypotenuse)| // | == (sqr(sin) sqr(den) + sqr(num) sqr(denom)) sq_hypotenuse - 2..| // | < sqr(dy) sqr(den) sqr(denom)| // | && (sqr(sin) sqr(den) + sqr(num) sqr(denom))sq_hypotenuse + 2..| // | > sqr(dy) sqr(den) sqr(denom)| common_part = (CGAL_NTS square(sin)*CGAL_NTS square(den) + CGAL_NTS square(num)*CGAL_NTS square(denom))* sq_hypotenuse; diff_part = RT(2)*num*sin*den*denom*sq_hypotenuse; rhs = CGAL_NTS square(dy)*CGAL_NTS square(den)* CGAL_NTS square(denom); upper_ok = (common_part - diff_part < rhs); lower_ok = (common_part + diff_part > rhs); if ( lower_ok && upper_ok ) { if ( CGAL_NTS square(p)%RT(2) + CGAL_NTS square(q)%RT(2) > 1) { sin = p*q; cos = (CGAL_NTS square(q) - CGAL_NTS square(p))/RT(2); // exact division denom = (CGAL_NTS square(p) + CGAL_NTS square(q))/RT(2); // exact division } else { cos = CGAL_NTS square(q) - CGAL_NTS square(p); } break; } else { /* |if ( dy/sqrt(sq_hypotenuse) < sin/denom )| */ if ( CGAL_NTS square(dy)*CGAL_NTS square(denom) < CGAL_NTS square(sin)*sq_hypotenuse ) { p1 = p; q1 = q; } else { p0 = p; q0 = q; } } } // for(;;) } dx = dir.dx(); dy = dir.dy(); if (dy > dx) { RT tmp = dx; dx = dy; dy = tmp; } if (dx < 0) sin = - sin; if (dy < 0) cos = - cos; Matrix& M = ptr->M_; for (int i=0; i>= /*{\Moperations 5 3}*/ int dimension() const { return ptr->M_.row_dimension()-1; } /*{\Mop the dimension of the underlying space }*/ const Matrix& matrix() const { return ptr->M_; } /*{\Mop returns the transformation matrix }*/ Vector operator()(const Vector& iv) const // transforms the ivector by a matrix multiplication { return matrix()*iv; } Aff_transformationHd inverse() const /*{\Mop returns the inverse transformation. \precond |\Mvar.matrix()| is invertible.}*/ { Aff_transformationHd Inv; RT D; Vector dummy; if ( !LA::inverse(matrix(),Inv.ptr->M_,D,dummy) ) CGAL_assertion_msg(0,"Aff_transformationHd::inverse: not invertible."); return Inv; } Aff_transformationHd operator*(const Aff_transformationHd& s) const /*{\Mbinop composition of transformations. Note that transformations are not necessarily commutative. |t*s| is the transformation which transforms first by |t| and then by |s|.}*/ { CGAL_assertion_msg((dimension()==s.dimension()), "Aff_transformationHd::operator*: dimensions disagree."); return Aff_transformationHd(matrix()*s.matrix()); } bool operator==(const Aff_transformationHd& a1) const { if ( identical(a1) ) return true; return ( matrix() == a1.matrix() ); } bool operator!=(const Aff_transformationHd& a1) const { return !operator==(a1); } }; // Aff_transformationHd template std::ostream& operator<<( std::ostream& os, const Aff_transformationHd& t) { os << t.matrix(); return os; } template std::istream& operator>>( std::istream& is, Aff_transformationHd& t) { typename LA::Matrix M(t.dimension()); is >> M; t = Aff_transformationHd(M); return is; } /*{\Mimplementation Affine Transformations are implemented by matrices of integers as an item type. All operations like creation, initialization, input and output on a transformation $t$ take time $O(|t.dimension()|^2)$. |dimension()| takes constant time. The operations for inversion and composition have the cubic costs of the used matrix operations. The space requirement is $O(|t.dimension()|^2)$. }*/ // ----------------------------- end of file ---------------------------- @ \end{document}