//==============================================================================
/*! \file
 * Medical Data Segmentation Toolkit (MDSTk)    \n
 * Copyright (c) 2003-2010 by Michal Spanel     \n
 *
 * Author:  Michal Spanel, spanel@fit.vutbr.cz  \n
 * Date:    2005/10/17                          \n
 *
 * $Id: mdsTriIterator.h 1619 2010-02-11 08:35:23Z spanel $
 *
 * This file is part of software developed for support of Rostislav Hulik's dissertation thesis at dcgm-robotics@FIT group.
 *
 * This file is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This file 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Description:
 * - Triangle pixels iterator.
 * - This header file origins in MDSTk Extensions, but it was rewriten for use with OpenMesh Triangles
 */

#ifndef _OM_TRI_ITERATOR_H_
#define _OM_TRI_ITERATOR_H_

// MDSTk
#include <MDSTk/Base/mdsMemory.h>
#include <MDSTk/Base/mdsIterator.h>
#include <MDSTk/Math/mdsBase.h>

#include <OpenMesh\Core\Geometry\VectorT.hh>

namespace OpenMesh
{
//==============================================================================
/**
 * Iterator used to traverse pixels inside a triangle.
 */
template <class VectorT>
class OMTriIterator : public mds::base::CIteratorBase<OMTriIterator<VectorT>>
{
	public:
		/**
		 * Constructor
		 * @param VectorT[3] array of triangle vertices
		 */
		OMTriIterator(VectorT *vertices)
		{
			init(vertices);
		}

		/**
		 * Destructor
		 */
		~OMTriIterator() {}

		/**
		 * Returns current iterator X position
		 * @return X Position
		 */
		mds::tSize getX() const 
		{ 
			return m_Impl.m_X; 
		}

		/**
		 * Returns current iterator Y position
		 * @return Y Position
		 */
		mds::tSize getY() const 
		{ 
			return m_Impl.m_Y; 
		}

		/**
		 * Control if iterator is still valid
		 * @return True if iterator points after the last triangle pixel
		 */
		bool isEnd() const 
		{ 
			return (m_Impl.m_Y > m_Impl.m_iMaxY); 
		}

protected:
    /**
	 * All data members
	 */
    struct SDataMembers
    {
        /**
		 * Bounding rectangle
		 */
        int m_iMinX, m_iMaxX, m_iMinY, m_iMaxY;

        /**
		 * Deltas
		 */
        int m_iFDX1, m_iFDX2, m_iFDX3;
        int m_iFDY1, m_iFDY2, m_iFDY3;

        /**
		 * Flags
		 */
        int m_iCY1, m_iCY2, m_iCY3;
        int m_iCX1, m_iCX2, m_iCX3;

        /**
		 * Iterator position
		 */
        mds::tSize m_X, m_Y;
    };

    /**
	 * Data members
	 */
    SDataMembers m_Impl;

public:
    /**
	 * Moves iterator to the next triangle pixel
	 */
    void advance()
    {
        do {
            if( isEnd() )
            {
                break;
            }
            else
            {
                next();
            }
        } while( !isInner() );
    }

protected:
    /**
	 * Returns true if the current pixel is inside the triangle
	 */
    bool isInner()
    {
        return (m_Impl.m_iCX1 <= 0 && m_Impl.m_iCX2 <= 0 && m_Impl.m_iCX3 <= 0);
    }

    /**
	 * Initializes the triangle iterator
	 */
    void init(VectorT *vertices)
    {
        // Fixed-point coordinates
        int iY1 = mds::math::round2Int(16.0 * vertices[0][1]);
        int iY2 = mds::math::round2Int(16.0 * vertices[1][1]);
        int iY3 = mds::math::round2Int(16.0 * vertices[2][1]);

        int iX1 = mds::math::round2Int(16.0 * vertices[0][0]);
        int iX2 = mds::math::round2Int(16.0 * vertices[1][0]);
        int iX3 = mds::math::round2Int(16.0 * vertices[2][0]);

        // Deltas
        int iDX1 = iX2 - iX1;
        int iDX2 = iX3 - iX2;
        int iDX3 = iX1 - iX3;

        int iDY1 = iY2 - iY1;
        int iDY2 = iY3 - iY2;
        int iDY3 = iY1 - iY3;

        // Fixed-point deltas
        m_Impl.m_iFDX1 = iDX1 << 4;
        m_Impl.m_iFDX2 = iDX2 << 4;
        m_Impl.m_iFDX3 = iDX3 << 4;

        m_Impl.m_iFDY1 = iDY1 << 4;
        m_Impl.m_iFDY2 = iDY2 << 4;
        m_Impl.m_iFDY3 = iDY3 << 4;

        // Bounding rectangle
        m_Impl.m_iMinX = mds::math::getMin(iX1, iX2, iX3) >> 4;
        m_Impl.m_iMaxX = mds::math::getMax(iX1, iX2, iX3) >> 4;
        m_Impl.m_iMinY = mds::math::getMin(iY1, iY2, iY3) >> 4;
        m_Impl.m_iMaxY = mds::math::getMax(iY1, iY2, iY3) >> 4;

        // Half-edge constants
        int iC1 = iDX1 * iY1 - iDY1 * iX1;
        int iC2 = iDX2 * iY2 - iDY2 * iX2;
        int iC3 = iDX3 * iY3 - iDY3 * iX3;

        // Initialization
        m_Impl.m_iCY1 = iC1 - iDX1 * (m_Impl.m_iMinY << 4) + iDY1 * (m_Impl.m_iMinX << 4);
        m_Impl.m_iCY2 = iC2 - iDX2 * (m_Impl.m_iMinY << 4) + iDY2 * (m_Impl.m_iMinX << 4);
        m_Impl.m_iCY3 = iC3 - iDX3 * (m_Impl.m_iMinY << 4) + iDY3 * (m_Impl.m_iMinX << 4);

        m_Impl.m_iCX1 = m_Impl.m_iCY1;
        m_Impl.m_iCX2 = m_Impl.m_iCY2;
        m_Impl.m_iCX3 = m_Impl.m_iCY3;

        m_Impl.m_X = m_Impl.m_iMinX;
        m_Impl.m_Y = m_Impl.m_iMinY;

        // Find the first pixel
        if( !isInner() )
        {
            advance();
        }
    }

    /**
	 * Moves iterator to the next pixel
	 */
    void next()
    {
        if( ++m_Impl.m_X > m_Impl.m_iMaxX )
        {
            m_Impl.m_X = m_Impl.m_iMinX;
            ++m_Impl.m_Y;
            m_Impl.m_iCY1 -= m_Impl.m_iFDX1;
            m_Impl.m_iCY2 -= m_Impl.m_iFDX2;
            m_Impl.m_iCY3 -= m_Impl.m_iFDX3;
            m_Impl.m_iCX1 = m_Impl.m_iCY1;
            m_Impl.m_iCX2 = m_Impl.m_iCY2;
            m_Impl.m_iCX3 = m_Impl.m_iCY3;
        }
        else
        {
            m_Impl.m_iCX1 += m_Impl.m_iFDY1;
            m_Impl.m_iCX2 += m_Impl.m_iFDY2;
            m_Impl.m_iCX3 += m_Impl.m_iFDY3;
        }
    }
};

} // namespace OMToolkit

#endif // _OM_TRI_ITERATOR_H_

