/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"
#include <osl/endian.h>
#include <tools/stream.hxx>
#include <tools/debug.hxx>
#include <tools/poly.hxx>

#include <svx/xpoly.hxx>
#include "xpolyimp.hxx"
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <vcl/salbtype.hxx>		// FRound
#include <basegfx/range/b2drange.hxx>
#include <basegfx/numeric/ftools.hxx>

#define GLOBALOVERFLOW

DBG_NAME(XPolygon);
DBG_NAME(XPolyPolygon);

/*************************************************************************
|*
|*    ImpXPolygon::ImpXPolygon()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

ImpXPolygon::ImpXPolygon( sal_uInt16 nInitSize, sal_uInt16 _nResize )
{
	pPointAry               = NULL;
	pFlagAry                = NULL;
	bDeleteOldPoints        = sal_False;
	nSize                   = 0;
	nResize					= _nResize;
	nPoints                 = 0;
	nRefCount               = 1;

	Resize( nInitSize );
}

/*************************************************************************
|*
|*    ImpXPolygon::ImpXPolygon()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

ImpXPolygon::ImpXPolygon( const ImpXPolygon& rImpXPoly )
{
	( (ImpXPolygon&) rImpXPoly ).CheckPointDelete();

	pPointAry               = NULL;
	pFlagAry                = NULL;
	bDeleteOldPoints        = sal_False;
	nSize                   = 0;
	ImpXPolygon::nResize    = rImpXPoly.nResize;
	nPoints                 = 0;
	nRefCount               = 1;

	Resize( rImpXPoly.nSize );

	// Kopieren
	nPoints = rImpXPoly.nPoints;
	memcpy( pPointAry, rImpXPoly.pPointAry, nSize*sizeof( Point ) );
	memcpy( pFlagAry, rImpXPoly.pFlagAry, nSize );
}

/*************************************************************************
|*
|*    ImpXPolygon::~ImpXPolygon()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

ImpXPolygon::~ImpXPolygon()
{
	delete[] (char*) pPointAry;
	delete[] pFlagAry;
	if ( bDeleteOldPoints )
		delete[] (char*) pOldPointAry;
}

/*************************************************************************
|*
|*    ImpXPolygon::operator==()
|*
|*    Ersterstellung    Joe 26-09-95
|*    Letzte Aenderung
|*
*************************************************************************/


bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
{
	return nPoints==rImpXPoly.nPoints &&
		   (nPoints==0 ||
			(memcmp(pPointAry,rImpXPoly.pPointAry,nPoints*sizeof(Point))==0 &&
			 memcmp(pFlagAry,rImpXPoly.pFlagAry,nPoints)==0));
}

/*************************************************************************
|*
|*    ImpXPolygon::Resize()
|*
|*    !!! Polygongroesse aendern - wenn bDeletePoints sal_False, dann den
|*    Point-Array nicht loeschen, sondern in pOldPointAry sichern und
|*    das Flag bDeleteOldPoints setzen. Beim naechsten Zugriff wird
|*    das Array dann geloescht.
|*    Damit wird verhindert, dass bei XPoly[n] = XPoly[0] durch ein
|*    Resize der fuer den rechten Ausdruck verwendete Point-Array
|*    vorzeitig geloescht wird.
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

void ImpXPolygon::Resize( sal_uInt16 nNewSize, sal_Bool bDeletePoints )
{
	if( nNewSize == nSize )
		return;

	sal_uInt8*   pOldFlagAry  = pFlagAry;
	sal_uInt16  nOldSize     = nSize;

	CheckPointDelete();
	pOldPointAry = pPointAry;

	// Neue Groesse auf vielfaches von nResize runden, sofern Objekt
	// nicht neu angelegt wurde (nSize != 0)
	if ( nSize != 0 && nNewSize > nSize )
	{
		DBG_ASSERT(nResize, "Resize-Versuch trotz nResize = 0 !");
		nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
	}
	// Punkt Array erzeugen
	nSize     = nNewSize;
	pPointAry = (Point*)new char[ nSize*sizeof( Point ) ];
	memset( pPointAry, 0, nSize*sizeof( Point ) );

	// Flag Array erzeugen
	pFlagAry = new sal_uInt8[ nSize ];
	memset( pFlagAry, 0, nSize );

	// Eventuell umkopieren
	if( nOldSize )
	{
		if( nOldSize < nSize )
		{
			memcpy( pPointAry, pOldPointAry, nOldSize*sizeof( Point ) );
			memcpy( pFlagAry,  pOldFlagAry, nOldSize );
		}
		else
		{
			memcpy( pPointAry, pOldPointAry, nSize*sizeof( Point ) );
			memcpy( pFlagAry, pOldFlagAry, nSize );

			// Anzahl der gueltigen Punkte anpassen
			if( nPoints > nSize )
				nPoints = nSize;
		}
		if ( bDeletePoints )    delete[] (char*) pOldPointAry;
		else                    bDeleteOldPoints = sal_True;
		delete[] pOldFlagAry;
	}
}


/*************************************************************************
|*
|*    ImpXPolygon::InsertSpace()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  29.03.95 ESO
|*
*************************************************************************/

void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
{
	CheckPointDelete();

	if ( nPos > nPoints )
		nPos = nPoints;

	// Wenn Polygon zu klein dann groesser machen
	if( (nPoints + nCount) > nSize )
		Resize( nPoints + nCount );

	// Wenn nicht hinter dem letzten Punkt eingefuegt wurde,
	// den Rest nach hinten schieben
	if( nPos < nPoints )
	{
		sal_uInt16 nMove = nPoints - nPos;
		memmove( &pPointAry[nPos+nCount], &pPointAry[nPos],
				 nMove * sizeof(Point) );
		memmove( &pFlagAry[nPos+nCount], &pFlagAry[nPos], nMove );
	}
	memset( &pPointAry[nPos], 0, nCount * sizeof( Point ) );
	memset( &pFlagAry [nPos], 0, nCount );

	nPoints = nPoints + nCount;
}


/*************************************************************************
|*
|*    ImpXPolygon::Remove()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.94 ESO
|*
*************************************************************************/

void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
{
	CheckPointDelete();

	if( (nPos + nCount) <= nPoints )
	{
		sal_uInt16 nMove = nPoints - nPos - nCount;

		if( nMove )
		{
			memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
					 nMove * sizeof(Point) );
			memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
		}
		memset( &pPointAry[nPoints - nCount], 0, nCount * sizeof( Point ) );
		memset( &pFlagAry [nPoints - nCount], 0, nCount );
		nPoints = nPoints - nCount;
	}
}


/*************************************************************************
|*
|*    XPolygon::XPolygon()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

XPolygon::XPolygon( sal_uInt16 nSize, sal_uInt16 nResize )
{
	DBG_CTOR(XPolygon,NULL);
	pImpXPolygon = new ImpXPolygon( nSize, nResize );
}

/*************************************************************************
|*
|*    XPolygon::XPolygon()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

XPolygon::XPolygon( const XPolygon& rXPoly )
{
	DBG_CTOR(XPolygon,NULL);
	pImpXPolygon = rXPoly.pImpXPolygon;
	pImpXPolygon->nRefCount++;
}

/*************************************************************************
|*
|*    XPolygon::XPolygon()
|*
|*    XPolygon aus einem Standardpolygon erstellen
|*    Ersterstellung    18.01.95 ESO
|*    Letzte Aenderung  18.01.95 ESO
|*
*************************************************************************/

XPolygon::XPolygon( const Polygon& rPoly )
{
	DBG_CTOR(XPolygon,NULL);

	sal_uInt16 nSize = rPoly.GetSize();
	pImpXPolygon = new ImpXPolygon( nSize );
	pImpXPolygon->nPoints = nSize;

	for( sal_uInt16 i = 0; i < nSize;  i++ )
	{
		pImpXPolygon->pPointAry[i] = rPoly[i];
		pImpXPolygon->pFlagAry[i] = (sal_uInt8) rPoly.GetFlags( i );
	}
}

