summaryrefslogtreecommitdiffstats
path: root/src/2geom/affine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/2geom/affine.cpp')
-rw-r--r--src/2geom/affine.cpp86
1 files changed, 57 insertions, 29 deletions
diff --git a/src/2geom/affine.cpp b/src/2geom/affine.cpp
index 925f43820..1be5d9fe8 100644
--- a/src/2geom/affine.cpp
+++ b/src/2geom/affine.cpp
@@ -1,9 +1,3 @@
-#define __Geom_MATRIX_C__
-
-/** \file
- * Various matrix routines. Currently includes some Geom::Rotate etc. routines too.
- */
-
/*
* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
@@ -150,6 +144,7 @@ bool Affine::isNonzeroTranslation(Coord eps) const {
0 & b & 0 \\
0 & 0 & 1 \end{array}\right]\f$. */
bool Affine::isScale(Coord eps) const {
+ if (isSingular(eps)) return false;
return are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
@@ -162,6 +157,7 @@ bool Affine::isScale(Coord eps) const {
0 & b & 0 \\
0 & 0 & 1 \end{array}\right]\f$ and \f$a, b \neq 1\f$. */
bool Affine::isNonzeroScale(Coord eps) const {
+ if (isSingular(eps)) return false;
return (!are_near(_c[0], 1.0, eps) || !are_near(_c[3], 1.0, eps)) && //NOTE: these are the diags, and the next line opposite diags
are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
@@ -171,11 +167,12 @@ bool Affine::isNonzeroScale(Coord eps) const {
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
- a & 0 & 0 \\
- 0 & a & 0 \\
- 0 & 0 & 1 \end{array}\right]\f$. */
+ a_1 & 0 & 0 \\
+ 0 & a_2 & 0 \\
+ 0 & 0 & 1 \end{array}\right]\f$ where \f$|a_1| = |a_2|\f$. */
bool Affine::isUniformScale(Coord eps) const {
- return are_near(_c[0], _c[3], eps) &&
+ if (isSingular(eps)) return false;
+ return are_near(fabs(_c[0]), fabs(_c[3]), eps) &&
are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
@@ -184,11 +181,16 @@ bool Affine::isUniformScale(Coord eps) const {
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
- a & 0 & 0 \\
- 0 & a & 0 \\
- 0 & 0 & 1 \end{array}\right]\f$ and \f$a \neq 1\f$. */
+ a_1 & 0 & 0 \\
+ 0 & a_2 & 0 \\
+ 0 & 0 & 1 \end{array}\right]\f$ where \f$|a_1| = |a_2|\f$
+ * and \f$a_1, a_2 \neq 1\f$. */
bool Affine::isNonzeroUniformScale(Coord eps) const {
- return !are_near(_c[0], 1.0, eps) && are_near(_c[0], _c[3], eps) &&
+ if (isSingular(eps)) return false;
+ // we need to test both c0 and c3 to handle the case of flips,
+ // which should be treated as nonzero uniform scales
+ return !(are_near(_c[0], 1.0, eps) && are_near(_c[3], 1.0, eps)) &&
+ are_near(fabs(_c[0]), fabs(_c[3]), eps) &&
are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
}
@@ -272,15 +274,17 @@ bool Affine::isNonzeroVShear(Coord eps) const {
}
/** @brief Check whether this matrix represents zooming.
- * Zooming is any combination of translation and uniform scaling. It preserves angles, ratios
- * of distances between arbitrary points and unit vectors of line segments.
+ * Zooming is any combination of translation and uniform non-flipping scaling.
+ * It preserves angles, ratios of distances between arbitrary points
+ * and unit vectors of line segments.
* @param eps Numerical tolerance
- * @return True iff the matrix is of the form
+ * @return True iff the matrix is invertible and of the form
* \f$\left[\begin{array}{ccc}
a & 0 & 0 \\
0 & a & 0 \\
b & c & 1 \end{array}\right]\f$. */
bool Affine::isZoom(Coord eps) const {
+ if (isSingular(eps)) return false;
return are_near(_c[0], _c[3], eps) && are_near(_c[1], 0, eps) && are_near(_c[2], 0, eps);
}
@@ -296,30 +300,42 @@ bool Affine::preservesArea(Coord eps) const
}
/** @brief Check whether the transformation preserves angles between lines.
- * This means that the transformation can be any combination of translation, uniform scaling
- * and rotation.
+ * This means that the transformation can be any combination of translation, uniform scaling,
+ * rotation and flipping.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
- a & b & 0 \\
- -b & a & 0 \\
- c & d & 1 \end{array}\right]\f$. */
+ a & b & 0 \\
+ -b & a & 0 \\
+ c & d & 1 \end{array}\right]\f$ or
+ \f$\left[\begin{array}{ccc}
+ -a & b & 0 \\
+ b & a & 0 \\
+ c & d & 1 \end{array}\right]\f$. */
bool Affine::preservesAngles(Coord eps) const
{
- return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps);
+ if (isSingular(eps)) return false;
+ return (are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) ||
+ (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps));
}
/** @brief Check whether the transformation preserves distances between points.
- * This means that the transformation can be any combination of translation and rotation.
+ * This means that the transformation can be any combination of translation,
+ * rotation and flipping.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
* \f$\left[\begin{array}{ccc}
a & b & 0 \\
-b & a & 0 \\
+ c & d & 1 \end{array}\right]\f$ or
+ \f$\left[\begin{array}{ccc}
+ -a & b & 0 \\
+ b & a & 0 \\
c & d & 1 \end{array}\right]\f$ and \f$a^2 + b^2 = 1\f$. */
bool Affine::preservesDistances(Coord eps) const
{
- return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
+ return ((are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) ||
+ (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps))) &&
are_near(_c[0] * _c[0] + _c[1] * _c[1], 1.0, eps);
}
@@ -387,10 +403,10 @@ Coord Affine::descrim2() const {
}
/** @brief Calculate the descriminant.
- * If the matrix doesn't contain a non-uniform scaling or shearing component, this value says
- * how will the length any line segment change after applying this transformation
- * to arbitrary objects on a plane (the new length will be
- * @code line_seg.length() * m.descrim()) @endcode.
+ * If the matrix doesn't contain a shearing or non-uniform scaling component, this value says
+ * how will the length of any line segment change after applying this transformation
+ * to arbitrary objects on a plane. The new length will be
+ * @code line_seg.length() * m.descrim()) @endcode
* @return \f$\sqrt{|\det A|}\f$. */
Coord Affine::descrim() const {
return sqrt(descrim2());
@@ -416,6 +432,9 @@ Affine &Affine::operator*=(Affine const &o) {
}
//TODO: What's this!?!
+/** Given a matrix m such that unit_circle = m*x, this returns the
+ * quadratic form x*A*x = 1.
+ * @relates Affine */
Affine elliptic_quadratic_form(Affine const &m) {
double od = m[0] * m[1] + m[2] * m[3];
Affine ret (m[0]*m[0] + m[1]*m[1], od,
@@ -475,6 +494,15 @@ Eigen::Eigen(double m[2][2]) {
vectors[i] = Point(0,0);
}
+/** @brief Nearness predicate for affine transforms
+ * @returns True if all entries of matrices are within eps of each other */
+bool are_near(Affine const &a, Affine const &b, Coord eps)
+{
+ return are_near(a[0], b[0], eps) && are_near(a[1], b[1], eps) &&
+ are_near(a[2], b[2], eps) && are_near(a[3], b[3], eps) &&
+ are_near(a[4], b[4], eps) && are_near(a[5], b[5], eps);
+}
+
} //namespace Geom
/*