// PreProcess.cpp: implementation of the CPreProcess class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PreProcess.h"
#include <math.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define PI 3.141592f
CPreProcess::CPreProcess()
{
	m_BecNum = 0;
}

CPreProcess::~CPreProcess()
{

}
/*
________________________________________________________________
  
Function Name	: GetAngle
	
Purpose			: 두점을 지나는 직선이 수평축과 이루는 각도를 구한다.
		
Input			: No
	StPt		: 직선의 시작점좌표
	EdPt		: 직선의 끝점좌표

Output			: 두점을 지나는 직선이 수평축과 이루는 각도(라디안 값)

Return Value	: No

Description		: 얻어지는 각도구간은 -PI부터 +PI까지이다.

Ref Journal		: No
	
date			: 2009/07/5
________________________________________________________________
*/
float CPreProcess::GetAngle(OVR_POINT StPt,OVR_POINT EdPt)
{
	int dx = EdPt.nX - StPt.nX;
	int dy = EdPt.nY - StPt.nY;
	if(dx==0){ 
		if(dy>0)
			return PI/2;
		else
			return -PI/2;
	}
	else {
		return (float)atan2((float)dy,(float)dx);
	}

}
/*
________________________________________________________________
  
Function Name	: GetPointAryBetweenTwoPoints
	
Purpose			: 두점을 맺는 직선상에 놓이는 점 배열을 구한다.
		
Input			: No
	P1			: 직선의 시작점좌표
	P2			: 직선의 끝점좌표
	

Output			: 
	PtAry		: 두점을 맺는 직선상에 놓이는 점 배열

Return Value	: 두점을 맺는 직선상에 놓이는 점 배열의 길이 
				 

Description		: 

Ref Journal		: No
	
date			: 2009/07/5
________________________________________________________________
*/
int CPreProcess::GetPointAryBetweenTwoPoints(OVR_POINT P1,OVR_POINT P2,OVR_POINT* PtAry)
{
	int PtNum=0; 

	int i;
	if(P1.nX == P2.nX && P1.nY == P2.nY){
		return PtNum;
	}
	else if(P1.nY==P2.nY){
		if(P1.nX<P2.nX){
			for(i=P1.nX;i<P2.nX;i++){
				PtAry[PtNum].nX = i;PtAry[PtNum++].nY = P1.nY;
			}
		}
		else{
			for(i=P1.nX;i>P2.nX;i--){
				PtAry[PtNum].nX = i;PtAry[PtNum++].nY = P1.nY;
			}
		}
	}
	else if(P1.nX==P2.nX){
		if(P1.nY<P2.nY){
			for(i=P1.nY;i<P2.nY;i++){
				PtAry[PtNum].nX = P1.nX;PtAry[PtNum++].nY = i;
			}
		}
		else{
			for(i=P1.nY;i>P2.nY;i--){
				PtAry[PtNum].nX = P1.nX;PtAry[PtNum++].nY = i;
			}
		}
	}
	else{
		double ta;
		int y,x;
		int dx = abs(P1.nX-P2.nX);
		int dy = abs(P1.nY-P2.nY);
		if(dx>=dy){
			if(P1.nX<P2.nX){
				ta = (double)(P2.nY-P1.nY)/(double)(P2.nX-P1.nX);
				for(i=P1.nX;i<P2.nX;i++){
					y = (int)(P1.nY+(i-P1.nX)*ta);
					PtAry[PtNum].nX = i;PtAry[PtNum++].nY = y;
				}
			}
			else{
				ta = (double)(P2.nY-P1.nY)/(double)(P1.nX-P2.nX);
				for(i=P1.nX;i>P2.nX;i--){
					y = (int)(P1.nY+(P1.nX-i)*ta);
					PtAry[PtNum].nX = i;PtAry[PtNum++].nY = y;
				}
			}
		}
		else{
			if(P1.nY<P2.nY){
				ta = (double)(P2.nX-P1.nX)/(double)(P2.nY-P1.nY);
				for(i=P1.nY;i<P2.nY;i++){
					x = (int)(P1.nX+(i-P1.nY)*ta);
					PtAry[PtNum].nX = x;PtAry[PtNum++].nY = i;
				}
			}
			else{
				ta = (double)(P2.nX-P1.nX)/(double)(P1.nY-P2.nY);
				for(i=P1.nY;i>P2.nY;i--){
					x = (int)(P1.nX+(P1.nY-i)*ta);
					PtAry[PtNum].nX = x;PtAry[PtNum++].nY = i;
				}
			}
		}
	}
	return PtNum;
}



