//----------------------------------------------------------------------------
// 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
//----------------------------------------------------------------------------
//
// Perspective 2D transformations
//
//----------------------------------------------------------------------------
#ifndef AGG_TRANS_PERSPECTIVE_INCLUDED
#define AGG_TRANS_PERSPECTIVE_INCLUDED

#include "agg_basics.h"
#include "agg_simul_eq.h"

namespace agg
{
    //=======================================================trans_perspective
    class trans_perspective
    {
    public:
        //--------------------------------------------------------------------
        trans_perspective() : m_valid(false) {}


        //--------------------------------------------------------------------
        // Arbitrary quadrangle transformations
        trans_perspective(const double* src, const double* dst) 
        {
            quad_to_quad(src, dst);
        }


        //--------------------------------------------------------------------
        // Direct transformations 
        trans_perspective(double x1, double y1, double x2, double y2, 
                          const double* quad)
        {
            rect_to_quad(x1, y1, x2, y2, quad);
        }


        //--------------------------------------------------------------------
        // Reverse transformations 
        trans_perspective(const double* quad, 
                          double x1, double y1, double x2, double y2)
        {
            quad_to_rect(quad, x1, y1, x2, y2);
        }


        //--------------------------------------------------------------------
        // Set the transformations using two arbitrary quadrangles.
        void quad_to_quad(const double* src, const double* dst)
        {

            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);
        }


        //--------------------------------------------------------------------
        // Set the direct transformations, i.e., rectangle -> quadrangle
        void rect_to_quad(double x1, double y1, double x2, double y2, 
                          const double* quad)
        {
            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);
        }


        //--------------------------------------------------------------------
        // Set the reverse transformations, i.e., quadrangle -> rectangle
        void quad_to_rect(const double* quad, 
                          double x1, double y1, double x2, double y2)
        {
            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);
        }

        //--------------------------------------------------------------------
        // 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
        {
            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;
        }

        //--------------------------------------------------------------------
        class iterator_x
        {
            double den;
            double den_step;
            double nom_x;
            double nom_x_step;
            double nom_y;
            double nom_y_step;

        public:
            double x;
            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),
                x(nom_x / den),
                y(nom_y / den)
            {
            }

            void operator ++ ()
            {
                den   += den_step;
                nom_x += nom_x_step;
                nom_y += nom_y_step;
                double d = 1.0 / den;
                x = nom_x * d;
                y = nom_y * d;
            }
        };

        //--------------------------------------------------------------------
        iterator_x begin(double x, double y, double step) const
        {
            return iterator_x(x, y, step, m_mtx);
        }

    private:
        double m_mtx[8][1];
        bool   m_valid;
    };

}

#endif
