affine.h revision c8589a6c7367d09fa756755cef0dd448c7328a71
/**
* \file
* \brief 3x3 affine transformation matrix.
*//*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com> (Original NRAffine definition and related macros)
* Nathan Hurst <njh@mail.csse.monash.edu.au> (Geom::Affine class version of the above)
* Michael G. Sloan <mgsloan@gmail.com> (reorganization and additions)
* Krzysztof KosiƄski <tweenk.pl@gmail.com> (removal of boilerplate, docs)
*
* This code is in public domain.
*/
#ifndef SEEN_LIB2GEOM_AFFINE_H
#define SEEN_LIB2GEOM_AFFINE_H
#include <boost/operators.hpp>
#include <2geom/forward.h>
#include <2geom/point.h>
#include <2geom/utils.h>
namespace Geom {
/**
* @brief 3x3 matrix representing an affine transformation.
*
* Affine transformations on elements of a vector space are transformations which can be
* expressed in terms of matrix multiplication followed by addition
* (\f$x \mapsto A x + b\f$). They can be thought of as generalizations of linear functions
* (\f$y = a x + b\f$) to vector spaces. Affine transformations of points on a 2D plane preserve
* the following properties:
*
* - Colinearity: if three points lie on the same line, they will still be colinear after
* an affine transformation.
* - Ratios of distances between points on the same line are preserved
* - Parallel lines remain parallel.
*
* All affine transformations on 2D points can be written as combinations of scaling, rotation,
* shearing and translation. They can be represented as a combination of a vector and a 2x2 matrix,
* but this form is inconvenient to work with. A better solution is to represent all affine
* transformations on the 2D plane as 3x3 matrices, where the last column has fixed values.
* \f[ A = \left[ \begin{array}{ccc}
c_0 & c_1 & 0 \\
c_2 & c_3 & 0 \\
c_4 & c_5 & 1 \end{array} \right]\f]
*
* We then interpret points as row vectors of the form \f$[p_X, p_Y, 1]\f$. Applying a
* transformation to a point can be written as right-multiplication by a 3x3 matrix
* (\f$p' = pA\f$). This subset of matrices is closed under multiplication - combination
* of any two transforms can be expressed as the multiplication of their matrices.
* In this representation, the \f$c_4\f$ and \f$c_5\f$ coefficients represent
* the translation component of the transformation.
*
* Matrices can be multiplied by other more specific transformations. When multiplying,
* the transformations are applied from left to right, so for example <code>m = a * b</code>
* means: @a m first transforms by a, then by b.
*
* @ingroup Transforms
*/
class Affine
: boost::equality_comparable< Affine // generates operator!= from operator==
, boost::multipliable1< Affine
, MultipliableNoncommutative< Affine, Translate
, MultipliableNoncommutative< Affine, Scale
, MultipliableNoncommutative< Affine, Rotate
, MultipliableNoncommutative< Affine, HShear
, MultipliableNoncommutative< Affine, VShear
, MultipliableNoncommutative< Affine, Zoom
> > > > > > > >
{
Coord _c[6];
public:
Affine() {
_c[0] = _c[3] = 1;
_c[1] = _c[2] = _c[4] = _c[5] = 0;
}
/** @brief Create a matrix from its coefficient values.
* It's rather inconvenient to directly create matrices in this way. Use transform classes
* if your transformation has a geometric interpretation.
* @see Translate
* @see Scale
* @see Rotate
* @see HShear
* @see VShear
* @see Zoom */
Affine(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) {
_c[0] = c0; _c[1] = c1;
_c[2] = c2; _c[3] = c3;
_c[4] = c4; _c[5] = c5;
}
/** @brief Access a coefficient by its index. */
inline Coord operator[](unsigned i) const { return _c[i]; }
inline Coord &operator[](unsigned i) { return _c[i]; }
/// @name Combine with other transformations
/// @{
Affine &operator*=(Affine const &m);
// implemented in transforms.cpp
Affine &operator*=(Translate const &t);
Affine &operator*=(Scale const &s);
Affine &operator*=(Rotate const &r);
Affine &operator*=(HShear const &h);
Affine &operator*=(VShear const &v);
Affine &operator*=(Zoom const &);
/// @}
bool operator==(Affine const &o) const {
for(unsigned i = 0; i < 6; ++i) {
if ( _c[i] != o._c[i] ) return false;
}
return true;
}
/// @name Get the parameters of the matrix's transform
/// @{
Point xAxis() const;
Point yAxis() const;
Point translation() const;
Coord expansionX() const;
Coord expansionY() const;
Point expansion() const { return Point(expansionX(), expansionY()); }
/// @}
/// @name Modify the matrix
/// @{
void setXAxis(Point const &vec);
void setYAxis(Point const &vec);
void setTranslation(Point const &loc);
void setExpansionX(Coord val);
void setExpansionY(Coord val);
void setIdentity();
/// @}
/// @name Inspect the matrix's transform
/// @{
bool isIdentity(Coord eps = EPSILON) const;
bool isTranslation(Coord eps = EPSILON) const;
bool isScale(Coord eps = EPSILON) const;
bool isUniformScale(Coord eps = EPSILON) const;
bool isRotation(Coord eps = EPSILON) const;
bool isHShear(Coord eps = EPSILON) const;
bool isVShear(Coord eps = EPSILON) const;
bool isNonzeroTranslation(Coord eps = EPSILON) const;
bool isNonzeroScale(Coord eps = EPSILON) const;
bool isNonzeroUniformScale(Coord eps = EPSILON) const;
bool isNonzeroRotation(Coord eps = EPSILON) const;
bool isNonzeroHShear(Coord eps = EPSILON) const;
bool isNonzeroVShear(Coord eps = EPSILON) const;
bool isZoom(Coord eps = EPSILON) const;
bool preservesArea(Coord eps = EPSILON) const;
bool preservesAngles(Coord eps = EPSILON) const;
bool preservesDistances(Coord eps = EPSILON) const;
bool flips() const;
bool isSingular(Coord eps = EPSILON) const;
/// @}
/// @name Compute other matrices
/// @{
Affine withoutTranslation() const {
Affine ret(*this);
ret.setTranslation(Point(0,0));
return ret;
}
Affine inverse() const;
/// @}
/// @name Compute scalar values
/// @{
Coord det() const;
Coord descrim2() const;
Coord descrim() const;
/// @}
inline static Affine identity();
};
/** @brief Print out the Affine (for debugging).
* @relates Affine */
inline std::ostream &operator<< (std::ostream &out_file, const Geom::Affine &m) {
out_file << "A: " << m[0] << " C: " << m[2] << " E: " << m[4] << "\n";
out_file << "B: " << m[1] << " D: " << m[3] << " F: " << m[5] << "\n";
return out_file;
}
// Affine factories
Affine from_basis(const Point &x_basis, const Point &y_basis, const Point &offset=Point(0,0));
Affine elliptic_quadratic_form(Affine const &m);
/** Given a matrix (ignoring the translation) this returns the eigen
* values and vectors. */
class Eigen{
public:
Point vectors[2];
double values[2];
Eigen(Affine const &m);
Eigen(double M[2][2]);
};
/** @brief Create an identity matrix.
* This is a convenience function identical to Affine::identity(). */
inline Affine identity() {
Affine ret(Affine::identity());
return ret; // allow NRVO
}
/** @brief Create an identity matrix.
* @return The matrix
* \f$\left[\begin{array}{ccc}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1 \end{array}\right]\f$.
* @relates Affine */
inline Affine Affine::identity() {
Affine ret(1.0, 0.0,
0.0, 1.0,
0.0, 0.0);
return ret; // allow NRVO
}
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON);
} // end namespace Geom
#endif // LIB2GEOM_SEEN_AFFINE_H
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :