From 3e2e4bb6dc7b919c0fc2a56b3e9eed9e30cfabed Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Mon, 25 Jul 2011 01:24:28 +0200 Subject: Update 2Geom to fix serious IntRect bug I've found in my GSoC branch (bzr r10502) --- src/2geom/2geom.h | 75 +++++++++++++++++++++++++++++++ src/2geom/affine.h | 4 +- src/2geom/coord.h | 6 --- src/2geom/forward.h | 1 + src/2geom/generic-interval.h | 4 +- src/2geom/generic-rect.h | 44 +++++++++++------- src/2geom/int-point.h | 5 +++ src/2geom/int-rect.h | 1 - src/2geom/interval.h | 15 ++++++- src/2geom/path-intersection.cpp | 6 ++- src/2geom/point.cpp | 4 +- src/2geom/point.h | 15 ++++--- src/2geom/rect.h | 30 +++---------- src/2geom/solve-bezier-parametric.cpp | 12 ++--- src/2geom/solver.h | 4 +- src/2geom/transforms.cpp | 41 ++++++++++++++--- src/2geom/transforms.h | 85 ++++++++++++++++++++++++++++------- 17 files changed, 258 insertions(+), 94 deletions(-) create mode 100644 src/2geom/2geom.h (limited to 'src') diff --git a/src/2geom/2geom.h b/src/2geom/2geom.h new file mode 100644 index 000000000..000f3423d --- /dev/null +++ b/src/2geom/2geom.h @@ -0,0 +1,75 @@ +/** + * \file + * \brief Include everything + *//* + * Authors: + * Krzysztof KosiƄski + * + * Copyright 2011 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 SEEN_LIB2GEOM_2GEOM_H +#define SEEN_LIB2GEOM_2GEOM_H + +#include <2geom/forward.h> + +// primitives +#include <2geom/coord.h> +#include <2geom/point.h> +#include <2geom/interval.h> +#include <2geom/rect.h> +#include <2geom/angle.h> +#include <2geom/ray.h> +#include <2geom/line.h> +#include <2geom/affine.h> +#include <2geom/transforms.h> + +// curves and paths +#include <2geom/curves.h> +#include <2geom/path.h> +#include <2geom/pathvector.h> + +// fragments +#include <2geom/d2.h> +#include <2geom/linear.h> +#include <2geom/bezier.h> +#include <2geom/sbasis.h> + +// others +#include <2geom/math-utils.h> +#include <2geom/utils.h> + +#endif // SEEN_LIB2GEOM_HEADER_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:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/affine.h b/src/2geom/affine.h index b07fba0f7..d7a7a0692 100644 --- a/src/2geom/affine.h +++ b/src/2geom/affine.h @@ -65,7 +65,8 @@ class Affine , MultipliableNoncommutative< Affine, Rotate , MultipliableNoncommutative< Affine, HShear , MultipliableNoncommutative< Affine, VShear - > > > > > > > + , MultipliableNoncommutative< Affine, Zoom + > > > > > > > > { Coord _c[6]; public: @@ -113,6 +114,7 @@ public: 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 { diff --git a/src/2geom/coord.h b/src/2geom/coord.h index c7bbcdcd4..f7bf2c5d0 100644 --- a/src/2geom/coord.h +++ b/src/2geom/coord.h @@ -69,9 +69,6 @@ struct CoordTraits { typedef OptIntInterval OptIntervalType; typedef IntRect RectType; typedef OptIntRect OptRectType; - inline static bool contains(IntCoord low, IntCoord high, IntCoord testlow, IntCoord testhigh) { - return low <= testlow && testhigh < high; - } }; template<> @@ -81,9 +78,6 @@ struct CoordTraits { typedef OptInterval OptIntervalType; typedef Rect RectType; typedef OptRect OptRectType; - inline static bool contains(Coord low, Coord high, Coord testlow, Coord testhigh) { - return low <= testlow && testhigh <= high; - } }; } // end namespace Geom diff --git a/src/2geom/forward.h b/src/2geom/forward.h index b1cad6f1f..0dbd9fa94 100644 --- a/src/2geom/forward.h +++ b/src/2geom/forward.h @@ -97,6 +97,7 @@ class Rotate; class Scale; class HShear; class VShear; +class Zoom; // templates template class D2; diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h index d719c16c8..a32e97d4b 100644 --- a/src/2geom/generic-interval.h +++ b/src/2geom/generic-interval.h @@ -106,11 +106,11 @@ public: /// @{ /** @brief Check whether the interval includes this number. */ bool contains(C val) const { - return CoordTraits::contains(min(), max(), val, val); + return min() <= val && val <= max(); } /** @brief Check whether the interval includes the given interval. */ bool contains(Self const &val) const { - return CoordTraits::contains(min(), max(), val.min(), val.max()); + return min() <= val.min() && val.max() <= max(); } /** @brief Check whether the intervals have any common elements. */ bool intersects(Self const &val) const { diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index d60c4bb0f..2db30dfa9 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -40,6 +40,7 @@ #ifndef LIB2GEOM_SEEN_GENERIC_RECT_H #define LIB2GEOM_SEEN_GENERIC_RECT_H +#include #include namespace Geom { @@ -53,10 +54,10 @@ class GenericOptRect; */ template class GenericRect - : boost::additive< GenericRect, typename CoordTraits::PointType - , boost::equality_comparable< GenericRect - , boost::orable< GenericRect - , boost::orable< GenericRect, typename CoordTraits::OptRectType + : boost::additive< typename CoordTraits::RectType, typename CoordTraits::PointType + , boost::equality_comparable< typename CoordTraits::RectType + , boost::orable< typename CoordTraits::RectType + , boost::orable< typename CoordTraits::RectType, typename CoordTraits::OptRectType > > > > { typedef typename CoordTraits::IntervalType CInterval; @@ -93,30 +94,37 @@ public: * @param end End of the range * @return Rectangle that contains all points from [start, end). */ template - static GenericRect from_range(InputIterator start, InputIterator end) { + static CRect from_range(InputIterator start, InputIterator end) { assert(start != end); CPoint p1 = *start++; - GenericRect result(p1, p1); + CRect result(p1, p1); for (; start != end; ++start) { result.expandTo(*start); } return result; } /** @brief Create a rectangle from a C-style array of points it should contain. */ - static GenericRect from_array(CPoint const *c, unsigned n) { - GenericRect result = GenericRect::from_range(c, c+n); + static CRect from_array(CPoint const *c, unsigned n) { + CRect result = GenericRect::from_range(c, c+n); return result; } /** @brief Create rectangle from origin and dimensions. */ - static GenericRect from_xywh(C x, C y, C w, C h) { + static CRect from_xywh(C x, C y, C w, C h) { CPoint xy(x, y); CPoint wh(w, h); - GenericRect result(xy, xy + wh); + CRect result(xy, xy + wh); return result; } /** @brief Create rectangle from origin and dimensions. */ - static GenericRect from_xywh(CPoint const &xy, CPoint const &wh) { - GenericRect result(xy, xy + wh); + static CRect from_xywh(CPoint const &xy, CPoint const &wh) { + CRect result(xy, xy + wh); + return result; + } + /// Create infinite rectangle. + static CRect infinite() { + CPoint p0(std::numeric_limits::min(), std::numeric_limits::min()); + CPoint p1(std::numeric_limits::max(), std::numeric_limits::max()); + CRect result(p0, p1); return result; } /// @} @@ -155,6 +163,8 @@ public: C width() const { return f[X].extent(); } /** @brief Get the vertical extent of the rectangle. */ C height() const { return f[Y].extent(); } + /** @brief Get the ratio of width to height of the rectangle. */ + Coord aspectRatio() const { return Coord(width()) / Coord(height()); } /** @brief Get rectangle's width and height as a point. * @return Point with X coordinate corresponding to the width and the Y coordinate @@ -215,7 +225,7 @@ public: f[X].expandTo(p[X]); f[Y].expandTo(p[Y]); } /** @brief Enlarge the rectangle to contain the given rectangle. */ - void unionWith(GenericRect const &b) { + void unionWith(CRect const &b) { f[X].unionWith(b[X]); f[Y].unionWith(b[Y]); } /** @brief Enlarge the rectangle to contain the given rectangle. @@ -255,7 +265,7 @@ public: return *this; } /** @brief Union two rectangles. */ - GenericRect &operator|=(GenericRect const &o) { + GenericRect &operator|=(CRect const &o) { unionWith(o); return *this; } @@ -275,9 +285,9 @@ public: template class GenericOptRect : public boost::optional::RectType> - , boost::orable< GenericOptRect - , boost::andable< GenericOptRect - , boost::andable< GenericOptRect, typename CoordTraits::RectType + , boost::orable< typename CoordTraits::OptRectType + , boost::andable< typename CoordTraits::OptRectType + , boost::andable< typename CoordTraits::OptRectType, typename CoordTraits::RectType > > > { typedef typename CoordTraits::IntervalType CInterval; diff --git a/src/2geom/int-point.h b/src/2geom/int-point.h index cf2fe720f..1a16ecb7a 100644 --- a/src/2geom/int-point.h +++ b/src/2geom/int-point.h @@ -83,6 +83,11 @@ public: } IntCoord operator[](Dim2 d) const { return _pt[d]; } IntCoord &operator[](Dim2 d) { return _pt[d]; } + + IntCoord x() const throw() { return _pt[X]; } + IntCoord &x() throw() { return _pt[X]; } + IntCoord y() const throw() { return _pt[Y]; } + IntCoord &y() throw() { return _pt[Y]; } /// @} /// @name Vector-like arithmetic operations diff --git a/src/2geom/int-rect.h b/src/2geom/int-rect.h index a143b3ac5..567d42da5 100644 --- a/src/2geom/int-rect.h +++ b/src/2geom/int-rect.h @@ -32,7 +32,6 @@ #define LIB2GEOM_SEEN_INT_RECT_H #include <2geom/coord.h> -#include <2geom/int-point.h> #include <2geom/int-interval.h> #include <2geom/generic-rect.h> diff --git a/src/2geom/interval.h b/src/2geom/interval.h index ee6d674d2..e95da4811 100644 --- a/src/2geom/interval.h +++ b/src/2geom/interval.h @@ -64,7 +64,7 @@ typedef GenericOptInterval OptInterval; class Interval : public GenericInterval , boost::multipliable< Interval - , boost::multipliable< Interval, Coord + , boost::multiplicative< Interval, Coord > > { typedef GenericInterval Base; @@ -180,7 +180,20 @@ public: /// @} }; +// functions required for Python bindings +inline Interval unify(Interval const &a, Interval const &b) +{ + Interval r = a | b; + return r; +} +inline OptInterval intersect(Interval const &a, Interval const &b) +{ + OptInterval r = a & b; + return r; } + +} // end namespace Geom + #endif //SEEN_INTERVAL_H /* diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp index 7aa662abb..c38776304 100644 --- a/src/2geom/path-intersection.cpp +++ b/src/2geom/path-intersection.cpp @@ -226,8 +226,8 @@ intersect_polish_f (const gsl_vector * x, void *params, #endif static void -intersect_polish_root (Curve const &A, double &s, - Curve const &B, double &t) { +intersect_polish_root (Curve const &A, double &s, Curve const &B, double &t) +{ std::vector as, bs; as = A.pointAndDerivatives(s, 2); bs = B.pointAndDerivatives(t, 2); @@ -271,6 +271,8 @@ intersect_polish_root (Curve const &A, double &s, } #ifdef HAVE_GSL + int status; + size_t iter = 0; if(0) { // the GSL version is more accurate, but taints this with GPL const size_t n = 2; struct rparams p = {A, B}; diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp index cafc0fdba..3ad9dd1fd 100644 --- a/src/2geom/point.cpp +++ b/src/2geom/point.cpp @@ -49,8 +49,8 @@ namespace Geom { * from the origin (point at 0,0) to the stored coordinates, * and has methods implementing several vector operations (like length()). * - * @par Operator note - * @par + * @section OpNotePoint Operator note + * * Most operators are provided by Boost operator helpers, so they are not visible in this class. * If @a p, @a q, @a r denote points, @a s a floating-point scalar, and @a m a transformation matrix, * then the following operations are available: diff --git a/src/2geom/point.h b/src/2geom/point.h index 69da8a4ae..0eb771874 100644 --- a/src/2geom/point.h +++ b/src/2geom/point.h @@ -58,7 +58,8 @@ class Point , MultipliableNoncommutative< Point, Scale , MultipliableNoncommutative< Point, HShear , MultipliableNoncommutative< Point, VShear - > > > > > > > > > // this uses chaining so it looks weird, but works + , MultipliableNoncommutative< Point, Zoom + > > > > > > > > > > // this uses chaining so it looks weird, but works { Coord _pt[2]; public: @@ -111,6 +112,11 @@ public: Coord operator[](Dim2 d) const throw() { return _pt[d]; } Coord &operator[](Dim2 d) throw() { return _pt[d]; } + + Coord x() const throw() { return _pt[X]; } + Coord &x() throw() { return _pt[X]; } + Coord y() const throw() { return _pt[Y]; } + Coord &y() throw() { return _pt[Y]; } /// @} /// @name Vector operations @@ -172,12 +178,7 @@ public: Point &operator*=(Rotate const &r); Point &operator*=(HShear const &s); Point &operator*=(VShear const &s); - /** @brief Transform the point by the inverse of the specified matrix. */ - template - Point &operator/=(T const &m) { - *this *= m.inverse(); - return *this; - } + Point &operator*=(Zoom const &z); /// @} /// @name Conversion to integer points diff --git a/src/2geom/rect.h b/src/2geom/rect.h index e9f6cbeb7..f7d331523 100644 --- a/src/2geom/rect.h +++ b/src/2geom/rect.h @@ -73,31 +73,7 @@ public: Rect(Point const &a, Point const &b) : Base(a,b) {} Rect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {} Rect(Base const &b) : Base(b) {} - /** @brief Create a rectangle from a range of points. - * The resulting rectangle will contain all ponts from the range. - * The return type of iterators must be convertible to Point. - * The range must not be empty. For possibly empty ranges, see OptRect. - * @param start Beginning of the range - * @param end End of the range - * @return Rectangle that contains all points from [start, end). */ - template - static Rect from_range(InputIterator start, InputIterator end) { - Rect result = Base::from_range(start, end); - return result; - } - /** @brief Create a rectangle from a C-style array of points it should contain. */ - static Rect from_array(Point const *c, unsigned n) { - Rect result = Rect::from_range(c, c+n); - return result; - } - static Rect from_xywh(Coord x, Coord y, Coord w, Coord h) { - Rect result = Base::from_xywh(x, y, w, h); - return result; - } - static Rect from_xywh(Point const &o, Point const &dim) { - Rect result = Base::from_xywh(o, dim); - return result; - } + Rect(IntRect const &ir) : Base(ir.min(), ir.max()) {} /// @} /// @name Inspect dimensions. @@ -114,6 +90,10 @@ public: bool interiorIntersects(Rect const &r) const { return f[X].interiorIntersects(r[X]) && f[Y].interiorIntersects(r[Y]); } + /** @brief Check whether the interior includes the given point. */ + bool interiorContains(Point const &p) const { + return f[X].interiorContains(p[X]) && f[Y].interiorContains(p[Y]); + } /** @brief Check whether the interior includes all points in the given rectangle. * Interior of the rectangle is the entire rectangle without its borders. */ bool interiorContains(Rect const &r) const { diff --git a/src/2geom/solve-bezier-parametric.cpp b/src/2geom/solve-bezier-parametric.cpp index 437f073a3..76cf65e17 100644 --- a/src/2geom/solve-bezier-parametric.cpp +++ b/src/2geom/solve-bezier-parametric.cpp @@ -68,13 +68,13 @@ find_parametric_bezier_roots(Geom::Point const *w, /* The control points */ break; } - // Otherwise, solve recursively after subdividing control polygon - std::vector Left(degree + 1); // New left and right - std::vector Right(degree + 1); // control polygons - Bezier(w, degree, 0.5, &Left[0], &Right[0]); + /* Otherwise, solve recursively after subdividing control polygon */ + Geom::Point Left[degree+1], /* New left and right */ + Right[degree+1]; /* control polygons */ + Bezier(w, degree, 0.5, Left, Right); total_subs ++; - find_parametric_bezier_roots(&Left[0], degree, solutions, depth + 1); - find_parametric_bezier_roots(&Right[0], degree, solutions, depth + 1); + find_parametric_bezier_roots(Left, degree, solutions, depth+1); + find_parametric_bezier_roots(Right, degree, solutions, depth+1); } diff --git a/src/2geom/solver.h b/src/2geom/solver.h index 5e77f13dc..793939b2a 100644 --- a/src/2geom/solver.h +++ b/src/2geom/solver.h @@ -1,7 +1,7 @@ /** * \file - * \brief \todo brief description - * + * \brief Finding roots of Bernstein-Bezier polynomials + *//* * Authors: * ? * diff --git a/src/2geom/transforms.cpp b/src/2geom/transforms.cpp index 2658719c4..b8355cadc 100644 --- a/src/2geom/transforms.cpp +++ b/src/2geom/transforms.cpp @@ -35,9 +35,21 @@ #include #include <2geom/point.h> #include <2geom/transforms.h> +#include <2geom/rect.h> namespace Geom { +/** @brief Zoom between rectangles. + * Given two rectangles, compute a zoom that maps one to the other. + * Rectangles are assumed to have the same aspect ratio. */ +Zoom Zoom::map_rect(Rect const &old_r, Rect const &new_r) +{ + Zoom ret; + ret._scale = new_r.width() / old_r.width(); + ret._trans = new_r.min() - old_r.min(); + return ret; +} + // Point transformation methods. Point &Point::operator*=(Translate const &t) { @@ -68,6 +80,14 @@ Point &Point::operator*=(VShear const &v) _pt[Y] += v.f * _pt[Y]; return *this; } +Point &Point::operator*=(Zoom const &z) +{ + _pt[X] += z._trans[X]; + _pt[Y] += z._trans[Y]; + _pt[X] *= z._scale; + _pt[Y] *= z._scale; + return *this; +} // Affine multiplication methods. @@ -110,6 +130,14 @@ Affine &Affine::operator*=(VShear const &v) { return *this; } +Affine &Affine::operator*=(Zoom const &z) { + _c[0] *= z._scale; _c[1] *= z._scale; + _c[2] *= z._scale; _c[3] *= z._scale; + _c[4] += z._trans[X]; _c[5] += z._trans[Y]; + _c[4] *= z._scale; _c[5] *= z._scale; + return *this; +} + // this checks whether the requirements of TransformConcept are satisfied for all transforms. // if you add a new transform type, include it here! void check_transforms() @@ -120,6 +148,7 @@ void check_transforms() BOOST_CONCEPT_ASSERT((TransformConcept)); BOOST_CONCEPT_ASSERT((TransformConcept)); BOOST_CONCEPT_ASSERT((TransformConcept)); + BOOST_CONCEPT_ASSERT((TransformConcept)); BOOST_CONCEPT_ASSERT((TransformConcept)); // Affine is also a transform #endif @@ -130,14 +159,16 @@ void check_transforms() Rotate r(Rotate::identity()); HShear h(HShear::identity()); VShear v(VShear::identity()); + Zoom z(Zoom::identity()); // notice that the first column is always the same and enumerates all transform types, // while the second one changes to each transform type in turn. - m = t * t; m = t * s; m = t * r; m = t * h; m = t * v; - m = s * t; m = s * s; m = s * r; m = s * h; m = s * v; - m = r * t; m = r * s; m = r * r; m = r * h; m = r * v; - m = h * t; m = h * s; m = h * r; m = h * h; m = h * v; - m = v * t; m = v * s; m = v * r; m = v * h; m = v * v; + m = t * t; m = t * s; m = t * r; m = t * h; m = t * v; m = t * z; + m = s * t; m = s * s; m = s * r; m = s * h; m = s * v; m = s * z; + m = r * t; m = r * s; m = r * r; m = r * h; m = r * v; m = r * z; + m = h * t; m = h * s; m = h * r; m = h * h; m = h * v; m = h * z; + m = v * t; m = v * s; m = v * r; m = v * h; m = v * v; m = v * z; + m = z * t; m = z * s; m = z * r; m = z * h; m = z * v; m = z * z; } } diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h index 9623bed26..5627e8b6f 100644 --- a/src/2geom/transforms.h +++ b/src/2geom/transforms.h @@ -106,13 +106,14 @@ T pow(T const &t, int n) { class Translate : public TransformOperations< Translate > { - Translate() : vec(0, 0) {} Point vec; public: - /** @brief Construct a translation from its vector. */ - explicit Translate(Point const &p) : vec(p) {} - /** @brief Construct a translation from its coordinates. */ - explicit Translate(Coord x, Coord y) : vec(x, y) {} + /// 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]; } @@ -120,9 +121,10 @@ public: Translate &operator*=(Translate const &o) { vec += o.vec; return *this; } bool operator==(Translate const &o) const { return vec == o.vec; } - /** @brief Get the inverse translation. */ + Point vector() const { return vec; } + /// Get the inverse translation. Translate inverse() const { return Translate(-vec); } - /** @brief Get a translation that doesn't do anything. */ + /// Get a translation that doesn't do anything. static Translate identity() { Translate ret; return ret; } friend class Point; @@ -136,10 +138,14 @@ class Scale : public TransformOperations< Scale > { Point vec; - Scale() : vec(1, 1) {} 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; } @@ -150,6 +156,8 @@ public: 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; } @@ -162,15 +170,16 @@ public: class Rotate : public TransformOperations< Rotate > { - Rotate() : vec(1, 0) {} - Point vec; + 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)) {} - /** @brief Construct a rotation from its characteristic vector. */ + /// Construct a rotation from its characteristic vector. explicit Rotate(Point const &p) : vec(unit_vector(p)) {} - /** @brief Construct a rotation from the coordinates of its characteristic vector. */ + /// 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; } @@ -186,10 +195,10 @@ public: r.vec = Point(vec[X], -vec[Y]); return r; } - /** @brief Get a 0-degree rotation. */ + /// @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 counter-clockwise rotations (if Y grows upwards). */ + * 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); @@ -213,8 +222,8 @@ public: void setFactor(Coord nf) { f = nf; } S &operator*=(S const &s) { f += s.f; return static_cast(*this); } bool operator==(S const &s) const { return f == s.f; } - S inverse() const { return S(-f); } - static S identity() { return S(0); } + S inverse() const { S ret(-f); return ret; } + static S identity() { S ret(0); return ret; } friend class Point; friend class Affine; @@ -244,6 +253,48 @@ public: operator Affine() const { Affine ret(1, f, 0, 1, 0, 0); return ret; } }; +/** @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 Transform */ +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; +}; + /** @brief Specialization of exponentiation for Scale. * @relates Scale */ template<> @@ -259,7 +310,7 @@ inline Translate pow(Translate const &t, int n) { return ret; } -//TODO: matrix to trans/scale/rotate +//TODO: decomposition of Affine into some finite combination of the above classes } // end namespace Geom -- cgit v1.2.3