/*
________________________________________________________________
  
Function Name	: GetAllPtArryAndResample
	
Purpose			: 연속점 배열 및 표본화점 배열 생성.
		
Input			:
	pPt			: 입력점 배열(크기정규화된 점 배열)
	PtNum		: 입력점 배열의 점개수
	pStroke		: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			:
	pSamPtProp	: 펜up-down정보
	pSamPtNo	: 표본화점 번호 배열 
	SamPtNum	: 표본화점의 개수
	NewPtNum	: 연속점 배열의 점개수

Return Value	: 연속점 배열

Description		: 앞단계에서 얻은 크기정규화된 점 배열을 스캔하면서 이웃한 두점을 잇는 직선상에 놓이는 모든점들을  
				  추가하여 연속점 배열을 만든다.  이때 펜up-down점(가상획을 이루는 점)들도 추가한다.
				  이 연속점 배열에 대하여 획의 끝점으로 될수있는 점번호 배열을 만든다(표본화)
				  이것은 동적계획법으로 획을 추정할때 계산량을 줄이기위한것이다.
				  표본화점은 펜up-down점들과 각도변화가 큰점들,그리고 이점들사이를 
				  등간격으로 나눈점들로 이루어진다.

Ref Journal		: No
	
date			: 2009/06/7
________________________________________________________________
*/
OVR_POINT* CPreProcess::GetAllPtArryAndResample(OVR_POINT *pPt, int PtNum,
			STROKE* pStroke,int nStrokeNum,int* pSamPtProp,int* pSamPtNo, int& SamPtNum,int& NewPtNum)
{
	OVR_POINT* pNewPt = (OVR_POINT*)malloc(H1*W1*sizeof(OVR_POINT));
	OVR_POINT* pTempPt = (OVR_POINT*)malloc(max(H1,W1)*sizeof(OVR_POINT));
	int MaxPtNum = H1*W1/2;

/////////////// 중간점생성/////////////
	int i,j,l,k,st,ed;
	int gap = 12,gap1;
	float ang1,ang2,ang,th = PI/2.0f;
	int pre,n,len;

	SamPtNum = NewPtNum = 0;

	BOOL bSpacal;
	for (i=0;i<nStrokeNum;i++) {
		st = pStroke[i].stPtNo;	ed = pStroke[i].edPtNo;
		pSamPtProp[SamPtNum] = DOWN_Flag;
		pSamPtNo[SamPtNum++] = NewPtNum;
		
		for(j=st+1;j<=ed;j++){
			l = GetPointAryBetweenTwoPoints(pPt[j-1],pPt[j],pTempPt);
			if(l<=0)continue;
			for(k=0;k<l;k++){
				if(NewPtNum>=MaxPtNum) break;
				pNewPt[NewPtNum].nX = pTempPt[k].nX;
				pNewPt[NewPtNum++].nY = pTempPt[k].nY;
			}
	///////////   변곡점탐색  ///////////////////////
			if(j>=ed )break;
			pre = pSamPtNo[SamPtNum-1];
			len = NewPtNum-pre;
			bSpacal = FALSE;

			ang1 = GetAngle(pPt[j-1],pPt[j]);
			ang2 = GetAngle(pPt[j],pPt[j+1]);
			ang1 = (float)fabs(ang1-ang2);
			ang2 = PI*2-ang1;
			ang = min(ang2,ang1);
			
			if(len<=1) bSpacal = FALSE;
			if(ang>=PI*0.15f) bSpacal = TRUE;
			if(len>=4 && ang>=PI*0.1f){
				bSpacal = TRUE;
			}
			if(bSpacal == TRUE ){
				
				n = len/gap;
				gap1= gap;
				if(len%gap < 4)	n--;
				if(n>0)gap1 = len/(n+1);
				for(k=1;k<=n;k++){
					pSamPtNo[SamPtNum++] = pre+gap1*k;
					pSamPtProp[SamPtNum-1] = CJZ_Flag;
				}
				len-=gap1*max(0,n);
				pSamPtNo[SamPtNum++] = NewPtNum;
				if(len>=gap1 || ang>=PI*0.2f)				
					pSamPtProp[SamPtNum-1] = CJZ_Flag;
			}
		}
		
		pNewPt[NewPtNum].nX = pPt[ed].nX;	pNewPt[NewPtNum++].nY = pPt[ed].nY;
		pre = pSamPtNo[SamPtNum-1];	len = NewPtNum-pre-1;
		
		if(pSamPtProp[SamPtNum-1] != DOWN_Flag && len<=1){
			pSamPtNo[SamPtNum-1] = NewPtNum-1;
			pSamPtProp[SamPtNum-1] = CJZ_Flag;
		}else{
			n = len/gap;
			if(len%gap < 4) n--;
			gap1= gap;
			if(n>0)gap1 = len/(n+1);
			for(k=1;k<=n;k++){
				pSamPtNo[SamPtNum++] = pre+gap1*k;
				pSamPtProp[SamPtNum-1] = CJZ_Flag;
			}
			pSamPtNo[SamPtNum++] = NewPtNum-1;
			pSamPtProp[SamPtNum-1] = CJZ_Flag;
		}

		if(i != nStrokeNum-1){
			pSamPtProp[SamPtNum-1] = UP_Flag;
			st = pStroke[i+1].stPtNo;
			l = GetPointAryBetweenTwoPoints(pPt[ed],pPt[st],pTempPt);
			for(k=1;k<l;k++){
				if(NewPtNum>=MaxPtNum) break;
				pNewPt[NewPtNum].nX = pTempPt[k].nX;
				pNewPt[NewPtNum++].nY = pTempPt[k].nY;
			}
		}
	}
	free(pTempPt);
/////////////////////////////////
	int MaxSamNum = 60;
	if(SamPtNum>MaxSamNum){
		k=0;
		BOOL b = FALSE;
		for(i=0;i<SamPtNum;i++){
			if(pSamPtProp[i] != UP_Flag && pSamPtProp[i] != DOWN_Flag){
				if(b==TRUE){
					pSamPtNo[k] = pSamPtNo[i];
					pSamPtProp[k++] = pSamPtProp[i];
					b = FALSE;
				}else	b = TRUE;
			}else{
				pSamPtNo[k] = pSamPtNo[i];
				pSamPtProp[k++] = pSamPtProp[i];
				b = FALSE;
			}

		}
		SamPtNum = k;
		if(SamPtNum > MaxSamNum) SamPtNum = MaxSamNum;
	}

	return pNewPt;
}
/*
________________________________________________________________
  
Function Name	: NoisProcess
	
Purpose			: 입력점 배열에 대하여 불필요한 잡음을 제거.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			: 
	pNewStroke	: 잡음제거한 점 배열의 획정보 
	NewPtNum	: 잡음제거한 점 배열의 점개수

Return Value	: 잡음제거한 점 배열

  Description	: 획의 끝에서 생기는 갈고리(hook)와 굴곡점,손의 떨림으로 인하여 생기는 울퉁불퉁한 점들,
				  지나치게 밀집된 점들과 각도변화가 작은 점들,이웃한 점사이의 거리가  어떤 임계값보다  작은점들, 
				  점 배열에 대하여 매점의 각도변화가 어떤 임계값보다  작은점들을 제거한다.

date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::NoisProcess(OVR_POINT *pOrgPt, int OrgPtNum,STROKE* pOrgStroke,STROKE* pNewStroke,
			int nStrokeNum,int& NewPtNum)
{


	STROKE* pNorStroke = (STROKE*)malloc(nStrokeNum*sizeof(STROKE));
	int NorPtNum;
	OVR_POINT* pNewPt	= RemoveHookFromOrg(pOrgPt,OrgPtNum,pOrgStroke,pNewStroke,nStrokeNum,NorPtNum );
	OVR_POINT* pNewPt1	= SmoothData(pNewPt,NorPtNum,pNewStroke,nStrokeNum );
	OVR_POINT* pNewPt2	= SelectPtData(pNewPt1,NorPtNum,pNewStroke,pNorStroke,nStrokeNum,NorPtNum );
	OVR_POINT* pNewPt3	= SelectPtData2(pNewPt2,NorPtNum,pNorStroke,pNewStroke,nStrokeNum,NorPtNum );

	NewPtNum = NorPtNum;
	
	free(pNewPt);
	free(pNewPt1);
	free(pNewPt2);
	free(pNorStroke);
	return pNewPt3;
}
/*
________________________________________________________________
  
Function Name	: GetNormalizePtsAndStroke
	
Purpose			: 크기정규화,연속점 배열 만들기 및 표본화점 배열 얻기.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			:
	pNorStroke	:	크기정규화한 점 배열의 획정보 
	nNorStrokeNum:	크기정규화한 점 배열의 획개수
	NorPtNum	:	크기정규화한 점 배열의 점개수

Return Value	: 크기정규화한 점 배열

Description		: No

Ref Journal		: No
	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::GetNormalizePtsAndStroke(OVR_POINT *pOrgPt, int OrgPtNum,STROKE* pOrgStroke,
			int nStrokeNum,int* pSamPtProp,int* pSamPtNo, int& SamPtNum,int& NewPtNum, int StId,int EndId,int RealSt,int RealEd)
{
	STROKE* pNorStroke = (STROKE*)malloc(nStrokeNum*sizeof(STROKE));
	int NorPtNum,nNorStrokeNum;
	
	OVR_POINT* pNorPt = LinearNormalize(pOrgPt,OrgPtNum,pOrgStroke,nStrokeNum,pNorStroke,nNorStrokeNum,NorPtNum,StId,EndId,RealSt,RealEd);	
	OVR_POINT* pNewPt = GetAllPtArryAndResample(pNorPt,NorPtNum,pNorStroke,nNorStrokeNum,pSamPtProp,pSamPtNo,SamPtNum,NewPtNum);

	for(int i=0;i<NewPtNum;i++){
		m_Bec[i].nX = (float)pNewPt[i].nX; 
		m_Bec[i].nY = (float)pNewPt[i].nY; 
	}
	m_BecNum = NewPtNum;
	
	free(pNorStroke);
	free(pNorPt);
	return pNewPt;
}
/*
________________________________________________________________
  
Function Name	: GetNormalizeOnlyHorWPtsAndStroke
	
Purpose			: 크기정규화,연속점 배열 만들기 및 표본화점 배열 얻기.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			:
	pNorStroke	:	크기정규화한 점 배열의 획정보 
	nNorStrokeNum:	크기정규화한 점 배열의 획개수
	NorPtNum	:	크기정규화한 점 배열의 점개수

Return Value	: 크기정규화한 점 배열

Description		: No

Ref Journal		: No
	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::GetNormalizeOnlyHorWPtsAndStroke(OVR_POINT *pOrgPt, int OrgPtNum,STROKE* pOrgStroke,
			int nStrokeNum,int* pSamPtProp,int* pSamPtNo, int& SamPtNum,int& NewPtNum, int StId,int EndId,int RealSt,int RealEd)
{
	STROKE* pNorStroke = (STROKE*)malloc(nStrokeNum*sizeof(STROKE));
	int NorPtNum,nNorStrokeNum;
	
	OVR_POINT* pNorPt = LinearNormalizeOnlyHorW(pOrgPt,OrgPtNum,pOrgStroke,nStrokeNum,pNorStroke,nNorStrokeNum,NorPtNum,StId,EndId,RealSt,RealEd);	
	OVR_POINT* pNewPt = GetAllPtArryAndResample(pNorPt,NorPtNum,pNorStroke,nNorStrokeNum,pSamPtProp,pSamPtNo,SamPtNum,NewPtNum);

	for(int i=0;i<NewPtNum;i++){
		m_Bec[i].nX = (float)pNewPt[i].nX;
		m_Bec[i].nY = (float)pNewPt[i].nY;
	}
	m_BecNum = NewPtNum;
	
	free(pNorStroke);
	free(pNorPt);
	return pNewPt;
}

/*
________________________________________________________________
  
Function Name	: RemoveHookFromOrg
	
Purpose			: 획의 시작이나 끝부분에 생기는 삐침을 제거.
		
Input			:
	pPt			: 입력점 배열
	PtNum		: 입력점 배열의 점개수
	pStroke		: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			: 
	pNewStroke	: 갈고리(hook)제거한 점 배열의 획정보 
	NewPtNum	: 갈고리(hook)제거한 점 배열의 점개수

Return Value	: 갈고리(hook)제거한 점 배열

Description		: 획의 시작이나 끝부분에서 각도변화가 크고  길이가 어떤 임계값보다 작은 부분을 삐침으로 검출하여 제거.	
				  이 함수를 실행하면 삐침들이 제거되여 획정보와 입구점 배열이 변한다는데 주목해야 한다. 

			  
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT* CPreProcess::RemoveHookFromOrg(OVR_POINT *pPt, int PtNum,
			STROKE* pStroke,STROKE* pNewStroke,int nStrokeNum,int& NewPtNum)
{
	OVR_POINT* pNewPt = (OVR_POINT*)malloc(PtNum*sizeof(OVR_POINT));

/////////////// Hook Remove /////////////
	int i,j,st,ed,newed,newst;
	float ang1,ang2,ang,angB1,angB2,Curang;
	int Hooklen;
	NewPtNum = 0;
	RECT rt;
	int StepLen,len;
	int x,y;
	int pre,aft;
	for (i=0;i<nStrokeNum;i++) {
		st = pStroke[i].stPtNo;
		ed = pStroke[i].edPtNo;

		//---------- i번째획의 외접4각형 얻기 -----------
		rt = GetBoundingBox(&pPt[st],ed-st+1);	
		//--------------------------------------------
		
		//- i번째획을 이루는 입력점사이 평균거리 얻기 -
		len=0;	
		for(j=st+1;j<=ed;j++){
			x = abs(pPt[j].nX-pPt[j-1].nX);
			y = abs(pPt[j].nY-pPt[j-1].nY);
			len+=max(x,y);
		}
		if(len==0 || ed==st)StepLen = len;
		else StepLen = len /(ed-st) +1;		
		//--------------------------------------------

		x=rt.right-rt.left;	y=rt.bottom -rt.top;

		len=max(x,y);	newed = ed;

		Hooklen = (int)((float)(len)*0.4f); //--- 외접4각형의 장축길이의 0.4배를 갈고리길이로 설정   
		len=0;
		
		//------------- 끝점검사 및 조건성립되면 끝점 변경 ---------------
		//--- 변경조건은 필기시에 끝점에 이를때 대체로 웃방향 or 왼쪽방향으로 삐침이 생긴다고 보고 조건에 따라 제거
		//--- 길이가 어떤임계값보다 작고 각도변화가 90도이상인 경우 제거  

		for(j=ed-1;j>=st;j--){
			if(j<=st+2)break;
			x = abs(pPt[j+1].nX-pPt[j].nX);	y = abs(pPt[j+1].nY-pPt[j].nY);
			len+=max(x,y);

			if(len>=Hooklen)break;

			for(pre=j-1;pre>st+2;pre--){
				x = abs(pPt[j].nX-pPt[pre].nX);	y = abs(pPt[j].nY-pPt[pre].nY);
				if(max(x,y) > StepLen/2) break;
			}

			for(aft=j+1;aft<ed;aft++){
				x = abs(pPt[j].nX-pPt[aft].nX);	y = abs(pPt[j].nY-pPt[aft].nY);
				if(max(x,y) > StepLen/2) break;
			}

			ang1 = GetAngle(pPt[pre],pPt[j]); ang2 = GetAngle(pPt[j],pPt[aft]);
			Curang = ang2;

			ang1 = (float)fabs(ang1-ang2);	ang2 = PI*2-ang1;
			ang = min(ang2,ang1);

			if(ang>=PI*0.7f ){
				if(len<=(int)((float)StepLen*1.2f)){newed = j;break;}
				Curang = GetAngle(pPt[j],pPt[ed]);

				if(Curang<=PI*(-0.35f) && Curang>=PI*(-0.65f)){	newed = j;	break;	}

				if(Curang>=PI*0.85f || Curang<=PI*(-0.85f)){ newed = j;	break;	}

				if(len<=(int)((float)Hooklen*0.4f)){newed = j;	break; }
				continue;				
			}
			
			if(ang>=PI*0.4f){
				ang1 = GetAngle(pPt[pre-1],pPt[pre]);	
				ang2 = GetAngle(pPt[pre],pPt[j]);
				ang1 = (float)fabs(ang1-ang2);			
				ang2 = PI*2-ang1;
				angB1 = min(ang2,ang1);
				ang1 = GetAngle(pPt[pre-2],pPt[pre-1]);
				ang2 = GetAngle(pPt[pre-1],pPt[pre]);
				ang1 = (float)fabs(ang1-ang2);
				ang2 = PI*2-ang1;
				angB2 = min(ang2,ang1);
				angB1 = (angB1+angB2);
				
				if(ang>angB1){
					Curang = GetAngle(pPt[j],pPt[ed]);
					if(Curang>=PI*(0.35f) && Curang<=PI*(0.65f) && len>=Hooklen/3)continue;

					if(Curang<=PI*(-0.35f) && Curang>=PI*(-0.65f)&& len<=(int)((float)Hooklen*0.6f)){newed = j;break;}

					if((Curang>=PI*0.85f || Curang<=PI*(-0.85f) )&& len<=(int)((float)Hooklen*0.6f)){newed = j;	break;}

					if(len<=(int)((float)Hooklen*0.4f)){newed = j;	break;	}
				}
			}
		}
		//----------------------------------------------------------------

		//---------- 시작점검사 및 조건성립되면 시작점 변경 --------------
		//--- 변경조건은 필기시에 시작점에 이를때 대체로 오른쪽에서 왼쪽으로 삐침이 생긴다고 보고 조건에 따라 제거
		//--- 길이가 어떤임계값보다 작고 각도변화가 90도이상인 경우 제거  
		newst = st;
		rt = GetBoundingBox(&pPt[st],newed-st+1);
		len=0;

		for(j=st+1;j<=newed;j++){
			x = abs(pPt[j].nX-pPt[j-1].nX);
			y = abs(pPt[j].nY-pPt[j-1].nY);
			len+=max(x,y);
		}

		if(len==0 || newed==st)StepLen = len;
		else StepLen = len /(newed-st) +1;

		x=rt.right-rt.left;		y=rt.bottom -rt.top;
		len=max(x,y);

		Hooklen = (int)((float)(len)*0.25f);
		len=0;

		for(j=st+1;j<=newed;j++){
			if(j>=newed-2)break;
			x = abs(pPt[j].nX-pPt[j-1].nX);
			y = abs(pPt[j].nY-pPt[j-1].nY);
			len+=max(x,y);
			if(len>=Hooklen)break;
			for(pre=j-1;pre>st;pre--){
				x = abs(pPt[j].nX-pPt[pre].nX);
				y = abs(pPt[j].nY-pPt[pre].nY);
				if(max(x,y) > StepLen/2) break;
			}
			for(aft=j+1;aft<newed-2;aft++){
				x = abs(pPt[j].nX-pPt[aft].nX);
				y = abs(pPt[j].nY-pPt[aft].nY);
				if(max(x,y) > StepLen/2) break;
			}

			ang1 = GetAngle(pPt[pre],pPt[j]);
			ang2 = GetAngle(pPt[j],pPt[aft]);
			Curang = ang1;
			ang1 = (float)fabs(ang1-ang2);
			ang2 = PI*2-ang1;
			ang = min(ang2,ang1);
			if(ang>=PI*0.7f ){
				if(Curang>=PI*0.85f || Curang<=PI*(-0.85f) ){
					newst = j;
					break;
				}
				if(len<=Hooklen/2){
					newst = j;
					break;
				}
			}
			if(ang>=PI*0.4f && len<=Hooklen/2){
				ang1 = GetAngle(pPt[j],pPt[aft]);
				ang2 = GetAngle(pPt[aft],pPt[aft+1]);
				ang1 = (float)fabs(ang1-ang2);
				ang2 = PI*2-ang1;
				angB1 = min(ang2,ang1);
				ang1 = GetAngle(pPt[aft],pPt[aft+1]);
				ang2 = GetAngle(pPt[aft+1],pPt[aft+2]);
				ang1 = (float)fabs(ang1-ang2);
				ang2 = PI*2-ang1;
				angB2 = min(ang2,ang1);
				angB1 = (angB1+angB2);
				
				if(ang>angB1){
					newst = j;
					break;
				}
			}
		}
		pNewStroke[i].stPtNo = NewPtNum;
		for(j=newst;j<=newed;j++){
			pNewPt[NewPtNum].nX = pPt[j].nX;
			pNewPt[NewPtNum++].nY = pPt[j].nY;
		}
		pNewStroke[i].edPtNo = NewPtNum-1;
	}
	//--------------------------------------------------------------------

/////////////////////Resampling /////////////
	return pNewPt;
}

OVR_POINT*	CPreProcess::RotateData(OVR_POINT* pOrgPt,int OrgPtNum,float fAng)
{
	double ang = fAng*acos(0.0f)/90.0f;		//convert angle to radians and invert (positive angle performs clockwise rotation)
	float cos_angle = (float) cos(ang);			//these two are needed later (to rotate)
	float sin_angle = (float) sin(ang);
	float x,y;
	OVR_POINT* pNewPt = (OVR_POINT*)malloc(OrgPtNum*sizeof(OVR_POINT));
	for(int i=0;i<OrgPtNum;i++){
		x=(float)pOrgPt[i].nX;
		y=(float)pOrgPt[i].nY;
		pNewPt[i].nX = (int)(cos_angle*x+sin_angle*y+0.5f);
		pNewPt[i].nY = (int)(cos_angle*y-sin_angle*x+0.5f);
	}
	return pNewPt;

}
/*
________________________________________________________________
  
Function Name	: SmoothData
	
Purpose			: 손의 떨림으로 인하여 생기는 울퉁불퉁한 점들을 교정.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			: 
	pNewStroke	: 잡음제거한 점 배열의 획정보 
	NewPtNum	: 잡음제거한 점 배열의 점개수

Return Value	: 잡음제거한 점 배열

Description		: 평활화후 점 배열의 개수와 획정보(시작,끝점번호)는 변하지 않고 다만 점좌표만이 변한다.

	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::SmoothData(OVR_POINT* pOrgPt,int OrgPtNum,STROKE* pOrgStroke,int nStrokeNum)
{
	OVR_POINT* pNewPt = (OVR_POINT*)malloc(OrgPtNum*sizeof(OVR_POINT));

	int i,j,x,y,st,ed;
	for (i=0;i<nStrokeNum;i++) {
		st = pOrgStroke[i].stPtNo;
		ed = pOrgStroke[i].edPtNo;
		memcpy(&(pNewPt[st]),&pOrgPt[st],sizeof(OVR_POINT)*(ed-st+1));
		for(j=st+2;j<ed-1;j++){
			x = pOrgPt[j-2].nX*(-3) + pOrgPt[j-1].nX*(12) +pOrgPt[j].nX*(17) + 
				pOrgPt[j+1].nX*(12) +pOrgPt[j+2].nX*(-3);
			y = pOrgPt[j-2].nY*(-3) + pOrgPt[j-1].nY*(12) +pOrgPt[j].nY*(17) + 
				pOrgPt[j+1].nY*(12) +pOrgPt[j+2].nY*(-3);
			pNewPt[j].nX = x/35;
			pNewPt[j].nY = y/35;
		}			
	}

	return pNewPt;
}
/*
________________________________________________________________
  
Function Name	: SelectPtData
	
Purpose			: 지나치게 밀집된 점들과 각도변화가 작은 점들 제거.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			: 
	pNewStroke	: 려파한 점 배열의 획정보  
	NewPtNum	: 여과한 점 배열의 점개수

Return Value	: 여과한 점 배열

Description		: 이웃한 점사이의 거리가  어떤 임계값보다  작은점들은 제거.
				  이때 굴곡점(뾰족점)제거를 막기 위해 이웃한 두점의 각도변화를 보고
				  그중 각도변화가  작은점을 제거한다.

Ref Journal		: No
	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::SelectPtData(OVR_POINT* pOrgPt,int OrgPtNum,STROKE* pOrgStroke,STROKE* pNewStroke,int nStrokeNum,int& NewPtNum)
{
	OVR_POINT* pNewPt = (OVR_POINT*)malloc((OrgPtNum*2)*sizeof(OVR_POINT));

	float ang1,ang2,ang,Curang;
	int i,j,x,y,st,ed,len,StepLen;
	NewPtNum = 0;

	for (i=0;i<nStrokeNum;i++) {
		st = pOrgStroke[i].stPtNo; ed = pOrgStroke[i].edPtNo;

		pNewStroke[i].stPtNo = NewPtNum; pNewPt[NewPtNum++] = pOrgPt[st];
		
		//-------------------- i번째획안의 평균점사이간격 계산 ------------------------- 
		len=0;
		for(j=st+1;j<=ed;j++){
			x = abs(pOrgPt[j].nX-pOrgPt[j-1].nX);	y = abs(pOrgPt[j].nY-pOrgPt[j-1].nY);
			len+=max(x,y);
		}
		if(len==0 || ed==st)StepLen = len;
		else StepLen = len /(ed-st) +1;
		//------------------------------------------------------------------------------

		for(j=st+1;j<ed;j++){
			x = abs(pOrgPt[j].nX-pOrgPt[j-1].nX); y = abs(pOrgPt[j].nY-pOrgPt[j-1].nY);

			len=max(x,y);
			if(len<=StepLen*3/5){
				if(j<=st+1)continue; //첫점,둘째점은 고려안함 
				
				//- 주목점 j에서 량옆점과의 각도를 계산하여 
				//- 임계값(PI*0.2f:36도)보다 작으면 j번째 점을 제거 
				ang1 = GetAngle(pOrgPt[j-1],pOrgPt[j]);	ang2 = GetAngle(pOrgPt[j],pOrgPt[j+1]);
				ang1 = (float)fabs(ang1-ang2);	ang2 = PI*2-ang1;
				Curang = min(ang2,ang1);
				if(Curang<=PI*0.2f) continue;

				//- 점 j-1에서 량옆점과의 각도를 계산하여 
				//- 임계값(PI*0.2f:36도)보다 작으면 j-1번째 점을 제거 
				ang1 = GetAngle(pOrgPt[j-2],pOrgPt[j-1]); ang2 = GetAngle(pOrgPt[j-1],pOrgPt[j]);
				ang1 = (float)fabs(ang1-ang2); ang2 = PI*2-ang1;
				ang = min(ang2,ang1);

				if(Curang>ang){	pNewPt[NewPtNum-1] = pOrgPt[j];	continue;}
				else continue;

			}
			pNewPt[NewPtNum++] = pOrgPt[j];
		}
		if(st==ed) pNewStroke[i].edPtNo = NewPtNum-1;
		else{
			x = abs(pOrgPt[ed].nX-pOrgPt[ed-1].nX);	y = abs(pOrgPt[ed].nY-pOrgPt[ed-1].nY);
			len=max(x,y);

			if(pNewStroke[i].stPtNo!=NewPtNum-1 && len<=StepLen*3/5) pNewPt[NewPtNum-1] = pOrgPt[ed];
			else pNewPt[NewPtNum++] = pOrgPt[ed];
			pNewStroke[i].edPtNo = NewPtNum-1;

		}
	}

	return pNewPt;
}
/*
________________________________________________________________
  
Function Name	: SelectPtData2
	
Purpose			: 점 배열에 대하여 매점의 각도변화가 어떤 임계값보다  작은점들 제거(9도이하).
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			: 
	pNewStroke	: 여과한 점 배열의 획정보  
	NewPtNum	: 여과한 점 배열의 점개수

Return Value	: 여과한 점 배열

Description		: 점 배열에 대하여 매점의 각도변화가 어떤 임계값보다  작은점들은 제거한다.

Ref Journal		: No
	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::SelectPtData2(OVR_POINT* pOrgPt,int OrgPtNum,STROKE* pOrgStroke,STROKE* pNewStroke,int nStrokeNum,int& NewPtNum)
{
	
	OVR_POINT* pNewPt = (OVR_POINT*)malloc(OrgPtNum*sizeof(OVR_POINT));

	float ang1,ang2,Curang;
	int i,j,st,ed;
	NewPtNum = 0;
	for (i=0;i<nStrokeNum;i++) {
		st = pOrgStroke[i].stPtNo;
		ed = pOrgStroke[i].edPtNo;
		pNewStroke[i].stPtNo = NewPtNum;
		pNewPt[NewPtNum++] = pOrgPt[st];
		for(j=st+1;j<ed;j++){
			ang1 = GetAngle(pOrgPt[j-1],pOrgPt[j]);
			ang2 = GetAngle(pOrgPt[j],pOrgPt[j+1]);
			ang1 = (float)fabs(ang1-ang2);
			ang2 = PI*2-ang1;
			Curang = min(ang2,ang1);
			if(Curang<=PI*0.05f) continue;
			pNewPt[NewPtNum++] = pOrgPt[j];
		}
		if(st!=ed)	pNewPt[NewPtNum++] = pOrgPt[ed];
		pNewStroke[i].edPtNo = NewPtNum-1;
	}

	return pNewPt;
}

/*
________________________________________________________________
  
Function Name	: LinearNormalize
	
Purpose			: 크기정규화 및 일정한 조건에 맞는점 제거.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			:
	pNorStroke	:  크기정규화한 점 배열의 획정보 
	nNorStrokeNum: 크기정규화한 점 배열의 획개수
	NorPtNum	:  크기정규화한 점 배열의 점개수

Return Value	: 크기정규화한 점 배열

Description		: 문자의 크기를 64*64로 정규화한다.
				  이때 이웃한  점들의  좌표가 같아지면 그점을 삭제한다.
				  결국 점 배열의 개수와 획정보가 달라진다.

Ref Journal		: No
	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::LinearNormalize(OVR_POINT* pOrgPt,int OrgPtNum,STROKE* pOrgStroke,int nStrokeNum,
			STROKE* pNorStroke,int& nNorStrokeNum, int& NorPtNum,int StId,int EndId,int RealSt,int RealEd)
{
	RECT rect = GetBoundingBox(pOrgPt, OrgPtNum,RealSt,RealEd);
	int i,j,l,t,x,y,OrgW,OrgH,st,ed;
	OrgW = rect.right - rect.left; OrgH = rect.bottom - rect.top;
	l = rect.left;	t = rect.top;
	float rateW = (float)(W1-1)/(float)OrgW;
	float rateH = (float)(H1-1)/(float)OrgH;

	
	OVR_POINT* pNewPt = (OVR_POINT*)malloc(OrgPtNum*sizeof(OVR_POINT));

	if(StId==0 && EndId == -1){
		StId=0;EndId = OrgPtNum-1;
	}
	NorPtNum = 0;
	nNorStrokeNum = 0;
	for (i=0;i<nStrokeNum;i++) {
		st = pOrgStroke[i].stPtNo;
		ed = pOrgStroke[i].edPtNo;

		if(StId>ed || EndId<st) continue;
		if(st<StId) st = StId;
		if(ed>EndId) ed = EndId;

		pNorStroke[nNorStrokeNum].stPtNo = NorPtNum;

		x = (int)((float)(pOrgPt[st].nX - l) * rateW + 0.5f);
		y = (int)((float)(pOrgPt[st].nY - t) * rateH + 0.5f);

		pNewPt[NorPtNum].nX = x; pNewPt[NorPtNum].nY = y;
		NorPtNum++;

		for(j=st+1;j<=ed;j++){
			x = (int)((float)(pOrgPt[j].nX - l) * rateW + 0.5f);
			y = (int)((float)(pOrgPt[j].nY - t) * rateH + 0.5f);
			if(pNewPt[NorPtNum-1].nX == x && pNewPt[NorPtNum-1].nY == y)continue;
			pNewPt[NorPtNum].nX = x;
			pNewPt[NorPtNum].nY = y;
			NorPtNum++;
		}

		pNorStroke[nNorStrokeNum].edPtNo = NorPtNum-1;
		nNorStrokeNum++;
	}

	return pNewPt;
}

/*
________________________________________________________________
  
Function Name	: LinearNormalizeOnlyHorW
	
Purpose			: 크기정규화 및 일정한 조건에 맞는점 제거.
		
Input			:
	pOrgPt		: 입력점 배열
	OrgPtNum	: 입력점 배열의 점개수
	pOrgStroke	: 입력점 배열의 획정보
	nStrokeNum	: 입력점 배열의 획개수

Output			:
	pNorStroke	:  크기정규화한 점 배열의 획정보 
	nNorStrokeNum: 크기정규화한 점 배열의 획개수
	NorPtNum	:  크기정규화한 점 배열의 점개수

Return Value	: 크기정규화한 점 배열

Description		: 문자의 크기를 64*64로 정규화한다.이 함수는 영어와 숫자를  정규화할때만 이용되는 함수이다.
				  정규화방법은 64*64크기와 입력점 배열의 외접4각형과의 너비,높이비율중에서 작은것으로 정규화한다. 
				  이때 이웃한  점들의  좌표가 같아지면 그점을 삭제한다. 결국 점 배열의 개수와 획정보가 달라진다.

Ref Journal		: No
	
date			: 2009/06/5
________________________________________________________________
*/
OVR_POINT*	CPreProcess::LinearNormalizeOnlyHorW(OVR_POINT* pOrgPt,int OrgPtNum,STROKE* pOrgStroke,int nStrokeNum,
			STROKE* pNorStroke,int& nNorStrokeNum, int& NorPtNum,int StId,int EndId,int RealSt,int RealEd)
{
	RECT rect = GetBoundingBox(pOrgPt, OrgPtNum,RealSt,RealEd);
	int i,j,l,t,x,y,OrgW,OrgH,st,ed;
	OrgW = rect.right - rect.left;
	OrgH = rect.bottom - rect.top;
	l = rect.left;
	t = rect.top;
	float rate;
	float rateW = (float)W1/(float)OrgW;
	float rateH = (float)H1/(float)OrgH;

	int offx=0,offy=0;
	if(rateH>rateW){
		rate = rateW;
		offy = (int)(((float)H1 - (float)OrgH*rate)/2.0f);
	}else{
		rate = rateH;
		offx = (int)(((float)W1 - (float)OrgW*rate)/2.0f);
	}
	rate = min(rateH,rateW);
	
	OVR_POINT* pNewPt = new OVR_POINT[OrgPtNum];

	if(StId==0 && EndId == -1){
		StId=0;EndId = OrgPtNum-1;
	}
	NorPtNum = 0;
	nNorStrokeNum = 0;
	for (i=0;i<nStrokeNum;i++) {
		st = pOrgStroke[i].stPtNo;
		ed = pOrgStroke[i].edPtNo;
		if(StId>ed || EndId<st) continue;
		if(st<StId) st = StId;
		if(ed>EndId) ed = EndId;
		pNorStroke[nNorStrokeNum].stPtNo = NorPtNum;
		x = (int)(((float)(pOrgPt[st].nX - l) + 0.5f) * rate)+offx;
		y = (int)(((float)(pOrgPt[st].nY - t) + 0.5f) * rate)+offy;
		pNewPt[NorPtNum].nX = x;
		pNewPt[NorPtNum].nY = y;
		NorPtNum++;
		for(j=st+1;j<=ed;j++){
			x = (int)(((float)(pOrgPt[j].nX - l)+ 0.5f) * rate)+offx;
			y = (int)(((float)(pOrgPt[j].nY - t)+ 0.5f) * rate)+offy;
			if(pNewPt[NorPtNum-1].nX == x && pNewPt[NorPtNum-1].nY == y)continue;
			pNewPt[NorPtNum].nX = x;
			pNewPt[NorPtNum].nY = y;
			NorPtNum++;
		}
		pNorStroke[nNorStrokeNum].edPtNo = NorPtNum-1;
		nNorStrokeNum++;
	}

	return pNewPt;
}

