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