/*************************************************************************
|*
|*    XPolygon::XPolygon()
|*
|*    Rechteck (auch mit abgerundeten Ecken) als Bezierpolygon erzeugen
|*    Ersterstellung    09.01.95 ESO
|*    Letzte Aenderung  09.01.95 ESO
|*
*************************************************************************/

XPolygon::XPolygon(const Rectangle& rRect, long nRx, long nRy)
{
	DBG_CTOR(XPolygon,NULL);
	pImpXPolygon = new ImpXPolygon(17);
	long nWh = (rRect.GetWidth()  - 1) / 2;
	long nHh = (rRect.GetHeight() - 1) / 2;

	if ( nRx > nWh )    nRx = nWh;
	if ( nRy > nHh )    nRy = nHh;

	// Rx negativ, damit Umlauf im Uhrzeigersinn erfolgt
	nRx = -nRx;

	// Faktor fuer Kontrollpunkte der Bezierkurven: 8/3 * (sin(45g) - 0.5)
	long    nXHdl = (long)(0.552284749 * nRx);
	long    nYHdl = (long)(0.552284749 * nRy);
	sal_uInt16  nPos = 0;

	if ( nRx && nRy )
	{
		Point aCenter;

		for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
		{
			switch ( nQuad )
			{
				case 0:	aCenter = rRect.TopLeft();
						aCenter.X() -= nRx;
						aCenter.Y() += nRy;
						break;
				case 1:	aCenter = rRect.TopRight();
						aCenter.X() += nRx;
						aCenter.Y() += nRy;
						break;
				case 2:	aCenter = rRect.BottomRight();
						aCenter.X() += nRx;
						aCenter.Y() -= nRy;
						break;
				case 3:	aCenter = rRect.BottomLeft();
						aCenter.X() -= nRx;
						aCenter.Y() -= nRy;
						break;
			}
			GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0, 900, nQuad, nPos);
			pImpXPolygon->pFlagAry[nPos  ] = (sal_uInt8) XPOLY_SMOOTH;
			pImpXPolygon->pFlagAry[nPos+3] = (sal_uInt8) XPOLY_SMOOTH;
			nPos += 4;
		}
	}
	else
	{
		pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
		pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
		pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
		pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
	}
	pImpXPolygon->pPointAry[nPos] = pImpXPolygon->pPointAry[0];
	pImpXPolygon->nPoints = nPos + 1;
}

/*************************************************************************
|*
|*    XPolygon::XPolygon()
|*
|*    Ellipsen(bogen) als Bezierpolygon erzeugen
|*    Ersterstellung    09.01.95
|*    Letzte Aenderung  09.01.95
|*
*************************************************************************/

XPolygon::XPolygon(const Point& rCenter, long nRx, long nRy,
				   sal_uInt16 nStartAngle, sal_uInt16 nEndAngle, sal_Bool bClose)
{
	DBG_CTOR(XPolygon,NULL);
	pImpXPolygon = new ImpXPolygon(17);

	nStartAngle %= 3600;
	if ( nEndAngle > 3600 ) nEndAngle %= 3600;
	sal_Bool bFull = (nStartAngle == 0 && nEndAngle == 3600);

	// Faktor fuer Kontrollpunkte der Bezierkurven: 8/3 * (sin(45g) - 0.5)
	long    nXHdl = (long)(0.552284749 * nRx);
	long    nYHdl = (long)(0.552284749 * nRy);
	sal_uInt16  nPos = 0;
	sal_Bool    bLoopEnd = sal_False;

	do
	{
		sal_uInt16 nA1, nA2;
		sal_uInt16 nQuad = nStartAngle / 900;
		if ( nQuad == 4 ) nQuad = 0;
		bLoopEnd = CheckAngles(nStartAngle, nEndAngle, nA1, nA2);
		GenBezArc(rCenter, nRx, nRy, nXHdl, nYHdl, nA1, nA2, nQuad, nPos);
		nPos += 3;
		if ( !bLoopEnd )
			pImpXPolygon->pFlagAry[nPos] = (sal_uInt8) XPOLY_SMOOTH;

	} while ( !bLoopEnd );

	// Wenn kein Vollkreis, dann ggf. Enden mit Mittelpunkt verbinden
	if ( !bFull && bClose )
		pImpXPolygon->pPointAry[++nPos] = rCenter;

	if ( bFull )
	{
		pImpXPolygon->pFlagAry[0   ] = (sal_uInt8) XPOLY_SMOOTH;
		pImpXPolygon->pFlagAry[nPos] = (sal_uInt8) XPOLY_SMOOTH;
	}
	pImpXPolygon->nPoints = nPos + 1;
}

/*************************************************************************
|*
|*    XPolygon::~XPolygon()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

XPolygon::~XPolygon()
{
	DBG_DTOR(XPolygon,NULL);
	if( pImpXPolygon->nRefCount > 1 )
		pImpXPolygon->nRefCount--;
	else
		delete pImpXPolygon;
}

/*************************************************************************
|*
|*    XPolygon::CheckReference()
|*
|*    Referenzzaehler desImpXPoly pruefen und ggf. von diesem abkoppeln
|*    Ersterstellung    17.01.95 ESO
|*    Letzte Aenderung  17.01.95 ESO
|*
*************************************************************************/

void XPolygon::CheckReference()
{
	if( pImpXPolygon->nRefCount > 1 )
	{
		pImpXPolygon->nRefCount--;
		pImpXPolygon = new ImpXPolygon( *pImpXPolygon );
	}
}

/*************************************************************************
|*
|*    XPolygon::SetSize()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

void XPolygon::SetSize( sal_uInt16 nNewSize )
{
	CheckReference();
	pImpXPolygon->Resize( nNewSize );
}

/*************************************************************************
|*
|*    XPolygon::GetSize()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

sal_uInt16 XPolygon::GetSize() const
{
	pImpXPolygon->CheckPointDelete();
	return pImpXPolygon->nSize;
}

/*************************************************************************
|*
|*    XPolygon::SetPointCount()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

void XPolygon::SetPointCount( sal_uInt16 nPoints )
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	if( pImpXPolygon->nSize < nPoints )
		pImpXPolygon->Resize( nPoints );

	if ( nPoints < pImpXPolygon->nPoints )
	{
		sal_uInt16 nSize = pImpXPolygon->nPoints - nPoints;
		memset( &pImpXPolygon->pPointAry[nPoints], 0, nSize * sizeof( Point ) );
		memset( &pImpXPolygon->pFlagAry [nPoints], 0, nSize );
	}
	pImpXPolygon->nPoints = nPoints;
}

/*************************************************************************
|*
|*    XPolygon::GetPointCount()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

sal_uInt16 XPolygon::GetPointCount() const
{
	pImpXPolygon->CheckPointDelete();
	return pImpXPolygon->nPoints;
}

/*************************************************************************
|*
|*    XPolygon::Insert()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, XPolyFlags eFlags )
{
	CheckReference();
	if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
	pImpXPolygon->InsertSpace( nPos, 1 );
	pImpXPolygon->pPointAry[nPos] = rPt;
	pImpXPolygon->pFlagAry[nPos]  = (sal_uInt8)eFlags;
}

/*************************************************************************
|*
|*    XPolygon::Insert()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
{
	CheckReference();
	if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;

	sal_uInt16 nPoints = rXPoly.GetPointCount();

	pImpXPolygon->InsertSpace( nPos, nPoints );

	memcpy( &(pImpXPolygon->pPointAry[nPos]),
			rXPoly.pImpXPolygon->pPointAry,
			nPoints*sizeof( Point ) );
	memcpy( &(pImpXPolygon->pFlagAry[nPos]),
			rXPoly.pImpXPolygon->pFlagAry,
			nPoints );
}

/*************************************************************************
|*
|*    XPolygon::Insert()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

void XPolygon::Insert( sal_uInt16 nPos, const Polygon& rPoly )
{
	CheckReference();
	if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;

	sal_uInt16 nPoints = rPoly.GetSize();

	pImpXPolygon->InsertSpace( nPos, nPoints );

	sal_uInt16 i;
	for( i=0; i < nPoints; i++ )
		pImpXPolygon->pPointAry[i] = rPoly[i];

	// Die Flags sind durch das InsertSpace bereits auf 0 gesetzt
}

/*************************************************************************
|*
|*    XPolygon::Remove()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  08.11.94
|*
*************************************************************************/

