diff --git a/headers/libs/agg/agg_trans_affine.h b/headers/libs/agg/agg_trans_affine.h index a9fa628aa8..4430918fee 100644 --- a/headers/libs/agg/agg_trans_affine.h +++ b/headers/libs/agg/agg_trans_affine.h @@ -2,8 +2,8 @@ // Anti-Grain Geometry - Version 2.4 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) // -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // @@ -19,44 +19,44 @@ #ifndef AGG_TRANS_AFFINE_INCLUDED #define AGG_TRANS_AFFINE_INCLUDED -#include +#include #include "agg_basics.h" namespace agg { - const double affine_epsilon = 1e-14; // About of precision of doubles + const double affine_epsilon = 1e-14; //============================================================trans_affine // // See Implementation agg_trans_affine.cpp // // Affine transformation are linear transformations in Cartesian coordinates - // (strictly speaking not only in Cartesian, but for the beginning we will - // think so). They are rotation, scaling, translation and skewing. - // After any affine transformation a line segment remains a line segment - // and it will never become a curve. + // (strictly speaking not only in Cartesian, but for the beginning we will + // think so). They are rotation, scaling, translation and skewing. + // After any affine transformation a line segment remains a line segment + // and it will never become a curve. // - // There will be no math about matrix calculations, since it has been + // There will be no math about matrix calculations, since it has been // described many times. Ask yourself a very simple question: - // "why do we need to understand and use some matrix stuff instead of just + // "why do we need to understand and use some matrix stuff instead of just // rotating, scaling and so on". The answers are: // // 1. Any combination of transformations can be done by only 4 multiplications // and 4 additions in floating point. // 2. One matrix transformation is equivalent to the number of consecutive - // discrete transformations, i.e. the matrix "accumulates" all transformations - // in the order of their settings. Suppose we have 4 transformations: + // discrete transformations, i.e. the matrix "accumulates" all transformations + // in the order of their settings. Suppose we have 4 transformations: // * rotate by 30 degrees, - // * scale X to 2.0, - // * scale Y to 1.5, - // * move to (100, 100). - // The result will depend on the order of these transformations, + // * scale X to 2.0, + // * scale Y to 1.5, + // * move to (100, 100). + // The result will depend on the order of these transformations, // and the advantage of matrix is that the sequence of discret calls: - // rotate(30), scaleX(2.0), scaleY(1.5), move(100,100) + // rotate(30), scaleX(2.0), scaleY(1.5), move(100,100) // will have exactly the same result as the following matrix transformations: - // + // // affine_matrix m; - // m *= rotate_matrix(30); + // m *= rotate_matrix(30); // m *= scaleX_matrix(2.0); // m *= scaleY_matrix(1.5); // m *= move_matrix(100,100); @@ -64,7 +64,7 @@ namespace agg // m.transform_my_point_at_last(x, y); // // What is the good of it? In real life we will set-up the matrix only once - // and then transform many points, let alone the convenience to set any + // and then transform many points, let alone the convenience to set any // combination of transformations. // // So, how to use it? Very easy - literally as it's shown above. Not quite, @@ -77,71 +77,87 @@ namespace agg // m.transform(&x, &y); // // The affine matrix is all you need to perform any linear transformation, - // but all transformations have origin point (0,0). It means that we need to + // but all transformations have origin point (0,0). It means that we need to // use 2 translations if we want to rotate someting around (100,100): - // + // // m *= agg::trans_affine_translation(-100.0, -100.0); // move to (0,0) // m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // rotate // m *= agg::trans_affine_translation(100.0, 100.0); // move back to (100,100) //---------------------------------------------------------------------- - class trans_affine + struct trans_affine { - public: + double sx, shy, shx, sy, tx, ty; + //------------------------------------------ Construction - // Construct an identity matrix - it does not transform anything + // Identity matrix trans_affine() : - m0(1.0), m1(0.0), m2(0.0), m3(1.0), m4(0.0), m5(0.0) + sx(1.0), shy(0.0), shx(0.0), sy(1.0), tx(0.0), ty(0.0) {} - // Construct a custom matrix. Usually used in derived classes - trans_affine(double v0, double v1, double v2, double v3, double v4, double v5) : - m0(v0), m1(v1), m2(v2), m3(v3), m4(v4), m5(v5) + // Custom matrix. Usually used in derived classes + trans_affine(double v0, double v1, double v2, + double v3, double v4, double v5) : + sx(v0), shy(v1), shx(v2), sy(v3), tx(v4), ty(v5) {} - // Construct a matrix to transform a parallelogram to another one. - trans_affine(const double* rect, const double* parl) - { - parl_to_parl(rect, parl); - } + // Custom matrix from m[6] + explicit trans_affine(const double* m) : + sx(m[0]), shy(m[1]), shx(m[2]), sy(m[3]), tx(m[4]), ty(m[5]) + {} - // Construct a matrix to transform a rectangle to a parallelogram. - trans_affine(double x1, double y1, double x2, double y2, + // Rectangle to a parallelogram. + trans_affine(double x1, double y1, double x2, double y2, const double* parl) { rect_to_parl(x1, y1, x2, y2, parl); } - // Construct a matrix to transform a parallelogram to a rectangle. - trans_affine(const double* parl, + // Parallelogram to a rectangle. + trans_affine(const double* parl, double x1, double y1, double x2, double y2) { parl_to_rect(parl, x1, y1, x2, y2); } + // Arbitrary parallelogram transformation. + trans_affine(const double* src, const double* dst) + { + parl_to_parl(src, dst); + } //---------------------------------- Parellelogram transformations - // Calculate a matrix to transform a parallelogram to another one. - // src and dst are pointers to arrays of three points - // (double[6], x,y,...) that identify three corners of the - // parallelograms assuming implicit fourth points. - // There are also transformations rectangtle to parallelogram and - // parellelogram to rectangle - const trans_affine& parl_to_parl(const double* src, + // transform a parallelogram to another one. Src and dst are + // pointers to arrays of three points (double[6], x1,y1,...) that + // identify three corners of the parallelograms assuming implicit + // fourth point. The arguments are arrays of double[6] mapped + // to x1,y1, x2,y2, x3,y3 where the coordinates are: + // *-----------------* + // / (x3,y3)/ + // / / + // /(x1,y1) (x2,y2)/ + // *-----------------* + const trans_affine& parl_to_parl(const double* src, const double* dst); - const trans_affine& rect_to_parl(double x1, double y1, - double x2, double y2, + const trans_affine& rect_to_parl(double x1, double y1, + double x2, double y2, const double* parl); - const trans_affine& parl_to_rect(const double* parl, - double x1, double y1, + const trans_affine& parl_to_rect(const double* parl, + double x1, double y1, double x2, double y2); //------------------------------------------ Operations - // Reset - actually load an identity matrix + // Reset - load an identity matrix const trans_affine& reset(); + // Direct transformations operations + const trans_affine& translate(double x, double y); + const trans_affine& rotate(double a); + const trans_affine& scale(double s); + const trans_affine& scale(double x, double y); + // Multiply matrix to another one const trans_affine& multiply(const trans_affine& m); @@ -154,8 +170,8 @@ namespace agg // Multiply inverse of "m" to "this" and assign the result to "this" const trans_affine& premultiply_inv(const trans_affine& m); - // Invert matrix. Do not try to invert degenerate matrices, - // there's no check for validity. If you set scale to 0 and + // Invert matrix. Do not try to invert degenerate matrices, + // there's no check for validity. If you set scale to 0 and // then try to invert matrix, expect unpredictable result. const trans_affine& invert(); @@ -169,38 +185,38 @@ namespace agg // Store matrix to an array [6] of double void store_to(double* m) const { - *m++ = m0; *m++ = m1; *m++ = m2; *m++ = m3; *m++ = m4; *m++ = m5; + *m++ = sx; *m++ = shy; *m++ = shx; *m++ = sy; *m++ = tx; *m++ = ty; } // Load matrix from an array [6] of double const trans_affine& load_from(const double* m) { - m0 = *m++; m1 = *m++; m2 = *m++; m3 = *m++; m4 = *m++; m5 = *m++; + sx = *m++; shy = *m++; shx = *m++; sy = *m++; tx = *m++; ty = *m++; return *this; } //------------------------------------------- Operators - - // Multiply current matrix to another one + + // Multiply the matrix by another one const trans_affine& operator *= (const trans_affine& m) { return multiply(m); } - // Multiply current matrix to inverse of another one + // Multiply the matrix by inverse of another one const trans_affine& operator /= (const trans_affine& m) { return multiply_inv(m); } - // Multiply current matrix to another one and return + // Multiply the matrix by another one and return // the result in a separete matrix. trans_affine operator * (const trans_affine& m) const { return trans_affine(*this).multiply(m); } - // Multiply current matrix to inverse of another one + // Multiply the matrix by inverse of another one // and return the result in a separete matrix. trans_affine operator / (const trans_affine& m) const { @@ -227,86 +243,136 @@ namespace agg } //-------------------------------------------- Transformations - // Direct transformation x and y + // Direct transformation of x and y void transform(double* x, double* y) const; - // Direct transformation x and y, 2x2 matrix only, no translation + // Direct transformation of x and y, 2x2 matrix only, no translation void transform_2x2(double* x, double* y) const; - // Inverse transformation x and y. It works slower than the - // direct transformation, so if the performance is critical - // it's better to invert() the matrix and then use transform() + // Inverse transformation of x and y. It works slower than the + // direct transformation. For massive operations it's better to + // invert() the matrix and then use direct transformations. void inverse_transform(double* x, double* y) const; //-------------------------------------------- Auxiliary // Calculate the determinant of matrix double determinant() const { - return 1.0 / (m0 * m3 - m1 * m2); + return sx * sy - shy * shx; } - // Get the average scale (by X and Y). + // Calculate the reciprocal of the determinant + double determinant_reciprocal() const + { + return 1.0 / (sx * sy - shy * shx); + } + + // Get the average scale (by X and Y). // Basically used to calculate the approximation_scale when // decomposinting curves into line segments. double scale() const; + // Check to see if the matrix is not degenerate + bool is_valid(double epsilon = affine_epsilon) const; + // Check to see if it's an identity matrix bool is_identity(double epsilon = affine_epsilon) const; // Check to see if two matrices are equal bool is_equal(const trans_affine& m, double epsilon = affine_epsilon) const; - // Determine the major parameters. Use carefully considering degenerate matrices + // Determine the major parameters. Use with caution considering + // possible degenerate cases. double rotation() const; void translation(double* dx, double* dy) const; - void scaling(double* sx, double* sy) const; - void scaling_abs(double* sx, double* sy) const - { - *sx = sqrt(m0*m0 + m2*m2); - *sy = sqrt(m1*m1 + m3*m3); - } - - private: - double m0; - double m1; - double m2; - double m3; - double m4; - double m5; + void scaling(double* x, double* y) const; + void scaling_abs(double* x, double* y) const; }; //------------------------------------------------------------------------ inline void trans_affine::transform(double* x, double* y) const { - double tx = *x; - *x = tx * m0 + *y * m2 + m4; - *y = tx * m1 + *y * m3 + m5; + double tmp = *x; + *x = tmp * sx + *y * shx + tx; + *y = tmp * shy + *y * sy + ty; } //------------------------------------------------------------------------ inline void trans_affine::transform_2x2(double* x, double* y) const { - double tx = *x; - *x = tx * m0 + *y * m2; - *y = tx * m1 + *y * m3; + double tmp = *x; + *x = tmp * sx + *y * shx; + *y = tmp * shy + *y * sy; } //------------------------------------------------------------------------ inline void trans_affine::inverse_transform(double* x, double* y) const { - double d = determinant(); - double a = (*x - m4) * d; - double b = (*y - m5) * d; - *x = a * m3 - b * m2; - *y = b * m0 - a * m1; + double d = determinant_reciprocal(); + double a = (*x - tx) * d; + double b = (*y - ty) * d; + *x = a * sy - b * shx; + *y = b * sx - a * shy; } //------------------------------------------------------------------------ inline double trans_affine::scale() const { - double x = M_SQRT1_2 * m0 + M_SQRT1_2 * m2; - double y = M_SQRT1_2 * m1 + M_SQRT1_2 * m3; - return sqrt(x*x + y*y); + double x = M_SQRT1_2 * sx + M_SQRT1_2 * shx; + double y = M_SQRT1_2 * shy + M_SQRT1_2 * sy; + return std::sqrt(x*x + y*y); + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::translate(double x, double y) + { + tx += x; + ty += y; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::rotate(double a) + { + double ca = std::cos(a); + double sa = std::sin(a); + double t0 = sx * ca - shy * sa; + double t2 = shx * ca - sy * sa; + double t4 = tx * ca - ty * sa; + shy = sx * sa + shy * ca; + sy = shx * sa + sy * ca; + ty = tx * sa + ty * ca; + sx = t0; + shx = t2; + tx = t4; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double x, double y) + { + double mm0 = x; // Possible hint for the optimizer + double mm3 = y; + sx *= mm0; + shx *= mm0; + tx *= mm0; + shy *= mm3; + sy *= mm3; + ty *= mm3; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_affine& trans_affine::scale(double s) + { + double m = s; // Possible hint for the optimizer + sx *= m; + shx *= m; + tx *= m; + shy *= m; + sy *= m; + ty *= m; + return *this; } //------------------------------------------------------------------------ @@ -321,8 +387,7 @@ namespace agg { trans_affine t = m; t.invert(); - multiply(t); - return *this; + return multiply(t); } //------------------------------------------------------------------------ @@ -333,29 +398,39 @@ namespace agg return *this = t.multiply(*this); } + //------------------------------------------------------------------------ + inline void trans_affine::scaling_abs(double* x, double* y) const + { + // Used to calculate scaling coefficients in image resampling. + // When there is considerable shear this method gives us much + // better estimation than just sx, sy. + *x = std::sqrt(sx * sx + shx * shx); + *y = std::sqrt(shy * shy + sy * sy); + } + //====================================================trans_affine_rotation // Rotation matrix. sin() and cos() are calculated twice for the same angle. // There's no harm because the performance of sin()/cos() is very good on all - // modern processors. Besides, this operation is not going to be invoked too + // modern processors. Besides, this operation is not going to be invoked too // often. class trans_affine_rotation : public trans_affine { public: - trans_affine_rotation(double a) : - trans_affine(cos(a), sin(a), -sin(a), cos(a), 0.0, 0.0) + trans_affine_rotation(double a) : + trans_affine(std::cos(a), std::sin(a), -std::sin(a), std::cos(a), 0.0, 0.0) {} }; //====================================================trans_affine_scaling - // Scaling matrix. sx, sy - scale coefficients by X and Y respectively + // Scaling matrix. x, y - scale coefficients by X and Y respectively class trans_affine_scaling : public trans_affine { public: - trans_affine_scaling(double sx, double sy) : - trans_affine(sx, 0.0, 0.0, sy, 0.0, 0.0) + trans_affine_scaling(double x, double y) : + trans_affine(x, 0.0, 0.0, y, 0.0, 0.0) {} - trans_affine_scaling(double s) : + trans_affine_scaling(double s) : trans_affine(s, 0.0, 0.0, s, 0.0, 0.0) {} }; @@ -365,8 +440,8 @@ namespace agg class trans_affine_translation : public trans_affine { public: - trans_affine_translation(double tx, double ty) : - trans_affine(1.0, 0.0, 0.0, 1.0, tx, ty) + trans_affine_translation(double x, double y) : + trans_affine(1.0, 0.0, 0.0, 1.0, x, y) {} }; @@ -375,33 +450,67 @@ namespace agg class trans_affine_skewing : public trans_affine { public: - trans_affine_skewing(double sx, double sy) : - trans_affine(1.0, tan(sy), tan(sx), 1.0, 0.0, 0.0) + trans_affine_skewing(double x, double y) : + trans_affine(1.0, std::tan(y), std::tan(x), 1.0, 0.0, 0.0) {} }; //===============================================trans_affine_line_segment - // Rotate, Scale and Translate, associating 0...dist with line segment + // Rotate, Scale and Translate, associating 0...dist with line segment // x1,y1,x2,y2 class trans_affine_line_segment : public trans_affine { public: - trans_affine_line_segment(double x1, double y1, double x2, double y2, + trans_affine_line_segment(double x1, double y1, double x2, double y2, double dist) { double dx = x2 - x1; double dy = y2 - y1; if(dist > 0.0) { - multiply(trans_affine_scaling(sqrt(dx * dx + dy * dy) / dist)); + multiply(trans_affine_scaling(std::sqrt(dx * dx + dy * dy) / dist)); } - multiply(trans_affine_rotation(atan2(dy, dx))); + multiply(trans_affine_rotation(std::atan2(dy, dx))); multiply(trans_affine_translation(x1, y1)); } }; + //============================================trans_affine_reflection_unit + // Reflection matrix. Reflect coordinates across the line through + // the origin containing the unit vector (ux, uy). + // Contributed by John Horigan + class trans_affine_reflection_unit : public trans_affine + { + public: + trans_affine_reflection_unit(double ux, double uy) : + trans_affine(2.0 * ux * ux - 1.0, + 2.0 * ux * uy, + 2.0 * ux * uy, + 2.0 * uy * uy - 1.0, + 0.0, 0.0) + {} + }; + + + //=================================================trans_affine_reflection + // Reflection matrix. Reflect coordinates across the line through + // the origin at the angle a or containing the non-unit vector (x, y). + // Contributed by John Horigan + class trans_affine_reflection : public trans_affine_reflection_unit + { + public: + trans_affine_reflection(double a) : + trans_affine_reflection_unit(std::cos(a), std::sin(a)) + {} + + + trans_affine_reflection(double x, double y) : + trans_affine_reflection_unit(x / std::sqrt(x * x + y * y), y / std::sqrt(x * x + y * y)) + {} + }; + } diff --git a/headers/libs/agg/agg_trans_perspective.h b/headers/libs/agg/agg_trans_perspective.h index 0937494a19..1c650aac8d 100644 --- a/headers/libs/agg/agg_trans_perspective.h +++ b/headers/libs/agg/agg_trans_perspective.h @@ -19,125 +19,213 @@ #ifndef AGG_TRANS_PERSPECTIVE_INCLUDED #define AGG_TRANS_PERSPECTIVE_INCLUDED -#include "agg_basics.h" -#include "agg_simul_eq.h" +#include +#include "agg_trans_affine.h" namespace agg { //=======================================================trans_perspective - class trans_perspective + struct trans_perspective { - public: - //-------------------------------------------------------------------- - trans_perspective() : m_valid(false) {} + double sx, shy, w0, shx, sy, w1, tx, ty, w2; + //------------------------------------------------------- Construction + // Identity matrix + trans_perspective() : + sx (1), shy(0), w0(0), + shx(0), sy (1), w1(0), + tx (0), ty (0), w2(1) {} - //-------------------------------------------------------------------- - // Arbitrary quadrangle transformations - trans_perspective(const double* src, const double* dst) - { - quad_to_quad(src, dst); - } + // Custom matrix + trans_perspective(double v0, double v1, double v2, + double v3, double v4, double v5, + double v6, double v7, double v8) : + sx (v0), shy(v1), w0(v2), + shx(v3), sy (v4), w1(v5), + tx (v6), ty (v7), w2(v8) {} + // Custom matrix from m[9] + explicit trans_perspective(const double* m) : + sx (m[0]), shy(m[1]), w0(m[2]), + shx(m[3]), sy (m[4]), w1(m[5]), + tx (m[6]), ty (m[7]), w2(m[8]) {} - //-------------------------------------------------------------------- - // Direct transformations + // From affine + explicit trans_perspective(const trans_affine& a) : + sx (a.sx ), shy(a.shy), w0(0), + shx(a.shx), sy (a.sy ), w1(0), + tx (a.tx ), ty (a.ty ), w2(1) {} + + // Rectangle to quadrilateral trans_perspective(double x1, double y1, double x2, double y2, - const double* quad) - { - rect_to_quad(x1, y1, x2, y2, quad); - } + const double* quad); - - //-------------------------------------------------------------------- - // Reverse transformations + // Quadrilateral to rectangle trans_perspective(const double* quad, - double x1, double y1, double x2, double y2) + double x1, double y1, double x2, double y2); + + // Arbitrary quadrilateral transformations + trans_perspective(const double* src, const double* dst); + + //-------------------------------------- Quadrilateral transformations + // The arguments are double[8] that are mapped to quadrilaterals: + // x1,y1, x2,y2, x3,y3, x4,y4 + bool quad_to_quad(const double* qs, const double* qd); + + bool rect_to_quad(double x1, double y1, + double x2, double y2, + const double* q); + + bool quad_to_rect(const double* q, + double x1, double y1, + double x2, double y2); + + // Map square (0,0,1,1) to the quadrilateral and vice versa + bool square_to_quad(const double* q); + bool quad_to_square(const double* q); + + + //--------------------------------------------------------- Operations + // Reset - load an identity matrix + const trans_perspective& reset(); + + // Invert matrix. Returns false in degenerate case + bool invert(); + + // Direct transformations operations + const trans_perspective& translate(double x, double y); + const trans_perspective& rotate(double a); + const trans_perspective& scale(double s); + const trans_perspective& scale(double x, double y); + + // Multiply the matrix by another one + const trans_perspective& multiply(const trans_perspective& m); + + // Multiply "m" by "this" and assign the result to "this" + const trans_perspective& premultiply(const trans_perspective& m); + + // Multiply matrix to inverse of another one + const trans_perspective& multiply_inv(const trans_perspective& m); + + // Multiply inverse of "m" by "this" and assign the result to "this" + const trans_perspective& premultiply_inv(const trans_perspective& m); + + // Multiply the matrix by another one + const trans_perspective& multiply(const trans_affine& m); + + // Multiply "m" by "this" and assign the result to "this" + const trans_perspective& premultiply(const trans_affine& m); + + // Multiply the matrix by inverse of another one + const trans_perspective& multiply_inv(const trans_affine& m); + + // Multiply inverse of "m" by "this" and assign the result to "this" + const trans_perspective& premultiply_inv(const trans_affine& m); + + //--------------------------------------------------------- Load/Store + void store_to(double* m) const; + const trans_perspective& load_from(const double* m); + + //---------------------------------------------------------- Operators + // Multiply the matrix by another one + const trans_perspective& operator *= (const trans_perspective& m) { - quad_to_rect(quad, x1, y1, x2, y2); + return multiply(m); + } + const trans_perspective& operator *= (const trans_affine& m) + { + return multiply(m); } - - //-------------------------------------------------------------------- - // Set the transformations using two arbitrary quadrangles. - void quad_to_quad(const double* src, const double* dst) + // Multiply the matrix by inverse of another one + const trans_perspective& operator /= (const trans_perspective& m) { - - double left[8][8]; - double right[8][1]; - - unsigned i; - for (i = 0; i < 4; i++) - { - unsigned ix = i * 2; - unsigned iy = ix + 1; - - left[ix][0] = 1.0; - left[ix][1] = src[ix]; - left[ix][2] = src[iy]; - left[ix][3] = 0.0; - left[ix][4] = 0.0; - left[ix][5] = 0.0; - left[ix][6] = -src[ix] * dst[ix]; - left[ix][7] = -src[iy] * dst[ix]; - right[ix][0] = dst[ix]; - - left[iy][0] = 0.0; - left[iy][1] = 0.0; - left[iy][2] = 0.0; - left[iy][3] = 1.0; - left[iy][4] = src[ix]; - left[iy][5] = src[iy]; - left[iy][6] = -src[ix] * dst[iy]; - left[iy][7] = -src[iy] * dst[iy]; - right[iy][0] = dst[iy]; - } - m_valid = simul_eq<8, 1>::solve(left, right, m_mtx); + return multiply_inv(m); + } + const trans_perspective& operator /= (const trans_affine& m) + { + return multiply_inv(m); } - - //-------------------------------------------------------------------- - // Set the direct transformations, i.e., rectangle -> quadrangle - void rect_to_quad(double x1, double y1, double x2, double y2, - const double* quad) + // Multiply the matrix by another one and return + // the result in a separete matrix. + trans_perspective operator * (const trans_perspective& m) const { - double src[8]; - src[0] = src[6] = x1; - src[2] = src[4] = x2; - src[1] = src[3] = y1; - src[5] = src[7] = y2; - quad_to_quad(src, quad); + return trans_perspective(*this).multiply(m); + } + trans_perspective operator * (const trans_affine& m) const + { + return trans_perspective(*this).multiply(m); } - - //-------------------------------------------------------------------- - // Set the reverse transformations, i.e., quadrangle -> rectangle - void quad_to_rect(const double* quad, - double x1, double y1, double x2, double y2) + // Multiply the matrix by inverse of another one + // and return the result in a separete matrix. + trans_perspective operator / (const trans_perspective& m) const { - double dst[8]; - dst[0] = dst[6] = x1; - dst[2] = dst[4] = x2; - dst[1] = dst[3] = y1; - dst[5] = dst[7] = y2; - quad_to_quad(quad, dst); + return trans_perspective(*this).multiply_inv(m); + } + trans_perspective operator / (const trans_affine& m) const + { + return trans_perspective(*this).multiply_inv(m); } - //-------------------------------------------------------------------- - // Check if the equations were solved successfully - bool is_valid() const { return m_valid; } - - //-------------------------------------------------------------------- - // Transform a point (x, y) - void transform(double* x, double* y) const + // Calculate and return the inverse matrix + trans_perspective operator ~ () const { - double tx = *x; - double ty = *y; - double d = 1.0 / (m_mtx[6][0] * tx + m_mtx[7][0] * ty + 1.0); - *x = (m_mtx[0][0] + m_mtx[1][0] * tx + m_mtx[2][0] * ty) * d; - *y = (m_mtx[3][0] + m_mtx[4][0] * tx + m_mtx[5][0] * ty) * d; + trans_perspective ret = *this; + ret.invert(); + return ret; } + // Equal operator with default epsilon + bool operator == (const trans_perspective& m) const + { + return is_equal(m, affine_epsilon); + } + + // Not Equal operator with default epsilon + bool operator != (const trans_perspective& m) const + { + return !is_equal(m, affine_epsilon); + } + + //---------------------------------------------------- Transformations + // Direct transformation of x and y + void transform(double* x, double* y) const; + + // Direct transformation of x and y, affine part only + void transform_affine(double* x, double* y) const; + + // Direct transformation of x and y, 2x2 matrix only, no translation + void transform_2x2(double* x, double* y) const; + + // Inverse transformation of x and y. It works slow because + // it explicitly inverts the matrix on every call. For massive + // operations it's better to invert() the matrix and then use + // direct transformations. + void inverse_transform(double* x, double* y) const; + + + //---------------------------------------------------------- Auxiliary + const trans_perspective& from_affine(const trans_affine& a); + double determinant() const; + double determinant_reciprocal() const; + + bool is_valid(double epsilon = affine_epsilon) const; + bool is_identity(double epsilon = affine_epsilon) const; + bool is_equal(const trans_perspective& m, + double epsilon = affine_epsilon) const; + + // Determine the major affine parameters. Use with caution + // considering possible degenerate cases. + double scale() const; + double rotation() const; + void translation(double* dx, double* dy) const; + void scaling(double* x, double* y) const; + void scaling_abs(double* x, double* y) const; + + + //-------------------------------------------------------------------- class iterator_x { @@ -153,17 +241,16 @@ namespace agg double y; iterator_x() {} - iterator_x(double tx, double ty, double step, const double m[8][1]) : - den(m[6][0] * tx + m[7][0] * ty + 1.0), - den_step(m[6][0] * step), - nom_x(m[0][0] + m[1][0] * tx + m[2][0] * ty), - nom_x_step(m[1][0] * step), - nom_y(m[3][0] + m[4][0] * tx + m[5][0] * ty), - nom_y_step(m[4][0] * step), + iterator_x(double px, double py, double step, const trans_perspective& m) : + den(px * m.w0 + py * m.w1 + m.w2), + den_step(m.w0 * step), + nom_x(px * m.sx + py * m.shx + m.tx), + nom_x_step(step * m.sx), + nom_y(px * m.shy + py * m.sy + m.ty), + nom_y_step(step * m.shy), x(nom_x / den), y(nom_y / den) - { - } + {} void operator ++ () { @@ -179,14 +266,467 @@ namespace agg //-------------------------------------------------------------------- iterator_x begin(double x, double y, double step) const { - return iterator_x(x, y, step, m_mtx); + return iterator_x(x, y, step, *this); } - - private: - double m_mtx[8][1]; - bool m_valid; }; + + + + + + + + + + + + + + //------------------------------------------------------------------------ + inline bool trans_perspective::square_to_quad(const double* q) + { + double dx = q[0] - q[2] + q[4] - q[6]; + double dy = q[1] - q[3] + q[5] - q[7]; + if(dx == 0.0 && dy == 0.0) + { + // Affine case (parallelogram) + //--------------- + sx = q[2] - q[0]; + shy = q[3] - q[1]; + w0 = 0.0; + shx = q[4] - q[2]; + sy = q[5] - q[3]; + w1 = 0.0; + tx = q[0]; + ty = q[1]; + w2 = 1.0; + } + else + { + double dx1 = q[2] - q[4]; + double dy1 = q[3] - q[5]; + double dx2 = q[6] - q[4]; + double dy2 = q[7] - q[5]; + double den = dx1 * dy2 - dx2 * dy1; + if(den == 0.0) + { + // Singular case + //--------------- + sx = shy = w0 = shx = sy = w1 = tx = ty = w2 = 0.0; + return false; + } + // General case + //--------------- + double u = (dx * dy2 - dy * dx2) / den; + double v = (dy * dx1 - dx * dy1) / den; + sx = q[2] - q[0] + u * q[2]; + shy = q[3] - q[1] + u * q[3]; + w0 = u; + shx = q[6] - q[0] + v * q[6]; + sy = q[7] - q[1] + v * q[7]; + w1 = v; + tx = q[0]; + ty = q[1]; + w2 = 1.0; + } + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::invert() + { + double d0 = sy * w2 - w1 * ty; + double d1 = w0 * ty - shy * w2; + double d2 = shy * w1 - w0 * sy; + double d = sx * d0 + shx * d1 + tx * d2; + if(d == 0.0) + { + sx = shy = w0 = shx = sy = w1 = tx = ty = w2 = 0.0; + return false; + } + d = 1.0 / d; + trans_perspective a = *this; + sx = d * d0; + shy = d * d1; + w0 = d * d2; + shx = d * (a.w1 *a.tx - a.shx*a.w2); + sy = d * (a.sx *a.w2 - a.w0 *a.tx); + w1 = d * (a.w0 *a.shx - a.sx *a.w1); + tx = d * (a.shx*a.ty - a.sy *a.tx); + ty = d * (a.shy*a.tx - a.sx *a.ty); + w2 = d * (a.sx *a.sy - a.shy*a.shx); + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::quad_to_square(const double* q) + { + if(!square_to_quad(q)) return false; + invert(); + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::quad_to_quad(const double* qs, + const double* qd) + { + trans_perspective p; + if(! quad_to_square(qs)) return false; + if(!p.square_to_quad(qd)) return false; + multiply(p); + return true; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::rect_to_quad(double x1, double y1, + double x2, double y2, + const double* q) + { + double r[8]; + r[0] = r[6] = x1; + r[2] = r[4] = x2; + r[1] = r[3] = y1; + r[5] = r[7] = y2; + return quad_to_quad(r, q); + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::quad_to_rect(const double* q, + double x1, double y1, + double x2, double y2) + { + double r[8]; + r[0] = r[6] = x1; + r[2] = r[4] = x2; + r[1] = r[3] = y1; + r[5] = r[7] = y2; + return quad_to_quad(q, r); + } + + //------------------------------------------------------------------------ + inline trans_perspective::trans_perspective(double x1, double y1, + double x2, double y2, + const double* quad) + { + rect_to_quad(x1, y1, x2, y2, quad); + } + + //------------------------------------------------------------------------ + inline trans_perspective::trans_perspective(const double* quad, + double x1, double y1, + double x2, double y2) + { + quad_to_rect(quad, x1, y1, x2, y2); + } + + //------------------------------------------------------------------------ + inline trans_perspective::trans_perspective(const double* src, + const double* dst) + { + quad_to_quad(src, dst); + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::reset() + { + sx = 1; shy = 0; w0 = 0; + shx = 0; sy = 1; w1 = 0; + tx = 0; ty = 0; w2 = 1; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::multiply(const trans_perspective& a) + { + trans_perspective b = *this; + sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0; + shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1; + tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2; + shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0; + sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1; + ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2; + w0 = a.w0 *b.sx + a.w1 *b.shy + a.w2*b.w0; + w1 = a.w0 *b.shx + a.w1 *b.sy + a.w2*b.w1; + w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2*b.w2; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::multiply(const trans_affine& a) + { + trans_perspective b = *this; + sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0; + shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1; + tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2; + shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0; + sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1; + ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::premultiply(const trans_perspective& b) + { + trans_perspective a = *this; + sx = a.sx *b.sx + a.shx*b.shy + a.tx*b.w0; + shx = a.sx *b.shx + a.shx*b.sy + a.tx*b.w1; + tx = a.sx *b.tx + a.shx*b.ty + a.tx*b.w2; + shy = a.shy*b.sx + a.sy *b.shy + a.ty*b.w0; + sy = a.shy*b.shx + a.sy *b.sy + a.ty*b.w1; + ty = a.shy*b.tx + a.sy *b.ty + a.ty*b.w2; + w0 = a.w0 *b.sx + a.w1 *b.shy + a.w2*b.w0; + w1 = a.w0 *b.shx + a.w1 *b.sy + a.w2*b.w1; + w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2*b.w2; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::premultiply(const trans_affine& b) + { + trans_perspective a = *this; + sx = a.sx *b.sx + a.shx*b.shy; + shx = a.sx *b.shx + a.shx*b.sy; + tx = a.sx *b.tx + a.shx*b.ty + a.tx; + shy = a.shy*b.sx + a.sy *b.shy; + sy = a.shy*b.shx + a.sy *b.sy; + ty = a.shy*b.tx + a.sy *b.ty + a.ty; + w0 = a.w0 *b.sx + a.w1 *b.shy; + w1 = a.w0 *b.shx + a.w1 *b.sy; + w2 = a.w0 *b.tx + a.w1 *b.ty + a.w2; + return *this; + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::multiply_inv(const trans_perspective& m) + { + trans_perspective t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::multiply_inv(const trans_affine& m) + { + trans_affine t = m; + t.invert(); + return multiply(t); + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::premultiply_inv(const trans_perspective& m) + { + trans_perspective t = m; + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + const trans_perspective& + trans_perspective::premultiply_inv(const trans_affine& m) + { + trans_perspective t(m); + t.invert(); + return *this = t.multiply(*this); + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::translate(double x, double y) + { + tx += x; + ty += y; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::rotate(double a) + { + multiply(trans_affine_rotation(a)); + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::scale(double s) + { + multiply(trans_affine_scaling(s)); + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::scale(double x, double y) + { + multiply(trans_affine_scaling(x, y)); + return *this; + } + + //------------------------------------------------------------------------ + inline void trans_perspective::transform(double* px, double* py) const + { + double x = *px; + double y = *py; + double m = 1.0 / (x*w0 + y*w1 + w2); + *px = m * (x*sx + y*shx + tx); + *py = m * (x*shy + y*sy + ty); + } + + //------------------------------------------------------------------------ + inline void trans_perspective::transform_affine(double* x, double* y) const + { + double tmp = *x; + *x = tmp * sx + *y * shx + tx; + *y = tmp * shy + *y * sy + ty; + } + + //------------------------------------------------------------------------ + inline void trans_perspective::transform_2x2(double* x, double* y) const + { + double tmp = *x; + *x = tmp * sx + *y * shx; + *y = tmp * shy + *y * sy; + } + + //------------------------------------------------------------------------ + inline void trans_perspective::inverse_transform(double* x, double* y) const + { + trans_perspective t(*this); + if(t.invert()) t.transform(x, y); + } + + //------------------------------------------------------------------------ + inline void trans_perspective::store_to(double* m) const + { + *m++ = sx; *m++ = shy; *m++ = w0; + *m++ = shx; *m++ = sy; *m++ = w1; + *m++ = tx; *m++ = ty; *m++ = w2; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& trans_perspective::load_from(const double* m) + { + sx = *m++; shy = *m++; w0 = *m++; + shx = *m++; sy = *m++; w1 = *m++; + tx = *m++; ty = *m++; w2 = *m++; + return *this; + } + + //------------------------------------------------------------------------ + inline const trans_perspective& + trans_perspective::from_affine(const trans_affine& a) + { + sx = a.sx; shy = a.shy; w0 = 0; + shx = a.shx; sy = a.sy; w1 = 0; + tx = a.tx; ty = a.ty; w2 = 1; + return *this; + } + + //------------------------------------------------------------------------ + inline double trans_perspective::determinant() const + { + return sx * (sy * w2 - ty * w1) + + shx * (ty * w0 - shy * w2) + + tx * (shy * w1 - sy * w0); + } + + //------------------------------------------------------------------------ + inline double trans_perspective::determinant_reciprocal() const + { + return 1.0 / determinant(); + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::is_valid(double epsilon) const + { + return std::fabs(sx) > epsilon && std::fabs(sy) > epsilon && std::fabs(w2) > epsilon; + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::is_identity(double epsilon) const + { + return is_equal_eps(sx, 1.0, epsilon) && + is_equal_eps(shy, 0.0, epsilon) && + is_equal_eps(w0, 0.0, epsilon) && + is_equal_eps(shx, 0.0, epsilon) && + is_equal_eps(sy, 1.0, epsilon) && + is_equal_eps(w1, 0.0, epsilon) && + is_equal_eps(tx, 0.0, epsilon) && + is_equal_eps(ty, 0.0, epsilon) && + is_equal_eps(w2, 1.0, epsilon); + } + + //------------------------------------------------------------------------ + inline bool trans_perspective::is_equal(const trans_perspective& m, + double epsilon) const + { + return is_equal_eps(sx, m.sx, epsilon) && + is_equal_eps(shy, m.shy, epsilon) && + is_equal_eps(w0, m.w0, epsilon) && + is_equal_eps(shx, m.shx, epsilon) && + is_equal_eps(sy, m.sy, epsilon) && + is_equal_eps(w1, m.w1, epsilon) && + is_equal_eps(tx, m.tx, epsilon) && + is_equal_eps(ty, m.ty, epsilon) && + is_equal_eps(w2, m.w2, epsilon); + } + + //------------------------------------------------------------------------ + inline double trans_perspective::scale() const + { + double x = 0.707106781 * sx + 0.707106781 * shx; + double y = 0.707106781 * shy + 0.707106781 * sy; + return std::sqrt(x*x + y*y); + } + + //------------------------------------------------------------------------ + inline double trans_perspective::rotation() const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 0.0; + transform(&x1, &y1); + transform(&x2, &y2); + return std::atan2(y2-y1, x2-x1); + } + + //------------------------------------------------------------------------ + void trans_perspective::translation(double* dx, double* dy) const + { + *dx = tx; + *dy = ty; + } + + //------------------------------------------------------------------------ + void trans_perspective::scaling(double* x, double* y) const + { + double x1 = 0.0; + double y1 = 0.0; + double x2 = 1.0; + double y2 = 1.0; + trans_perspective t(*this); + t *= trans_affine_rotation(-rotation()); + t.transform(&x1, &y1); + t.transform(&x2, &y2); + *x = x2 - x1; + *y = y2 - y1; + } + + //------------------------------------------------------------------------ + void trans_perspective::scaling_abs(double* x, double* y) const + { + *x = std::sqrt(sx * sx + shx * shx); + *y = std::sqrt(shy * shy + sy * sy); + } + + } #endif + diff --git a/src/libs/agg/src/agg_trans_affine.cpp b/src/libs/agg/src/agg_trans_affine.cpp index b8a18517e6..961f8ee278 100644 --- a/src/libs/agg/src/agg_trans_affine.cpp +++ b/src/libs/agg/src/agg_trans_affine.cpp @@ -27,12 +27,12 @@ namespace agg const trans_affine& trans_affine::parl_to_parl(const double* src, const double* dst) { - m0 = src[2] - src[0]; - m1 = src[3] - src[1]; - m2 = src[4] - src[0]; - m3 = src[5] - src[1]; - m4 = src[0]; - m5 = src[1]; + sx = src[2] - src[0]; + shy = src[3] - src[1]; + shx = src[4] - src[0]; + sy = src[5] - src[1]; + tx = src[0]; + ty = src[1]; invert(); multiply(trans_affine(dst[2] - dst[0], dst[3] - dst[1], dst[4] - dst[0], dst[5] - dst[1], @@ -69,15 +69,15 @@ namespace agg //------------------------------------------------------------------------ const trans_affine& trans_affine::multiply(const trans_affine& m) { - double t0 = m0 * m.m0 + m1 * m.m2; - double t2 = m2 * m.m0 + m3 * m.m2; - double t4 = m4 * m.m0 + m5 * m.m2 + m.m4; - m1 = m0 * m.m1 + m1 * m.m3; - m3 = m2 * m.m1 + m3 * m.m3; - m5 = m4 * m.m1 + m5 * m.m3 + m.m5; - m0 = t0; - m2 = t2; - m4 = t4; + double t0 = sx * m.sx + shy * m.shx; + double t2 = shx * m.sx + sy * m.shx; + double t4 = tx * m.sx + ty * m.shx + m.tx; + shy = sx * m.shy + shy * m.sy; + sy = shx * m.shy + sy * m.sy; + ty = tx * m.shy + ty * m.sy + m.ty; + sx = t0; + shx = t2; + tx = t4; return *this; } @@ -85,18 +85,18 @@ namespace agg //------------------------------------------------------------------------ const trans_affine& trans_affine::invert() { - double d = determinant(); + double d = determinant_reciprocal(); - double t0 = m3 * d; - m3 = m0 * d; - m1 = -m1 * d; - m2 = -m2 * d; + double t0 = sy * d; + sy = sx * d; + shy = -shy * d; + shx = -shx * d; - double t4 = -m4 * t0 - m5 * m2; - m5 = -m4 * m1 - m5 * m3; + double t4 = -tx * t0 - ty * shx; + ty = -tx * shy - ty * sy; - m0 = t0; - m4 = t4; + sx = t0; + tx = t4; return *this; } @@ -104,55 +104,55 @@ namespace agg //------------------------------------------------------------------------ const trans_affine& trans_affine::flip_x() { - m0 = -m0; - m1 = -m1; - m4 = -m4; + sx = -sx; + shy = -shy; + tx = -tx; return *this; } //------------------------------------------------------------------------ const trans_affine& trans_affine::flip_y() { - m2 = -m2; - m3 = -m3; - m5 = -m5; + shx = -shx; + sy = -sy; + ty = -ty; return *this; } //------------------------------------------------------------------------ const trans_affine& trans_affine::reset() { - m0 = m3 = 1.0; - m1 = m2 = m4 = m5 = 0.0; + sx = sy = 1.0; + shy = shx = tx = ty = 0.0; return *this; } - //------------------------------------------------------------------------ - inline bool is_equal_eps(double v1, double v2, double epsilon) - { - return fabs(v1 - v2) < epsilon; - } - //------------------------------------------------------------------------ bool trans_affine::is_identity(double epsilon) const { - return is_equal_eps(m0, 1.0, epsilon) && - is_equal_eps(m1, 0.0, epsilon) && - is_equal_eps(m2, 0.0, epsilon) && - is_equal_eps(m3, 1.0, epsilon) && - is_equal_eps(m4, 0.0, epsilon) && - is_equal_eps(m5, 0.0, epsilon); + return is_equal_eps(sx, 1.0, epsilon) && + is_equal_eps(shy, 0.0, epsilon) && + is_equal_eps(shx, 0.0, epsilon) && + is_equal_eps(sy, 1.0, epsilon) && + is_equal_eps(tx, 0.0, epsilon) && + is_equal_eps(ty, 0.0, epsilon); + } + + //------------------------------------------------------------------------ + bool trans_affine::is_valid(double epsilon) const + { + return std::fabs(sx) > epsilon && std::fabs(sy) > epsilon; } //------------------------------------------------------------------------ bool trans_affine::is_equal(const trans_affine& m, double epsilon) const { - return is_equal_eps(m0, m.m0, epsilon) && - is_equal_eps(m1, m.m1, epsilon) && - is_equal_eps(m2, m.m2, epsilon) && - is_equal_eps(m3, m.m3, epsilon) && - is_equal_eps(m4, m.m4, epsilon) && - is_equal_eps(m5, m.m5, epsilon); + return is_equal_eps(sx, m.sx, epsilon) && + is_equal_eps(shy, m.shy, epsilon) && + is_equal_eps(shx, m.shx, epsilon) && + is_equal_eps(sy, m.sy, epsilon) && + is_equal_eps(tx, m.tx, epsilon) && + is_equal_eps(ty, m.ty, epsilon); } //------------------------------------------------------------------------ @@ -164,18 +164,18 @@ namespace agg double y2 = 0.0; transform(&x1, &y1); transform(&x2, &y2); - return atan2(y2-y1, x2-x1); + return std::atan2(y2-y1, x2-x1); } //------------------------------------------------------------------------ void trans_affine::translation(double* dx, double* dy) const { - *dx = m4; - *dy = m5; + *dx = tx; + *dy = ty; } //------------------------------------------------------------------------ - void trans_affine::scaling(double* sx, double* sy) const + void trans_affine::scaling(double* x, double* y) const { double x1 = 0.0; double y1 = 0.0; @@ -185,8 +185,8 @@ namespace agg t *= trans_affine_rotation(-rotation()); t.transform(&x1, &y1); t.transform(&x2, &y2); - *sx = x2 - x1; - *sy = y2 - y1; + *x = x2 - x1; + *y = y2 - y1; }