///////////////////////////////////////////////////////////////////////////////
// Name:        affinematrix2d.cpp
// Purpose:     implementation of wxAffineMatrix2D
// Author:      Based on wxTransformMatrix by Chris Breeze, Julian Smart
// Created:     2011-04-05
// Copyright:   (c) wxWidgets team
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#include "wx/wxprec.h"

#include "wx/affinematrix2d.h"
#include "wx/math.h"

// sets the matrix to the respective values
void wxAffineMatrix2D::Set(const wxMatrix2D &mat2D, const wxPoint2DDouble &tr)
{
    m_11 = mat2D.m_11;
    m_12 = mat2D.m_12;
    m_21 = mat2D.m_21;
    m_22 = mat2D.m_22;
    m_tx = tr.m_x;
    m_ty = tr.m_y;
}

// gets the component values of the matrix
void wxAffineMatrix2D::Get(wxMatrix2D *mat2D, wxPoint2DDouble *tr) const
{
    wxASSERT_MSG( mat2D || tr,
                  wxT("at least one parameter must be non-null") );

    if( mat2D )
    {
        mat2D->m_11 = m_11;
        mat2D->m_12 = m_12;
        mat2D->m_21 = m_21;
        mat2D->m_22 = m_22;
    }

    if ( tr )
    {
        tr->m_x = m_tx;
        tr->m_y = m_ty;
    }
}

// concatenates the matrix
// | t.m_11  t.m_12  0 |   | m_11  m_12   0 |
// | t.m_21  t.m_22  0 | x | m_21  m_22   0 |
// | t.m_tx  t.m_ty  1 |   | m_tx  m_ty   1 |
void wxAffineMatrix2D::Concat(const wxAffineMatrix2DBase &t)
{
    wxMatrix2D mat;
    wxPoint2DDouble tr;
    t.Get(&mat, &tr);

    m_tx += tr.m_x*m_11 + tr.m_y*m_21;
    m_ty += tr.m_x*m_12 + tr.m_y*m_22;
    wxDouble e11 = mat.m_11*m_11 + mat.m_12*m_21;
    wxDouble e12 = mat.m_11*m_12 + mat.m_12*m_22;
    wxDouble e21 = mat.m_21*m_11 + mat.m_22*m_21;
    m_22 = mat.m_21*m_12 + mat.m_22*m_22;
    m_11 = e11;
    m_12 = e12;
    m_21 = e21;
}

// makes this its inverse matrix.
// Invert
// | m_11  m_12   0 |
// | m_21  m_22   0 |
// | m_tx  m_ty   1 |
bool wxAffineMatrix2D::Invert()
{
    const wxDouble det = m_11*m_22 - m_12*m_21;

    if ( det == 0 )
        return false;

    wxDouble ex = (m_21*m_ty - m_22*m_tx) / det;
    m_ty = (-m_11*m_ty + m_12*m_tx) / det;
    m_tx = ex;
    wxDouble e11 = m_22 / det;
    m_12 = -m_12 / det;
    m_21 = -m_21 / det;
    m_22 = m_11 / det;
    m_11 = e11;

    return true;
}

// returns true if the elements of the transformation matrix are equal
bool wxAffineMatrix2D::IsEqual(const wxAffineMatrix2DBase& t) const
{
    wxMatrix2D mat;
    wxPoint2DDouble tr;
    t.Get(&mat, &tr);

    return m_11 == mat.m_11 && m_12 == mat.m_12 &&
           m_21 == mat.m_21 && m_22 == mat.m_22 &&
           m_tx == tr.m_x && m_ty == tr.m_y;
}

//
// transformations
//

// add the translation to this matrix
// |  1   0   0 |   | m_11  m_12   0 |
// |  0   1   0 | x | m_21  m_22   0 |
// | dx  dy   1 |   | m_tx  m_ty   1 |
void wxAffineMatrix2D::Translate(wxDouble dx, wxDouble dy)
{
    m_tx += m_11 * dx + m_21 * dy;
    m_ty += m_12 * dx + m_22 * dy;
}

// add the scale to this matrix
// | xScale   0      0 |   | m_11  m_12   0 |
// |   0    yScale   0 | x | m_21  m_22   0 |
// |   0      0      1 |   | m_tx  m_ty   1 |
void wxAffineMatrix2D::Scale(wxDouble xScale, wxDouble yScale)
{
    m_11 *= xScale;
    m_12 *= xScale;
    m_21 *= yScale;
    m_22 *= yScale;
}

// add the rotation to this matrix (clockwise, radians)
// | cos    sin   0 |   | m_11  m_12   0 |
// | -sin   cos   0 | x | m_21  m_22   0 |
// |  0      0    1 |   | m_tx  m_ty   1 |
void wxAffineMatrix2D::Rotate(wxDouble cRadians)
{
    wxDouble c = cos(cRadians);
    wxDouble s = sin(cRadians);

    wxDouble e11 = c*m_11 + s*m_21;
    wxDouble e12 = c*m_12 + s*m_22;
    m_21 = c*m_21 - s*m_11;
    m_22 = c*m_22 - s*m_12;
    m_11 = e11;
    m_12 = e12;
}

//
// apply the transforms
//

// applies that matrix to the point
//                           | m_11  m_12   0 |
// | src.m_x  src._my  1 | x | m_21  m_22   0 |
//                           | m_tx  m_ty   1 |
wxPoint2DDouble
wxAffineMatrix2D::DoTransformPoint(const wxPoint2DDouble& src) const
{
    if ( IsIdentity() )
        return src;

    return wxPoint2DDouble(src.m_x * m_11 + src.m_y * m_21 + m_tx,
                           src.m_x * m_12 + src.m_y * m_22 + m_ty);
}

// applies the matrix except for translations
//                           | m_11  m_12   0 |
// | src.m_x  src._my  0 | x | m_21  m_22   0 |
//                           | m_tx  m_ty   1 |
wxPoint2DDouble
wxAffineMatrix2D::DoTransformDistance(const wxPoint2DDouble& src) const
{
    if ( IsIdentity() )
        return src;

    return wxPoint2DDouble(src.m_x * m_11 + src.m_y * m_21,
                           src.m_x * m_12 + src.m_y * m_22);
}

bool wxAffineMatrix2D::IsIdentity() const
{
    return m_11 == 1 && m_12 == 0 &&
           m_21 == 0 && m_22 == 1 &&
           m_tx == 0 && m_ty == 0;
}