void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
{
	CheckReference();
	pImpXPolygon->Remove( nPos, nCount );
}

/*************************************************************************
|*
|*    XPolygon::Move()
|*
|*    Beschreibung
|*    Ersterstellung    09.11.94
|*    Letzte Aenderung  09.11.94
|*
*************************************************************************/

void XPolygon::Move( long nHorzMove, long nVertMove )
{
	if ( !nHorzMove && !nVertMove )
		return;

	CheckReference();

	// Punkte verschieben
	sal_uInt16 nCount = pImpXPolygon->nPoints;
	for ( sal_uInt16 i = 0; i < nCount; i++ )
	{
		Point* pPt = &(pImpXPolygon->pPointAry[i]);
		pPt->X() += nHorzMove;
		pPt->Y() += nVertMove;
	}
}

/*************************************************************************
|*
|*    XPolygon::GetBoundRect()
|*
|*    Beschreibung
|*    Ersterstellung    09.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

Rectangle XPolygon::GetBoundRect() const
{
	pImpXPolygon->CheckPointDelete();
	Rectangle aRetval;

	if(pImpXPolygon->nPoints)
	{
		// #i37709#
		// For historical reasons the control points are not part of the
		// BoundRect. This makes it necessary to subdivide the polygon to
		// get a relatively correct BoundRect. Numerically, this is not
		// correct and never was.

		const basegfx::B2DRange aPolygonRange(basegfx::tools::getRange(getB2DPolygon()));
		aRetval = Rectangle(
			FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
			FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
	}

	return aRetval;
}

/*************************************************************************
|*
|*    XPolygon::operator[]()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95
|*
*************************************************************************/

const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
{
	DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Ungueltiger Index bei const-Arrayzugriff auf XPolygon");

	pImpXPolygon->CheckPointDelete();
	return pImpXPolygon->pPointAry[nPos];
}

/*************************************************************************
|*
|*    XPolygon::operator[]()
|*
|*    Beschreibung
|*    Ersterstellung    08.11.94
|*    Letzte Aenderung  12.01.95 ESO
|*
*************************************************************************/

Point& XPolygon::operator[]( sal_uInt16 nPos )
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	if( nPos >= pImpXPolygon->nSize )
	{
		DBG_ASSERT(pImpXPolygon->nResize, "Ungueltiger Index bei Arrayzugriff auf XPolygon");
		pImpXPolygon->Resize(nPos + 1, sal_False);
	}
	if( nPos >= pImpXPolygon->nPoints )
		pImpXPolygon->nPoints = nPos + 1;

	return pImpXPolygon->pPointAry[nPos];
}

/*************************************************************************
|*
|*    XPolygon::operator=()
|*
|*    Beschreibung      Zuweisungsoperator
|*    Ersterstellung    ESO 22.11.94
|*    Letzte Aenderung  ESO 12.01.95
|*
*************************************************************************/

XPolygon& XPolygon::operator=( const XPolygon& rXPoly )
{
	pImpXPolygon->CheckPointDelete();

	rXPoly.pImpXPolygon->nRefCount++;

	if( pImpXPolygon->nRefCount > 1 )
		pImpXPolygon->nRefCount--;
	else
		delete pImpXPolygon;

	pImpXPolygon = rXPoly.pImpXPolygon;
	return *this;
}

/*************************************************************************
|*
|*    XPolygon::operator==()
|*
|*    Beschreibung      Gleichheitsoperator
|*    Ersterstellung    ESO 22.11.94
|*    Letzte Aenderung  Joe 26.09.95
|*
*************************************************************************/

sal_Bool XPolygon::operator==( const XPolygon& rXPoly ) const
{
	pImpXPolygon->CheckPointDelete();
	if (rXPoly.pImpXPolygon==pImpXPolygon) return sal_True;
	return *rXPoly.pImpXPolygon == *pImpXPolygon;
}

/*************************************************************************
|*
|*    XPolygon::operator!=()
|*
|*    Beschreibung      Ungleichheitsoperator
|*    Ersterstellung    ESO 22.11.94
|*    Letzte Aenderung  Joe 26.09.95
|*
*************************************************************************/

sal_Bool XPolygon::operator!=( const XPolygon& rXPoly ) const
{
	pImpXPolygon->CheckPointDelete();
	if (rXPoly.pImpXPolygon==pImpXPolygon) return sal_False;
	return *rXPoly.pImpXPolygon != *pImpXPolygon;
}

/*************************************************************************
|*
|*    XPolygon::GetFlags()
|*
|*    Flags fuer den Punkt an der Position nPos zurueckgeben
|*    Ersterstellung    ESO 11.11.94
|*    Letzte Aenderung  ESO 12.01.95
|*
*************************************************************************/

XPolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
{
	pImpXPolygon->CheckPointDelete();
	return (XPolyFlags) pImpXPolygon->pFlagAry[nPos];
}

/*************************************************************************
|*
|*    XPolygon::SetFlags()
|*
|*    Flags fuer den Punkt an der Position nPos setzen
|*    Ersterstellung    ESO 11.11.94
|*    Letzte Aenderung  ESO 12.01.95
|*
*************************************************************************/

void XPolygon::SetFlags( sal_uInt16 nPos, XPolyFlags eFlags )
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();
	pImpXPolygon->pFlagAry[nPos] = (sal_uInt8) eFlags;
}

/*************************************************************************
|*
|*    XPolygon::IsControl()
|*
|*    Kurzform zur Abfrage des CONTROL-Flags
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 12.01.95
|*
*************************************************************************/

sal_Bool XPolygon::IsControl(sal_uInt16 nPos) const
{
	return ( (XPolyFlags) pImpXPolygon->pFlagAry[nPos] == XPOLY_CONTROL );
}

/*************************************************************************
|*
|*    XPolygon::IsSmooth()
|*
|*    Kurzform zur Abfrage von SMOOTH- und SYMMTR-Flag
|*    Ersterstellung    ESO 18.04.95
|*    Letzte Aenderung  ESO 18.04.95
|*
*************************************************************************/

sal_Bool XPolygon::IsSmooth(sal_uInt16 nPos) const
{
	XPolyFlags eFlag = (XPolyFlags) pImpXPolygon->pFlagAry[nPos];
	return ( eFlag == XPOLY_SMOOTH || eFlag == XPOLY_SYMMTR );
}

/*************************************************************************
|*
|*    XPolygon::CalcDistance()
|*
|*    Abstand zwischen zwei Punkten berechnen
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 09.01.95
|*
*************************************************************************/

double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
{
	const Point& rP1 = pImpXPolygon->pPointAry[nP1];
	const Point& rP2 = pImpXPolygon->pPointAry[nP2];
	double fDx = rP2.X() - rP1.X();
	double fDy = rP2.Y() - rP1.Y();
	return sqrt(fDx * fDx + fDy * fDy);
}

