//---------------------------------------------------------------------------- // 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. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // //---------------------------------------------------------------------------- // Contact: mcseem@antigrain.com // mcseemagg@yahoo.com // http://www.antigrain.com //---------------------------------------------------------------------------- // // Affine transformation classes. // //---------------------------------------------------------------------------- #ifndef AGG_TRANS_AFFINE_INCLUDED #define AGG_TRANS_AFFINE_INCLUDED #include #include "agg_basics.h" namespace agg { 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. // // 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 // 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: // * 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, // and the advantage of matrix is that the sequence of discret calls: // 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 *= scaleX_matrix(2.0); // m *= scaleY_matrix(1.5); // m *= move_matrix(100,100); // // 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 // combination of transformations. // // So, how to use it? Very easy - literally as it's shown above. Not quite, // let us write a correct example: // // agg::trans_affine m; // m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // m *= agg::trans_affine_scaling(2.0, 1.5); // m *= agg::trans_affine_translation(100.0, 100.0); // 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 // 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) //---------------------------------------------------------------------- struct trans_affine { double sx, shy, shx, sy, tx, ty; //------------------------------------------ Construction // Identity matrix trans_affine() : sx(1.0), shy(0.0), shx(0.0), sy(1.0), tx(0.0), ty(0.0) {} // 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) {} // 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]) {} // 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); } // 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 // 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 double* parl); const trans_affine& parl_to_rect(const double* parl, double x1, double y1, double x2, double y2); //------------------------------------------ Operations // 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); // Multiply "m" to "this" and assign the result to "this" const trans_affine& premultiply(const trans_affine& m); // Multiply matrix to inverse of another one const trans_affine& multiply_inv(const trans_affine& m); // 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 // then try to invert matrix, expect unpredictable result. const trans_affine& invert(); // Mirroring around X const trans_affine& flip_x(); // Mirroring around Y const trans_affine& flip_y(); //------------------------------------------- Load/Store // Store matrix to an array [6] of double void store_to(double* m) const { *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) { sx = *m++; shy = *m++; shx = *m++; sy = *m++; tx = *m++; ty = *m++; return *this; } //------------------------------------------- Operators // Multiply the matrix by another one const trans_affine& operator *= (const trans_affine& m) { return multiply(m); } // Multiply the matrix by inverse of another one const trans_affine& operator /= (const trans_affine& m) { return multiply_inv(m); } // 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 the matrix by inverse of another one // and return the result in a separete matrix. trans_affine operator / (const trans_affine& m) const { return trans_affine(*this).multiply_inv(m); } // Calculate and return the inverse matrix trans_affine operator ~ () const { trans_affine ret = *this; return ret.invert(); } // Equal operator with default epsilon bool operator == (const trans_affine& m) const { return is_equal(m, affine_epsilon); } // Not Equal operator with default epsilon bool operator != (const trans_affine& 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, 2x2 matrix only, no translation void transform_2x2(double* x, double* y) const; // 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 sx * sy - shy * shx; } // 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 with caution considering // possible degenerate cases. 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; }; //------------------------------------------------------------------------ inline void trans_affine::transform(double* x, double* y) const { 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 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_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 * 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; } //------------------------------------------------------------------------ inline const trans_affine& trans_affine::premultiply(const trans_affine& m) { trans_affine t = m; return *this = t.multiply(*this); } //------------------------------------------------------------------------ inline const trans_affine& trans_affine::multiply_inv(const trans_affine& m) { trans_affine t = m; t.invert(); return multiply(t); } //------------------------------------------------------------------------ inline const trans_affine& trans_affine::premultiply_inv(const trans_affine& m) { trans_affine t = m; t.invert(); 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 // often. class trans_affine_rotation : public trans_affine { public: 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. x, y - scale coefficients by X and Y respectively class trans_affine_scaling : public trans_affine { public: 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(s, 0.0, 0.0, s, 0.0, 0.0) {} }; //================================================trans_affine_translation // Translation matrix class trans_affine_translation : public trans_affine { public: trans_affine_translation(double x, double y) : trans_affine(1.0, 0.0, 0.0, 1.0, x, y) {} }; //====================================================trans_affine_skewing // Sckewing (shear) matrix class trans_affine_skewing : public trans_affine { public: 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 // 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, double dist) { double dx = x2 - x1; double dy = y2 - y1; if(dist > 0.0) { multiply(trans_affine_scaling(std::sqrt(dx * dx + dy * dy) / dist)); } 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)) {} }; } #endif