//------------------------------------------------------------------------------
// emATMatrix.h
//
// Copyright (C) 2005-2008 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emATMatrix_h
#define emATMatrix_h

#ifndef emStd1_h
#include <emCore/emStd1.h>
#endif


//==============================================================================
//================================= emATMatrix =================================
//==============================================================================

class emATMatrix {

public:

        // Class for an affine transformation matrix. It is a 3x3 matrix with
        // constant elements in the last column:
        //    _                 _
        //   |   a00  a01  0.0   |
        //   |   a10  a11  0.0   |
        //   |_  a20  a21  1.0  _|
        //
        // Transforming a source coordinate (sx,sy) to a target coordinate
        // (tx,ty) is:
        //
        //   tx = a00*sx + a10*sy + a20
        //   ty = a01*sx + a11*sy + a21
        //
        // Please see the classes emIdentityATM, emInvertATM, emMultiplyATM,
        // emTranslateATM, emScaleATM, emRotateATM and emShearATM more below.
        // They provide lots of constructors for creating typical matrices. Here
        // is an example of constructing a matrix which performs rotation first,
        // then scaling and finally translation:
        //
        //   emTranslateATM(5.0,7.0,emScaleATM(2.0,3.0,emRotateATM(90.0)))
        //
        // The following constructing would result equal, but a bit slower:
        //
        //   emRotateATM(90.0) * emScaleATM(2.0,3.0) * emTranslateATM(5.0,7.0)


        emATMatrix();
                // Performs no initialization (this is a "primitive" type).

        emATMatrix(double a00, double a01,
                   double a10, double a11,
                   double a20, double a21);
                // Construct from the given elements.

        emATMatrix(const emATMatrix & m);
                // Construct a copied matrix.

        emATMatrix & operator = (const emATMatrix & m);
                // Copy a matrix.

        double Get(int i, int j) const;
        void Set(int i, int j, double aij);
                // Get or set an element of this matrix. i is the row index and
                // must be 0, 1 or 2. j is the column index and must be 0 or 1.

        double TransX(double sx, double sy) const;
        double TransY(double sx, double sy) const;
                // Transform a point from source coordinates to target
                // coordinates.

        double InverseTransX(double tx, double ty) const;
        double InverseTransY(double tx, double ty) const;
                // Transform a point from target coordinates back to source
                // coordinates. This is quite slower than TransX and TransY. For
                // inverse-transforming more than about 4 points, it would be
                // faster to create and use an emInvertATM.

        bool operator == (const emATMatrix & m) const;
        bool operator != (const emATMatrix & m) const;
                // Compare matrices.

        emATMatrix & operator *= (const emATMatrix & m);
                // Like *this = emMultiplyATM(*this,m).

        emATMatrix operator * (const emATMatrix & m) const;
                // Like emMultiplyATM(*this,m).

private:
        friend class emIdentityATM;
        friend class emInvertATM;
        friend class emMultiplyATM;
        friend class emTranslateATM;
        friend class emScaleATM;
        friend class emRotateATM;
        friend class emShearATM;
        double A[3][2];
};


//==============================================================================
//=============================== emIdentityATM ================================
//==============================================================================

class emIdentityATM : public emATMatrix {

public:

        emIdentityATM();
                // Construct an identity matrix. This means, source coordinates
                // are equal to target coordinates.
};


//==============================================================================
//================================ emInvertATM =================================
//==============================================================================

class emInvertATM : public emATMatrix {

public:

        emInvertATM(const emATMatrix & m);
                // Construct an inverse matrix. It performs inverse
                // transformations in compare to m.
};


//==============================================================================
//=============================== emMultiplyATM ================================
//==============================================================================

class emMultiplyATM : public emATMatrix {

public:

        emMultiplyATM(const emATMatrix & m1, const emATMatrix & m2);
                // Construct a multiplied matrix. Transforming a point with this
                // matrix gives the same result as transforming the point first
                // with m1 and then with m2.

        emMultiplyATM(const emATMatrix & m1, const emATMatrix & m2,
                      const emATMatrix & m3);
                // Like emMultiplyATM(emMultiplyATM(m1,m2),m3)

        emMultiplyATM(const emATMatrix & m1, const emATMatrix & m2,
                      const emATMatrix & m3, const emATMatrix & m4);
                // Like emMultiplyATM(emMultiplyATM(m1,m2,m3),m4)
};