/*************************************************************************
|*
|*    XPolygon::SubdivideBezier()
|*
|*    Bezierkurve unterteilen
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 09.01.95
|*
*************************************************************************/

void XPolygon::SubdivideBezier(sal_uInt16 nPos, sal_Bool bCalcFirst, double fT)
{
	Point*  pPoints = pImpXPolygon->pPointAry;
	double  fT2 = fT * fT;
	double  fT3 = fT * fT2;
	double  fU = 1.0 - fT;
	double  fU2 = fU * fU;
	double  fU3 = fU * fU2;
	sal_uInt16  nIdx = nPos;
	short   nPosInc, nIdxInc;

	if ( bCalcFirst )
	{
		nPos += 3;
		nPosInc = -1;
		nIdxInc = 0;
	}
	else
	{
		nPosInc = 1;
		nIdxInc = 1;
	}
	pPoints[nPos].X() = (long) (fU3 *       pPoints[nIdx  ].X() +
								fT  * fU2 * pPoints[nIdx+1].X() * 3 +
								fT2 * fU  * pPoints[nIdx+2].X() * 3 +
								fT3 *       pPoints[nIdx+3].X());
	pPoints[nPos].Y() = (long) (fU3 *       pPoints[nIdx  ].Y() +
								fT  * fU2 * pPoints[nIdx+1].Y() * 3 +
								fT2 * fU  * pPoints[nIdx+2].Y() * 3 +
								fT3 *       pPoints[nIdx+3].Y());
	nPos = nPos + nPosInc;
	nIdx = nIdx + nIdxInc;
	pPoints[nPos].X() = (long) (fU2 *       pPoints[nIdx  ].X() +
								fT  * fU *  pPoints[nIdx+1].X() * 2 +
								fT2 *       pPoints[nIdx+2].X());
	pPoints[nPos].Y() = (long) (fU2 *       pPoints[nIdx  ].Y() +
								fT  * fU *  pPoints[nIdx+1].Y() * 2 +
								fT2 *       pPoints[nIdx+2].Y());
	nPos = nPos + nPosInc;
	nIdx = nIdx + nIdxInc;
	pPoints[nPos].X() = (long) (fU * pPoints[nIdx  ].X() +
								fT * pPoints[nIdx+1].X());
	pPoints[nPos].Y() = (long) (fU * pPoints[nIdx  ].Y() +
								fT * pPoints[nIdx+1].Y());
}

/************************************************************************/

void XPolygon::GenBezArc(const Point& rCenter, long nRx, long nRy,
						 long nXHdl, long nYHdl, sal_uInt16 nStart, sal_uInt16 nEnd,
						 sal_uInt16 nQuad, sal_uInt16 nFirst)
{
	Point* pPoints = pImpXPolygon->pPointAry;
	pPoints[nFirst  ] = rCenter;
	pPoints[nFirst+3] = rCenter;

	if ( nQuad == 1 || nQuad == 2 )
	{
		nRx   = -nRx; nXHdl = -nXHdl;
	}
	if ( nQuad == 0 || nQuad == 1 )
	{
		nRy   = -nRy; nYHdl = -nYHdl;
	}

	if ( nQuad == 0 || nQuad == 2 )
	{
		pPoints[nFirst].X() += nRx; pPoints[nFirst+3].Y() += nRy;
	}
	else
	{
		pPoints[nFirst].Y() += nRy; pPoints[nFirst+3].X() += nRx;
	}
	pPoints[nFirst+1] = pPoints[nFirst];
	pPoints[nFirst+2] = pPoints[nFirst+3];

	if ( nQuad == 0 || nQuad == 2 )
	{
		pPoints[nFirst+1].Y() += nYHdl; pPoints[nFirst+2].X() += nXHdl;
	}
	else
	{
		pPoints[nFirst+1].X() += nXHdl; pPoints[nFirst+2].Y() += nYHdl;
	}
	if ( nStart > 0 )
		SubdivideBezier(nFirst, sal_False, (double)nStart / 900);
	if ( nEnd < 900 )
		SubdivideBezier(nFirst, sal_True, (double)(nEnd-nStart) / (900-nStart));
	SetFlags(nFirst+1, XPOLY_CONTROL);
	SetFlags(nFirst+2, XPOLY_CONTROL);
}

/************************************************************************/

sal_Bool XPolygon::CheckAngles(sal_uInt16& nStart, sal_uInt16 nEnd, sal_uInt16& nA1, sal_uInt16& nA2)
{
	if ( nStart == 3600 ) nStart = 0;
	if ( nEnd == 0 ) nEnd = 3600;
	sal_uInt16 nStPrev = nStart;
	sal_uInt16 nMax = (nStart / 900 + 1) * 900;
	sal_uInt16 nMin = nMax - 900;

	if ( nEnd >= nMax || nEnd <= nStart )   nA2 = 900;
	else                                    nA2 = nEnd - nMin;
	nA1 = nStart - nMin;
	nStart = nMax;

	// sal_True zurueck, falls letztes Segment berechnet wurde
	return (nStPrev < nEnd && nStart >= nEnd);
}

/*************************************************************************
|*
|*    XPolygon::CalcSmoothJoin()
|*
|*    glatten Uebergang zu einer Bezierkurve berechnen, indem der
|*    entsprechende Punkt auf die Verbindungslinie von zwei anderen
|*    Punkten projiziert wird
|*     Center = End- bzw. Anfangspunkt der Bezierkurve
|*     Drag   = der bewegte Punkt, der die Verschiebung von Pnt vorgibt
|*     Pnt    = der zu modifizierende Punkt
|*    Wenn Center am Anfang bzw. Ende des Polygons liegt, wird Pnt
|*    auf die entgegengesetzte Seite verlegt
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 18.04.95
|*
\************************************************************************/

void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
{
	CheckReference();

//	sal_uInt16  nMaxPnt = pImpXPolygon->nPoints - 1;

//  if ( nCenter == nMaxPnt )   nPnt = 1;
//  else if ( nCenter == 0 )    nPnt = nMaxPnt - 1;

	// Wenn nPnt kein Control-Punkt, d.h. nicht verschiebbar, dann
	// statt dessen nDrag auf der Achse nCenter-nPnt verschieben
	if ( !IsControl(nPnt) )
	{
		sal_uInt16 nTmp = nDrag;
		nDrag = nPnt;
		nPnt = nTmp;
	}
	Point*  pPoints = pImpXPolygon->pPointAry;
	Point   aDiff   = pPoints[nDrag] - pPoints[nCenter];
	double  fDiv    = CalcDistance(nCenter, nDrag);

	if ( fDiv )
	{
		double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
		// bei SMOOTH bisherige Laenge beibehalten
		if ( GetFlags(nCenter) == XPOLY_SMOOTH || !IsControl(nDrag) )
		{
			aDiff.X() = (long) (fRatio * aDiff.X());
			aDiff.Y() = (long) (fRatio * aDiff.Y());
		}
		pPoints[nPnt] = pPoints[nCenter] - aDiff;
	}
}

/*************************************************************************
|*
|*    XPolygon::CalcTangent()
|*
|*    Tangente fuer den Uebergang zwischen zwei Bezierkurven berechnen
|*     Center = End- bzw. Anfangspunkt der Bezierkurven
|*     Prev   = vorheriger Zugpunkt
|*     Next   = naechster Zugpunkt
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 18.04.95
|*
\************************************************************************/

