Code

Applying fixes for gcc 4.3 build issues (closes LP: #169115)
[inkscape.git] / src / libnr / nr-matrix.cpp
1 #define __NR_MATRIX_C__
3 /** \file
4  * Various matrix routines.  Currently includes some NR::rotate etc. routines too.
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *
11  * This code is in public domain
12  */
14 #include <cstdlib>
15 #include "nr-matrix.h"
19 /**
20  *  Multiply two NRMatrices together, storing the result in d.
21  */
22 NRMatrix *
23 nr_matrix_multiply(NRMatrix *d, NRMatrix const *m0, NRMatrix const *m1)
24 {
25     if (m0) {
26         if (m1) {
27             NR::Coord d0 = m0->c[0] * m1->c[0]  +  m0->c[1] * m1->c[2];
28             NR::Coord d1 = m0->c[0] * m1->c[1]  +  m0->c[1] * m1->c[3];
29             NR::Coord d2 = m0->c[2] * m1->c[0]  +  m0->c[3] * m1->c[2];
30             NR::Coord d3 = m0->c[2] * m1->c[1]  +  m0->c[3] * m1->c[3];
31             NR::Coord d4 = m0->c[4] * m1->c[0]  +  m0->c[5] * m1->c[2]  +  m1->c[4];
32             NR::Coord d5 = m0->c[4] * m1->c[1]  +  m0->c[5] * m1->c[3]  +  m1->c[5];
34             NR::Coord *dest = d->c;
35             *dest++ = d0;
36             *dest++ = d1;
37             *dest++ = d2;
38             *dest++ = d3;
39             *dest++ = d4;
40             *dest   = d5;
41         } else {
42             *d = *m0;
43         }
44     } else {
45         if (m1) {
46             *d = *m1;
47         } else {
48             nr_matrix_set_identity(d);
49         }
50     }
52     return d;
53 }
58 /**
59  *  Store the inverted value of Matrix m in d
60  */
61 NRMatrix *
62 nr_matrix_invert(NRMatrix *d, NRMatrix const *m)
63 {
64     if (m) {
65         NR::Coord const det = m->c[0] * m->c[3] - m->c[1] * m->c[2];
66         if (!NR_DF_TEST_CLOSE(det, 0.0, NR_EPSILON)) {
68             NR::Coord const idet = 1.0 / det;
69             NR::Coord *dest = d->c;
71             /* Cache m->c[0] and m->c[4] in case d == m. */
72             NR::Coord const m_c0(m->c[0]);
73             NR::Coord const m_c4(m->c[4]);
75             /*0*/ *dest++ =  m->c[3] * idet;
76             /*1*/ *dest++ = -m->c[1] * idet;
77             /*2*/ *dest++ = -m->c[2] * idet;
78             /*3*/ *dest++ =   m_c0   * idet;
79             /*4*/ *dest++ = -m_c4 * d->c[0] - m->c[5] * d->c[2];
80             /*5*/ *dest   = -m_c4 * d->c[1] - m->c[5] * d->c[3];
82         } else {
83             nr_matrix_set_identity(d);
84         }
85     } else {
86         nr_matrix_set_identity(d);
87     }
89     return d;
90 }
96 /**
97  *  Set this matrix to a translation of x and y
98  */
99 NRMatrix *
100 nr_matrix_set_translate(NRMatrix *m, NR::Coord const x, NR::Coord const y)
102     NR::Coord *dest = m->c;
104     *dest++ = 1.0;   //0
105     *dest++ = 0.0;   //1
106     *dest++ = 0.0;   //2
107     *dest++ = 1.0;   //3
108     *dest++ =   x;   //4
109     *dest   =   y;   //5
111     return m;
118 /**
119  *  Set this matrix to a scaling transform in sx and sy
120  */
121 NRMatrix *
122 nr_matrix_set_scale(NRMatrix *m, NR::Coord const sx, NR::Coord const sy)
124     NR::Coord *dest = m->c;
126     *dest++ =  sx;   //0
127     *dest++ = 0.0;   //1
128     *dest++ = 0.0;   //2
129     *dest++ =  sy;   //3
130     *dest++ = 0.0;   //4
131     *dest   = 0.0;   //5
133     return m;
140 /**
141  *  Set this matrix to a rotating transform of angle 'theta' radians
142  */
143 NRMatrix *
144 nr_matrix_set_rotate(NRMatrix *m, NR::Coord const theta)
146     NR::Coord const s = sin(theta);
147     NR::Coord const c = cos(theta);
149     NR::Coord *dest = m->c;
151     *dest++ =   c;   //0
152     *dest++ =   s;   //1
153     *dest++ =  -s;   //2
154     *dest++ =   c;   //3
155     *dest++ = 0.0;   //4
156     *dest   = 0.0;   //5
158     return m;
169 /**
170  *  Implement NR functions and methods
171  */
172 namespace NR {
178 /**
179  *  Constructor.  Assign to nr if not null, else identity
180  */
181 Matrix::Matrix(NRMatrix const *nr)
183     if (nr) {
184         assign(nr->c);
185     } else {
186         set_identity();
187     }
194 /**
195  *  Multiply two matrices together
196  */
197 Matrix operator*(Matrix const &m0, Matrix const &m1)
199     NR::Coord const d0 = m0[0] * m1[0]  +  m0[1] * m1[2];
200     NR::Coord const d1 = m0[0] * m1[1]  +  m0[1] * m1[3];
201     NR::Coord const d2 = m0[2] * m1[0]  +  m0[3] * m1[2];
202     NR::Coord const d3 = m0[2] * m1[1]  +  m0[3] * m1[3];
203     NR::Coord const d4 = m0[4] * m1[0]  +  m0[5] * m1[2]  +  m1[4];
204     NR::Coord const d5 = m0[4] * m1[1]  +  m0[5] * m1[3]  +  m1[5];
206     Matrix ret( d0, d1, d2, d3, d4, d5 );
208     return ret;
215 /**
216  *  Multiply a matrix by another
217  */
218 Matrix &Matrix::operator*=(Matrix const &o)
220     *this = *this * o;
221     return *this;
228 /**
229  *  Multiply by a scaling matrix
230  */
231 Matrix &Matrix::operator*=(scale const &other)
233     /* This loop is massive overkill.  Let's unroll.
234      *   o    _c[] goes from 0..5
235      *   o    other[] alternates between 0 and 1
236      */
237     /*
238      * for (unsigned i = 0; i < 3; ++i) {
239      *     for (unsigned j = 0; j < 2; ++j) {
240      *         this->_c[i * 2 + j] *= other[j];
241      *     }
242      * }
243      */
245     NR::Coord const xscale = other[0];
246     NR::Coord const yscale = other[1];
247     NR::Coord *dest = _c;
249     /*i=0 j=0*/  *dest++ *= xscale;
250     /*i=0 j=1*/  *dest++ *= yscale;
251     /*i=1 j=0*/  *dest++ *= xscale;
252     /*i=1 j=1*/  *dest++ *= yscale;
253     /*i=2 j=0*/  *dest++ *= xscale;
254     /*i=2 j=1*/  *dest   *= yscale;
256     return *this;
263 /**
264  *  Return the inverse of this matrix.  If an inverse is not defined,
265  *  then return the identity matrix.
266  */
267 Matrix Matrix::inverse() const
269     Matrix d(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
271     NR::Coord const det = _c[0] * _c[3] - _c[1] * _c[2];
272     if (!NR_DF_TEST_CLOSE(det, 0.0, NR_EPSILON)) {
274         NR::Coord const idet = 1.0 / det;
275         NR::Coord *dest = d._c;
277         /*0*/ *dest++ =  _c[3] * idet;
278         /*1*/ *dest++ = -_c[1] * idet;
279         /*2*/ *dest++ = -_c[2] * idet;
280         /*3*/ *dest++ =  _c[0] * idet;
281         /*4*/ *dest++ = -_c[4] * d._c[0] - _c[5] * d._c[2];
282         /*5*/ *dest   = -_c[4] * d._c[1] - _c[5] * d._c[3];
284     } else {
285         d.set_identity();
286     }
288     return d;
295 /**
296  *  Set this matrix to Identity
297  */
298 void Matrix::set_identity()
300     NR::Coord *dest = _c;
302     *dest++ = 1.0; //0
303     *dest++ = 0.0; //1
304     *dest++ = 0.0; //2
305     *dest++ = 1.0; //3
306     // translation
307     *dest++ = 0.0; //4
308     *dest   = 0.0; //5
315 /**
316  *  return an Identity matrix
317  */
318 Matrix identity()
320     Matrix ret(1.0, 0.0,
321                0.0, 1.0,
322                0.0, 0.0);
323     return ret;
330 /**
331  *
332  */
333 Matrix from_basis(Point const x_basis, Point const y_basis, Point const offset)
335     Matrix const ret(x_basis[X], y_basis[X],
336                      x_basis[Y], y_basis[Y],
337                      offset[X], offset[Y]);
338     return ret;
344 /**
345  * Returns a rotation matrix corresponding by the specified angle (in radians) about the origin.
346  *
347  * \see NR::rotate_degrees
348  *
349  * Angle direction in Inkscape code: If you use the traditional mathematics convention that y
350  * increases upwards, then positive angles are anticlockwise as per the mathematics convention.  If
351  * you take the common non-mathematical convention that y increases downwards, then positive angles
352  * are clockwise, as is common outside of mathematics.
353  */
354 rotate::rotate(NR::Coord const theta) :
355     vec(cos(theta),
356         sin(theta))
364 /**
365  *  Return the determinant of the Matrix
366  */
367 NR::Coord Matrix::det() const
369     return _c[0] * _c[3] - _c[1] * _c[2];
376 /**
377  * Return the scalar of the descriminant of the Matrix
378  */
379 NR::Coord Matrix::descrim2() const
381     return fabs(det());
388 /**
389  *  Return the descriminant of the Matrix
390  */
391 NR::Coord Matrix::descrim() const
393     return sqrt(descrim2());
400 /**
401  *  Assign a matrix to a given coordinate array
402  */
403 Matrix &Matrix::assign(Coord const *array)
405     assert(array != NULL);
407     Coord const *src = array;
408     Coord *dest = _c;
410     *dest++ = *src++;  //0
411     *dest++ = *src++;  //1
412     *dest++ = *src++;  //2
413     *dest++ = *src++;  //3
414     *dest++ = *src++;  //4
415     *dest   = *src  ;  //5
417     return *this;
424 /**
425  *  Copy this matrix's value to a NRMatrix
426  */
427 NRMatrix *Matrix::copyto(NRMatrix *nrm) const {
429     assert(nrm != NULL);
431     Coord const *src = _c;
432     Coord *dest = nrm->c;
434     *dest++ = *src++;  //0
435     *dest++ = *src++;  //1
436     *dest++ = *src++;  //2
437     *dest++ = *src++;  //3
438     *dest++ = *src++;  //4
439     *dest   = *src  ;  //5
441     return nrm;
447 /**
448  *  Copy this matrix's values to an array
449  */
450 NR::Coord *Matrix::copyto(NR::Coord *array) const {
452     assert(array != NULL);
454     Coord const *src = _c;
455     Coord *dest = array;
457     *dest++ = *src++;  //0
458     *dest++ = *src++;  //1
459     *dest++ = *src++;  //2
460     *dest++ = *src++;  //3
461     *dest++ = *src++;  //4
462     *dest   = *src  ;  //5
464     return array;
471 /**
472  *
473  */
474 double expansion(Matrix const &m) {
475     return sqrt(fabs(m.det()));
482 /**
483  *
484  */
485 double Matrix::expansion() const {
486     return sqrt(fabs(det()));
493 /**
494  *
495  */
496 double Matrix::expansionX() const {
497     return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
504 /**
505  *
506  */
507 double Matrix::expansionY() const {
508     return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
515 /**
516  *
517  */
518 bool Matrix::is_translation(Coord const eps) const {
519     return ( fabs(_c[0] - 1.0) < eps &&
520              fabs(_c[3] - 1.0) < eps &&
521              fabs(_c[1])       < eps &&
522              fabs(_c[2])       < eps   );
526 /**
527  *
528  */
529 bool Matrix::is_scale(Coord const eps) const {
530     return ( (fabs(_c[0] - 1.0) > eps || fabs(_c[3] - 1.0) > eps) &&
531              fabs(_c[1])       < eps &&
532              fabs(_c[2])       < eps   );
536 /**
537  *
538  */
539 bool Matrix::is_rotation(Coord const eps) const {
540     return ( fabs(_c[1]) > eps &&
541              fabs(_c[2]) > eps &&
542              fabs(_c[1] + _c[2]) < 2 * eps);
549 /**
550  *
551  */
552 bool Matrix::test_identity() const {
553     return NR_MATRIX_DF_TEST_CLOSE(this, &NR_MATRIX_IDENTITY, NR_EPSILON);
560 /**
561  *
562  */
563 bool transform_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon) {
564     return NR_MATRIX_DF_TEST_TRANSFORM_CLOSE(&m0, &m1, epsilon);
571 /**
572  *
573  */
574 bool translate_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon) {
575     return NR_MATRIX_DF_TEST_TRANSLATE_CLOSE(&m0, &m1, epsilon);
582 /**
583  *
584  */
585 bool matrix_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon)
587     return ( NR_MATRIX_DF_TEST_TRANSFORM_CLOSE(&m0, &m1, epsilon) &&
588              NR_MATRIX_DF_TEST_TRANSLATE_CLOSE(&m0, &m1, epsilon)   );
595 /**
596  *  A home-made assertion.  Stop if the two matrixes are not 'close' to
597  *  each other.
598  */
599 void assert_close(Matrix const &a, Matrix const &b)
601     if (!matrix_equalp(a, b, 1e-3)) {
602         fprintf(stderr,
603                 "a = | %g %g |,\tb = | %g %g |\n"
604                 "    | %g %g | \t    | %g %g |\n"
605                 "    | %g %g | \t    | %g %g |\n",
606                 a[0], a[1], b[0], b[1],
607                 a[2], a[3], b[2], b[3],
608                 a[4], a[5], b[4], b[5]);
609         std::abort();
610     }
615 }  //namespace NR
619 /*
620   Local Variables:
621   mode:c++
622   c-file-style:"stroustrup"
623   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
624   indent-tabs-mode:nil
625   fill-column:99
626   End:
627 */
628 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :