#ifndef __UtilArc_h
#define __UtilArc_h

#include "UtilMisc.h"	// For M_PI.

template<class T> class FCPolyBezier;
typedef FCPolyBezier<DBLPoint> FCMmPolyBezier;
struct FCBezierControls;

extern const double g_aSin[];

extern void FFGetArcBeziers (	FCMmPolyBezier& polyBezier,
								const DBLPoint& mmptArcO,
								mmetol mmLx, mmetol mmLy,
								angle dBeginR, angle dEndR,
								short nType,
								mmetol mmShiftX,
								angle dRotate,
								const DBLPoint& mmptRotateO);

extern void FFCalcArcBeziers (	FCMmPolyBezier& polyBezier,
								const DBLPoint& mmptO,
								mmetol mmLx,
								mmetol mmLy,
								angle dBeginR,
								angle dEndR);

extern BOOL FFCalcArcBezier (	FCBezierControls& bc,
								const DBLPoint& mmptO,
								mmetol mmLx,
								mmetol mmLy,
								angle dRa,
								angle dRd);

/*************************************************************************
 * <_xZkqqHx_iKnGjAjE_xHqqkZx_>	GetArcPolygon
 *
 * <_xZkqqHx_iLeAjEfM_xHqqkZx_>	_xZkqqHx_iJhOiCmMjCiGjAfDjDfPiBeB_xHqqkZx_X_xZkqqHx_iOlCjElMiMgBiBeB_xHqqkZx_Y_xZkqqHx_iOlCjElMiMgBiBeBiJhOiMmKiCmMiKeKiOgOiKhAiBeBiPeJjHlJiKhAiCpAiOhHjCoIiClFiBeB_xHqqkZx_
 *			_xZkqqHx_iJhOiMmKiCmMjIeBiMiLjApMiCmMiNmAjFfHiPoOjFpBiCpAiOfKiPgPiClHiCoJiBeC_xHqqkZx_
 *
 * <_xZkqqHx_iIpIjAjE_xHqqkZx_>	vPoint	:_xZkqqHx_jIeBiMiLjApMiCmMiNmAjFfHjEhKjHpBiCpAiOpLiCnPiCoJiDgPiDgCiDhEiDeA_xHqqkZx_
 *			dCX		:_xZkqqHx_jCiGjAfDjDfPiCmM_xHqqkZx_X_xZkqqHx_iNmAjFfH_xHqqkZx_
 *			dCY		:_xZkqqHx_jCiGjAfDjDfPiCmM_xHqqkZx_Y_xZkqqHx_iNmAjFfH_xHqqkZx_
 *			dLX		:X_xZkqqHx_iOlCjElMiMgB_xHqqkZx_
 *			dLY		:Y_xZkqqHx_iOlCjElMiMgB_xHqqkZx_
 *			dStart	:_xZkqqHx_iKeKiOgOiKhAiBeC_xHqqkZx_0.0 <= dStart < 2.0
 *			dEnd	:_xZkqqHx_iPeJjHlJiKhAiBeC_xHqqkZx_0.0 <= dEnd < 2.0
 *			nType	:_xZkqqHx_iJhOiCmMiMgAiBeCiImIiJlKiCmMiMgAiCkKiOhHjCoIiCmFiCkLiCoJiBeC_xHqqkZx_
 *					1	:_xZkqqHx_iDhAiDeDiMfO_xHqqkZx_
 *					2	:_xZkqqHx_iLhMiMfO_xHqqkZx_
 *					3	:_xZkqqHx_iJhOiMmKiCmMiCnN_xHqqkZx_
 *			dElemLen :_xZkqqHx_jClIjDfPiKnEiCmMiLjHjHkDiCpAiOhHjCoIiBeCjGnKiImAjCpGjDhIiBeC_xHqqkZx_
 *			dSftX	:X_xZkqqHx_jFpLiMpMiCnGiCmMiDfGiDhEiDgHjFmPiMgAiCmMjBoFiCkLiClD_xHqqkZx_
 *			dRotate	:_xZkqqHx_iJpBjDfNjFmPiMgAiCmMiKhAjDhI_xHqqkZx_
 *			dRotX	:_xZkqqHx_iJpBjDfNjFmPiMgAiCmMiJpBjDfNiOlCiCmM_xHqqkZx_X_xZkqqHx_iNmAjFfH_xHqqkZx_
 *			dRotX	:_xZkqqHx_iJpBjDfNjFmPiMgAiCmMiJpBjDfNiOlCiCmM_xHqqkZx_Y_xZkqqHx_iNmAjFfH_xHqqkZx_
 *
 * <_xZkqqHx_iJpAjAoA_xHqqkZx_>	_xZkqqHx_jEhKjHpB_xHqqkZx_ g_aSin[i] _xZkqqHx_iCmNiBeBiKhAjDhI_xHqqkZx_ 0 _xZkqqHx_iCkJiCoHiKhAjDhI_xHqqkZx_ _xZkqqHx_iDmO_xHqqkZx_/2 _xZkqqHx_iCnMiCmFiCmM_xHqqkZx_sin_xZkqqHx_iKnGjAjEiCmM_xHqqkZx_
 *			_xZkqqHx_jCgMiCpA_xHqqkZx_45_xZkqqHx_jFkKiKiEiClFiCmEiLeMjIfOiClFiCmEiCkCiCoJiBeCiCmCiCnMiCoIiBeB_xHqqkZx_g_aSin[0]_xZkqqHx_iCmN_xHqqkZx_ sin(0_xZkqqHx_jDhI_xHqqkZx_)_xZkqqHx_iBeB_xHqqkZx_
 *			g_aSin[1]_xZkqqHx_iCmN_xHqqkZx_ sin(2_xZkqqHx_jDhI_xHqqkZx_)_xZkqqHx_iCmFiCkAiCoJiBeC_xHqqkZx_
 *			UtilArc.mdrw_xZkqqHx_iCpAiOfBiPmGiBeC_xHqqkZx_
 *************************************************************************/
template<class POINTVECT>
void GetArcPolygon (POINTVECT& vPoint, double dCX, double dCY,
					double dLX, double dLY, double dStart, double dEnd,
					short nType, double dElemLen,
					double dSftX = 0.0, double dRotate = 0.0,
					double dRotX = 0.0, double dRotY = 0.0)
{
	ASSERT(0.0 <= dStart && dStart < 2.0);
	ASSERT(0.0 <= dEnd && dEnd < 2.0);

#if 0
	vPoint.clear();
	for (long l = 0; l < 360; l += 4)
	{
		double dX = dLX * cos(2.0 * (double(l) / 360.0) * M_PI);
		double dY = dLY * sin(2.0 * (double(l) / 360.0) * M_PI);
		dX += dCX;
		dY += dCY;
		vPoint.insert(vPoint.end());
		RSUB(vPoint.back().x, dX);
		RSUB(vPoint.back().y, dY);
	}
	return;
#endif//0

	long lS;	// _xZkqqHx_iKeKiOgOjDfPiCmMjClMiMoDiCmMiDhMiDeDiDjDiDgHiBeC_xHqqkZx_
	long lE;	// _xZkqqHx_iPeJjHlJjDfPiCmMjClMjBePiCmMiDhMiDeDiDjDiDgHiBeC_xHqqkZx_
	// sin(2_xZkqqHx_jDhI_xHqqkZx_) * _xZkqqHx_jFlNiLmPjElMiMgB_xHqqkZx_ _xZkqqHx_iCmG_xHqqkZx_ dElemLen _xZkqqHx_iCpAjEoEiKhCiClFiBeBiJlNjDhIiCkIiCkLiCmJiMhGiOfKiClHiCnHiCkLiCkJiLiBiCnPiCoJiBeC_xHqqkZx_
	// _xZkqqHx_jAlDiKgNiCpAiKpKiClHiCmIiCoHiCmO_xHqqkZx_(dLX + dLY)_xZkqqHx_iCmN_xHqqkZx_ 2_xZkqqHx_iCmFiKiEiCoJiCnHiCkLiClOiCkKiBeBiDhIiDfHiDeHiCmJjEoEiCnHiCmEiBeB_xHqqkZx_
	// _xZkqqHx_iJhOiMmKiCmNjDkPiClG_xHqqkZx_dElemLen_xZkqqHx_iCmMjCgMiCmFiKhAiCkKiPgPiCoCiClHiCkCiClNiCnPiBeBiClLiCmMiCnMiCnMiOgHjHhAiBeC_xHqqkZx_
	ASSERT(dLX + dLY != 0);
	long lSkip = D2L(dElemLen / (g_aSin[1] * (dLX + dLY)));
	// _xZkqqHx_iDfIiDeMiDgCiDhGiCmFiCkLiCoJiCmMiCmNiCfEiBgJiCfAiCePjDhIiBgKiCkKiMmAjDhIiBeCiMkJiJgIiCkGiCkKiIkLiCkNiCmIiCoJiBeC_xHqqkZx_
	if (lSkip < 1) lSkip = 1;
//	else if (5 < lSkip) lSkip = 5;

	POINTVECT::iterator iP;
	double	dX, dY;
	double	dSinR, dCosR;
	short	nFunc = 0;

	vPoint.clear();

// 00.09.20 Fukushiro M. 1_xZkqqHx_iNhDjFmPiNfI_xHqqkZx_ ()
//	if (dSftX != 0.0 && dLY != 0.0)
	if (dSftX != 0.0 && !almost0(dLY))
	{
		dSftX /= dLY;
		nFunc |= 0x01;
	}
	if (dRotate != 0.0)
	{
		dSinR = sin(dRotate * M_PI);
		dCosR = cos(dRotate * M_PI);
		nFunc |= 0x02;
	}

	/*----------------------------------------------------
	 * dStart_xZkqqHx_iCmG_xHqqkZx_dEnd_xZkqqHx_iCmMjBoFiPkMiKnGiMfHiCmFiBeBiCfCiCmCiCmJiPiIjHjNiCpAjFkKiKiEiClHiCoJiBeC_xHqqkZx_
	 * dStart < dEnd _xZkqqHx_iCmMiPoKiNiHiBeB_xHqqkZx_vPoint[0]_xZkqqHx_iCmJiKhAjDhI_xHqqkZx_dStart_xZkqqHx_iCmMiPoKiNiH_xHqqkZx_
	 * _xZkqqHx_iCmMiNmAjFfHiCpAiOpLiCnPiCoJiBeC_xHqqkZx_vPoint[1]_xZkqqHx_iImIiNhOiCmN_xHqqkZx_ g_aSin_xZkqqHx_iCkJiCoHiPiHiCmJjCgMiCpA_xHqqkZx_
	 * _xZkqqHx_iOfKiPgPiClHiCoJiBeC_xHqqkZx_lC_xZkqqHx_iCmJiCmNiBeBiKpEiCmC_xHqqkZx_g_aSin_xZkqqHx_iCkJiCoHjCgMiCpAiOgHiCkEiCkJiCpAiOfKiPgPiClHiCoJiBeC_xHqqkZx_
	 *----------------------------------------------------*/
	if (dStart == dEnd)
	{
		dStart = dEnd = 0.0;
		lS = 0;
		lE = 180;
		nType = 3;
	} else
	{
		lS = (long)(dStart * 90.0) + lSkip;
		if (dStart < dEnd)	lE = (long)(dEnd * 90.0);
		else				lE = (long)(dEnd * 90.0) + 180;

		//----- _xZkqqHx_iKeKiOgOjDfP_xHqqkZx_ dStart _xZkqqHx_iCmMiNmAjFfHiCpAiMhGiOfKiClHiCoJ_xHqqkZx_ -----
		dX = cos(dStart * M_PI) * dLX + dCX;
		dY = sin(dStart * M_PI) * dLY + dCY;
		iP = vPoint.insert(vPoint.end());
		switch (nFunc)
		{
		case 0x01:	// Shift_xZkqqHx_iCmMiCnN_xHqqkZx_.
			RSUB((*iP).x, dX + dSftX * (dY - dCY));
			RSUB((*iP).y, dY);
			break;
		case 0x02:	// Rotate_xZkqqHx_iCmMiCnN_xHqqkZx_.
			RSUB((*iP).x, dRotX + dCosR * (dX - dRotX) - dSinR * (dY - dRotY));
			RSUB((*iP).y, dRotY + dSinR * (dX - dRotX) + dCosR * (dY - dRotY));
			break;
		case 0x03:	// Shift,Rotate.
			dX += dSftX * (dY - dCY);
			RSUB((*iP).x, dRotX + dCosR * (dX - dRotX) - dSinR * (dY - dRotY));
			RSUB((*iP).y, dRotY + dSinR * (dX - dRotX) + dCosR * (dY - dRotY));
			break;
		default:	// case 0x00:	_xZkqqHx_jFmPiKlHjGlDiClF_xHqqkZx_.
			RSUB((*iP).x, dX);
			RSUB((*iP).y, dY);
		}
	}
	/*-----------------------------------------------------
	 * _xZkqqHx_iClBiClBiCoGiCoIiImIiNhOiCmNiBeB_xHqqkZx_lS_xZkqqHx_iCkJiCoH_xHqqkZx_lC_xZkqqHx_iMmCiCmM_xHqqkZx_g_aSin_xZkqqHx_iCpAiOgHiCkCiBeB_xHqqkZx_vPoint_xZkqqHx_iCpA_xHqqkZx_
	 * _xZkqqHx_iMhGiOfKiClFiCmEiCoEiCkNiBeC_xHqqkZx_
	 *-----------------------------------------------------*/
	switch (nFunc)
	{
	case 0x00:	// _xZkqqHx_jFmPiKlHjGlDiClF_xHqqkZx_.
		for (; lS < lE; lS += lSkip)
		{
			iP = vPoint.insert(vPoint.end());
			RSUB((*iP).x, g_aSin[225 - (lS % 180)] * dLX + dCX);
			RSUB((*iP).y, g_aSin[lS % 180] * dLY + dCY);
		}
		break;
	case 0x01:	// Shift_xZkqqHx_iCmMiCnN_xHqqkZx_.
		for (; lS < lE; lS += lSkip)
		{
			dX	= g_aSin[225 - (lS % 180)] * dLX + dCX;
			dY	= g_aSin[lS % 180] * dLY + dCY;
			iP = vPoint.insert(vPoint.end());
			RSUB((*iP).x, dX + dSftX * (dY - dCY));
			RSUB((*iP).y, dY);
		}
		break;
	case 0x02:	// Rotate_xZkqqHx_iCmMiCnN_xHqqkZx_.
		for (; lS < lE; lS += lSkip)
		{
			dX	= g_aSin[225 - (lS % 180)] * dLX + dCX - dRotX;
			dY	= g_aSin[lS % 180] * dLY + dCY - dRotY;
			iP = vPoint.insert(vPoint.end());
			RSUB((*iP).x, dRotX + dCosR * dX - dSinR * dY);
			RSUB((*iP).y, dRotY + dSinR * dX + dCosR * dY);
		}
		break;
	case 0x03:	// Shift,Rotate.
		for (; lS < lE; lS += lSkip)
		{
			dX	= g_aSin[225 - (lS % 180)] * dLX + dCX;
			dY	= g_aSin[lS % 180] * dLY + dCY;
			dX	+= dSftX * (dY - dCY);
			iP = vPoint.insert(vPoint.end());
			RSUB((*iP).x, dRotX + dCosR * (dX - dRotX)
										- dSinR * (dY - dRotY));
			RSUB((*iP).y, dRotY + dSinR * (dX - dRotX)
										+ dCosR * (dY - dRotY));
		}
		break;
	}

	/*----------------------------------------------------
	 * _xZkqqHx_iNmFiMoDiCmM_xHqqkZx_dEnd_xZkqqHx_iCmMiDhMiDeDiDjDiDgHiCpAiOfKiPgPiClHiCoJiBeC_xHqqkZx_
	 *----------------------------------------------------*/
	iP = vPoint.insert(vPoint.end());
	switch (nFunc)
	{
	case 0x01:	// Shift_xZkqqHx_iCmMiCnN_xHqqkZx_.
		dX	= cos(dEnd * M_PI) * dLX + dCX;
		dY	= sin(dEnd * M_PI) * dLY + dCY;
		RSUB((*iP).x, dX + dSftX * (dY - dCY));
		RSUB((*iP).y, dY);
		break;
	case 0x02:	// Rotate_xZkqqHx_iCmMiCnN_xHqqkZx_.
		dX	= cos(dEnd * M_PI) * dLX + dCX - dRotX;
		dY	= sin(dEnd * M_PI) * dLY + dCY - dRotY;
		RSUB((*iP).x, dRotX + dCosR * dX - dSinR * dY);
		RSUB((*iP).y, dRotY + dSinR * dX + dCosR * dY);
		break;
	case 0x03:	// Shift,Rotate.
		dX	= cos(dEnd * M_PI) * dLX + dCX;
		dY	= sin(dEnd * M_PI) * dLY + dCY;
		dX	+= dSftX * (dY - dCY);
		RSUB((*iP).x, dRotX + dCosR * (dX - dRotX)
									- dSinR * (dY - dRotY));
		RSUB((*iP).y, dRotY + dSinR * (dX - dRotX)
									+ dCosR * (dY - dRotY));
		break;
	default:	// case 0x00:	_xZkqqHx_jFmPiKlHjGlDiClF_xHqqkZx_.
		RSUB((*iP).x, cos(dEnd * M_PI) * dLX + dCX);
		RSUB((*iP).y, sin(dEnd * M_PI) * dLY + dCY);
	}

	/*----------------------------------------------------
	 * _xZkqqHx_iKgFiMfOjGiIiCmJ_xHqqkZx_dStart - dEnd_xZkqqHx_iKnEiCmJiIpIiCkNjApMiCpAiMhGiOfKiClHiCoJiBeC_xHqqkZx_
	 *----------------------------------------------------*/
	switch (nType)
	{
	case 1:		// _xZkqqHx_iDhAiDeDiMfO_xHqqkZx_.
		iP = vPoint.insert(vPoint.end());
		if (nFunc & 0x02)	// Rotate_xZkqqHx_iCkKiKnMiCnMiCoKiCoJiPoKiNiH_xHqqkZx_.
		{
			RSUB((*iP).x, dRotX + dCosR * (dCX - dRotX)
										- dSinR * (dCY - dRotY));
			RSUB((*iP).y, dRotY + dSinR * (dCX - dRotX)
										+ dCosR * (dCY - dRotY));
		} else
		{
			RSUB((*iP).x, dCX);
			RSUB((*iP).y, dCY);
		}
		vPoint.push_back(vPoint.front());
		break;
	case 2:		// _xZkqqHx_iLhMiMfO_xHqqkZx_.
		vPoint.push_back(vPoint.front());
		break;
	}
} // GetArcPolygon.

#endif//__UtilArc_h