void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
{
	CheckReference();

	double fAbsLen = CalcDistance(nNext, nPrev);

	if ( fAbsLen )
	{
		const Point& rCenter = pImpXPolygon->pPointAry[nCenter];
		Point&  rNext = pImpXPolygon->pPointAry[nNext];
		Point&  rPrev = pImpXPolygon->pPointAry[nPrev];
		Point   aDiff = rNext - rPrev;
		double  fNextLen = CalcDistance(nCenter, nNext) / fAbsLen;
		double  fPrevLen = CalcDistance(nCenter, nPrev) / fAbsLen;

		// bei SYMMTR gleiche Laenge fuer beide Seiten
		if ( GetFlags(nCenter) == XPOLY_SYMMTR )
		{
			fPrevLen = (fNextLen + fPrevLen) / 2;
			fNextLen = fPrevLen;
		}
		rNext.X() = rCenter.X() + (long) (fNextLen * aDiff.X());
		rNext.Y() = rCenter.Y() + (long) (fNextLen * aDiff.Y());
		rPrev.X() = rCenter.X() - (long) (fPrevLen * aDiff.X());
		rPrev.Y() = rCenter.Y() - (long) (fPrevLen * aDiff.Y());
	}
}

/*************************************************************************
|*
|*    XPolygon::PointsToBezier()
|*
|*    wandelt vier Polygonpunkte in eine Bezierkurve durch diese Punkte um
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 09.01.95
|*
\************************************************************************/

void XPolygon::PointsToBezier(sal_uInt16 nFirst)
{
	double  nFullLength, nPart1Length, nPart2Length;
	double  fX0, fY0, fX1, fY1, fX2, fY2, fX3, fY3;
	double  fTx1, fTx2, fTy1, fTy2;
	double  fT1, fU1, fT2, fU2, fV;
	Point*  pPoints = pImpXPolygon->pPointAry;

	if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
		 IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
		return;

	CheckReference();

	fTx1 = pPoints[nFirst+1].X();
	fTy1 = pPoints[nFirst+1].Y();
	fTx2 = pPoints[nFirst+2].X();
	fTy2 = pPoints[nFirst+2].Y();
	fX0  = pPoints[nFirst  ].X();
	fY0  = pPoints[nFirst  ].Y();
	fX3  = pPoints[nFirst+3].X();
	fY3  = pPoints[nFirst+3].Y();

	nPart1Length = CalcDistance(nFirst, nFirst+1);
	nPart2Length = nPart1Length + CalcDistance(nFirst+1, nFirst+2);
	nFullLength  = nPart2Length + CalcDistance(nFirst+2, nFirst+3);
	if ( nFullLength < 20 )
		return;

	if ( nPart2Length == nFullLength )
		nPart2Length -= 1;
	if ( nPart1Length == nFullLength )
		nPart1Length = nPart2Length - 1;
	if ( nPart1Length <= 0 )
		nPart1Length = 1;
	if ( nPart2Length <= 0 || nPart2Length == nPart1Length )
		nPart2Length = nPart1Length + 1;

	fT1 = nPart1Length / nFullLength;
	fU1 = 1.0 - fT1;
	fT2 = nPart2Length / nFullLength;
	fU2 = 1.0 - fT2;
	fV = 3 * (1.0 - (fT1 * fU2) / (fT2 * fU1));

	fX1 = fTx1 / (fT1 * fU1 * fU1) - fTx2 * fT1 / (fT2 * fT2 * fU1 * fU2);
	fX1 /= fV;
	fX1 -= fX0 * ( fU1 / fT1 + fU2 / fT2) / 3;
	fX1 += fX3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;

	fY1 = fTy1 / (fT1 * fU1 * fU1) - fTy2 * fT1 / (fT2 * fT2 * fU1 * fU2);
	fY1 /= fV;
	fY1 -= fY0 * ( fU1 / fT1 + fU2 / fT2) / 3;
	fY1 += fY3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;

	fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
	fX2 -= fX1 * fU2 / fT2;
	fX2 -= fX3 * fT2 / (fU2 * 3);

	fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
	fY2 -= fY1 * fU2 / fT2;
	fY2 -= fY3 * fT2 / (fU2 * 3);

	pPoints[nFirst+1] = Point((long) fX1, (long) fY1);
	pPoints[nFirst+2] = Point((long) fX2, (long) fY2);
	SetFlags(nFirst+1, XPOLY_CONTROL);
	SetFlags(nFirst+2, XPOLY_CONTROL);
}

/*************************************************************************
|*
|*    XPolygon::Translate()
|*
|*    Polygon auf den uebergebenen Punkt verschieben
|*    Ersterstellung    ESO 17.01.95
|*    Letzte Aenderung  ESO 17.01.95
|*
*************************************************************************/

void XPolygon::Translate(const Point& rTrans)
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

	for (sal_uInt16 i = 0; i < nPntCnt; i++)
		pImpXPolygon->pPointAry[i] += rTrans;
}

/*************************************************************************
|*
|*    XPolygon::Rotate()
|*
|*    Alle Punkte um den Punkt rCenter drehen, Sinus und Cosinus
|*    muessen uebergeben werden
|*    Ersterstellung    ESO 09.01.95
|*    Letzte Aenderung  ESO 17.01.95
|*
*************************************************************************/

void XPolygon::Rotate(const Point& rCenter, double fSin, double fCos)
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	long nX;
	long nY;
	long nNewX;
	long nNewY;
	long nCenterX = rCenter.X();
	long nCenterY = rCenter.Y();

	sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

	for (sal_uInt16 i = 0; i < nPntCnt; i++)
	{
		Point *pPt = &(pImpXPolygon->pPointAry[i]);
		nX = pPt->X()-nCenterX;
		nY = pPt->Y()-nCenterY;
		nNewX =  (long)floor(fCos * nX + fSin * nY + 0.5);
		nNewY = -(long)floor(fSin * nX - fCos * nY + 0.5);
		pPt->X() = nNewX + nCenterX;
		pPt->Y() = nNewY + nCenterY;

	/* und so stand das in einem anderen File auf T:
	   dass ich am 29-11-1995 gegettet habe. Joe M.
	sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

	for (sal_uInt16 i = 0; i < nPntCnt; i++)
	{
		Point P = pImpXPolygon->pPointAry[i] - rCenter;
		long X = P.X();
		long Y = P.Y();
		P.X() =  (long)floor(fCos * X + fSin * Y + 0.5);
		P.Y() = -(long)floor(fSin * X - fCos * Y + 0.5);
		pImpXPolygon->pPointAry[i] = P + rCenter;
	*/
	}
}

/*************************************************************************
|*
|*    XPolygon::Rotate()
|*
|*    Alle Punkte um den Punkt rCenter mit dem Winkel nAngle drehen
|*    Winkel in 10tel Grad, Wertebereich 0 - 3600
|*    Ersterstellung    ESO 17.01.95
|*    Letzte Aenderung  ESO 17.01.95
|*
*************************************************************************/

void XPolygon::Rotate(const Point& rCenter, sal_uInt16 nAngle)
{
	nAngle %= 3600;

	if ( nAngle != 0 )
	{
		double fAngle = F_PI * nAngle / 1800;
		double fSin = sin(fAngle);
		double fCos = cos(fAngle);
		Rotate(rCenter, fSin, fCos);
	}
}

/*************************************************************************
|*
|*    XPolygon::Scale()
|*
|*    XPolygon in X- und/oder Y-Richtung skalieren
|*    Ersterstellung    ESO 01.02.95
|*    Letzte Aenderung  ESO 01.02.95
|*
*************************************************************************/

void XPolygon::Scale(double fSx, double fSy)
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

	for (sal_uInt16 i = 0; i < nPntCnt; i++)
	{
		Point& rPnt = pImpXPolygon->pPointAry[i];
		rPnt.X() = (long)(fSx * rPnt.X());
		rPnt.Y() = (long)(fSy * rPnt.Y());
	}
}

