GCC Code Coverage Report


Directory: ./
File: include/2geom/transforms.h
Date: 2024-03-18 17:01:34
Exec Total Coverage
Lines: 21 54 38.9%
Functions: 18 80 22.5%
Branches: 0 6 0.0%

Line Branch Exec Source
1 /**
2 * @file
3 * @brief Affine transformation classes
4 *//*
5 * Authors:
6 * ? <?@?.?>
7 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
8 * Johan Engelen
9 *
10 * Copyright ?-2012 Authors
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it either under the terms of the GNU Lesser General Public
14 * License version 2.1 as published by the Free Software Foundation
15 * (the "LGPL") or, at your option, under the terms of the Mozilla
16 * Public License Version 1.1 (the "MPL"). If you do not alter this
17 * notice, a recipient may use your version of this file under either
18 * the MPL or the LGPL.
19 *
20 * You should have received a copy of the LGPL along with this library
21 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * You should have received a copy of the MPL along with this library
24 * in the file COPYING-MPL-1.1
25 *
26 * The contents of this file are subject to the Mozilla Public License
27 * Version 1.1 (the "License"); you may not use this file except in
28 * compliance with the License. You may obtain a copy of the License at
29 * http://www.mozilla.org/MPL/
30 *
31 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
32 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
33 * the specific language governing rights and limitations.
34 */
35
36 #ifndef LIB2GEOM_SEEN_TRANSFORMS_H
37 #define LIB2GEOM_SEEN_TRANSFORMS_H
38
39 #include <cmath>
40 #include <2geom/forward.h>
41 #include <2geom/affine.h>
42 #include <2geom/angle.h>
43 #include <boost/concept/assert.hpp>
44
45 namespace Geom {
46
47 /** @brief Type requirements for transforms.
48 * @ingroup Concepts */
49 template <typename T>
50 struct TransformConcept {
51 T t, t2;
52 Affine m;
53 Point p;
54 bool bool_;
55 Coord epsilon;
56 void constraints() {
57 m = t; //implicit conversion
58 m *= t;
59 m = m * t;
60 m = t * m;
61 p *= t;
62 p = p * t;
63 t *= t;
64 t = t * t;
65 t = pow(t, 3);
66 bool_ = (t == t);
67 bool_ = (t != t);
68 t = T::identity();
69 t = t.inverse();
70 bool_ = are_near(t, t2);
71 bool_ = are_near(t, t2, epsilon);
72 }
73 };
74
75 /** @brief Base template for transforms.
76 * This class is an implementation detail and should not be used directly. */
77 template <typename T>
78 class TransformOperations
79 : boost::equality_comparable< T
80 , boost::multipliable< T
81 > >
82 {
83 public:
84 template <typename T2>
85 803786 Affine operator*(T2 const &t) const {
86 803786 Affine ret(*static_cast<T const*>(this)); ret *= t; return ret;
87 }
88 };
89
90 /** @brief Integer exponentiation for transforms.
91 * Negative exponents will yield the corresponding power of the inverse. This function
92 * can also be applied to matrices.
93 * @param t Affine or transform to exponantiate
94 * @param n Exponent
95 * @return \f$A^n\f$ if @a n is positive, \f$(A^{-1})^n\f$ if negative, identity if zero.
96 * @ingroup Transforms */
97 template <typename T>
98 T pow(T const &t, int n) {
99 BOOST_CONCEPT_ASSERT((TransformConcept<T>));
100 if (n == 0) return T::identity();
101 T result(T::identity());
102 T x(n < 0 ? t.inverse() : t);
103 if (n < 0) n = -n;
104 while ( n ) { // binary exponentiation - fast
105 if ( n & 1 ) { result *= x; --n; }
106 x *= x; n /= 2;
107 }
108 return result;
109 }
110
111 /** @brief Translation by a vector.
112 * @ingroup Transforms */
113 class Translate
114 : public TransformOperations< Translate >
115 {
116 Point vec;
117 public:
118 /// Create a translation that doesn't do anything.
119 Translate() = default;
120 /// Construct a translation from its vector.
121 80637 explicit Translate(Point const &p) : vec(p) {}
122 /// Construct a translation from its coordinates.
123 Translate(Coord x, Coord y) : vec(x, y) {}
124
125 71634 operator Affine() const { return Affine(1, 0, 0, 1, vec[X], vec[Y]); }
126 4356054 Coord operator[](Dim2 dim) const { return vec[dim]; }
127 Coord operator[](unsigned dim) const { return vec[dim]; }
128 Translate &operator*=(Translate const &o) { vec += o.vec; return *this; }
129 bool operator==(Translate const &o) const { return vec == o.vec; }
130
131 1 Point vector() const { return vec; }
132 /// Get the inverse translation.
133 Translate inverse() const { return Translate(-vec); }
134 /// Get a translation that doesn't do anything.
135 static Translate identity() { return {}; }
136
137 friend class Point;
138 };
139
140 inline bool are_near(Translate const &a, Translate const &b, Coord eps = EPSILON) {
141 return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
142 }
143
144 /** @brief Scaling from the origin.
145 * During scaling, the point (0,0) will not move. To obtain a scale with a different
146 * invariant point, combine with translation to the origin and back.
147 * @ingroup Transforms */
148 class Scale
149 : public TransformOperations< Scale >
150 {
151 Point vec = { 1, 1 };
152 public:
153 /// Create a scaling that doesn't do anything.
154 Scale() = default;
155 /// Create a scaling from two scaling factors given as coordinates of a point.
156 explicit Scale(Point const &p) : vec(p) {}
157 /// Create a scaling from two scaling factors.
158 80079 Scale(Coord x, Coord y) : vec(x, y) {}
159 /// Create an uniform scaling from a single scaling factor.
160 100 explicit Scale(Coord s) : vec(s, s) {}
161 104385 inline operator Affine() const { return Affine(vec[X], 0, 0, vec[Y], 0, 0); }
162
163 61866 Coord operator[](Dim2 d) const { return vec[d]; }
164 Coord operator[](unsigned d) const { return vec[d]; }
165 //TODO: should we keep these mutators? add them to the other transforms?
166 Coord &operator[](Dim2 d) { return vec[d]; }
167 Coord &operator[](unsigned d) { return vec[d]; }
168 Scale &operator*=(Scale const &b) { vec[X] *= b[X]; vec[Y] *= b[Y]; return *this; }
169 bool operator==(Scale const &o) const { return vec == o.vec; }
170
171 Point vector() const { return vec; }
172 Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
173 static Scale identity() { return {}; }
174
175 friend class Point;
176 };
177
178 inline bool are_near(Scale const &a, Scale const &b, Coord eps=EPSILON) {
179 return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
180 }
181
182 /** @brief Rotation around the origin.
183 * Combine with translations to the origin and back to get a rotation around a different point.
184 * @ingroup Transforms */
185 class Rotate
186 : public TransformOperations< Rotate >
187 {
188 Point vec = { 1, 0 };
189 public:
190 /// Construct a zero-degree rotation.
191 2 Rotate() = default;
192 /** @brief Construct a rotation from its angle in radians.
193 * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */
194 322 explicit Rotate(Coord theta) : vec(Point::polar(theta)) {}
195 /// Construct a rotation from its characteristic vector.
196 20124 explicit Rotate(Point const &p) : vec(p.normalized()) {}
197 /// Construct a rotation from the coordinates of its characteristic vector.
198 explicit Rotate(Coord x, Coord y) : Rotate(Point(x, y)) {}
199 271447 operator Affine() const { return Affine(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); }
200
201 /** @brief Get the characteristic vector of the rotation.
202 * @return A vector that would be obtained by applying this transform to the X versor. */
203 Point const &vector() const { return vec; }
204 10000 Coord angle() const { return atan2(vec); }
205 Coord operator[](Dim2 dim) const { return vec[dim]; }
206 Coord operator[](unsigned dim) const { return vec[dim]; }
207 Rotate &operator*=(Rotate const &o) { vec *= o; return *this; }
208 bool operator==(Rotate const &o) const { return vec == o.vec; }
209 10483 Rotate inverse() const {
210 10483 Rotate r;
211 10483 r.vec = Point(vec[X], -vec[Y]);
212 10483 return r;
213 }
214 /// @brief Get a zero-degree rotation.
215 static Rotate identity() { return {}; }
216 /** @brief Construct a rotation from its angle in degrees.
217 * Positive arguments correspond to clockwise rotations if Y grows downwards. */
218 static Rotate from_degrees(Coord deg) { return Rotate(rad_from_deg(deg)); }
219 static Affine around(Point const &p, Coord angle);
220
221 friend class Point;
222 };
223
224 inline bool are_near(Rotate const &a, Rotate const &b, Coord eps = EPSILON) {
225 return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
226 }
227
228 /** @brief Common base for shearing transforms.
229 * This class is an implementation detail and should not be used directly.
230 * @ingroup Transforms */
231 template <typename S>
232 class ShearBase
233 : public TransformOperations<S>
234 {
235 protected:
236 Coord f = 0;
237 ShearBase() = default;
238 explicit ShearBase(Coord _f) : f(_f) {}
239 public:
240 Coord factor() const { return f; }
241 void setFactor(Coord nf) { f = nf; }
242 S &operator*=(S const &s) { f += s.f; return static_cast<S &>(*this); }
243 bool operator==(ShearBase<S> const &s) const { return f == s.f; }
244 S inverse() const { return S(-f); }
245 static S identity() { return {}; }
246
247 friend class Point;
248 friend class Affine;
249 };
250
251 /** @brief Horizontal shearing.
252 * Points on the X axis will not move. Combine with translations to get a shear
253 * with a different invariant line.
254 * @ingroup Transforms */
255 class HShear
256 : public ShearBase<HShear>
257 {
258 public:
259 HShear() = default;
260 explicit HShear(Coord h) : ShearBase<HShear>(h) {}
261 operator Affine() const { return Affine(1, 0, f, 1, 0, 0); }
262 };
263
264 inline bool are_near(HShear const &a, HShear const &b, Coord eps=EPSILON) {
265 return are_near(a.factor(), b.factor(), eps);
266 }
267
268 /** @brief Vertical shearing.
269 * Points on the Y axis will not move. Combine with translations to get a shear
270 * with a different invariant line.
271 * @ingroup Transforms */
272 class VShear
273 : public ShearBase<VShear>
274 {
275 public:
276 VShear() = default;
277 explicit VShear(Coord h) : ShearBase<VShear>(h) {}
278 operator Affine() const { return Affine(1, f, 0, 1, 0, 0); }
279 };
280
281 inline bool are_near(VShear const &a, VShear const &b, Coord eps = EPSILON) {
282 return are_near(a.factor(), b.factor(), eps);
283 }
284
285 /** @brief Combination of a translation and uniform scale.
286 * The translation part is applied first, then the result is scaled from the new origin.
287 * This way when the class is used to accumulate a zoom transform, trans always points
288 * to the new origin in original coordinates.
289 * @ingroup Transforms */
290 class Zoom
291 : public TransformOperations< Zoom >
292 {
293 Coord _scale = 1;
294 Point _trans;
295 public:
296 Zoom() = default;
297 /// Construct a zoom from a scaling factor.
298 explicit Zoom(Coord s) : _scale(s) {}
299 /// Construct a zoom from a translation.
300 explicit Zoom(Point const &t) : _trans(t) {}
301 explicit Zoom(Translate const &t) : Zoom(t.vector()) {}
302 /// Construct a zoom from a scaling factor and a translation.
303 2 Zoom(Coord s, Point const &t) : _scale(s), _trans(t) {}
304 1 Zoom(Coord s, Translate const &t) : Zoom(s, t.vector()) {}
305
306 operator Affine() const {
307 return Affine(_scale, 0, 0, _scale, _trans[X] * _scale, _trans[Y] * _scale);
308 }
309 Zoom &operator*=(Zoom const &z) {
310 _trans += z._trans / _scale;
311 _scale *= z._scale;
312 return *this;
313 }
314 bool operator==(Zoom const &z) const { return _scale == z._scale && _trans == z._trans; }
315
316 Coord scale() const { return _scale; }
317 void setScale(Coord s) { _scale = s; }
318 Point translation() const { return _trans; }
319 void setTranslation(Point const &p) { _trans = p; }
320 Zoom inverse() const { return Zoom(1 / _scale, Translate(-_trans * _scale)); }
321 static Zoom identity() { return {}; }
322 static Zoom map_rect(Rect const &old_r, Rect const &new_r);
323
324 friend class Point;
325 friend class Affine;
326 };
327
328 inline bool are_near(Zoom const &a, Zoom const &b, Coord eps = EPSILON) {
329 return are_near(a.scale(), b.scale(), eps) &&
330 are_near(a.translation(), b.translation(), eps);
331 }
332
333 /** @brief Specialization of exponentiation for Scale.
334 * @relates Scale */
335 template<>
336 inline Scale pow(Scale const &s, int n) {
337 return Scale(::pow(s[X], n), ::pow(s[Y], n));
338 }
339 /** @brief Specialization of exponentiation for Translate.
340 * @relates Translate */
341 template<>
342 inline Translate pow(Translate const &t, int n) {
343 return Translate(t[X] * n, t[Y] * n);
344 }
345
346 /** @brief Reflects objects about line.
347 * The line, defined by a vector along the line and a point on it, acts as a mirror.
348 * @ingroup Transforms
349 * @see Line::reflection()
350 */
351 Affine reflection(Point const & vector, Point const & origin);
352
353 //TODO: decomposition of Affine into some finite combination of the above classes
354
355 } // namespace Geom
356
357 #endif // LIB2GEOM_SEEN_TRANSFORMS_H
358
359 /*
360 Local Variables:
361 mode:c++
362 c-file-style:"stroustrup"
363 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
364 indent-tabs-mode:nil
365 fill-column:99
366 End:
367 */
368 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
369