summaryrefslogtreecommitdiffstats
path: root/include/2geom/transforms.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/2geom/transforms.h370
1 files changed, 370 insertions, 0 deletions
diff --git a/include/2geom/transforms.h b/include/2geom/transforms.h
new file mode 100644
index 0000000..cc55e29
--- /dev/null
+++ b/include/2geom/transforms.h
@@ -0,0 +1,370 @@
+/**
+ * @file
+ * @brief Affine transformation classes
+ *//*
+ * Authors:
+ * ? <?@?.?>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ * Johan Engelen
+ *
+ * Copyright ?-2012 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_TRANSFORMS_H
+#define LIB2GEOM_SEEN_TRANSFORMS_H
+
+#include <cmath>
+#include <2geom/forward.h>
+#include <2geom/affine.h>
+#include <2geom/angle.h>
+#include <boost/concept/assert.hpp>
+
+namespace Geom {
+
+/** @brief Type requirements for transforms.
+ * @ingroup Concepts */
+template <typename T>
+struct TransformConcept {
+ T t, t2;
+ Affine m;
+ Point p;
+ bool bool_;
+ Coord epsilon;
+ void constraints() {
+ m = t; //implicit conversion
+ m *= t;
+ m = m * t;
+ m = t * m;
+ p *= t;
+ p = p * t;
+ t *= t;
+ t = t * t;
+ t = pow(t, 3);
+ bool_ = (t == t);
+ bool_ = (t != t);
+ t = T::identity();
+ t = t.inverse();
+ bool_ = are_near(t, t2);
+ bool_ = are_near(t, t2, epsilon);
+ }
+};
+
+/** @brief Base template for transforms.
+ * This class is an implementation detail and should not be used directly. */
+template <typename T>
+class TransformOperations
+ : boost::equality_comparable< T
+ , boost::multipliable< T
+ > >
+{
+public:
+ template <typename T2>
+ Affine operator*(T2 const &t) const {
+ Affine ret(*static_cast<T const*>(this)); ret *= t; return ret;
+ }
+};
+
+/** @brief Integer exponentiation for transforms.
+ * Negative exponents will yield the corresponding power of the inverse. This function
+ * can also be applied to matrices.
+ * @param t Affine or transform to exponantiate
+ * @param n Exponent
+ * @return \f$A^n\f$ if @a n is positive, \f$(A^{-1})^n\f$ if negative, identity if zero.
+ * @ingroup Transforms */
+template <typename T>
+T pow(T const &t, int n) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ if (n == 0) return T::identity();
+ T result(T::identity());
+ T x(n < 0 ? t.inverse() : t);
+ if (n < 0) n = -n;
+ while ( n ) { // binary exponentiation - fast
+ if ( n & 1 ) { result *= x; --n; }
+ x *= x; n /= 2;
+ }
+ return result;
+}
+
+/** @brief Translation by a vector.
+ * @ingroup Transforms */
+class Translate
+ : public TransformOperations< Translate >
+{
+ Point vec;
+public:
+ /// Create a translation that doesn't do anything.
+ Translate() : vec(0, 0) {}
+ /// Construct a translation from its vector.
+ Translate(Point const &p) : vec(p) {}
+ /// Construct a translation from its coordinates.
+ Translate(Coord x, Coord y) : vec(x, y) {}
+
+ operator Affine() const { Affine ret(1, 0, 0, 1, vec[X], vec[Y]); return ret; }
+ Coord operator[](Dim2 dim) const { return vec[dim]; }
+ Coord operator[](unsigned dim) const { return vec[dim]; }
+ Translate &operator*=(Translate const &o) { vec += o.vec; return *this; }
+ bool operator==(Translate const &o) const { return vec == o.vec; }
+
+ Point vector() const { return vec; }
+ /// Get the inverse translation.
+ Translate inverse() const { return Translate(-vec); }
+ /// Get a translation that doesn't do anything.
+ static Translate identity() { Translate ret; return ret; }
+
+ friend class Point;
+};
+
+inline bool are_near(Translate const &a, Translate const &b, Coord eps=EPSILON) {
+ return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Scaling from the origin.
+ * During scaling, the point (0,0) will not move. To obtain a scale with a different
+ * invariant point, combine with translation to the origin and back.
+ * @ingroup Transforms */
+class Scale
+ : public TransformOperations< Scale >
+{
+ Point vec;
+public:
+ /// Create a scaling that doesn't do anything.
+ Scale() : vec(1, 1) {}
+ /// Create a scaling from two scaling factors given as coordinates of a point.
+ explicit Scale(Point const &p) : vec(p) {}
+ /// Create a scaling from two scaling factors.
+ Scale(Coord x, Coord y) : vec(x, y) {}
+ /// Create an uniform scaling from a single scaling factor.
+ explicit Scale(Coord s) : vec(s, s) {}
+ inline operator Affine() const { Affine ret(vec[X], 0, 0, vec[Y], 0, 0); return ret; }
+
+ Coord operator[](Dim2 d) const { return vec[d]; }
+ Coord operator[](unsigned d) const { return vec[d]; }
+ //TODO: should we keep these mutators? add them to the other transforms?
+ Coord &operator[](Dim2 d) { return vec[d]; }
+ Coord &operator[](unsigned d) { return vec[d]; }
+ Scale &operator*=(Scale const &b) { vec[X] *= b[X]; vec[Y] *= b[Y]; return *this; }
+ bool operator==(Scale const &o) const { return vec == o.vec; }
+
+ Point vector() const { return vec; }
+ Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
+ static Scale identity() { Scale ret; return ret; }
+
+ friend class Point;
+};
+
+inline bool are_near(Scale const &a, Scale const &b, Coord eps=EPSILON) {
+ return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Rotation around the origin.
+ * Combine with translations to the origin and back to get a rotation around a different point.
+ * @ingroup Transforms */
+class Rotate
+ : public TransformOperations< Rotate >
+{
+ Point vec; ///< @todo Convert to storing the angle, as it's more space-efficient.
+public:
+ /// Construct a zero-degree rotation.
+ Rotate() : vec(1, 0) {}
+ /** @brief Construct a rotation from its angle in radians.
+ * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */
+ explicit Rotate(Coord theta) : vec(Point::polar(theta)) {}
+ /// Construct a rotation from its characteristic vector.
+ explicit Rotate(Point const &p) : vec(unit_vector(p)) {}
+ /// Construct a rotation from the coordinates of its characteristic vector.
+ explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); }
+ operator Affine() const { Affine ret(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); return ret; }
+
+ /** @brief Get the characteristic vector of the rotation.
+ * @return A vector that would be obtained by applying this transform to the X versor. */
+ Point vector() const { return vec; }
+ Coord angle() const { return atan2(vec); }
+ Coord operator[](Dim2 dim) const { return vec[dim]; }
+ Coord operator[](unsigned dim) const { return vec[dim]; }
+ Rotate &operator*=(Rotate const &o) { vec *= o; return *this; }
+ bool operator==(Rotate const &o) const { return vec == o.vec; }
+ Rotate inverse() const {
+ Rotate r;
+ r.vec = Point(vec[X], -vec[Y]);
+ return r;
+ }
+ /// @brief Get a zero-degree rotation.
+ static Rotate identity() { Rotate ret; return ret; }
+ /** @brief Construct a rotation from its angle in degrees.
+ * Positive arguments correspond to clockwise rotations if Y grows downwards. */
+ static Rotate from_degrees(Coord deg) {
+ Coord rad = (deg / 180.0) * M_PI;
+ return Rotate(rad);
+ }
+ static Affine around(Point const &p, Coord angle);
+
+ friend class Point;
+};
+
+inline bool are_near(Rotate const &a, Rotate const &b, Coord eps=EPSILON) {
+ return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Common base for shearing transforms.
+ * This class is an implementation detail and should not be used directly.
+ * @ingroup Transforms */
+template <typename S>
+class ShearBase
+ : public TransformOperations< S >
+{
+protected:
+ Coord f;
+ ShearBase(Coord _f) : f(_f) {}
+public:
+ Coord factor() const { return f; }
+ void setFactor(Coord nf) { f = nf; }
+ S &operator*=(S const &s) { f += s.f; return static_cast<S &>(*this); }
+ bool operator==(S const &s) const { return f == s.f; }
+ S inverse() const { S ret(-f); return ret; }
+ static S identity() { S ret(0); return ret; }
+
+ friend class Point;
+ friend class Affine;
+};
+
+/** @brief Horizontal shearing.
+ * Points on the X axis will not move. Combine with translations to get a shear
+ * with a different invariant line.
+ * @ingroup Transforms */
+class HShear
+ : public ShearBase<HShear>
+{
+public:
+ explicit HShear(Coord h) : ShearBase<HShear>(h) {}
+ operator Affine() const { Affine ret(1, 0, f, 1, 0, 0); return ret; }
+};
+
+inline bool are_near(HShear const &a, HShear const &b, Coord eps=EPSILON) {
+ return are_near(a.factor(), b.factor(), eps);
+}
+
+/** @brief Vertical shearing.
+ * Points on the Y axis will not move. Combine with translations to get a shear
+ * with a different invariant line.
+ * @ingroup Transforms */
+class VShear
+ : public ShearBase<VShear>
+{
+public:
+ explicit VShear(Coord h) : ShearBase<VShear>(h) {}
+ operator Affine() const { Affine ret(1, f, 0, 1, 0, 0); return ret; }
+};
+
+inline bool are_near(VShear const &a, VShear const &b, Coord eps=EPSILON) {
+ return are_near(a.factor(), b.factor(), eps);
+}
+
+/** @brief Combination of a translation and uniform scale.
+ * The translation part is applied first, then the result is scaled from the new origin.
+ * This way when the class is used to accumulate a zoom transform, trans always points
+ * to the new origin in original coordinates.
+ * @ingroup Transforms */
+class Zoom
+ : public TransformOperations< Zoom >
+{
+ Coord _scale;
+ Point _trans;
+ Zoom() : _scale(1), _trans() {}
+public:
+ /// Construct a zoom from a scaling factor.
+ explicit Zoom(Coord s) : _scale(s), _trans() {}
+ /// Construct a zoom from a translation.
+ explicit Zoom(Translate const &t) : _scale(1), _trans(t.vector()) {}
+ /// Construct a zoom from a scaling factor and a translation.
+ Zoom(Coord s, Translate const &t) : _scale(s), _trans(t.vector()) {}
+
+ operator Affine() const {
+ Affine ret(_scale, 0, 0, _scale, _trans[X] * _scale, _trans[Y] * _scale);
+ return ret;
+ }
+ Zoom &operator*=(Zoom const &z) {
+ _trans += z._trans / _scale;
+ _scale *= z._scale;
+ return *this;
+ }
+ bool operator==(Zoom const &z) const { return _scale == z._scale && _trans == z._trans; }
+
+ Coord scale() const { return _scale; }
+ void setScale(Coord s) { _scale = s; }
+ Point translation() const { return _trans; }
+ void setTranslation(Point const &p) { _trans = p; }
+ Zoom inverse() const { Zoom ret(1/_scale, Translate(-_trans*_scale)); return ret; }
+ static Zoom identity() { Zoom ret(1.0); return ret; }
+ static Zoom map_rect(Rect const &old_r, Rect const &new_r);
+
+ friend class Point;
+ friend class Affine;
+};
+
+inline bool are_near(Zoom const &a, Zoom const &b, Coord eps=EPSILON) {
+ return are_near(a.scale(), b.scale(), eps) &&
+ are_near(a.translation(), b.translation(), eps);
+}
+
+/** @brief Specialization of exponentiation for Scale.
+ * @relates Scale */
+template<>
+inline Scale pow(Scale const &s, int n) {
+ Scale ret(::pow(s[X], n), ::pow(s[Y], n));
+ return ret;
+}
+/** @brief Specialization of exponentiation for Translate.
+ * @relates Translate */
+template<>
+inline Translate pow(Translate const &t, int n) {
+ Translate ret(t[X] * n, t[Y] * n);
+ return ret;
+}
+
+
+/** @brief Reflects objects about line.
+ * The line, defined by a vector along the line and a point on it, acts as a mirror.
+ * @ingroup Transforms
+ * @see Line::reflection()
+ */
+Affine reflection(Point const & vector, Point const & origin);
+
+//TODO: decomposition of Affine into some finite combination of the above classes
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_TRANSFORMS_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 :