/*************************************************************************
|*
|*    XPolygon::SlantX()
|*
|*    XPolygon in X-Richtung um einen beliebigen Winkel kippen,
|*    bezogen auf eine Referenz-Y-Koordinate
|*    Ersterstellung    ESO 01.02.95
|*    Letzte Aenderung  ESO 01.02.95
|*
*************************************************************************/

void XPolygon::SlantX(long nYRef, double fSin, double fCos)
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

	for (sal_uInt16 i = 0; i < nPntCnt; i++)
	{
		Point& rPnt = pImpXPolygon->pPointAry[i];
		long nDy = rPnt.Y() - nYRef;
		rPnt.X() += (long)(fSin * nDy);
		rPnt.Y() = nYRef + (long)(fCos * nDy);
	}
}

/*************************************************************************
|*
|*    XPolygon::SlantY()
|*
|*    XPolygon in Y-Richtung um einen beliebigen Winkel kippen,
|*    bezogen auf eine Referenz-X-Koordinate
|*    Ersterstellung    ESO 01.02.95
|*    Letzte Aenderung  ESO 01.02.95
|*
*************************************************************************/

void XPolygon::SlantY(long nXRef, double fSin, double fCos)
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

	for (sal_uInt16 i = 0; i < nPntCnt; i++)
	{
		Point& rPnt = pImpXPolygon->pPointAry[i];
		long nDx = rPnt.X() - nXRef;
		rPnt.X() = nXRef + (long)(fCos * nDx);
		rPnt.Y() -= (long)(fSin * nDx);
	}
}

/*************************************************************************
|*
|*    XPolygon::Distort()
|*
|*    XPolygon verzerren, indem die Koordinaten relativ zu einem
|*    Referenzrechteck in ein beliebiges Viereck skaliert werden
|*    Zuordnung der Viereck-Punkte im Polygon zum Referenzrechteck:
|*    0: links oben      0----1
|*    1: rechts oben     |    |
|*    2: rechts unten    3----2
|*    3: links unten
|*    Ersterstellung    ESO 07.07.95
|*    Letzte Aenderung  ESO 07.07.95
|*
*************************************************************************/

void XPolygon::Distort(const Rectangle& rRefRect,
					   const XPolygon& rDistortedRect)
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	long    Xr, Wr, X1, X2, X3, X4;
	long    Yr, Hr, Y1, Y2, Y3, Y4;
	double  fTx, fTy, fUx, fUy;

	Xr = rRefRect.Left();
	Yr = rRefRect.Top();
	Wr = rRefRect.GetWidth();
	Hr = rRefRect.GetHeight();

	if ( Wr && Hr )
	{
		DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
				   "Distort-Rechteck zu klein");

		X1 = rDistortedRect[0].X();
		Y1 = rDistortedRect[0].Y();
		X2 = rDistortedRect[1].X();
		Y2 = rDistortedRect[1].Y();
		X3 = rDistortedRect[3].X();
		Y3 = rDistortedRect[3].Y();
		X4 = rDistortedRect[2].X();
		Y4 = rDistortedRect[2].Y();

		sal_uInt16 nPntCnt = pImpXPolygon->nPoints;

		for (sal_uInt16 i = 0; i < nPntCnt; i++)
		{
			Point& rPnt = pImpXPolygon->pPointAry[i];

			fTx = (double)(rPnt.X() - Xr) / Wr;
			fTy = (double)(rPnt.Y() - Yr) / Hr;
			fUx = 1.0 - fTx;
			fUy = 1.0 - fTy;

			rPnt.X() = (long) ( fUy * (fUx * X1 + fTx * X2) +
								fTy * (fUx * X3 + fTx * X4) );
			rPnt.Y() = (long) ( fUx * (fUy * Y1 + fTy * Y3) +
								fTx * (fUy * Y2 + fTy * Y4) );
		}
	}
}

/*************************************************************************
|*
|* Bestimme den linken, unteren Punkt des Polygons und richte das
|* Polygon so aus, dass dieser Punkt auf dem Index 0 liegt
|*
\************************************************************************/

void XPolygon::Rotate20()
{
	pImpXPolygon->CheckPointDelete();
	CheckReference();

	double   fMinY   = pImpXPolygon->pPointAry->Y();
	double   fMinX   = pImpXPolygon->pPointAry->X();
	long     nPntCnt = pImpXPolygon->nPoints;
	long     nIndex0 = 0;

	for (long nPoints = 1;
			  nPoints < nPntCnt;
			  nPoints ++)
	{
		Point &rPnt = pImpXPolygon->pPointAry[nPoints];

		if ((rPnt.X () < fMinX) || (fMinX == rPnt.X ()) &&
								   (fMinY >= rPnt.Y ()))
		{
			fMinX   = rPnt.X ();
			fMinY   = rPnt.Y ();
			nIndex0 = nPoints;
		}
	}

	if (nIndex0 < nPntCnt)
	{
		Point *pTemp = new Point [nIndex0];
		memcpy (pTemp, pImpXPolygon->pPointAry, nIndex0 * sizeof (Point));
		memcpy (pImpXPolygon->pPointAry, &pImpXPolygon->pPointAry [nIndex0], (nPntCnt - nIndex0) * sizeof (Point));
		memcpy (&pImpXPolygon->pPointAry [nIndex0], pTemp, nIndex0 * sizeof (Point));
		delete[] pTemp;
	}
}

basegfx::B2DPolygon XPolygon::getB2DPolygon() const
{
	// #i74631# use tools Polygon class for conversion to not have the code doubled
	// here. This needs one more conversion but avoids different convertors in
	// the long run
	DBG_ASSERT(pImpXPolygon != 0, "XPolygon::getB2DPolygon(): XPolygon has no implementation incarnated (!)");
	const Polygon aSource(GetPointCount(), pImpXPolygon->pPointAry, pImpXPolygon->pFlagAry);
	
	return aSource.getB2DPolygon();
}