/*
________________________________________________________________
  
Function Name	: GetBoundingBox
	
Purpose			: 입력점 배열의 RealSt부터 RealEd까지의 부분점 배열의 외접4각형을 구한다.
		
Input			: 
	pPts		: 입력점 배열
	PtNum		: 입력점개수
	RealSt		: 입력점 배열에서 고찰하려는 첫점번호
	RealEd		: 입력점 배열에서 고찰하려는 마지막점번호

Output			: 외접4각형

Return Value	: No

Description		: 
Ref Journal		: No
	
date			: 2009/07/5
________________________________________________________________
*/
RECT CPreProcess::GetBoundingBox(OVR_POINT *pPt, int PtNum,int RealSt,int RealEd)
{
	int i;
	RECT r;
	r.left=10000;r.top=10000;r.right=0;r.bottom=0;
	int x,y;
	if(RealSt>=PtNum){
		RealSt = PtNum-1;
	}
	if(RealEd == -1 || RealEd>=PtNum){
		RealEd = PtNum-1;
	}
	for(i=RealSt;i<=RealEd;i++){
		x = pPt[i].nX;
		y =pPt[i].nY;
		r.left = min(r.left,x);
		r.top = min(r.top,y);
		r.right = max(r.right,x);
		r.bottom = max(r.bottom,y);
	}
	r.right++;
	r.bottom++;
	return r;
}