//==============================================================================
//=============================== emTranslateATM ===============================
//==============================================================================

class emTranslateATM : public emATMatrix {

public:

        emTranslateATM(double addX, double addY);
                // Construct a matrix for translating. This matrix transforms
                // source coordinates (sx,sy) to target coordinates (tx,ty)
                // with:
                //   tx = sx + addX
                //   ty = sy + addY

        emTranslateATM(double addX, double addY, const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emTranslateATM(addX,addY)
                //   )
};


//==============================================================================
//================================= emScaleATM =================================
//==============================================================================

class emScaleATM : public emATMatrix {

public:

        emScaleATM(double facX, double facY);
                // Construct a matrix for scaling. This matrix transforms source
                // coordinates (sx,sy) to target coordinates (tx,ty) with:
                //   tx = sx * facX
                //   ty = sy * facY

        emScaleATM(double facX, double facY, const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emScaleATM(facX,facY)
                //   )

        emScaleATM(double facX, double facY, double fixX, double fixY);
                // Like:
                //   emMultiplyATM(
                //     emTranslateATM(-fixX,-fixY),
                //     emScaleATM(facX,facY),
                //     emTranslateATM(fixX,fixY)
                //   )

        emScaleATM(double facX, double facY, double fixX, double fixY,
                   const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emTranslateATM(-fixX,-fixY),
                //     emScaleATM(facX,facY),
                //     emTranslateATM(fixX,fixY)
                //   )
};


//==============================================================================
//================================ emRotateATM =================================
//==============================================================================

class emRotateATM : public emATMatrix {

public:

        emRotateATM(double angle);
                // Construct a matrix for rotating. The angle is in degrees.
                // This matrix transforms source coordinates (sx,sy) to target
                // coordinates (tx,ty) with:
                //   tx = sx*cos(angle/180.0*M_PI) - sy*sin(angle/180.0*M_PI)
                //   ty = sy*cos(angle/180.0*M_PI) + sx*sin(angle/180.0*M_PI)

        emRotateATM(double angle, const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emRotateATM(angle)
                //   )

        emRotateATM(double angle, double fixX, double fixY);
                // Like:
                //   emMultiplyATM(
                //     emTranslateATM(-fixX,-fixY),
                //     emRotateATM(angle),
                //     emTranslateATM(fixX,fixY)
                //   )

        emRotateATM(double angle, double fixX, double fixY,
                    const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emTranslateATM(-fixX,-fixY),
                //     emRotateATM(angle),
                //     emTranslateATM(fixX,fixY)
                //   )
};


//==============================================================================
//================================= emShearATM =================================
//==============================================================================

class emShearATM : public emATMatrix {

public:

        emShearATM(double shX, double shY);
                // Construct a matrix for shearing. This matrix transforms
                // source coordinates (sx,sy) to target coordinates (tx,ty)
                // with:
                //    tx = sx + sy*shX
                //    ty = sy + sx*shY

        emShearATM(double shX, double shY, const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emShearATM(shX,shY)
                //   )

        emShearATM(double shX, double shY, double fixX, double fixY);
                // Like:
                //   emMultiplyATM(
                //     emTranslateATM(-fixX,-fixY),
                //     emShearATM(shX,shY),
                //     emTranslateATM(fixX,fixY)
                //   )

        emShearATM(double shX, double shY, double fixX, double fixY,
                   const emATMatrix & m);
                // Like:
                //   emMultiplyATM(
                //     m,
                //     emTranslateATM(-fixX,-fixY),
                //     emShearATM(shX,shY),
                //     emTranslateATM(fixX,fixY)
                //   )
};


//==============================================================================
//=========================== Inline implementations ===========================
//==============================================================================

inline emATMatrix::emATMatrix()
{
}

inline double emATMatrix::Get(int i, int j) const
{
        return A[i][j];
}

inline void emATMatrix::Set(int i, int j, double aij)
{
        A[i][j]=aij;
}

inline double emATMatrix::TransX(double sx, double sy) const
{
        return A[0][0]*sx + A[1][0]*sy + A[2][0];
}

inline double emATMatrix::TransY(double sx, double sy) const
{
        return A[0][1]*sx + A[1][1]*sy + A[2][1];
}


#endif