XPolygon::XPolygon(const basegfx::B2DPolygon& rPolygon)
{
	// #i74631# use tools Polygon class for conversion to not have the code doubled
	// here. This needs one more conversion but avoids different convertors in
	// the long run
	DBG_CTOR(XPolygon,NULL);

	const Polygon aSource(rPolygon);
	sal_uInt16 nSize = aSource.GetSize();
	pImpXPolygon = new ImpXPolygon( nSize );
	pImpXPolygon->nPoints = nSize;

	for( sal_uInt16 i = 0; i < nSize;  i++ )
	{
		pImpXPolygon->pPointAry[i] = aSource[i];
		pImpXPolygon->pFlagAry[i] = (sal_uInt8) aSource.GetFlags( i );
	}
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+--------------- XPolyPolygon -----------------------------------------+
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

/*************************************************************************
|*
|*    ImpXPolyPolygon::ImpXPolyPolygon()
|*
|*    Beschreibung      Erzeugt das XPolygon-Array
|*    Ersterstellung    CL 09.11.94
|*    Letzte Aenderung  MM 09.11.94
|*
*************************************************************************/

ImpXPolyPolygon::ImpXPolyPolygon( const ImpXPolyPolygon& rImpXPolyPoly ) :
					 aXPolyList( rImpXPolyPoly.aXPolyList )
{
	nRefCount = 1;

	// Einzelne Elemente duplizieren
	XPolygon* pXPoly = aXPolyList.First();
	while ( pXPoly )
	{
		aXPolyList.Replace( new XPolygon( *(aXPolyList.GetCurObject()) ) );
		pXPoly = aXPolyList.Next();
	}
}


/*************************************************************************
|*
|*    ImpXPolyPolygon::~ImpXPolyPolygon()
|*
|*    Beschreibung      Loescht das Polygon-Array
|*    Ersterstellung    CL 09.06.93
|*    Letzte Aenderung  CL 09.06.93
|*
*************************************************************************/

ImpXPolyPolygon::~ImpXPolyPolygon()
{
	XPolygon* pXPoly = aXPolyList.First();
	while( pXPoly )
	{
		delete pXPoly;
		pXPoly = aXPolyList.Next();
	}
}

/*************************************************************************
|*
|*    ImpXPolyPolygon::operator==()
|*
|*    Ersterstellung    Joe 26-09-95
|*    Letzte Aenderung
|*
*************************************************************************/


bool ImpXPolyPolygon::operator==(const ImpXPolyPolygon& rImpXPolyPoly) const
{
	sal_uInt16 nAnz=(sal_uInt16)aXPolyList.Count();
	const XPolygonList& rCmpList=rImpXPolyPoly.aXPolyList;
	if (nAnz!=(sal_uInt16)rCmpList.Count()) return sal_False;
	bool bEq=true;
	for (sal_uInt16 i=nAnz; i>0 && bEq;) {
		i--;
		bEq= *aXPolyList.GetObject(i) == *rCmpList.GetObject(i);
	}
	return bEq;
}

/*************************************************************************
|*
|*    XPolyPolygon::XPolyPolygon()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolyPolygon::XPolyPolygon( sal_uInt16 nInitSize, sal_uInt16 nResize )
{
	DBG_CTOR(XPolyPolygon,NULL);
	pImpXPolyPolygon = new ImpXPolyPolygon( nInitSize, nResize );
}


/*************************************************************************
|*
|*    XPolyPolygon::XPolyPolygon()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolyPolygon::XPolyPolygon( const XPolygon& rXPoly )
{
	DBG_CTOR(XPolyPolygon,NULL);
	pImpXPolyPolygon = new ImpXPolyPolygon;
	pImpXPolyPolygon->aXPolyList.Insert( new XPolygon( rXPoly ) );
}

/*************************************************************************
|*
|*    XPolyPolygon::XPolyPolygon()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolyPolygon::XPolyPolygon( const XPolyPolygon& rXPolyPoly )
{
	DBG_CTOR(XPolyPolygon,NULL);
	pImpXPolyPolygon = rXPolyPoly.pImpXPolyPolygon;
	pImpXPolyPolygon->nRefCount++;
}

/*************************************************************************
|*
|*    XPolyPolygon::XPolyPolygon()
|*
|*    XPolyPolygon aus einen Standard-PolyPolygon erzeugen
|*    Ersterstellung    18.01.95 ESO
|*    Letzte Aenderung  18.01.95 ESO
|*
*************************************************************************/

XPolyPolygon::XPolyPolygon( const PolyPolygon& rPolyPoly )
{
	DBG_CTOR(XPolyPolygon,NULL);
	pImpXPolyPolygon = new ImpXPolyPolygon;

	for (sal_uInt16 i = 0; i < rPolyPoly.Count(); i++)
		pImpXPolyPolygon->aXPolyList.Insert(
									new XPolygon(rPolyPoly.GetObject(i)) );
}

/*************************************************************************
|*
|*    XPolyPolygon::~XPolyPolygon()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolyPolygon::~XPolyPolygon()
{
	DBG_DTOR(XPolyPolygon,NULL);
	if( pImpXPolyPolygon->nRefCount > 1 )
		pImpXPolyPolygon->nRefCount--;
	else
		delete pImpXPolyPolygon;
}

/*************************************************************************
|*
|*    XPolygon::CheckReference()
|*
|*    Referenzzaehler desImpXPolyPoly pruefen und ggf. von diesem abkoppeln
|*    Ersterstellung    18.01.95 ESO
|*    Letzte Aenderung  18.01.95 ESO
|*
*************************************************************************/

void XPolyPolygon::CheckReference()
{
	if( pImpXPolyPolygon->nRefCount > 1 )
	{
		pImpXPolyPolygon->nRefCount--;
		pImpXPolyPolygon = new ImpXPolyPolygon( *pImpXPolyPolygon );
	}
}

/*************************************************************************
|*
|*    XPolyPolygon::Insert()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

void XPolyPolygon::Insert( const XPolygon& rXPoly, sal_uInt16 nPos )
{
	CheckReference();
	XPolygon* pXPoly = new XPolygon( rXPoly );
	pImpXPolyPolygon->aXPolyList.Insert( pXPoly, nPos );
}

/*************************************************************************
|*
|*    XPolyPolygon::Insert()
|*
|*    saemtliche XPolygone aus einem XPolyPolygon einfuegen
|*    Ersterstellung    18.01.95 ESO
|*    Letzte Aenderung  18.01.95 ESO
|*
*************************************************************************/

void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly, sal_uInt16 nPos )
{
	CheckReference();

	for (sal_uInt16 i = 0; i < rXPolyPoly.Count(); i++)
	{
		XPolygon* pXPoly = new XPolygon(rXPolyPoly[i]);
		pImpXPolyPolygon->aXPolyList.Insert(pXPoly, nPos);
		if ( nPos != XPOLYPOLY_APPEND )
			nPos++;
	}
}

/*************************************************************************
|*
|*    XPolyPolygon::Remove()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolygon XPolyPolygon::Remove( sal_uInt16 nPos )
{
	CheckReference();
	XPolygon* pTmpXPoly = pImpXPolyPolygon->aXPolyList.Remove( nPos );
	XPolygon  aXPoly( *pTmpXPoly );
	delete pTmpXPoly;
	return aXPoly;
}


/*************************************************************************
|*
|*    XPolyPolygon::Replace()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolygon XPolyPolygon::Replace( const XPolygon& rXPoly, sal_uInt16 nPos )
{
	CheckReference();
	XPolygon* pXPoly = new XPolygon( rXPoly );
	XPolygon* pTmpXPoly = pImpXPolyPolygon->aXPolyList.Replace( pXPoly, nPos );
	XPolygon  aXPoly( *pTmpXPoly );
	delete pTmpXPoly;
	return aXPoly;
}


/*************************************************************************
|*
|*    XPolyPolygon::GetObject()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
{
	return *(pImpXPolyPolygon->aXPolyList.GetObject( nPos ));
}


/*************************************************************************
|*
|*    XPolyPolygon::Clear()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  TH 17.10.94
|*
*************************************************************************/

void XPolyPolygon::Clear()
{
	if ( pImpXPolyPolygon->nRefCount > 1 )
	{
		pImpXPolyPolygon->nRefCount--;
		pImpXPolyPolygon = new ImpXPolyPolygon();
	}
	else
	{
		XPolygon* pXPoly = pImpXPolyPolygon->aXPolyList.First();
		while( pXPoly )
		{
			delete pXPoly;
			pXPoly = pImpXPolyPolygon->aXPolyList.Next();
		}
		pImpXPolyPolygon->aXPolyList.Clear();
	}
}


/*************************************************************************
|*
|*    XPolyPolygon::Count()
|*
|*    Beschreibung
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

sal_uInt16 XPolyPolygon::Count() const
{
	return (sal_uInt16)(pImpXPolyPolygon->aXPolyList.Count());
}


/*************************************************************************
|*
|*    XPolyPolygon::Move()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    TH 04.10.94
|*    Letzte Aenderung  TH 04.10.94
|*
*************************************************************************/

void XPolyPolygon::Move( long nHorzMove, long nVertMove )
{
	// Diese Abfrage sollte man fuer die DrawEngine durchfuehren
	if ( !nHorzMove && !nVertMove )
		return;

	// Referenzcounter beruecksichtigen
	CheckReference();

	// Punkte verschieben
	XPolygon* pXPoly = pImpXPolyPolygon->aXPolyList.First();
	while( pXPoly )
	{
		pXPoly->Move( nHorzMove, nVertMove );
		pXPoly = pImpXPolyPolygon->aXPolyList.Next();
	}
}

