1 /**
2  * \file
3  * \brief 3x3 affine transformation matrix.
4  *//*
5  * Authors:
6  *   Lauris Kaplinski <lauris@kaplinski.com> (Original NRAffine definition and related macros)
7  *   Nathan Hurst <njh@mail.csse.monash.edu.au> (Geom::Affine class version of the above)
8  *   Michael G. Sloan <mgsloan@gmail.com> (reorganization and additions)
9  *   Krzysztof Kosiński <tweenk.pl@gmail.com> (removal of boilerplate, docs)
10  *
11  * This code is in public domain.
12  */
13 
14 #ifndef LIB2GEOM_SEEN_AFFINE_H
15 #define LIB2GEOM_SEEN_AFFINE_H
16 
17 #include <boost/operators.hpp>
18 #include <2geom/forward.h>
19 #include <2geom/point.h>
20 #include <2geom/utils.h>
21 
22 namespace Geom {
23 
24 /**
25  * @brief 3x3 matrix representing an affine transformation.
26  *
27  * Affine transformations on elements of a vector space are transformations which can be
28  * expressed in terms of matrix multiplication followed by addition
29  * (\f$x \mapsto A x + b\f$). They can be thought of as generalizations of linear functions
30  * (\f$y = a x + b\f$) to vector spaces. Affine transformations of points on a 2D plane preserve
31  * the following properties:
32  *
33  * - Colinearity: if three points lie on the same line, they will still be colinear after
34  *   an affine transformation.
35  * - Ratios of distances between points on the same line are preserved
36  * - Parallel lines remain parallel.
37  *
38  * All affine transformations on 2D points can be written as combinations of scaling, rotation,
39  * shearing and translation. They can be represented as a combination of a vector and a 2x2 matrix,
40  * but this form is inconvenient to work with. A better solution is to represent all affine
41  * transformations on the 2D plane as 3x3 matrices, where the last column has fixed values.
42  * \f[ A = \left[ \begin{array}{ccc}
43     c_0 & c_1 & 0 \\
44     c_2 & c_3 & 0 \\
45     c_4 & c_5 & 1 \end{array} \right]\f]
46  *
47  * We then interpret points as row vectors of the form \f$[p_X, p_Y, 1]\f$. Applying a
48  * transformation to a point can be written as right-multiplication by a 3x3 matrix
49  * (\f$p' = pA\f$). This subset of matrices is closed under multiplication - combination
50  * of any two transforms can be expressed as the multiplication of their matrices.
51  * In this representation, the \f$c_4\f$ and \f$c_5\f$ coefficients represent
52  * the translation component of the transformation.
53  *
54  * Matrices can be multiplied by other more specific transformations. When multiplying,
55  * the transformations are applied from left to right, so for example <code>m = a * b</code>
56  * means: @a m first transforms by a, then by b.
57  *
58  * @ingroup Transforms
59  */
60 class Affine
61     : boost::equality_comparable< Affine // generates operator!= from operator==
62     , boost::multipliable1< Affine
63     , MultipliableNoncommutative< Affine, Translate
64     , MultipliableNoncommutative< Affine, Scale
65     , MultipliableNoncommutative< Affine, Rotate
66     , MultipliableNoncommutative< Affine, HShear
67     , MultipliableNoncommutative< Affine, VShear
68     , MultipliableNoncommutative< Affine, Zoom
69       > > > > > > > >
70 {
71     Coord _c[6];
72 public:
Affine()73     Affine() {
74         _c[0] = _c[3] = 1;
75         _c[1] = _c[2] = _c[4] = _c[5] = 0;
76     }
77 
78     /** @brief Create a matrix from its coefficient values.
79      * It's rather inconvenient to directly create matrices in this way. Use transform classes
80      * if your transformation has a geometric interpretation.
81      * @see Translate
82      * @see Scale
83      * @see Rotate
84      * @see HShear
85      * @see VShear
86      * @see Zoom */
Affine(Coord c0,Coord c1,Coord c2,Coord c3,Coord c4,Coord c5)87     Affine(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) {
88         _c[0] = c0; _c[1] = c1;
89         _c[2] = c2; _c[3] = c3;
90         _c[4] = c4; _c[5] = c5;
91     }
92 
93     /** @brief Access a coefficient by its index. */
94     inline Coord operator[](unsigned i) const { return _c[i]; }
95     inline Coord &operator[](unsigned i) { return _c[i]; }
96 
97     /// @name Combine with other transformations
98     /// @{
99     Affine &operator*=(Affine const &m);
100     // implemented in transforms.cpp
101     Affine &operator*=(Translate const &t);
102     Affine &operator*=(Scale const &s);
103     Affine &operator*=(Rotate const &r);
104     Affine &operator*=(HShear const &h);
105     Affine &operator*=(VShear const &v);
106     Affine &operator*=(Zoom const &);
107     /// @}
108 
109     bool operator==(Affine const &o) const {
110         for(unsigned i = 0; i < 6; ++i) {
111             if ( _c[i] != o._c[i] ) return false;
112         }
113         return true;
114     }
115 
116     /// @name Get the parameters of the matrix's transform
117     /// @{
118     Point xAxis() const;
119     Point yAxis() const;
120     Point translation() const;
121     Coord expansionX() const;
122     Coord expansionY() const;
expansion()123     Point expansion() const { return Point(expansionX(), expansionY()); }
124     /// @}
125 
126     /// @name Modify the matrix
127     /// @{
128     void setXAxis(Point const &vec);
129     void setYAxis(Point const &vec);
130 
131     void setTranslation(Point const &loc);
132 
133     void setExpansionX(Coord val);
134     void setExpansionY(Coord val);
135     void setIdentity();
136     /// @}
137 
138     /// @name Inspect the matrix's transform
139     /// @{
140     bool isIdentity(Coord eps = EPSILON) const;
141 
142     bool isTranslation(Coord eps = EPSILON) const;
143     bool isScale(Coord eps = EPSILON) const;
144     bool isUniformScale(Coord eps = EPSILON) const;
145     bool isRotation(Coord eps = EPSILON) const;
146     bool isHShear(Coord eps = EPSILON) const;
147     bool isVShear(Coord eps = EPSILON) const;
148 
149     bool isNonzeroTranslation(Coord eps = EPSILON) const;
150     bool isNonzeroScale(Coord eps = EPSILON) const;
151     bool isNonzeroUniformScale(Coord eps = EPSILON) const;
152     bool isNonzeroRotation(Coord eps = EPSILON) const;
153     bool isNonzeroNonpureRotation(Coord eps = EPSILON) const;
154     Point rotationCenter() const;
155     bool isNonzeroHShear(Coord eps = EPSILON) const;
156     bool isNonzeroVShear(Coord eps = EPSILON) const;
157 
158     bool isZoom(Coord eps = EPSILON) const;
159     bool preservesArea(Coord eps = EPSILON) const;
160     bool preservesAngles(Coord eps = EPSILON) const;
161     bool preservesDistances(Coord eps = EPSILON) const;
162     bool flips() const;
163 
164     bool isSingular(Coord eps = EPSILON) const;
165     /// @}
166 
167     /// @name Compute other matrices
168     /// @{
withoutTranslation()169     Affine withoutTranslation() const {
170         Affine ret(*this);
171         ret.setTranslation(Point(0,0));
172         return ret;
173     }
174     Affine inverse() const;
175     /// @}
176 
177     /// @name Compute scalar values
178     /// @{
179     Coord det() const;
180     Coord descrim2() const;
181     Coord descrim() const;
182     /// @}
183     inline static Affine identity();
184 };
185 
186 /** @brief Print out the Affine (for debugging).
187  * @relates Affine */
188 inline std::ostream &operator<< (std::ostream &out_file, const Geom::Affine &m) {
189     out_file << "A: " << m[0] << "  C: " << m[2] << "  E: " << m[4] << "\n";
190     out_file << "B: " << m[1] << "  D: " << m[3] << "  F: " << m[5] << "\n";
191     return out_file;
192 }
193 
194 // Affine factories
195 Affine from_basis(const Point &x_basis, const Point &y_basis, const Point &offset=Point(0,0));
196 Affine elliptic_quadratic_form(Affine const &m);
197 
198 /** Given a matrix (ignoring the translation) this returns the eigen
199  * values and vectors. */
200 class Eigen{
201 public:
202     Point vectors[2];
203     double values[2];
204     Eigen(Affine const &m);
205     Eigen(double M[2][2]);
206 };
207 
208 /** @brief Create an identity matrix.
209  * This is a convenience function identical to Affine::identity(). */
identity()210 inline Affine identity() {
211     Affine ret(Affine::identity());
212     return ret; // allow NRVO
213 }
214 
215 /** @brief Create an identity matrix.
216  * @return The matrix
217  *         \f$\left[\begin{array}{ccc}
218            1 & 0 & 0 \\
219            0 & 1 & 0 \\
220            0 & 0 & 1 \end{array}\right]\f$.
221  * @relates Affine */
identity()222 inline Affine Affine::identity() {
223     Affine ret(1.0, 0.0,
224                0.0, 1.0,
225                0.0, 0.0);
226     return ret; // allow NRVO
227 }
228 
229 bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON);
230 
231 } // end namespace Geom
232 
233 #endif // LIB2GEOM_SEEN_AFFINE_H
234 
235 /*
236   Local Variables:
237   mode:c++
238   c-file-style:"stroustrup"
239   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
240   indent-tabs-mode:nil
241   fill-column:99
242   End:
243 */
244 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
245