Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
affine.cpp
Go to the documentation of this file.
1/*
2 * Authors:
3 * Lauris Kaplinski <lauris@kaplinski.com>
4 * Michael G. Sloan <mgsloan@gmail.com>
5 *
6 * This code is in public domain
7 */
8
9#include <2geom/affine.h>
10#include <2geom/point.h>
11#include <2geom/polynomial.h>
12#include <2geom/utils.h>
13
14namespace Geom {
15
25//NOTE: Inkscape's version is broken, so when including this version, you'll have to search for code with this func
26Affine from_basis(Point const &x_basis, Point const &y_basis, Point const &offset) {
27 return Affine(x_basis[X], x_basis[Y],
28 y_basis[X], y_basis[Y],
29 offset [X], offset [Y]);
30}
31
33 return Point(_c[0], _c[1]);
34}
35
37 return Point(_c[2], _c[3]);
38}
39
42 return Point(_c[4], _c[5]);
43}
44
45void Affine::setXAxis(Point const &vec) {
46 for(int i = 0; i < 2; i++)
47 _c[i] = vec[i];
48}
49
50void Affine::setYAxis(Point const &vec) {
51 for(int i = 0; i < 2; i++)
52 _c[i + 2] = vec[i];
53}
54
56void Affine::setTranslation(Point const &loc) {
57 for(int i = 0; i < 2; i++)
58 _c[i + 4] = loc[i];
59}
60
64double Affine::expansionX() const {
65 return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
66}
67
71double Affine::expansionY() const {
72 return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
73}
74
75void Affine::setExpansionX(double val) {
76 double exp_x = expansionX();
77 if (exp_x != 0.0) { //TODO: best way to deal with it is to skip op?
78 double coef = val / expansionX();
79 for (unsigned i = 0; i < 2; ++i) {
80 _c[i] *= coef;
81 }
82 }
83}
84
85void Affine::setExpansionY(double val) {
86 double exp_y = expansionY();
87 if (exp_y != 0.0) { //TODO: best way to deal with it is to skip op?
88 double coef = val / expansionY();
89 for (unsigned i = 2; i < 4; ++i) {
90 _c[i] *= coef;
91 }
92 }
93}
94
97 _c[0] = 1.0; _c[1] = 0.0;
98 _c[2] = 0.0; _c[3] = 1.0;
99 _c[4] = 0.0; _c[5] = 0.0;
100}
101
109bool Affine::isIdentity(Coord eps) const {
110 return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
111 are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
112 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
113}
114
124 return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
125 are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps);
126}
135 return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
136 are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
137 (!are_near(_c[4], 0.0, eps) || !are_near(_c[5], 0.0, eps));
138}
139
147bool Affine::isScale(Coord eps) const {
148 if (isSingular(eps)) return false;
149 return are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
150 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
151}
152
161 if (isSingular(eps)) return false;
162 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
163 are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
164 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
165}
166
175 if (isSingular(eps)) return false;
176 return are_near(fabs(_c[0]), fabs(_c[3]), eps) &&
177 are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
178 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
179}
180
190 if (isSingular(eps)) return false;
191 // we need to test both c0 and c3 to handle the case of flips,
192 // which should be treated as nonzero uniform scales
193 return !(are_near(_c[0], 1.0, eps) && are_near(_c[3], 1.0, eps)) &&
194 are_near(fabs(_c[0]), fabs(_c[3]), eps) &&
195 are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) &&
196 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
197}
198
206bool Affine::isRotation(Coord eps) const {
207 return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
208 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps) &&
209 are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
210}
211
220 return !are_near(_c[0], 1.0, eps) &&
221 are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
222 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps) &&
223 are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
224}
225
234 return !are_near(_c[0], 1.0, eps) &&
235 are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
236 are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
237}
238
244 Coord x = (_c[2]*_c[5]+_c[4]-_c[4]*_c[3]) / (1-_c[3]-_c[0]+_c[0]*_c[3]-_c[2]*_c[1]);
245 Coord y = (_c[1]*x + _c[5]) / (1 - _c[3]);
246 return Point(x,y);
247};
248
256bool Affine::isHShear(Coord eps) const {
257 return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
258 are_near(_c[3], 1.0, eps) && are_near(_c[4], 0.0, eps) &&
259 are_near(_c[5], 0.0, eps);
260}
269 return are_near(_c[0], 1.0, eps) && are_near(_c[1], 0.0, eps) &&
270 !are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
271 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
272}
273
281bool Affine::isVShear(Coord eps) const {
282 return are_near(_c[0], 1.0, eps) && are_near(_c[2], 0.0, eps) &&
283 are_near(_c[3], 1.0, eps) && are_near(_c[4], 0.0, eps) &&
284 are_near(_c[5], 0.0, eps);
285}
286
295 return are_near(_c[0], 1.0, eps) && !are_near(_c[1], 0.0, eps) &&
296 are_near(_c[2], 0.0, eps) && are_near(_c[3], 1.0, eps) &&
297 are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps);
298}
299
310bool Affine::isZoom(Coord eps) const {
311 if (isSingular(eps)) return false;
312 return are_near(_c[0], _c[3], eps) && are_near(_c[1], 0, eps) && are_near(_c[2], 0, eps);
313}
314
322{
323 return are_near(descrim2(), 1.0, eps);
324}
325
340{
341 if (isSingular(eps)) return false;
342 return (are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) ||
343 (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps));
344}
345
360{
361 return ((are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) ||
362 (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps))) &&
363 are_near(_c[0] * _c[0] + _c[1] * _c[1], 1.0, eps);
364}
365
368bool Affine::flips() const {
369 return det() < 0;
370}
371
377bool Affine::isSingular(Coord eps) const {
378 return are_near(det(), 0.0, eps);
379}
380
389 Affine d;
390
391 double mx = std::max(fabs(_c[0]) + fabs(_c[1]),
392 fabs(_c[2]) + fabs(_c[3])); // a random matrix norm (either l1 or linfty
393 if(mx > 0) {
394 Geom::Coord const determ = det();
395 if (!rel_error_bound(std::sqrt(fabs(determ)), mx)) {
396 Geom::Coord const ideterm = 1.0 / (determ);
397
398 d._c[0] = _c[3] * ideterm;
399 d._c[1] = -_c[1] * ideterm;
400 d._c[2] = -_c[2] * ideterm;
401 d._c[3] = _c[0] * ideterm;
402 d._c[4] = (-_c[4] * d._c[0] - _c[5] * d._c[2]);
403 d._c[5] = (-_c[4] * d._c[1] - _c[5] * d._c[3]);
404 } else {
405 d.setIdentity();
406 }
407 } else {
408 d.setIdentity();
409 }
410
411 return d;
412}
413
417 // TODO this can overflow
418 return _c[0] * _c[3] - _c[1] * _c[2];
419}
420
425 return fabs(det());
426}
427
435 return sqrt(descrim2());
436}
437
443 Coord nc[6];
444 for(int a = 0; a < 5; a += 2) {
445 for(int b = 0; b < 2; b++) {
446 nc[a + b] = _c[a] * o._c[b] + _c[a + 1] * o._c[b + 2];
447 }
448 }
449 for(int a = 0; a < 6; ++a) {
450 _c[a] = nc[a];
451 }
452 _c[4] += o._c[4];
453 _c[5] += o._c[5];
454 return *this;
455}
456
457//TODO: What's this!?!
462 double od = m[0] * m[1] + m[2] * m[3];
463 Affine ret (m[0]*m[0] + m[1]*m[1], od,
464 od, m[2]*m[2] + m[3]*m[3],
465 0, 0);
466 return ret; // allow NRVO
467}
468
470 double const B = -m[0] - m[3];
471 double const C = m[0]*m[3] - m[1]*m[2];
472
473 std::vector<double> v = solve_quadratic(1, B, C);
474
475 for (unsigned i = 0; i < v.size(); ++i) {
476 values[i] = v[i];
477 vectors[i] = unit_vector(rot90(Point(m[0] - values[i], m[1])));
478 }
479 for (unsigned i = v.size(); i < 2; ++i) {
480 values[i] = 0;
481 vectors[i] = Point(0,0);
482 }
483}
484
485Eigen::Eigen(double m[2][2]) {
486 double const B = -m[0][0] - m[1][1];
487 double const C = m[0][0]*m[1][1] - m[1][0]*m[0][1];
488
489 std::vector<double> v = solve_quadratic(1, B, C);
490
491 for (unsigned i = 0; i < v.size(); ++i) {
492 values[i] = v[i];
493 vectors[i] = unit_vector(rot90(Point(m[0][0] - values[i], m[0][1])));
494 }
495 for (unsigned i = v.size(); i < 2; ++i) {
496 values[i] = 0;
497 vectors[i] = Point(0,0);
498 }
499}
500
504bool are_near(Affine const &a, Affine const &b, Coord eps)
505{
506 return are_near(a[0], b[0], eps) && are_near(a[1], b[1], eps) &&
507 are_near(a[2], b[2], eps) && are_near(a[3], b[3], eps) &&
508 are_near(a[4], b[4], eps) && are_near(a[5], b[5], eps);
509}
510
511} //namespace Geom
512
513/*
514 Local Variables:
515 mode:c++
516 c-file-style:"stroustrup"
517 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
518 indent-tabs-mode:nil
519 fill-column:99
520 End:
521*/
522// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Cartesian point / 2D vector and related operations.
Various utility functions.
3x3 affine transformation matrix.
3x3 matrix representing an affine transformation.
Definition affine.h:70
void setYAxis(Point const &vec)
Definition affine.cpp:50
bool isHShear(Coord eps=EPSILON) const
Check whether this matrix represents pure horizontal shearing.
Definition affine.cpp:256
bool isNonzeroScale(Coord eps=EPSILON) const
Check whether this matrix represents pure, nonzero scaling.
Definition affine.cpp:160
Coord det() const
Calculate the determinant.
Definition affine.cpp:416
void setTranslation(Point const &loc)
Sets the translation imparted by the Affine.
Definition affine.cpp:56
bool flips() const
Check whether this transformation flips objects.
Definition affine.cpp:368
Coord _c[6]
Definition affine.h:71
bool preservesDistances(Coord eps=EPSILON) const
Check whether the transformation preserves distances between points.
Definition affine.cpp:359
bool preservesAngles(Coord eps=EPSILON) const
Check whether the transformation preserves angles between lines.
Definition affine.cpp:339
bool preservesArea(Coord eps=EPSILON) const
Check whether the transformation preserves areas of polygons.
Definition affine.cpp:321
Point yAxis() const
Definition affine.cpp:36
Point translation() const
Gets the translation imparted by the Affine.
Definition affine.cpp:41
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
bool isNonzeroNonpureRotation(Coord eps=EPSILON) const
Check whether this matrix represents a non-zero rotation about any point.
Definition affine.cpp:233
bool isScale(Coord eps=EPSILON) const
Check whether this matrix represents pure scaling.
Definition affine.cpp:147
bool isUniformScale(Coord eps=EPSILON) const
Check whether this matrix represents pure uniform scaling.
Definition affine.cpp:174
void setXAxis(Point const &vec)
Definition affine.cpp:45
bool isNonzeroTranslation(Coord eps=EPSILON) const
Check whether this matrix represents a pure nonzero translation.
Definition affine.cpp:134
Coord expansionX() const
Calculates the amount of x-scaling imparted by the Affine.
Definition affine.cpp:64
bool are_near(Affine const &a, Affine const &b, Coord eps)
Nearness predicate for affine transforms.
Definition affine.cpp:504
bool isNonzeroHShear(Coord eps=EPSILON) const
Check whether this matrix represents pure, nonzero horizontal shearing.
Definition affine.cpp:268
void setExpansionX(Coord val)
Definition affine.cpp:75
Coord descrim2() const
Calculate the square of the descriminant.
Definition affine.cpp:424
bool isVShear(Coord eps=EPSILON) const
Check whether this matrix represents pure vertical shearing.
Definition affine.cpp:281
bool isSingular(Coord eps=EPSILON) const
Check whether this matrix is singular.
Definition affine.cpp:377
bool isZoom(Coord eps=EPSILON) const
Check whether this matrix represents zooming.
Definition affine.cpp:310
bool isIdentity(Coord eps=EPSILON) const
Check whether this matrix is an identity matrix.
Definition affine.cpp:109
Affine elliptic_quadratic_form(Affine const &m)
Given a matrix m such that unit_circle = m*x, this returns the quadratic form x*A*x = 1.
Definition affine.cpp:461
Point xAxis() const
Definition affine.cpp:32
bool isNonzeroUniformScale(Coord eps=EPSILON) const
Check whether this matrix represents pure, nonzero uniform scaling.
Definition affine.cpp:189
Point rotationCenter() const
For a (possibly non-pure) non-zero-rotation matrix, calculate the rotation center.
Definition affine.cpp:243
void setIdentity()
Sets this matrix to be the Identity Affine.
Definition affine.cpp:96
bool isNonzeroVShear(Coord eps=EPSILON) const
Check whether this matrix represents pure, nonzero vertical shearing.
Definition affine.cpp:294
bool isTranslation(Coord eps=EPSILON) const
Check whether this matrix represents a pure translation.
Definition affine.cpp:123
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Affine & operator*=(Affine const &m)
Combine this transformation with another one.
Definition affine.cpp:442
void setExpansionY(Coord val)
Definition affine.cpp:85
bool isRotation(Coord eps=EPSILON) const
Check whether this matrix represents a pure rotation.
Definition affine.cpp:206
bool isNonzeroRotation(Coord eps=EPSILON) const
Check whether this matrix represents a pure, nonzero rotation.
Definition affine.cpp:219
Coord expansionY() const
Calculates the amount of y-scaling imparted by the Affine.
Definition affine.cpp:71
Eigen(Affine const &m)
Definition affine.cpp:469
double values[2]
Definition affine.h:203
Point vectors[2]
Definition affine.h:202
Two-dimensional point that doubles as a vector.
Definition point.h:66
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
double offset
Various utility functions.
Definition affine.h:22
Affine from_basis(const Point &x_basis, const Point &y_basis, const Point &offset=Point(0, 0))
Creates a Affine given an axis and origin point.
Definition affine.cpp:26
bool rel_error_bound(Coord a, Coord b, double eps=EPSILON)
Definition coord.h:93
std::vector< Coord > solve_quadratic(Coord a, Coord b, Coord c)
Analytically solve quadratic equation.
SBasisN< n > sqrt(SBasisN< n > const &a, int k)
Point unit_vector(Point const &a)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
Polynomial in canonical (monomial) basis.