/*************************************************************************
|*
|*    XPolyPolygon::GetBoundRect()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    TH 04.10.94
|*    Letzte Aenderung  TH 04.10.94
|*
*************************************************************************/

Rectangle XPolyPolygon::GetBoundRect() const
{
	sal_uInt16    nXPoly = (sal_uInt16)pImpXPolyPolygon->aXPolyList.Count();
	Rectangle aRect;

	for ( sal_uInt16 n = 0; n < nXPoly; n++ )
	{
		const XPolygon* pXPoly = pImpXPolyPolygon->aXPolyList.GetObject( n );
		aRect.Union( pXPoly->GetBoundRect() );
	}

	return aRect;
}


/*************************************************************************
|*
|*    XPolyPolygon::operator[]()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    TH 28.10.94
|*    Letzte Aenderung  TH 28.10.94
|*
*************************************************************************/

XPolygon& XPolyPolygon::operator[]( sal_uInt16 nPos )
{
	CheckReference();
	return *(pImpXPolyPolygon->aXPolyList.GetObject( nPos ));
}

/*************************************************************************
|*
|*    XPolyPolygon::operator=()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL 27.01.93
|*    Letzte Aenderung  CL 27.01.93
|*
*************************************************************************/

XPolyPolygon& XPolyPolygon::operator=( const XPolyPolygon& rXPolyPoly )
{
	rXPolyPoly.pImpXPolyPolygon->nRefCount++;

	if( pImpXPolyPolygon->nRefCount > 1 )
		pImpXPolyPolygon->nRefCount--;
	else
		delete pImpXPolyPolygon;

	pImpXPolyPolygon = rXPolyPoly.pImpXPolyPolygon;
	return *this;
}


/*************************************************************************
|*
|*    XPolyPolygon::operator==()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL  27.01.93
|*    Letzte Aenderung  Joe 27.01.93
|*
*************************************************************************/

sal_Bool XPolyPolygon::operator==( const XPolyPolygon& rXPolyPoly ) const
{
	if (pImpXPolyPolygon==rXPolyPoly.pImpXPolyPolygon) return sal_True;
	return *pImpXPolyPolygon == *rXPolyPoly.pImpXPolyPolygon;
}


/*************************************************************************
|*
|*    XPolyPolygon::operator!=()
|*
|*    Beschreibung      POLY.SDW
|*    Ersterstellung    CL  27.01.93
|*    Letzte Aenderung  Joe 27.01.93
|*
*************************************************************************/

sal_Bool XPolyPolygon::operator!=( const XPolyPolygon& rXPolyPoly ) const
{
	if (pImpXPolyPolygon==rXPolyPoly.pImpXPolyPolygon) return sal_False;
	return *pImpXPolyPolygon != *rXPolyPoly.pImpXPolyPolygon;
}

/*************************************************************************
|*
|*    XPolyPolygon::Translate()
|*
|*    Alle Polygone auf den uebergebenen Punkt verschieben
|*    Ersterstellung    ESO 25.01.95
|*    Letzte Aenderung  ESO 25.01.95
|*
*************************************************************************/

void XPolyPolygon::Translate(const Point& rTrans)
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->Translate(rTrans);
}

/*************************************************************************
|*
|*    XPolyPolygon::Rotate()
|*
|*    Alle Polygone um den Punkt rCenter drehen, Sinus und Cosinus
|*    muessen uebergeben werden
|*    Ersterstellung    ESO 25.01.95
|*    Letzte Aenderung  ESO 25.01.95
|*
*************************************************************************/

void XPolyPolygon::Rotate(const Point& rCenter, double fSin, double fCos)
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->Rotate(rCenter, fSin, fCos);
}

/*************************************************************************
|*
|* Bestimme den linken, unteren Punkt des Polygons und richte das
|* Polygon so aus, dass dieser Punkt auf dem Index 0 liegt
|*
\************************************************************************/

void XPolyPolygon::Rotate20()
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->Rotate20();
}

/*************************************************************************
|*
|*    XPolyPolygon::Rotate()
|*
|*    Alle Poylgone um den Punkt rCenter mit dem Winkel nAngle drehen
|*    Winkel in 10tel Grad, Wertebereich 0 - 3600
|*    Ersterstellung    ESO 25.01.95
|*    Letzte Aenderung  ESO 25.01.95
|*
*************************************************************************/

void XPolyPolygon::Rotate(const Point& rCenter, sal_uInt16 nAngle)
{
	nAngle %= 3600;

	if ( nAngle != 0 )
	{
		double fAngle = F_PI * nAngle / 1800;
		double fSin = sin(fAngle);
		double fCos = cos(fAngle);
		Rotate(rCenter, fSin, fCos);
	}
}

/*************************************************************************
|*
|*    XPolyPolygon::Scale()
|*
|*    Alle Polygone in X- und/oder Y-Richtung skalieren
|*    Ersterstellung    ESO 01.02.95
|*    Letzte Aenderung  ESO 01.02.95
|*
*************************************************************************/

void XPolyPolygon::Scale(double fSx, double fSy)
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->Scale(fSx, fSy);
}

/*************************************************************************
|*
|*    XPolyPolygon::SlantX()
|*
|*    Alle Polygone in X-Richtung um einen beliebigen Winkel kippen,
|*    bezogen auf eine Referenz-Y-Koordinate
|*    Ersterstellung    ESO 01.02.95
|*    Letzte Aenderung  ESO 01.02.95
|*
*************************************************************************/

void XPolyPolygon::SlantX(long nYRef, double fSin, double fCos)
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->SlantX(nYRef, fSin, fCos);
}

/*************************************************************************
|*
|*    XPolyPolygon::SlantY()
|*
|*    Alle Polygone in Y-Richtung um einen beliebigen Winkel kippen,
|*    bezogen auf eine Referenz-X-Koordinate
|*    Ersterstellung    ESO 01.02.95
|*    Letzte Aenderung  ESO 01.02.95
|*
*************************************************************************/

void XPolyPolygon::SlantY(long nXRef, double fSin, double fCos)
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->SlantY(nXRef, fSin, fCos);
}

/*************************************************************************
|*
|*    XPolygon::Distort()
|*
|*    XPolygon verzerren, indem die Koordinaten relativ zu einem
|*    Referenzrechteck in ein beliebiges Viereck skaliert werden
|*    Zuordnung der Viereck-Punkte im Polygon zum Referenzrechteck:
|*    0: links oben      0----1
|*    1: rechts oben     |    |
|*    2: rechts unten    3----2
|*    3: links unten
|*    Ersterstellung    ESO 07.07.95
|*    Letzte Aenderung  ESO 07.07.95
|*
*************************************************************************/

void XPolyPolygon::Distort(const Rectangle& rRefRect,
						   const XPolygon& rDistortedRect)
{
	CheckReference();

	for (sal_uInt16 i = 0; i < Count(); i++)
		pImpXPolyPolygon->aXPolyList.GetObject(i)->Distort(rRefRect,
														   rDistortedRect);
}

basegfx::B2DPolyPolygon XPolyPolygon::getB2DPolyPolygon() const
{
	basegfx::B2DPolyPolygon aRetval;

	for(sal_uInt16 a(0L); a < Count(); a++)
	{
		const XPolygon& rPoly = (*this)[a];
		aRetval.append(rPoly.getB2DPolygon());
	}

	return aRetval;
}

XPolyPolygon::XPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
{
	DBG_CTOR(XPolyPolygon,NULL);
	pImpXPolyPolygon = new ImpXPolyPolygon( 16, 16 );

	for(sal_uInt32 a(0L); a < rPolyPolygon.count(); a++)
	{
		basegfx::B2DPolygon aCandidate = rPolyPolygon.getB2DPolygon(a);
		XPolygon aNewPoly(aCandidate);
		Insert(aNewPoly);
	}
}

// eof
