#ifdef PC_SIM
#include <mem.h>
#endif
#ifdef TARGET_PC
#include <mem.h>
#endif
#include <math.h>
#include "CPB_Module_Front_3D.h"

//---------------------------------------------------------------------------
#pragma DATA_ALIGN(8)
#pragma DATA_SECTION(".sdram")
short circleMaskData[MAX_RADIAL_CASE_COUNT][MAX_ROUND_LENGTH * 2];

#pragma DATA_ALIGN(8)
int circleMaskDataCnt[MAX_RADIAL_CASE_COUNT];

#pragma DATA_ALIGN(8)
#pragma DATA_SECTION(".sdram")
int GlobalRotationOutlineInfoTable[361][4]; // length, height

#pragma DATA_ALIGN(8)
#pragma DATA_SECTION(".sdram")
unsigned short RealEdgeLineForThreeDImage[MAX_THREED_EDGE_POINT_COUNT][3];

int RealEdgeLineCountForThreeDImage;

#pragma DATA_ALIGN(8)
#pragma DATA_SECTION(".sdram")
short RealThreeDShiftArray[MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT][2];

//---------------------------------------------------------------------------
// 이 모듈 내에서만 사용되는 static 함수의 선언.
// static 선언은 컴파일러의 최적화에 도움을 준다.
//---------------------------------------------------------------------------
static void makeHalfTempImage(int startX, int endX, int startY, int endY);
static void makeHalfTempImage2(int startX, int endX, int startY, int endY);
static void accumHalfLabelImageProtoCenter(int startX, int endX, int startY, int endY, int maxLabelN, int &protoCenterX, int &protoCenterY, int &protoCenterZ, int &tempCount, int &minX2, int &maxX2);
static void makeHalfTempImageForRemoveSaturation(int startX, int endX, int startY, int endY, int maxImgSize);
static void removeSaturation(int startX, int endX, int startY, int endY, int maxLabelN, int protoCenterX, int protoCenterY);
static void calcProtoTabletCenterHeight(int protoCenterX, int protoCenterY, int &tempProtoTabletCenterHeight, int &tempCount);
static int checkTiltMinHeight(int halfstartX, int halfendX, int halfstartY, int halfendY, int protoCenterX, int protoCenterY, int threeDThreshold, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void makeProtoShapeArea(int startX, int endX, int startY, int endY, int halfstartX, int halfendX, int halfstartY, int halfendY, int protoCenterX, int protoCenterY, int &removedCenterY);
static void calcTiltUpDownAvg(int halfstartX, int halfendX, int halfstartY, int halfendY, int protoCenterZ, int removedCenterY, int normalizedValue, int &upAvgValue, int &downAvgValue);
static void makeAdjustThreeDData(int startX, int endX, int startY, int endY);
static void makeAdjustThreeDDataForOval(int startX, int endX, int startY, int endY, int maxLabelN, int tabletShapeHeight);
static void removeImpulseNoise(int startX, int endX, int startY, int endY, int protoCenterX);
static void removeImpulseNoiseForTilt(int startX, int endX, int startY, int endY, int protoCenterX);
static void makeShapeImage(unsigned char *RESTRICT(shapeBinaryImage), unsigned char *RESTRICT(adjustThreeD_Data), int startX, int endX, int startY, int endY, int *ROIMinX, int *ROIMaxX, int *ROIMinY, int *ROIMaxY);
static void makeShapeImage2(unsigned char *RESTRICT(shapeBinaryImage), unsigned char *RESTRICT(adjustThreeD_Data), int startX, int endX, int startY, int endY, int *ROIMinX, int *ROIMaxX, int *ROIMinY, int *ROIMaxY);
static void makeOutlineInfoTable(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void makeOutlineInfoTableMean(int startX, int endX, int startY, int endY, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void makeOutlineInfoTableFinal(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void makeAdjustThreeDDataByOutlineInfoTable(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void removeHole(unsigned char *RESTRICT(tempImage), unsigned char *RESTRICT(shapeBinaryImage), unsigned char *RESTRICT(adjustThreeD_Data), int ROIMinX, int ROIMaxX, int ROIMinY, int ROIMaxY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void removeOutlineNoise(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void makeThreeDOutlineImage(int startX, int endX, int startY, int endY, int &protoCenterX, int &protoCenterY, int &protoCenterZ, int &tempCount);
static void calcCOGY(int startX, int endX, int startY, int endY, int ROIMinY, int ROIMaxY, int &zeroYZAngle, int &AvgShiftCOGY);
static void calcCOGX(int startX, int endX, int startY, int endY, int ROIMinX, int ROIMaxX, int &zeroXZAngle, int &AvgShiftCOGX);
static void calcRotateHeight(int startX, int endX, int startY, int endY, int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int &minHeight, int &maxHeight, int &RotateMinHeight, int &RotateMaxHeight);
static void makeTempOutlineInfoTable(int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4]);
static void calcRatationAngleFirst(int maxXZRange, int zeroYZAngle, int yzAngleStartR, int yzAngleEndR, int protoCenterX, int protoCenterY, int protoCenterZ, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int &xzRotAngle, int &yzRotAngle, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4]);
static void calcRatationAngleSecond(int txzRotAngle, int maxXZRange, int tyzRotAngle, int maxYZRange, int protoCenterX, int protoCenterY, int protoCenterZ, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int &xzRotAngle, int &yzRotAngle, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4]);
static void makeRotationThreeDImage(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int xzSinValue, int xzCosValue, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int RealsizeScaleFactor);
static void removeRotationThreeDImageHole();
static int accumAdjustTabletCenter();
static void makeOutlineShapeBinaryImage(int protoCenterX, int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int xzSinValue, int xzCosValue, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int RealsizeScaleFactor, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4]);
static void makeGlobalRotationOutlineInfoTable(int RESTRICT_ARRAY(outlineInfoTable)[4]);
static void calcMax3DHeight(int &Max3DHeight);
//---------------------------------------------------------------------------
void ProcessingStartForFrontFace_3D(int processingMode, int cameraIndex, int option2)
{
	ProcessingModeGlobal = processingMode;
	ThickErrorSW = 0;
	DefectSW = 0;
	PrintIsSW = 0;
	PrintMatchingResult.dataNumberWithMaxValue = -1; // 초기값

	DataInitiate(THREED, cameraIndex);

	SetThreeDSourceImage(cameraIndex, (option2 & 0x0F));

	PreprocessingFor3D(cameraIndex); // PC에서 25ms 정도가 소요된다. 사실상 반정도의 시간이 여기에 사용된다.

	if (DefectSW == 0 || ThickErrorSW)
	{
		if (TabletCharacter.discriminationDisplay_kind == STAMP || TabletCharacter.tabletDivisionLineInfo)
		{
			ExtractDentedArea(cameraIndex); // PC에서 6ms 정도 소요된다.
			PrintMatchingForThreeD(cameraIndex); // PC에서 6ms 정도 소요된다.
		}

		if (processingMode == INSPECTION_MODE && DefectSW == 0)
		{
			FrontFace3DShapeEdgeAreaSetting(cameraIndex);
			DefectSearchFor3DImage(cameraIndex); // PC에서 9ms 정도 소요된다.

			if (DefectSW == 0 && TabletCharacter.kind == SUGARCOATING && TabletGradeData.ETC_CoatedTablet_ShapeCheck)
			{
				CheckTabletShapeForSugarCoated(cameraIndex);
			}
		}
	}

	CheckPrintMatchingResult(THREED);

#ifdef PC_SIM
	if (processingMode == FIRST_STUDY_MODE)
	{
		if (DefectSW)
		{
			// for first study
			memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			memset(DentedArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		}
		else if (!(TabletCharacter.discriminationDisplay_kind == STAMP || TabletCharacter.tabletDivisionLineInfo))
		{
			// for first study
			memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			memset(DentedArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		}
	}

	memcpy(InspectionArea_Virtual, InspectionArea, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif

}
//---------------------------------------------------------------------------
void PreprocessingFor3D(int cameraIndex)
{
	int x, y, z;
	int tempAddress;
	int halfImageWidth;
	int halfImageHeight;
	int halfstartX, halfendX, halfstartY, halfendY;
	int startX, endX, startY, endY;
	int maxImgSize;
	int maxLabelN;
	int protoCenterX, protoCenterY, protoCenterZ;
	int tempProtoTabletCenterHeight;
	int tempCount;
	int yzAngleStartR, yzAngleEndR;
	int xzAngleStartR, xzAngleEndR;
	int maxYZRange;
	int maxXZRange;
	int TiltDirection;
	int upAvgValue;	
	int downAvgValue;
	int xzValue, yzValue;
	int normalizedValue;
	int threeDThreshold;
	int outlineInfoTable[361][4];	  // length, height
	int tempOutlineInfoTable[361][4]; // length, height
	int angle;
	int xzCosValue, xzSinValue;
	int yzCosValue, yzSinValue;
	int xzRotAngle, yzRotAngle;
	int ROIMinX;
	int ROIMaxX;
	int ROIMinY;
	int ROIMaxY;
	int AvgShiftCOGX, AvgShiftCOGY;
	int minX2, maxX2;
	int tempAngle;
	int zeroXZAngle;
	int zeroYZAngle;
	int RotateMinHeight, RotateMaxHeight;
	int minHeight, maxHeight;
	int ThreeDMaxHeight;
	int RealTabletWidth;
	int RealTabletHeight;
	int ImageTabletWidth;
	const int pixelSizePer_mm = 2133; // 1mm에 대한 pixel 크기 : 21.33 pixel
	int RealsizeScaleFactor;	
	int AlignedTabletSW;
	int AverageLength;
	int xzAngleShiftStep;
	int yzAngleShiftStep;
	int removedCenterY;
	int minHeightValue;
	int maxHeightValue;

#ifdef PC_SIM
	memset(Debug_RealThreeD_Image, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(Debug_LabelingImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(Debug_ProtoShapeArea, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
	memset(Debug_NormalizedThreeDImage, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
	memset(Debug_OvelapRemoveResult, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(Debug_SatAreaImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(Debug_BeforeRotationResult, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif

	TiltDirection = -1;

	startX = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	if (cameraIndex == SD1_3D_FRONT_FACE_CAMERA_INDEX)
	{
		threeDThreshold = TabletSetupData.Disk1ThreeDMinThreshold * 80 / 100;
		normalizedValue = TabletSetupData.Disk1ThreeDNormalizedValue;
		ThreeDMaxHeight = TabletSetupData.Disk1AverageMaxHeight;
	}
	else
	{
		threeDThreshold = TabletSetupData.Disk2ThreeDMinThreshold * 80 / 100;
		normalizedValue = TabletSetupData.Disk2ThreeDNormalizedValue;
		ThreeDMaxHeight = TabletSetupData.Disk2AverageMaxHeight;
	}

	ThreeDRealCenterX = 0;
	ThreeDRealCenterY = 0;
	ThreeDRealLength = 0;

	AlignedTabletSW = 0;
	if (TabletCharacter.length > TabletCharacter.width * 150 / 100)
	{
		AlignedTabletSW = 1;
	}

	RealTabletWidth = TabletCharacter.width * TabletSetupData.cameraZoom[0] / 100;
	RealTabletHeight = TabletCharacter.height * TabletSetupData.cameraZoom[0] / 100;

	ImageTabletWidth = RealTabletWidth * pixelSizePer_mm / (100 * 100);

	halfImageWidth = HALF_IMAGE_WIDTH;
	halfImageHeight = HALF_IMAGE_HEIGHT;

	if (startX < 4)
		startX = 4;
	if (endX > MAX_IMAGE_WIDTH - 4)
		endX = MAX_IMAGE_WIDTH - 4;

	if (startY < 4)
		startY = 4;
	if (endY > MAX_IMAGE_HEIGHT - 4)
		endY = MAX_IMAGE_HEIGHT - 4;

	// 가속을 위해서 범위를 4 배수로 바꿔줌 (원래도 기본적으로 4의 배수로 설정하고 있었으나 확실히 하기 위해서 다시 설정)
	startX = startX / 4 * 4;
	endX = endX / 4 * 4;
	startY = startY / 4 * 4;
	endY = endY / 4 * 4;

	halfstartX = startX / 2;
	halfendX = endX / 2;
	halfstartY = startY / 2;
	halfendY = endY / 2;

	maxImgSize = halfImageWidth * halfImageHeight;

	threeDShellImage = TempImage3;
	ThreeDOutlineImage = TempImage2;
	ExpectedThreeDDefectArea = ExtractedSplitLineImage;

	memset(HalfTempImage, 0, maxImgSize);
	memset(HalfLabelImage, 0, 2 * maxImgSize);
	memset(ThreeDEraseArea, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
	memset(NormalizedThreeDImage, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
	memset(AdjustThreeD_Data, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(ThreeDOutlineImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(threeDShellImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(ExpectedThreeDDefectArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(RotationThreeDImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(DentedArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	ProtoTabletCenterX = 0;
	ProtoTabletCenterY = 0;
	ProtoTabletCenterZ = 0;

	AdjustTabletCenterX = 0;
	AdjustTabletCenterY = 0;
	AdjustTabletCenterZ = 0;

	GlobalTabletRotationXZAngle = GlobalTabletRotationYZAngle = 0;

	EdgeLineCountForThreeDImage = 0;

	ExecutiveSearchingRotationAngle = 0;
	ProtoTabletRotationAngle = 0;
	tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] = MAX_IMAGE_WIDTH;
	tabletAreaInfo[AREA_INFO_END_X_ADDRESS] = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS] = 0;

#ifdef PC_SIM
	memcpy(Debug_RealThreeD_Image, ThreeD_Image, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif

	makeHalfTempImage(startX, endX, startY, endY);

    int tempCount1=0;
	int tempCount2=0;

    for(int bbb = 0; bbb < (sizeof(HalfLabelImage)-1); bbb++)
    {
    	tempCount1 += HalfLabelImage[bbb];
    }

    for(int ccc = 0; ccc < (sizeof(HalfLabelImage)-1); ccc++)
    {
    	tempCount2 += HalfTempImage[ccc];
    }

	maxLabelN = LabellingForTabletImage(HalfLabelImage, HalfTempImage, halfstartX, halfendX, halfstartY, halfendY, halfImageWidth, halfImageHeight);

	protoCenterX = protoCenterY = protoCenterZ = tempCount = 0;
	minX2 = MAX_IMAGE_WIDTH;
	maxX2 = 0;
	if (maxLabelN != 0)
	{
		accumHalfLabelImageProtoCenter(startX, endX, startY, endY, maxLabelN, protoCenterX, protoCenterY, protoCenterZ, tempCount, minX2, maxX2);

		if (tempCount == 0)
		{
			DefectSW = 3;
			DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}
	}
	else
	{
		DefectSW = 3;
		DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
		return;
	}

#ifdef PC_SIM
	memcpy(Debug_LabelingImage, ThreeD_Image, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif

	for (y = startY + 4; y < endY - 4; y += 4)
	{
		if (ThreeD_Image[MAX_IMAGE_WIDTH * y + (startX + 4)])
		{
			DefectSW = 2;
			DefectInformationWrite(UNINS_CLOSED, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}

		if (ThreeD_Image[MAX_IMAGE_WIDTH * y + (endX - 5)])
		{
			DefectSW = 2;
			DefectInformationWrite(UNINS_CLOSED, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}
	}

	for (x = startX + 4; x < endX - 4; x += 4)
	{
		if (ThreeD_Image[MAX_IMAGE_WIDTH * (startY + 4) + x])
		{
			DefectSW = 3;
			DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}

		if (ThreeD_Image[MAX_IMAGE_WIDTH * (endY - 5) + x])
		{
			DefectSW = 3;
			DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}
	}

	if (ProcessingModeGlobal == INSPECTION_MODE)
	{
		if (tempCount < TabletAreaSize / 2)
		{
			DefectSW = 1;
			DefectInformationWrite(DEFECT_TABLET_SIZE, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}
	}

	makeHalfTempImageForRemoveSaturation(startX, endX, startY, endY, maxImgSize);

	memset(HalfLabelImage, 0, 2 * maxImgSize);
	maxLabelN = LabellingForTabletImage(HalfLabelImage, HalfTempImage, halfstartX, halfendX, halfstartY, halfendY, halfImageWidth, halfImageHeight);

	memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	if (maxLabelN != 0)
	{
		removeSaturation(startX, endX, startY, endY, maxLabelN, protoCenterX, protoCenterY);
	}
	else
	{
		DefectSW = 3;
		DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
		return;
	}

	memset(HalfTempImage, 0, maxImgSize);
	makeHalfTempImage2(startX, endX, startY, endY);

	// 옆면을 잘내거나 각종 보정을 가하기 전의 센터 위치의 높이 값을 구함 (학습 후 레이저 인쇄에 사용)
	tempCount = tempProtoTabletCenterHeight = 0;
	calcProtoTabletCenterHeight(protoCenterX, protoCenterY, tempProtoTabletCenterHeight, tempCount);

	memset(ShapeBinaryImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	if (TabletCharacter.plateInformation != PLATE && TabletCharacter.kind != SUGARCOATING)
	{
		// 자세불안정 checking 용도
		// 틸팅정도가 심한 경우 미검사 처리
		// 틸팅 판정 threshold 는 특징추출에서 학습
		tempCount = checkTiltMinHeight(halfstartX, halfendX, halfstartY, halfendY, protoCenterX, protoCenterY, threeDThreshold, outlineInfoTable);
		if (tempCount)
		{
			// 5도 범위
			DefectSW = 3;
			DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}
		// end
	}

	if (TabletCharacter.plateInformation != PLATE && TabletCharacter.kind != SUGARCOATING && (TabletCharacter.shape == ROUND || TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL))
	{
		// 평평한 정제, 당의정이 아닌 경우 정면의 3D 카메라에서 측면부가 보일 수 있음. 모양을 구함. 측면이 제거된 센터y를 구함(removedCenterY)
		memset(ProtoShapeArea, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
		makeProtoShapeArea(startX, endX, startY, endY, halfstartX, halfendX, halfstartY, halfendY, protoCenterX, protoCenterY, removedCenterY);
	}
	else if (TabletCharacter.plateInformation != PLATE && TabletCharacter.kind != SUGARCOATING)
	{
		memset(ProtoShapeArea, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
		removedCenterY = protoCenterY / 2;
	}

#ifdef PC_SIM
	memcpy(Debug_ProtoShapeArea, ProtoShapeArea, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
#endif

	ROIMinX = MAX_IMAGE_WIDTH;
	ROIMaxX = 0;
	ROIMinY = MAX_IMAGE_HEIGHT;
	ROIMaxY = 0;

	if (TabletCharacter.plateInformation != PLATE && TabletCharacter.kind != SUGARCOATING)
	{
		// 틸팅이 있는 정제의 경우.
		if (TabletGradeData.SideRemovalAlgorithm3DPassing == 0)
		{
			// 틸팅이 있고 측면이 보여서 제거가 필요한 경우.
			// 당의정, 평평한 정제를 제외하고 3D영상에서 측면부를 제거하기 위한 로직
			// round, oval, oblong은 대략적인 center 위치를 알고있음 / etc shape의 경우는.. 모양 특성상 측면부가 보일지라도 tilt계산에 영향을 미칠 수준은 아닐것이라고 가정

			// COG gain이 높은 경우 3D 영상의 단차가 커 rotation 적용 시 유효한 영역이 제거될 수 있음
			// 이를 방지하기 위해 z축에 대한 scale down 진행 (scale은 특징추출에서 학습)
			calcTiltUpDownAvg(halfstartX, halfendX, halfstartY, halfendY, protoCenterZ, removedCenterY, normalizedValue, upAvgValue, downAvgValue);

			minHeightValue = min(upAvgValue, downAvgValue);
			maxHeightValue = max(upAvgValue, downAvgValue);

			if (upAvgValue > downAvgValue)
			{
				TiltDirection = 0;
			}
			else
			{
				TiltDirection = 1;
			}
			
			if (TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL)
			{
				if (TabletCharacter.length > 250)
				{
					maxXZRange = 32;
					xzAngleShiftStep = 16;

					xzAngleStartR = -maxXZRange;
					xzAngleEndR = 0;
				}
				else
				{
					maxXZRange = 6;
					xzAngleShiftStep = 6;

					xzAngleStartR = -maxXZRange;
					xzAngleEndR = +maxXZRange;
				}

				maxYZRange = 40;
				yzAngleShiftStep = 8;
			}
			else
			{
				maxXZRange = 6;
				xzAngleShiftStep = 6;

				xzAngleStartR = -maxXZRange;
				xzAngleEndR = maxXZRange;

				maxYZRange = 32;
				yzAngleShiftStep = 8;
			}

			if (minHeightValue * 100 / maxHeightValue > 95)
			{
				TiltDirection = -1;
			}

			if (TiltDirection == -1)
			{
				yzAngleStartR = -16;
				yzAngleEndR = 16;
			}
			else if (TiltDirection == 0) // upside tilt
			{
				if (minHeightValue * 100 / maxHeightValue > 90)
				{
					yzAngleStartR = 0;
					yzAngleEndR = 32;
				}
				else
				{
					yzAngleStartR = 0;
					yzAngleEndR = maxYZRange;
				}
			}
			else
			{
				if (minHeightValue * 100 / maxHeightValue > 90)
				{
					yzAngleStartR = -32;
					yzAngleEndR = 0;
				}
				else
				{
					yzAngleStartR = -maxYZRange;
					yzAngleEndR = 0;
				}
			}

			// 삭제되어야할 측면 이미지(ThreeDEraseArea)를 구한다. NormalizedThreeDImage는 원본 3D 이미지. DarkPixelNeighborPrint는 임시 버퍼.
			// 이때 정면과 측면의 각도가 수직이라는 점을 이용하여 회전시 z값이 겹치는 부위를 측면으로 규정하고 삭제한다.
			// 이 작업은 PC에서 5ms 정도 소요된다.
			int xzValue, yzValue;
			for (xzValue = xzAngleStartR; xzValue <= xzAngleEndR; xzValue += xzAngleShiftStep)
			{
				for (yzValue = yzAngleStartR; yzValue <= yzAngleEndR; yzValue += yzAngleShiftStep)
				{
					if (yzValue == 0)
						continue;

					// 이 함수는 나누기를 많이 하므로 연산량이 매우 크다.
					threeDRotation(ThreeDEraseArea, DarkPixelNeighborPrint, AddressTable, shiftInfoArray, NormalizedThreeDImage, xzValue, yzValue, protoCenterX / 2, protoCenterY / 2, protoCenterZ, halfstartX, halfendX, halfstartY, halfendY);
				}
			}	

			// 여기서부터 makeAdjustThreeDDataByOutlineInfoTable()가 끝날 때까지는 PC에서 2ms 정도 밖에 안걸린다.

			// 측면을 삭제한 AdjustThreeD_Data를 구한다.
			if ((TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL) && TabletCharacter.length > 250)
			{
				memset(HalfLabelImage, 0, 2 * maxImgSize);
				maxLabelN = LabellingForTabletImage(HalfLabelImage, ThreeDEraseArea, halfstartX, halfendX, halfstartY, halfendY, halfImageWidth, halfImageHeight);

				makeAdjustThreeDDataForOval(startX, endX, startY, endY, maxLabelN, abs(TabletShapeMinY - TabletShapeMaxY) / 2); // TabletShapeMaxY 등은 half 좌표계의 값이다.
			}
			else
			{
				makeAdjustThreeDData(startX, endX, startY, endY);
			}

			// 임펄스 잡음 제거
			memcpy(TempImage, AdjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			removeImpulseNoiseForTilt(startX, endX, startY, endY, protoCenterX);

			// 덩어리 모양을 구한다.
			memset(ShapeBinaryImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			makeShapeImage(ShapeBinaryImage, AdjustThreeD_Data, startX, endX, startY, endY, &ROIMinX, &ROIMaxX, &ROIMinY, &ROIMaxY);

			// 외곽선을 구하고 그것으로 다시 AdjustThreeD_Data를 구한다.
			memset(outlineInfoTable, 0, sizeof(int) * 361 * 4);
			makeOutlineInfoTable(startX, endX, startY, endY, protoCenterX, protoCenterY, outlineInfoTable);
			makeOutlineInfoTableMean(startX, endX, startY, endY, outlineInfoTable); // 비어있는 outlineInfoTable을 평균값으로 채워 넣음.

			// 외곽 부분이 측면 제거 로직에 의하여 잘려 나간 경우 복원을 해줌.
			makeAdjustThreeDDataByOutlineInfoTable(startX, endX, startY, endY, protoCenterX, protoCenterY, outlineInfoTable);
		}
		else
		{
			// 틸팅이 있지만 측면 제거 로직은 불필요한 경우.
			memcpy(AdjustThreeD_Data, ThreeD_Image, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

			// 임펄스 잡음 제거
			memcpy(TempImage, AdjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			removeImpulseNoiseForTilt(startX, endX, startY, endY, protoCenterX);

			// 덩어리 모양을 구한다.
			memset(ShapeBinaryImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			makeShapeImage(ShapeBinaryImage, AdjustThreeD_Data, startX, endX, startY, endY, &ROIMinX, &ROIMaxX, &ROIMinY, &ROIMaxY);

			// 외곽선을 구하고 그것으로 다시 AdjustThreeD_Data를 구한다.
			memset(outlineInfoTable, 0, sizeof(int) * 361 * 4);
			makeOutlineInfoTable(startX, endX, startY, endY, protoCenterX, protoCenterY, outlineInfoTable);
			makeOutlineInfoTableMean(startX, endX, startY, endY, outlineInfoTable); // 비어있는 outlineInfoTable을 평균값으로 채워 넣음.
		}
	}
	else
	{
		// 평평한 정제이거나, 당의정인 경우
		// 틸팅을 계산할 필요가 없으므로 원본 영상(최대 label만 필터링된)을 그대로 적용
		memcpy(AdjustThreeD_Data, ThreeD_Image, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

		// 임펄스 잡음 제거
		memcpy(TempImage, AdjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		removeImpulseNoise(startX, endX, startY, endY, protoCenterX);

		// 덩어리 모양을 구한다.
		memset(ShapeBinaryImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		makeShapeImage2(ShapeBinaryImage, AdjustThreeD_Data, startX, endX, startY, endY, &ROIMinX, &ROIMaxX, &ROIMinY, &ROIMaxY);

		// 외곽선을 구한다.
		memset(outlineInfoTable, 0, sizeof(int) * 361 * 4);
		makeOutlineInfoTable(startX, endX, startY, endY, protoCenterX, protoCenterY, outlineInfoTable);
	}

#ifdef PC_SIM
	memcpy(Debug_NormalizedThreeDImage, NormalizedThreeDImage, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
	memcpy(Debug_OvelapRemoveResult, AdjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif

	// 인쇄 등에 의해(레이저 반사 문제임) ROI 내 hole이 존재할 수 있다.
	// hole 채우기. PC에서는 얼마안걸리는데 DSP에서는 느릴 때가 있는듯? 이미 해결된 것일지도?
	removeHole(TempImage, ShapeBinaryImage, AdjustThreeD_Data, ROIMinX, ROIMaxX, ROIMinY, ROIMaxY, protoCenterX, protoCenterY, outlineInfoTable);

	// 외곽선 최종 검증
	// 정제 종류에 관계없음. 어떤 유형이던 노이즈성 데이터에 의해 외곽선 정보에 에러가 발생될 수 있음
	// outlineInfoTable를 이용하여 AdjustThreeD_Data 이미지를 보정함.
	removeOutlineNoise(startX, endX, startY, endY, protoCenterX, protoCenterY, outlineInfoTable);

	// COG 보정에 사용될 외곽선 정보 추출
	// 이미지 확인 결과 가장 외곽선의 경우 데이터가 불안정한 것으로 보여져 축소영역에 대한 외곽선을 center 보정에 활용
	// 당의정의 경우 각인매칭은 안할지라도 정제의 최대 높이 정보를 확인해야하므로 cog 보정 및 rotation 적용
	// AdjustThreeD_Data를 사용하여 ThreeDOutlineImage 추출함.
	protoCenterX = protoCenterY = protoCenterZ = tempCount = 0;
	makeThreeDOutlineImage(startX, endX, startY, endY, protoCenterX, protoCenterY, protoCenterZ, tempCount);

	if (tempCount)
	{
		protoCenterX /= tempCount;
		protoCenterY /= tempCount;
		protoCenterZ /= tempCount;

		// 최종 외곽선 설정
		// 가장 외곽에서 4 pixel 안쪽 위치를 최종 외곽선으로 결정
		memset(outlineInfoTable, 0, sizeof(int) * 361 * 4);
		makeOutlineInfoTableFinal(startX, endX, startY, endY, protoCenterX, protoCenterY, outlineInfoTable);

		AdjustTabletCenterInfo[0] = protoCenterX;
		AdjustTabletCenterInfo[1] = protoCenterY;

		// y 축 무게중심 이동량 확인
		calcCOGY(startX, endX, startY, endY, ROIMinY, ROIMaxY, zeroYZAngle, AvgShiftCOGY);

		// x축 무게 중심 이동량 확인
		calcCOGX(startX, endX, startY, endY, ROIMinX, ROIMaxX, zeroXZAngle, AvgShiftCOGX);

		ProtoTabletCenterX = protoCenterX;
		ProtoTabletCenterY = protoCenterY;
		ProtoTabletCenterZ = protoCenterZ;

		ThreeDRealCenterX = protoCenterX;
		ThreeDRealCenterY = protoCenterY;
		ThreeDRealLength = abs(minX2 - maxX2);

		RotateMinHeight = 256;
		RotateMaxHeight = 0;

		minHeight = 256;
		maxHeight = 0;

		zeroYZAngle = -zeroYZAngle;
		zeroXZAngle = -zeroXZAngle;

		yzCosValue = CosData[(zeroYZAngle + 720) % 720];
		yzSinValue = SinData[(zeroYZAngle + 720) % 720];

		// 높이의 최대 최소값과 회전 보정된 높이의 최대 최소값을 구함.
		calcRotateHeight(startX, endX, startY, endY, protoCenterY, protoCenterZ, yzSinValue, yzCosValue, minHeight, maxHeight, RotateMinHeight, RotateMaxHeight);

		if ((RotateMaxHeight - RotateMinHeight) <= 0 || (maxHeight - minHeight) <= 0)
		{
			DefectSW = 3;
			DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}

		// 수평처리된 outline 데이터 생성. z값에 대한 normalize 용도
		memset(tempOutlineInfoTable, 0, sizeof(int) * 361 * 4);
		makeTempOutlineInfoTable(protoCenterY, protoCenterZ, yzSinValue, yzCosValue, outlineInfoTable, tempOutlineInfoTable);

		if (TabletCharacter.plateInformation != PLATE)
		{
			if (TabletCharacter.kind == SUGARCOATING)
			{
				// 당의정의 경우 정제 모양 특성 상 x축 틸팅이 심할 수 있음
				maxYZRange = 12;
			}
			else
			{
				if (TabletCharacter.shape == ROUND)
				{
					maxYZRange = 8;
				}
				else
				{
					maxYZRange = 16;
				}
			}

			if (TiltDirection == -1)
			{
				yzAngleStartR = -maxYZRange;
				yzAngleEndR = maxYZRange;
			}
			else
			{
				if (TiltDirection == 0)
				{
					yzAngleStartR = -maxYZRange / 2;
					yzAngleEndR = maxYZRange;
				}
				else
				{
					yzAngleStartR = -maxYZRange;
					yzAngleEndR = maxYZRange / 2;
				}
			}

			maxXZRange = 8;
		}
		else
		{
			maxYZRange = 8;
			maxXZRange = 8;

			yzAngleStartR = -maxYZRange;
			yzAngleEndR = maxYZRange;
		}

		xzRotAngle = 0;
		yzRotAngle = 0;
		calcRatationAngleFirst(maxXZRange, zeroYZAngle, yzAngleStartR, yzAngleEndR, protoCenterX, protoCenterY, protoCenterZ, RotateMinHeight, RotateMaxHeight, AvgShiftCOGX, AvgShiftCOGY, xzRotAngle, yzRotAngle, outlineInfoTable, tempOutlineInfoTable);

		int txzRotAngle, tyzRotAngle;
		txzRotAngle = xzRotAngle;
		tyzRotAngle = yzRotAngle;
		maxYZRange = 4;
		maxXZRange = 4;
		calcRatationAngleSecond(txzRotAngle, maxXZRange, tyzRotAngle, maxYZRange, protoCenterX, protoCenterY, protoCenterZ, RotateMinHeight, RotateMaxHeight, AvgShiftCOGX, AvgShiftCOGY, xzRotAngle, yzRotAngle, outlineInfoTable, tempOutlineInfoTable);

		// 외곽선 데이터 중 수평축으로 회전을 시도하였을 때 정제 하단으로 숨어버리는 점은 유효 데이터에서 제거
		// ThreeDRotationShiftArray : 정제 검사 영역 설정 및 원본이미지에 인쇄 매칭 용도

		if (ThreeDMaxHeight)
		{
			RealsizeScaleFactor = (RealTabletHeight * ImageTabletWidth * 1000) / (RealTabletWidth * ThreeDMaxHeight);
		}
		else
		{
			RealsizeScaleFactor = (RealTabletHeight * ImageTabletWidth * 1000) / (RealTabletWidth * maxHeight);
		}

		memset(ThreeDRotationShiftArray, 0, sizeof(short) * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * 2);
		memset(RealThreeDShiftArray, 0, sizeof(short) * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT * 2);

		GlobalTabletRotationXZAngle = xzRotAngle;
		GlobalTabletRotationYZAngle = yzRotAngle;

		xzCosValue = CosData[(xzRotAngle + 720) % 720];
		xzSinValue = SinData[(xzRotAngle + 720) % 720];

		yzCosValue = CosData[(yzRotAngle + 720) % 720];
		yzSinValue = SinData[(yzRotAngle + 720) % 720];

#ifdef PC_SIM
		// for debug
		{
			int tempX, tempY;
			int tempX2, tempY2;
			int rX, rY, rZ;
			int rX2, rY2, rZ2;
			int tempAddress2;			
			for (y = startY; y < endY; y += 2)
			{
				for (x = startX; x < endX; x += 2)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					z = AdjustThreeD_Data[tempAddress];

					if (z > 0)
					{
						tempX = x;
						tempY = y;

						if (tempX > 0 && tempX < MAX_IMAGE_WIDTH && tempY > 0 && tempY < MAX_IMAGE_HEIGHT)
						{
							rX = (((tempX - protoCenterX) * xzCosValue + (z - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
							rY = tempY;
							rZ = ((-(tempX - protoCenterX) * xzSinValue + (z - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

							if (rZ < 0)
								rZ = 0;
							if (rZ > 255)
								rZ = 255;

							rX2 = rX;
							rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
							rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

							if (rZ2 < 0)
								rZ2 = 0;
							if (rZ2 > 255)
								rZ2 = 255;

							if (rX2 > 0 && rX2 < MAX_IMAGE_WIDTH && rY2 > 0 && rY2 < MAX_IMAGE_HEIGHT)
							{
								for (tempY = 0; tempY <= 1; tempY++)
								{
									for (tempX = 0; tempX <= 1; tempX++)
									{
										tempX2 = rX2 + tempX;
										tempY2 = rY2 + tempY;

										tempAddress2 = MAX_IMAGE_WIDTH * tempY2 + tempX2;

										if (Debug_BeforeRotationResult[tempAddress2] < rZ2)
										{
											Debug_BeforeRotationResult[tempAddress2] = rZ2;
										}
									}
								}
							}
						}
					}
				}
			}

			memcpy(TempImage, Debug_BeforeRotationResult, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			int upBinarySW, downBinarySW, leftBinarySW, rightBinarySW;
			int upThreeDHeight, downThreeDHeight, leftThreeDHeight, rightThreeDHeight;
			int r;			
			for (y = 10; y < MAX_IMAGE_HEIGHT - 10; y++)
			{
				for (x = 10; x < MAX_IMAGE_WIDTH - 10; x++)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					z = TempImage[tempAddress];

					if (z == 0)
					{
						upBinarySW = downBinarySW = leftBinarySW = rightBinarySW = 0;
						upThreeDHeight = downThreeDHeight = leftThreeDHeight = rightThreeDHeight = 0;

						tempCount = 0;
						for (r = 4; r >= 1; r--)
						{
							if (TempImage[tempAddress - r])
							{
								leftBinarySW = 1;
								leftThreeDHeight += TempImage[tempAddress - r];
								tempCount++;
							}
						}

						if (tempCount)
						{
							leftThreeDHeight /= tempCount;
						}

						if (leftBinarySW)
						{
							tempCount = 0;
							for (r = 4; r >= 1; r--)
							{
								if (TempImage[tempAddress + r])
								{
									rightBinarySW = 1;
									rightThreeDHeight += TempImage[tempAddress + r];
									tempCount++;
								}
							}

							if (tempCount)
							{
								rightThreeDHeight /= tempCount;
							}
						}

						tempCount = 0;
						for (r = 4; r >= 1; r--)
						{
							if (TempImage[tempAddress - r * MAX_IMAGE_WIDTH])
							{
								upBinarySW = 1;
								upThreeDHeight += TempImage[tempAddress - r * MAX_IMAGE_WIDTH];
								tempCount++;
							}
						}

						if (tempCount)
						{
							upThreeDHeight /= tempCount;
						}

						if (upBinarySW)
						{
							tempCount = 0;
							for (r = 4; r >= 1; r--)
							{
								if (TempImage[tempAddress + r * MAX_IMAGE_WIDTH])
								{
									downBinarySW = 1;
									downThreeDHeight += TempImage[tempAddress + r * MAX_IMAGE_WIDTH];
									tempCount++;
								}
							}

							if (tempCount)
							{
								downThreeDHeight /= tempCount;
							}
						}

						if (upBinarySW && downBinarySW)
						{
							Debug_BeforeRotationResult[tempAddress] = (upThreeDHeight + downThreeDHeight) / 2;
						}
						else if (leftBinarySW && rightBinarySW)
						{
							Debug_BeforeRotationResult[tempAddress] = (leftThreeDHeight + rightThreeDHeight) / 2;
						}
					}
				}
			}
			// end
		}
#endif

		if (abs(yzRotAngle) >= 80) // 40도 * 2
		{
			// 30도이상 tilting 인경우 미검처리
			// 210324 tilting 각도 재검토 필요할것으로 보여짐
			// -> Cog Gain의 차이는 이미지에서 틸팅 각도 값에 영향을 미침. normalized 결과에 대한 확인 방식으로 변경해야함
			// -> 임시조치로 기준값을 상향조절하며, 추후 작업 예정
			DefectSW = 3;
			DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
			return;
		}

		// AdjustThreeD_Data를 이용하여 RotationThreeDImage 등을 구한다.
		// COG에 의한 Shearing과 정제의 기울어짐(평형)을 맞추는 회전을 동시에 적용.
		// AvgShiftCOG 값은 x, y축 기준 shift 정보임
		makeRotationThreeDImage(startX, endX, startY, endY, protoCenterX, protoCenterY, protoCenterZ, yzSinValue, yzCosValue, xzSinValue, xzCosValue, RotateMinHeight, RotateMaxHeight, AvgShiftCOGX, AvgShiftCOGY, RealsizeScaleFactor);

		// 확장
		tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] = max(10, tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] - 10);
		tabletAreaInfo[AREA_INFO_END_X_ADDRESS] = min(MAX_IMAGE_WIDTH - 10, tabletAreaInfo[AREA_INFO_END_X_ADDRESS] + 10);
		tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] = max(10, tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] - 10);
		tabletAreaInfo[AREA_INFO_END_Y_ADDRESS] = min(MAX_IMAGE_HEIGHT - 10, tabletAreaInfo[AREA_INFO_END_Y_ADDRESS] + 10);

		// 회전으로 발생한 구멍 메우기.
		removeRotationThreeDImageHole();
	}
	else
	{
		DefectSW = 3;
		DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
		return;
	}

	TabletMatchingResult.AdjustCenterXInImage = ProtoTabletCenterX;
	TabletMatchingResult.AdjustCenterYInImage = ProtoTabletCenterY;

	// 보정된 이미지에 대한 center 정보 추출
	tempCount = accumAdjustTabletCenter();
	if (tempCount)
	{
		AdjustTabletCenterX /= tempCount;
		AdjustTabletCenterY /= tempCount;
		AdjustTabletCenterZ /= tempCount;
	}
	else
	{
		DefectSW = 3;
		DefectInformationWrite(UNINS_TABLET_POSITION, cameraIndex, 0, 0, 0, 0, 0);
		return;
	}

	// 각인 추출을 위한 outline 설정
	memset(ShapeBinaryImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	makeOutlineShapeBinaryImage(protoCenterX, protoCenterY, protoCenterZ, yzSinValue, yzCosValue, xzSinValue, xzCosValue, RotateMinHeight, RotateMaxHeight, AvgShiftCOGX, AvgShiftCOGY, RealsizeScaleFactor, outlineInfoTable, tempOutlineInfoTable);
	makeGlobalRotationOutlineInfoTable(outlineInfoTable);

	// 보정된 이미지에서 높이 check
	Max3DHeight = 0;
	calcMax3DHeight(Max3DHeight);

	if (ProcessingModeGlobal == INSPECTION_MODE)
	{
		ThickErrorSW = 0;
		if (cameraIndex == SD1_3D_FRONT_FACE_CAMERA_INDEX)
		{
			if (TabletStudyData.tabletMaxThickForFirstDisk + TabletGradeData.threeD_Height_Plus_Limit <= Max3DHeight)
			{
				ThickErrorSW = 2;

				DefectInformationWrite(FAT_TALBET, cameraIndex, 0, 0, 0, 0, Max3DHeight - TabletStudyData.tabletMaxThickForFirstDisk);
			}
			else if (TabletStudyData.tabletMaxThickForFirstDisk - TabletGradeData.threeD_Height_Minus_Limit >= Max3DHeight)
			{
				ThickErrorSW = 1;

				DefectInformationWrite(THIN_TALBET, cameraIndex, 0, 0, 0, 0, Max3DHeight - TabletStudyData.tabletMaxThickForFirstDisk);
			}
		}
		else if (cameraIndex == SD2_3D_FRONT_FACE_CAMERA_INDEX)
		{
			if (TabletStudyData.tabletMaxThickForSecondDisk + TabletGradeData.threeD_Height_Plus_Limit <= Max3DHeight)
			{
				ThickErrorSW = 2;

				DefectInformationWrite(FAT_TALBET, cameraIndex, 0, 0, 0, 0, Max3DHeight - TabletStudyData.tabletMaxThickForSecondDisk);
			}
			else if (TabletStudyData.tabletMaxThickForSecondDisk - TabletGradeData.threeD_Height_Minus_Limit >= Max3DHeight)
			{
				ThickErrorSW = 1;

				DefectInformationWrite(THIN_TALBET, cameraIndex, 0, 0, 0, 0, Max3DHeight - TabletStudyData.tabletMaxThickForSecondDisk);
			}
		}
	}
	else if (ProcessingModeGlobal == STUDY_MODE)
	{
		if (cameraIndex == SD1_3D_FRONT_FACE_CAMERA_INDEX)
		{
			TabletStudyData.tabletMaxThickForFirstDisk = Max3DHeight;
			TabletStudyData.tabletCenterThickForFirstDisk = tempProtoTabletCenterHeight;
		}
		else if (cameraIndex == SD2_3D_FRONT_FACE_CAMERA_INDEX)
		{
			TabletStudyData.tabletMaxThickForSecondDisk = Max3DHeight;
			TabletStudyData.tabletCenterThickForSecondDisk = tempProtoTabletCenterHeight;
		}
	}

	// check xy rotation angle
	if (TabletCharacter.shape == OVAL || TabletCharacter.shape == OBLONG || AlignedTabletSW)
	{
		AverageLength = tempCount = 0;
		for (angle = 0; angle < 360; angle++)
		{
			if (GlobalRotationOutlineInfoTable[angle][0] > -1)
			{
				AverageLength += GlobalRotationOutlineInfoTable[angle][0];
				tempCount++;
			}
		}

		if (tempCount)
		{
			AverageLength /= tempCount;

			tempAngle = tempCount = 0;
			for (angle = 0; angle < 360; angle++)
			{
				if (GlobalRotationOutlineInfoTable[angle][0] > AverageLength)
				{
					if (angle >= 0 && angle < 180)
					{
						tempAngle += (angle - 90);
					}
					else
					{
						tempAngle += (angle - 270);
					}

					tempCount++;
				}
			}

			if (tempCount)
			{
				tempAngle /= tempCount;

				ExecutiveSearchingRotationAngle = 1;
				ProtoTabletRotationAngle = tempAngle * 2; // 720 degree scale
				ProtoTabletRotationAngle = (ProtoTabletRotationAngle + 720) % 720;
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeHalfTempImage(int startX, int endX, int startY, int endY)
{
	int x, y;
	int tempAddress;
	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			tempAddress = yhalf * HALF_IMAGE_WIDTH + xhalf;

			HalfTempImage[tempAddress] = max(HalfTempImage[tempAddress], ThreeD_Image[MAX_IMAGE_WIDTH * y + x]);
		}
	}
}
//---------------------------------------------------------------------------
void makeHalfTempImage2(int startX, int endX, int startY, int endY)
{
	int x, y;
	int tempAddress, tempAddress2;
	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			tempAddress = yhalf * HALF_IMAGE_WIDTH + xhalf;
			tempAddress2 = MAX_IMAGE_WIDTH * y + x;

			if (ThreeD_Image[tempAddress2])
			{
				HalfTempImage[tempAddress] = max(HalfTempImage[tempAddress], ThreeD_Image[tempAddress2]);
			}
		}
	}
}
//---------------------------------------------------------------------------
void accumHalfLabelImageProtoCenter(int startX, int endX, int startY, int endY, int maxLabelN, int &protoCenterX, int &protoCenterY, int &protoCenterZ, int &tempCount, int &minX2, int &maxX2)
{
	int x, y;
	int tempAddress;
	int xhalf, yhalf;

    int tmpCount = 0;

    for(int aaa = 0; aaa < (sizeof(HalfLabelImage)-1); aaa++)
    {
    	tmpCount += HalfLabelImage[aaa];
    }

	#pragma UNROLL(4) // 속도 증가를 위해 시도해 보았으나 그다지 효과는 없었다.
	#pragma MUST_ITERATE(48, 480, 4)
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))	
	{
		#pragma UNROLL(4)
		#pragma MUST_ITERATE(64, 640, 4)
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))		
		{
			if (HalfLabelImage[yhalf * HALF_IMAGE_WIDTH + xhalf] != maxLabelN)
			{
				ThreeD_Image[MAX_IMAGE_WIDTH * y + x] = 0;
			}
			else
			{
				if (minX2 > x)
				{
					minX2 = x;
				}
				if (maxX2 < x)
				{
					maxX2 = x;
				}
				protoCenterX += x;
				protoCenterY += y;
				protoCenterZ += ThreeD_Image[MAX_IMAGE_WIDTH * y + x];
				tempCount++;
			}
		}
	}

	if (tempCount)
	{
		protoCenterX /= tempCount;
		protoCenterY /= tempCount;
		protoCenterZ /= tempCount;
	}
}
//---------------------------------------------------------------------------
void makeHalfTempImageForRemoveSaturation(int startX, int endX, int startY, int endY, int maxImgSize)
{
	int x, y;
	int tempAddress, tempAddress2;

	// 발산 & saturation 제거
	memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = y * MAX_IMAGE_WIDTH + x;
			if (ThreeD_Image[tempAddress] &&
				(ThreeD_Image[tempAddress - 8] == 0 ||
					ThreeD_Image[tempAddress + 8] == 0 ||
					ThreeD_Image[tempAddress - 8 * MAX_IMAGE_WIDTH] == 0 ||
					ThreeD_Image[tempAddress + 8 * MAX_IMAGE_WIDTH] == 0))
			{
				TempImage[tempAddress] = 1;
			}
		}
	}

	memset(HalfTempImage, 0, maxImgSize);
	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			if (TempImage[y * MAX_IMAGE_WIDTH + x])
			{
				tempAddress2 = HALF_IMAGE_WIDTH * yhalf + xhalf;

				HalfTempImage[tempAddress2] = 1;
				HalfTempImage[tempAddress2 + 1] = 1;
				HalfTempImage[tempAddress2 - 1] = 1;
				HalfTempImage[tempAddress2 + HALF_IMAGE_WIDTH] = 1;
				HalfTempImage[tempAddress2 - HALF_IMAGE_WIDTH] = 1;
				HalfTempImage[tempAddress2 + HALF_IMAGE_WIDTH + 1] = 1;
				HalfTempImage[tempAddress2 - HALF_IMAGE_WIDTH - 1] = 1;
				HalfTempImage[tempAddress2 + HALF_IMAGE_WIDTH - 1] = 1;
				HalfTempImage[tempAddress2 - HALF_IMAGE_WIDTH + 1] = 1;
			}
		}
	}
}
//---------------------------------------------------------------------------
void removeSaturation(int startX, int endX, int startY, int endY, int maxLabelN, int protoCenterX, int protoCenterY)
{
	int x, y;
	int tempAddress;
	int edgeCheckSW, eraseSW;
	int r;

	// 외곽선 영역
	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			if (HalfLabelImage[yhalf * HALF_IMAGE_WIDTH + xhalf] == maxLabelN)
			{
				TempImage[MAX_IMAGE_WIDTH * y + x] = 1;
			}
		}
	}

	memcpy(ShapeBinaryImage, ThreeD_Image, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;
			if (TempImage[tempAddress]) // 외곽부
			{
				if (ShapeBinaryImage[tempAddress])
				{
					if (x < protoCenterX)
					{
						edgeCheckSW = eraseSW = 0;
						for (r = 4; r <= 8; r += 2)
						{
							if (ShapeBinaryImage[tempAddress - r] == 0)
							{
								edgeCheckSW = 1;
								break;
							}
						}
						if (edgeCheckSW)
						{
							for (r = 4; r <= 8; r += 2)
							{
								if (ShapeBinaryImage[tempAddress + r] &&
									ShapeBinaryImage[tempAddress] - ShapeBinaryImage[tempAddress + r] > 8)
								{
									eraseSW = 1;
									break;
								}
							}

							if (eraseSW)
							{
								ThreeD_Image[tempAddress] = 0;
#ifdef PC_SIM
								Debug_SatAreaImage[tempAddress] = 1;
#endif
								continue;
							}
						}
					}
					else
					{
						edgeCheckSW = eraseSW = 0;
						for (r = 4; r <= 8; r += 2)
						{
							if (ShapeBinaryImage[tempAddress + r] == 0)
							{
								edgeCheckSW = 1;
								break;
							}
						}
						if (edgeCheckSW)
						{
							for (r = 4; r <= 8; r += 2)
							{
								if (ShapeBinaryImage[tempAddress - r] &&
									ShapeBinaryImage[tempAddress] - ShapeBinaryImage[tempAddress - r] > 8)
								{
									eraseSW = 1;
									break;
								}
							}

							if (eraseSW)
							{
								ThreeD_Image[tempAddress] = 0;
#ifdef PC_SIM
								Debug_SatAreaImage[tempAddress] = 1;
#endif
								continue;
							}
						}
					}

					if (y < protoCenterY)
					{
						edgeCheckSW = eraseSW = 0;
						for (r = 4; r <= 8; r += 2)
						{
							if (ShapeBinaryImage[tempAddress - r * MAX_IMAGE_WIDTH] == 0)
							{
								edgeCheckSW = 1;
								break;
							}
						}
						if (edgeCheckSW)
						{
							for (r = 4; r <= 8; r += 2)
							{
								if (ShapeBinaryImage[tempAddress + r * MAX_IMAGE_WIDTH] &&
									ShapeBinaryImage[tempAddress] - ShapeBinaryImage[tempAddress + r * MAX_IMAGE_WIDTH] > 8)
								{
									eraseSW = 1;
									break;
								}
							}

							if (eraseSW)
							{
								ThreeD_Image[tempAddress] = 0;
#ifdef PC_SIM
								Debug_SatAreaImage[tempAddress] = 1;
#endif
								continue;
							}
						}
					}
					else
					{
						edgeCheckSW = eraseSW = 0;
						for (r = 4; r <= 8; r += 2)
						{
							if (ShapeBinaryImage[tempAddress + r * MAX_IMAGE_WIDTH] == 0)
							{
								edgeCheckSW = 1;
								break;
							}
						}
						if (edgeCheckSW)
						{
							for (r = 4; r <= 8; r += 2)
							{
								if (ShapeBinaryImage[tempAddress - r * MAX_IMAGE_WIDTH] && 
									ShapeBinaryImage[tempAddress] - ShapeBinaryImage[tempAddress - r * MAX_IMAGE_WIDTH] > 8)
								{
									eraseSW = 1;
									break;
								}
							}

							if (eraseSW)
							{
								ThreeD_Image[tempAddress] = 0;
#ifdef PC_SIM
								Debug_SatAreaImage[tempAddress] = 1;
#endif
								continue;
							}
						}
					}
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void calcProtoTabletCenterHeight(int protoCenterX, int protoCenterY, int &tempProtoTabletCenterHeight, int &tempCount)
{
	int x, y;

	// 옆면을 잘내거나 각종 보정을 가하기 전의 센터 위치의 높이 값을 구함 (학습 후 레이저 인쇄에 사용)
	for (y = protoCenterY - 20; y <= protoCenterY + 20; y += 4)
	{
		for (x = protoCenterX - 20; x <= protoCenterX + 20; x += 4)
		{
			if (ThreeD_Image[MAX_IMAGE_WIDTH * y + x])
			{
				tempProtoTabletCenterHeight += ThreeD_Image[MAX_IMAGE_WIDTH * y + x];
				tempCount++;
			}
		}
	}

	if (tempCount)
	{
		tempProtoTabletCenterHeight /= tempCount;
	}

}
//---------------------------------------------------------------------------
int checkTiltMinHeight(int halfstartX, int halfendX, int halfstartY, int halfendY, int protoCenterX, int protoCenterY, int threeDThreshold, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int pX, pY, uLength, angle, sinV;
	int SymmetryAngle1, SymmetryAngle2;
	int tempX, tempY;
	int A1MaxHeight, A2MaxHeight;

	int tempCount = 0;

	// 자세불안정 checking 용도
	// 틸팅정도가 심한 경우 미검사 처리
	// 틸팅 판정 threshold 는 특징추출에서 학습
	for (y = halfstartY; y < halfendY; y++)
	{
		for (x = halfstartX; x < halfendX; x++)
		{
			tempAddress = HALF_IMAGE_WIDTH * y + x;

			if (HalfTempImage[tempAddress] &&
				(HalfTempImage[tempAddress - 1] == 0 ||
					HalfTempImage[tempAddress + 1] == 0 ||
					HalfTempImage[tempAddress - HALF_IMAGE_WIDTH] == 0 ||
					HalfTempImage[tempAddress + HALF_IMAGE_WIDTH] == 0))
			{
				ShapeBinaryImage[tempAddress] = 1;
			}
		}
	}

	memset(outlineInfoTable, 0, sizeof(int) * 361 * 4);
	int halfProtoCenterX = protoCenterX / 2;
	int halfProtoCenterY = protoCenterY / 2;
	for (y = halfstartY; y < halfendY; y++)
	{
		for (x = halfstartX; x < halfendX; x++)
		{
			tempAddress = HALF_IMAGE_WIDTH * y + x;
			if (ShapeBinaryImage[tempAddress])
			{
				pX = x - halfProtoCenterX;
				pY = y - halfProtoCenterY;
				uLength = pX * pX + pY * pY;
				if (uLength < 90000)
				{
					uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
					sinV = 1280000 * pX / uLength;
					if (sinV > 10000)
						sinV = 10000;

					if (pX >= 0 && pY <= 0)
						angle = ArcSinData[sinV];
					else if (pX >= 0 && pY > 0)
						angle = 180 - ArcSinData[sinV];
					else if (pX < 0 && pY > 0)
						angle = 180 + ArcSinData[-sinV];
					else
						angle = 360 - ArcSinData[-sinV];

					if (angle >= 0 && angle < 360 && outlineInfoTable[angle][0] < uLength)
					{
						outlineInfoTable[angle][0] = uLength;
						outlineInfoTable[angle][1] = HalfTempImage[tempAddress];
						outlineInfoTable[angle][2] = x;
						outlineInfoTable[angle][3] = y;
					}
				}
			}
		}
	}
	
	for (angle = -90; angle <= 90; angle += 20)
	{
		SymmetryAngle1 = (angle + 360) % 360;
		SymmetryAngle2 = (angle + 540) % 360;

		if (outlineInfoTable[SymmetryAngle1][1] && outlineInfoTable[SymmetryAngle2][1])
		{
			A1MaxHeight = A2MaxHeight = 0;

			tempX = outlineInfoTable[SymmetryAngle1][2];
			tempY = outlineInfoTable[SymmetryAngle1][3];

			for (y = tempY - 1; y <= tempY + 1; y++)
			{
				for (x = tempX - 2; x <= tempX + 2; x++)
				{
					tempAddress = HALF_IMAGE_WIDTH * y + x;

					if (A1MaxHeight < HalfTempImage[tempAddress])
					{
						A1MaxHeight = HalfTempImage[tempAddress];
					}
				}
			}

			if (A1MaxHeight < threeDThreshold)
			{
				tempX = outlineInfoTable[SymmetryAngle2][2];
				tempY = outlineInfoTable[SymmetryAngle2][3];

				for (y = tempY - 1; y <= tempY + 1; y++)
				{
					for (x = tempX - 2; x <= tempX + 2; x++)
					{
						tempAddress = HALF_IMAGE_WIDTH * y + x;

						if (A2MaxHeight < HalfTempImage[tempAddress])
						{
							A2MaxHeight = HalfTempImage[tempAddress];
						}
					}
				}

				if (A2MaxHeight < threeDThreshold)
				{
					tempCount++;
				}
			}
		}
	}

	return tempCount;	
}
//---------------------------------------------------------------------------
void makeProtoShapeArea(int startX, int endX, int startY, int endY, int halfstartX, int halfendX, int halfstartY, int halfendY, int protoCenterX, int protoCenterY, int &removedCenterY)
{
	int x, y;
	int tempAddress, tempAddress2;
	int gradX, gradY;
	int tempCount;
		
	int xhalf, yhalf;
	for (y = startY + 4, yhalf = y / 2; y < endY - 4; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX + 4, xhalf = x / 2; x < endX - 4; ++x, ((x & 1) == 0 && ++xhalf))
		{				
			tempAddress = y * MAX_IMAGE_WIDTH + x;
			
			if (x < protoCenterX)
			{
				gradX = ThreeD_Image[tempAddress] - ThreeD_Image[tempAddress - 4];
			}
			else
			{
				gradX = ThreeD_Image[tempAddress] - ThreeD_Image[tempAddress + 4];
			}

			if (y < protoCenterY)
			{
				gradY = ThreeD_Image[tempAddress] - ThreeD_Image[tempAddress - MAX_IMAGE_WIDTH * 4];
			}
			else
			{
				gradY = ThreeD_Image[tempAddress] - ThreeD_Image[tempAddress + MAX_IMAGE_WIDTH * 4];
			}

			if (ThreeD_Image[tempAddress] && (gradX > 20 || gradY > 12))
			{
				tempAddress2 = HALF_IMAGE_WIDTH * yhalf + xhalf;
				ProtoShapeArea[tempAddress2] = max(ProtoShapeArea[tempAddress2], max(gradX, gradY));
			}
		}
	}

	removedCenterY = 0;
	tempCount = 0;
	for (y = halfstartY; y < halfendY; y++)
	{
		for (x = halfstartX; x < halfendX; x++)
		{
			tempAddress = HALF_IMAGE_WIDTH * y + x;
			if (HalfTempImage[tempAddress] && ProtoShapeArea[tempAddress] == 0)
			{
				removedCenterY += y;
				tempCount++;
			}
		}
	}

	if (tempCount)
	{
		removedCenterY /= tempCount;
	}
	else
	{
		removedCenterY = protoCenterY / 2;
	}
}
//---------------------------------------------------------------------------
void calcTiltUpDownAvg(int halfstartX, int halfendX, int halfstartY, int halfendY, int protoCenterZ, int removedCenterY, int normalizedValue, int &upAvgValue, int &downAvgValue)
{
	int x, y;
	int tempAddress, tempAddress2;
	int tempValue;
	int upCount, downCount;

	for (y = halfstartY; y < halfendY; y++)
	{
		for (x = halfstartX; x < halfendX; x++)
		{
			tempAddress = HALF_IMAGE_WIDTH * y + x;

			if (HalfTempImage[tempAddress])
			{
				tempValue = (HalfTempImage[tempAddress] - protoCenterZ) * normalizedValue / 255 + protoCenterZ;
				NormalizedThreeDImage[tempAddress] = (tempValue > 10) ? tempValue : 0;
			}
		}
	}

	upAvgValue = upCount = downAvgValue = downCount = 0;
	for (y = halfstartY; y < halfendY; y++)
	{
		for (x = halfstartX; x < halfendX; x++)
		{
			tempAddress = HALF_IMAGE_WIDTH * y + x;
			tempValue = NormalizedThreeDImage[tempAddress];

			if (ProtoShapeArea[tempAddress] == 0 && tempValue)
			{
				if (y < removedCenterY)
				{
					upAvgValue += tempValue;
					upCount++;
				}
				else if (y > removedCenterY)
				{
					downAvgValue += tempValue;
					downCount++;
				}
			}
		}
	}

	if (upCount)
	{
		upAvgValue /= upCount;
	}

	if (downCount)
	{
		downAvgValue /= downCount;
	}	
}
//---------------------------------------------------------------------------
void makeAdjustThreeDData(int startX, int endX, int startY, int endY)
{
	int x, y;
	
	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			if (!ThreeDEraseArea[yhalf * HALF_IMAGE_WIDTH + xhalf])
			{
				AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + x] = ThreeD_Image[MAX_IMAGE_WIDTH * y + x];
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeAdjustThreeDDataForOval(int startX, int endX, int startY, int endY, int maxLabelN, int tabletShapeHeight)
{
	int x, y;
	int tempAddress, tempAddress2, tempAddress3;
	int r;
	int m;
	int labelCenterX, labelCenterY;

	r = tabletShapeHeight - 1;

	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			tempAddress = yhalf * HALF_IMAGE_WIDTH + xhalf;
			tempAddress2 = MAX_IMAGE_WIDTH * y + x;

			if (!ThreeDEraseArea[tempAddress]) // 삭제된 부분이 아닐 때.
			{
				AdjustThreeD_Data[tempAddress2] = ThreeD_Image[tempAddress2];
			}
			else if (maxLabelN != 0) // 삭제된 부분이고 삭제된 조각이 존재할 때.
			{
				// 현재의 점이 측면일 가능성이 있는 삭제 가능한 후보인지 파악.
				// 정제의 정 중앙부이거나 정제가 아닌 빈공간이면 삭제 불가 영역.
				// 이때 tabletShapeHeight는 half좌표계에서 중앙에서 외곽까지의 Y축 거리와 유사한 값이다. 
				// 따라서 이 값은 일반 좌표계에서는 중심부로부터 외곽까지 거리의 1/2에 해당한다.
				if (y + r < endY && ThreeD_Image[tempAddress2 + r * MAX_IMAGE_WIDTH] != 0 &&
					y - r > startY && ThreeD_Image[tempAddress2 - r * MAX_IMAGE_WIDTH] != 0 && 
					x + r < endX && ThreeD_Image[tempAddress2 + r] != 0 &&
					x - r > startX && ThreeD_Image[tempAddress2 - r] != 0)
				{
					// 깊은 안쪽에 있어서 삭제가 불가능한 점이다. 삭제된 점을 다시 복원 시도.
					m = HalfLabelImage[tempAddress];

					labelCenterX = StartX_Label[m] + EndX_Label[m]; // 삭제된 조각의 중심위치를 구함. 하프 좌표를 일반 좌표로 바꾸기 위해서 2로 나누지 않았음.
					labelCenterY = StartY_Label[m] + EndY_Label[m];
					
					tempAddress3 = MAX_IMAGE_WIDTH * labelCenterY + labelCenterX; // 삭제된 점이 포함된 삭제된 조각의 중심좌표를 찾음.

					// 삭제된 조각의 중심좌표가 삭제 가능한 좌표인지 파악.
					// 삭제된 조각의 센터가 삭제 가능한 영역인 경우 최종적으로 삭제. 그외에는 복원.
					// 이렇게 해주는 이유는 정제 중심부의 각인이 각도에 따라 ThreeDEraseArea에 포함될 수 있기 때문에 그것을 방지하기 위함이다.
					if (labelCenterY + r < endY && ThreeD_Image[tempAddress3 + r * MAX_IMAGE_WIDTH] != 0 &&
						labelCenterY - r > startY && ThreeD_Image[tempAddress3 - r * MAX_IMAGE_WIDTH] != 0 && 
						labelCenterX + r < endX && ThreeD_Image[tempAddress3 + r] != 0 &&
						labelCenterX - r > startX && ThreeD_Image[tempAddress3 - r] != 0)
					{
						AdjustThreeD_Data[tempAddress2] = ThreeD_Image[tempAddress2]; // 삭제가 불가능한 조각이므로 복원.
					}
					else
					{
						AdjustThreeD_Data[tempAddress2] = 0;
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void removeImpulseNoise(int startX, int endX, int startY, int endY, int protoCenterX)
{
	int x, y;
	int tempAddress;
	int leftSW, rightSW, bottomSW, topSW;
	int r;

	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (TempImage[tempAddress])
			{
				leftSW = rightSW = bottomSW = topSW = 0;

				if (x < protoCenterX)
				{
					for (r = 4; r >= 1; r--)
					{
						if (x - r > startX && TempImage[tempAddress - r] == 0)
						{
							leftSW = 1;
							break;
						}
					}

					if (!leftSW)
						continue;

					for (r = 4; r >= 1; r--)
					{
						if (x + r < endX && TempImage[tempAddress + r] == 0)
						{
							rightSW = 1;
							break;
						}
					}

					if (!rightSW)
						continue;
				}
				else
				{
					for (r = 4; r >= 1; r--)
					{
						if (x + r < endX && TempImage[tempAddress + r] == 0)
						{
							rightSW = 1;
							break;
						}
					}

					if (!rightSW)
						continue;

					for (r = 4; r >= 1; r--)
					{
						if (x - r > startX && TempImage[tempAddress - r] == 0)
						{
							leftSW = 1;
							break;
						}
					}

					if (!leftSW)
						continue;
				}

				for (r = 4; r >= 1; r--)
				{
					if (y - r > startY && TempImage[tempAddress - r * MAX_IMAGE_WIDTH] == 0)
					{
						topSW = 1;
						break;
					}
				}

				if (!topSW)
					continue;

				for (r = 4; r >= 1; r--)
				{
					if (y + r < endY && TempImage[tempAddress + r * MAX_IMAGE_WIDTH] == 0)
					{
						bottomSW = 1;
						break;
					}
				}

				if (leftSW && rightSW && topSW && bottomSW)
				{
					AdjustThreeD_Data[tempAddress] = 0;
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void removeImpulseNoiseForTilt(int startX, int endX, int startY, int endY, int protoCenterX)
{
	int x, y;
	int tempAddress;
	int leftSW, rightSW, bottomSW, topSW;
	int r;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (TempImage[tempAddress] &&
				(TempImage[tempAddress - 4] == 0 ||
					TempImage[tempAddress + 4] == 0 ||
					TempImage[tempAddress - 4 * MAX_IMAGE_WIDTH] == 0 ||
					TempImage[tempAddress + 4 * MAX_IMAGE_WIDTH] == 0))
			{
				leftSW = rightSW = bottomSW = topSW = 0;

				if (x < protoCenterX)
				{
					for (r = 4; r >= 1; r--)
					{
						if (x - r > startX && TempImage[tempAddress - r] == 0)
						{
							leftSW = 1;
							break;
						}
					}

					if (!leftSW)
						continue;

					for (r = 4; r >= 1; r--)
					{
						if (x + r < endX && TempImage[tempAddress + r] == 0)
						{
							rightSW = 1;
							break;
						}
					}

					if (!rightSW)
						continue;
				}
				else
				{
					for (r = 4; r >= 1; r--)
					{
						if (x + r < endX && TempImage[tempAddress + r] == 0)
						{
							rightSW = 1;
							break;
						}
					}

					if (!rightSW)
						continue;

					for (r = 4; r >= 1; r--)
					{
						if (x - r > startX && TempImage[tempAddress - r] == 0)
						{
							leftSW = 1;
							break;
						}
					}

					if (!leftSW)
						continue;
				}

				for (r = 4; r >= 1; r--)
				{
					if (y - r > startY && TempImage[tempAddress - r * MAX_IMAGE_WIDTH] == 0)
					{
						topSW = 1;
						break;
					}
				}

				if (!topSW)
					continue;

				for (r = 4; r >= 1; r--)
				{
					if (y + r < endY && TempImage[tempAddress + r * MAX_IMAGE_WIDTH] == 0)
					{
						bottomSW = 1;
						break;
					}
				}

				if (leftSW && rightSW && topSW && bottomSW)
				{
					AdjustThreeD_Data[tempAddress] = 0;
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeShapeImage(unsigned char *RESTRICT(shapeBinaryImage), unsigned char *RESTRICT(adjustThreeD_Data), int startX, int endX, int startY, int endY, int *ROIMinX, int *ROIMaxX, int *ROIMinY, int *ROIMaxY)
{	
	int x, y;
	int tempAddress;
	int _ROIMinX = *ROIMinX;
	int _ROIMaxX = *ROIMaxX; 
	int _ROIMinY = *ROIMinY;
	int _ROIMaxY = *ROIMaxY;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (adjustThreeD_Data[tempAddress] &&
				(adjustThreeD_Data[tempAddress - 1] == 0 ||
					adjustThreeD_Data[tempAddress + 1] == 0 ||
					adjustThreeD_Data[tempAddress - 1 * MAX_IMAGE_WIDTH] == 0 ||
					adjustThreeD_Data[tempAddress + 1 * MAX_IMAGE_WIDTH] == 0))
			{
				shapeBinaryImage[tempAddress] = 1;

				if (_ROIMinX > x)
					_ROIMinX = x;
				if (_ROIMaxX < x)
					_ROIMaxX = x;
				if (_ROIMinY > y)
					_ROIMinY = y;
				if (_ROIMaxY < y)
					_ROIMaxY = y;
			}
		}
	}

	*ROIMinX = _ROIMinX;
	*ROIMaxX = _ROIMaxX;
	*ROIMinY = _ROIMinY;
	*ROIMaxY = _ROIMaxY;
}
//---------------------------------------------------------------------------
void makeShapeImage2(unsigned char *RESTRICT(shapeBinaryImage), unsigned char *RESTRICT(adjustThreeD_Data), int startX, int endX, int startY, int endY, int *ROIMinX, int *ROIMaxX, int *ROIMinY, int *ROIMaxY)
{
	int x, y;
	int tempAddress;
	int _ROIMinX = *ROIMinX;
	int _ROIMaxX = *ROIMaxX;
	int _ROIMinY = *ROIMinY;
	int _ROIMaxY = *ROIMaxY;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (adjustThreeD_Data[tempAddress] && 
				(adjustThreeD_Data[tempAddress - 2] == 0 ||
					adjustThreeD_Data[tempAddress + 2] == 0 ||
					adjustThreeD_Data[tempAddress - 2 * MAX_IMAGE_WIDTH] == 0 ||
					adjustThreeD_Data[tempAddress + 2 * MAX_IMAGE_WIDTH] == 0))
			{
				shapeBinaryImage[tempAddress] = 1;

				if (_ROIMinX > x)
					_ROIMinX = x;
				if (_ROIMaxX < x)
					_ROIMaxX = x;
				if (_ROIMinY > y)
					_ROIMinY = y;
				if (_ROIMaxY < y)
					_ROIMaxY = y;
			}
		}
	}

	*ROIMinX = _ROIMinX;
	*ROIMaxX = _ROIMaxX;
	*ROIMinY = _ROIMinY;
	*ROIMaxY = _ROIMaxY;
}
//---------------------------------------------------------------------------
void makeOutlineInfoTable(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int pX, pY, uLength, angle, sinV;
		
	#pragma UNROLL(4) // 속도 증가를 위해 시도해 보았으나 그다지 효과는 없었다.
	#pragma MUST_ITERATE(48, 480, 4)
	for (y = startY; y < endY; y++)
	{
		#pragma UNROLL(4)
		#pragma MUST_ITERATE(64, 640, 4)
		for (x = startX; x < endX; x++)
		{
			tempAddress = y * MAX_IMAGE_WIDTH + x;
			if (ShapeBinaryImage[tempAddress])
			{
				pX = x - protoCenterX;
				pY = y - protoCenterY;
				uLength = pX * pX + pY * pY;
				if (uLength < 90000)
				{
					uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
					sinV = 1280000 * pX / uLength;
					if (sinV > 10000)
						sinV = 10000;

					if (pX >= 0 && pY <= 0)
						angle = ArcSinData[sinV];
					else if (pX >= 0 && pY > 0)
						angle = 180 - ArcSinData[sinV];
					else if (pX < 0 && pY > 0)
						angle = 180 + ArcSinData[-sinV];
					else
						angle = 360 - ArcSinData[-sinV];

					if (angle >= 0 && angle < 360 && outlineInfoTable[angle][0] < uLength)
					{
						outlineInfoTable[angle][0] = uLength;
						outlineInfoTable[angle][1] = ThreeD_Image[tempAddress];
						outlineInfoTable[angle][2] = x;
						outlineInfoTable[angle][3] = y;
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeOutlineInfoTableMean(int startX, int endX, int startY, int endY, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int uLength, angle, angle2, angle3, tempHeight, tempX, tempY, tempCount, tempAngle;
	
	for (angle = 0; angle < 360; angle++)
	{
		if (outlineInfoTable[angle][0] == 0)
		{
			uLength = tempHeight = tempX = tempY = tempCount = 0;
			for (tempAngle = 1; tempAngle <= 4; tempAngle++)
			{
				angle2 = (angle + tempAngle + 360) % 360;
				angle3 = (angle - tempAngle + 360) % 360;
				if (outlineInfoTable[angle2][0])
				{
					uLength += outlineInfoTable[angle2][0];
					tempHeight += outlineInfoTable[angle2][1];
					tempX += outlineInfoTable[angle2][2];
					tempY += outlineInfoTable[angle2][3];
					tempCount++;
				}

				if (outlineInfoTable[angle3][0])
				{
					uLength += outlineInfoTable[angle3][0];
					tempHeight += outlineInfoTable[angle3][1];
					tempX += outlineInfoTable[angle3][2];
					tempY += outlineInfoTable[angle3][3];
					tempCount++;
				}
			}

			if (tempCount)
			{
				outlineInfoTable[angle][0] = uLength /= tempCount;
				outlineInfoTable[angle][1] = tempHeight /= tempCount;
				outlineInfoTable[angle][2] = tempX /= tempCount;
				outlineInfoTable[angle][3] = tempY /= tempCount;
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeOutlineInfoTableFinal(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int pX, pY, uLength, angle, sinV;
	
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = y * MAX_IMAGE_WIDTH + x;
			if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
			{
				if (ThreeDOutlineImage[tempAddress - 1] ||
					ThreeDOutlineImage[tempAddress + 1] ||
					ThreeDOutlineImage[tempAddress - MAX_IMAGE_WIDTH] ||
					ThreeDOutlineImage[tempAddress + MAX_IMAGE_WIDTH])
				{
					pX = x - protoCenterX;
					pY = y - protoCenterY;
					uLength = pX * pX + pY * pY;
					if (uLength < 90000)
					{
						uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
						sinV = 1280000 * pX / uLength;
						if (sinV > 10000)
							sinV = 10000;

						if (pX >= 0 && pY <= 0)
							angle = ArcSinData[sinV];
						else if (pX >= 0 && pY > 0)
							angle = 180 - ArcSinData[sinV];
						else if (pX < 0 && pY > 0)
							angle = 180 + ArcSinData[-sinV];
						else
							angle = 360 - ArcSinData[-sinV];

						if (angle >= 0 && angle < 360)
						{
							if (outlineInfoTable[angle][0] < uLength)
							{
								outlineInfoTable[angle][0] = uLength;
								outlineInfoTable[angle][1] = ThreeD_Image[tempAddress];
								outlineInfoTable[angle][2] = x;
								outlineInfoTable[angle][3] = y;
							}
						}
					}
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void makeAdjustThreeDDataByOutlineInfoTable(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int pX, pY, uLength, angle, sinV;
	
	int xhalf, yhalf;
	for (y = startY, yhalf = y / 2; y < endY; ++y, ((y & 1) == 0 && ++yhalf))
	{
		for (x = startX, xhalf = x / 2; x < endX; ++x, ((x & 1) == 0 && ++xhalf))
		{
			if (ThreeDEraseArea[yhalf * HALF_IMAGE_WIDTH + xhalf] && AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + x] == 0) // 지워진 영역
			{
				pX = x - protoCenterX;
				pY = y - protoCenterY;
				uLength = pX * pX + pY * pY;
				if (uLength < 90000)
				{
					uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
					sinV = 1280000 * pX / uLength;
					if (sinV > 10000)
						sinV = 10000;

					if (pX >= 0 && pY <= 0)
						angle = ArcSinData[sinV];
					else if (pX >= 0 && pY > 0)
						angle = 180 - ArcSinData[sinV];
					else if (pX < 0 && pY > 0)
						angle = 180 + ArcSinData[-sinV];
					else
						angle = 360 - ArcSinData[-sinV];

					if (angle >= 0 && angle < 360 && outlineInfoTable[angle][0] > uLength)
					{
						AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + x] = ThreeD_Image[MAX_IMAGE_WIDTH * y + x];
					}
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void removeHole(unsigned char *RESTRICT(tempImage), unsigned char *RESTRICT(shapeBinaryImage), unsigned char *RESTRICT(adjustThreeD_Data), int ROIMinX, int ROIMaxX, int ROIMinY, int ROIMaxY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int tempAddress2;	
	int r;
	int ActiveCount;
	int n;
	int pX, pY;
	int angle;
	int uLength;
	int sinV;
	int tempSum;
	int tempCount;
	int tempX, tempY;

	// 침식 팽창 연산으로 구멍 제거
	// 확장

	if (TabletCharacter.discriminationDisplay_kind == PRINT)
	{
		ActiveCount = 2; // 인쇄의 경우 같은 것을 두번을 반복하여 hole 채우기를 더 완벽히 처리한다.
	}
	else
	{
		ActiveCount = 1;
	}
	
	for (n = 0; n < ActiveCount; n++)
	{
		memcpy(shapeBinaryImage, adjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (y = ROIMinY; y <= ROIMaxY; y++)
		{
			for (x = ROIMinX; x <= ROIMaxX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (adjustThreeD_Data[tempAddress] == 0 &&
					(adjustThreeD_Data[tempAddress - REMOVE_HOLE_SEARCH_RANGE] ||
						adjustThreeD_Data[tempAddress + REMOVE_HOLE_SEARCH_RANGE] ||
						adjustThreeD_Data[tempAddress - REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH] ||
						adjustThreeD_Data[tempAddress + REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH]))
				{
					shapeBinaryImage[tempAddress] = 1;
				}
			}
		}

		// 축소
		memcpy(tempImage, shapeBinaryImage, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (y = ROIMinY; y <= ROIMaxY; y++)
		{
			for (x = ROIMinX; x <= ROIMaxX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (shapeBinaryImage[tempAddress])
				{
					for (r = REMOVE_HOLE_SEARCH_RANGE; r >= 5; r -= 5)
					{
						if (!((adjustThreeD_Data[tempAddress - REMOVE_HOLE_SEARCH_RANGE] && adjustThreeD_Data[tempAddress + REMOVE_HOLE_SEARCH_RANGE]) ||
							  (adjustThreeD_Data[tempAddress - REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH] && adjustThreeD_Data[tempAddress + REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH]) ||
							  (adjustThreeD_Data[tempAddress - REMOVE_HOLE_SEARCH_RANGE + REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH] && adjustThreeD_Data[tempAddress + REMOVE_HOLE_SEARCH_RANGE - REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH]) ||
							  (adjustThreeD_Data[tempAddress + REMOVE_HOLE_SEARCH_RANGE - REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH] && adjustThreeD_Data[tempAddress - REMOVE_HOLE_SEARCH_RANGE + REMOVE_HOLE_SEARCH_RANGE * MAX_IMAGE_WIDTH])))
						{
							tempImage[tempAddress] = 0;
							break;
						}
					}
				}
			}
		}

		memcpy(shapeBinaryImage, adjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (y = ROIMinY; y <= ROIMaxY; y++)
		{
			for (x = ROIMinX; x <= ROIMaxX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (tempImage[tempAddress] && adjustThreeD_Data[tempAddress] == 0) // hole point
				{
					pX = x - protoCenterX;
					pY = y - protoCenterY;
					uLength = pX * pX + pY * pY;
					if (uLength < 90000)
					{
						uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
						sinV = 1280000 * pX / uLength;
						if (sinV > 10000)
							sinV = 10000;

						if (pX >= 0 && pY <= 0)
							angle = ArcSinData[sinV];
						else if (pX >= 0 && pY > 0)
							angle = 180 - ArcSinData[sinV];
						else if (pX < 0 && pY > 0)
							angle = 180 + ArcSinData[-sinV];
						else
							angle = 360 - ArcSinData[-sinV];

						if (angle >= 0 && angle < 360)
						{
							// NOTE: % 나머지를 구하는 연산은 느리지만 그래도 if문이나 삼항연산자보다는 빠르다. 바꾸지말고 그냥 쓰자.
							if (uLength < outlineInfoTable[angle][0] ||
								uLength < outlineInfoTable[((angle - 4) + 360) % 360][0] ||
								uLength < outlineInfoTable[((angle + 4) + 360) % 360][0])
							{
								tempSum = 0;
								tempCount = 0;

								//if (x > REMOVE_HOLE_SEARCH_RANGE && x < MAX_IMAGE_WIDTH - REMOVE_HOLE_SEARCH_RANGE && y > REMOVE_HOLE_SEARCH_RANGE && y < MAX_IMAGE_HEIGHT - REMOVE_HOLE_SEARCH_RANGE) // 읽기만하므로 속도를 위해서 범위 무시.
								{
									for (tempY = y - REMOVE_HOLE_SEARCH_RANGE; tempY <= y + REMOVE_HOLE_SEARCH_RANGE; tempY += 2)
									{
										for (tempX = x - REMOVE_HOLE_SEARCH_RANGE; tempX <= x + REMOVE_HOLE_SEARCH_RANGE; tempX += 2)
										{
											tempAddress2 = MAX_IMAGE_WIDTH * tempY + tempX;

											if (shapeBinaryImage[tempAddress2])
											{
												tempSum += shapeBinaryImage[tempAddress2];
												tempCount++;
											}
										}
									}
								}

								if (tempCount)
								{
									tempSum /= tempCount;
									adjustThreeD_Data[tempAddress] = tempSum;
								}
							}
						}
					}
				}
			}
		}		
	}
}
//---------------------------------------------------------------------------
void removeOutlineNoise(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int r;
	int leftSW, rightSW, bottomSW, topSW;
	int eraseSW;
	int angle;
	int startAngle, endAngle, tempAngle;
	int minLength, tempLength, realLength;
	int tempX, tempY;
	int pX, pY, uLength, sinV;

	memcpy(TempImage, AdjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (angle = 0; angle < 360; angle++)
	{
		eraseSW = 0;
		if (outlineInfoTable[angle][1])
		{
			x = outlineInfoTable[angle][2];
			y = outlineInfoTable[angle][3];

			tempAddress = MAX_IMAGE_WIDTH * y + x;

			leftSW = rightSW = bottomSW = topSW = 0;

			for (r = 5; r >= 1; r -= 2)
			{
				if (x - r > startX && TempImage[tempAddress - r] == 0)
				{
					leftSW = 1;
					break;
				}
			}

			if (leftSW)
			{
				for (r = 5; r >= 1; r -= 2)
				{
					if (x + r < endX && TempImage[tempAddress + r] == 0)
					{
						rightSW = 1;
						break;
					}
				}
			}

			for (r = 5; r >= 1; r -= 2)
			{
				if (y - r > startY && TempImage[tempAddress - r * MAX_IMAGE_WIDTH] == 0)
				{
					topSW = 1;
					break;
				}
			}

			if (topSW)
			{
				for (r = 5; r >= 1; r -= 2)
				{
					if (y + r < endY && TempImage[tempAddress + r * MAX_IMAGE_WIDTH] == 0)
					{
						bottomSW = 1;
						break;
					}
				}
			}

			if ((leftSW && rightSW) || (topSW && bottomSW))
			{
				eraseSW = 1;
			}

			if (eraseSW && outlineInfoTable[angle][0])
			{
				startAngle = (angle - 20);
				endAngle = (angle + 20);

				minLength = 0x0FFFFFFF;
				for (tempAngle = startAngle; tempAngle <= endAngle; tempAngle += 2)
				{
					if (outlineInfoTable[(tempAngle + 360) % 360][0])
					{
						if (minLength > outlineInfoTable[(tempAngle + 360) % 360][0])
						{
							minLength = outlineInfoTable[(tempAngle + 360) % 360][0];
						}
					}
				}

				// 256 : 128배 x 2length
				for (tempLength = outlineInfoTable[angle][0]; tempLength > minLength; tempLength -= 256)
				{
					realLength = tempLength / 128;

					// angle - 90, 회전축 이동
					x = realLength * CosData[((angle - 90) * 2 + 720) % 720] / 1024 + protoCenterX;
					y = realLength * SinData[((angle - 90) * 2 + 720) % 720] / 1024 + protoCenterY;

					eraseSW = 0;
					for (tempY = y - 4; tempY <= y + 4; tempY++)
					{
						for (tempX = x - 4; tempX <= x + 4; tempX++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * tempY + tempX])
							{
								pX = tempX - protoCenterX;
								pY = tempY - protoCenterY;
								uLength = pX * pX + pY * pY;
								if (uLength < 90000)
								{
									uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
									sinV = 1280000 * pX / uLength;
									if (sinV > 10000)
										sinV = 10000;

									if (pX >= 0 && pY <= 0)
										tempAngle = ArcSinData[sinV];
									else if (pX >= 0 && pY > 0)
										tempAngle = 180 - ArcSinData[sinV];
									else if (pX < 0 && pY > 0)
										tempAngle = 180 + ArcSinData[-sinV];
									else
										tempAngle = 360 - ArcSinData[-sinV];

									if (angle == tempAngle && uLength >= tempLength)
									{
										tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;

										leftSW = rightSW = bottomSW = topSW = 0;
										for (r = 5; r >= 1; r -= 2)
										{
											if (x - r > startX && TempImage[tempAddress - r] == 0)
											{
												leftSW = 1;
												break;
											}
										}

										if (leftSW)
										{
											for (r = 5; r >= 1; r -= 2)
											{
												if (x + r < endX && TempImage[tempAddress + r] == 0)
												{
													rightSW = 1;
													break;
												}
											}
										}

										for (r = 5; r >= 1; r -= 2)
										{
											if (y - r > startY && TempImage[tempAddress - r * MAX_IMAGE_WIDTH] == 0)
											{
												topSW = 1;
												break;
											}
										}

										if (topSW)
										{
											for (r = 5; r >= 1; r -= 2)
											{
												if (y + r < endY && TempImage[tempAddress + r * MAX_IMAGE_WIDTH] == 0)
												{
													bottomSW = 1;
													break;
												}
											}
										}

										if ((leftSW && rightSW) || (topSW && bottomSW))
										{
											AdjustThreeD_Data[tempAddress] = 0;
											eraseSW = 1;
										}
									}
								}
							}
						}
					}

					if (eraseSW == 0)
					{
						break;
					}
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void makeThreeDOutlineImage(int startX, int endX, int startY, int endY, int &protoCenterX, int &protoCenterY, int &protoCenterZ, int &tempCount)
{
	int x, y;
	int tempAddress;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress])
			{
				protoCenterX += x;
				protoCenterY += y;
				protoCenterZ += AdjustThreeD_Data[tempAddress];
				tempCount++;

				if (AdjustThreeD_Data[tempAddress - 4] == 0 ||
					AdjustThreeD_Data[tempAddress + 4] == 0 ||
					AdjustThreeD_Data[tempAddress - 4 * MAX_IMAGE_WIDTH] == 0 ||
					AdjustThreeD_Data[tempAddress + 4 * MAX_IMAGE_WIDTH] == 0)
				{
					ThreeDOutlineImage[tempAddress] = 1; // 외곽선
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void calcCOGY(int startX, int endX, int startY, int endY, int ROIMinY, int ROIMaxY, int &zeroYZAngle, int &AvgShiftCOGY)
{
	int x, y;
	int tempAddress;
	int minLength;
	int zeroYZAngleCheckCount;
	int tempCount;
	int minY, maxY;
	int tempLength, heightDiff, CenterHeight, realCenterPosition;
	int upperPos[2];
	int lowerPos[2];
	int tempX;
	int pX, pY, uLength, sinV;
	int tiltR;
	int ProtoTabletCenterInfo[2];
	int CenterOfGravity, splitValue;
	int centerHisto[MAX_IMAGE_WIDTH];
	int adjustHeightArry[MAX_IMAGE_WIDTH];
	int m;

	minLength = abs(ROIMinY - ROIMaxY) / 2;

	memset(adjustHeightArry, 0, sizeof(int) * MAX_IMAGE_WIDTH);
	zeroYZAngle = zeroYZAngleCheckCount = 0;
	AvgShiftCOGY = tempCount = 0;
	for (x = startX; x < endX; x += 2)
	{
		minY = MAX_IMAGE_HEIGHT;
		maxY = 0;

		for (y = startY; y < endY; y += 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
			{
				minY = y;
				break;
			}
		}

		for (y = endY - 1; y >= startY; y -= 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
			{
				maxY = y;
				break;
			}
		}

		if (minY != MAX_IMAGE_HEIGHT && maxY != 0)
		{
			tempLength = abs(minY - maxY);

			if (tempLength > minLength)
			{
				upperPos[0] = minY;
				upperPos[1] = AdjustThreeD_Data[MAX_IMAGE_WIDTH * minY + x];

				lowerPos[0] = maxY;
				lowerPos[1] = AdjustThreeD_Data[MAX_IMAGE_WIDTH * maxY + x];

				heightDiff = abs(upperPos[1] - lowerPos[1]);
				CenterHeight = (upperPos[1] + lowerPos[1]) / 2;

				realCenterPosition = (minY + maxY) / 2; // 초기값 설정, heightdiff 가 존재하지 않거나 length에 에러가 발생한 경우 minY, maxY의 중간 지점이 real center
				if (heightDiff > 0)						// 틸팅이 발생하는 경우 정제의 위 아래의 최소 높이차
				{
					pX = upperPos[0] - lowerPos[0];
					pY = upperPos[1] - lowerPos[1];

					uLength = pX * pX + pY * pY;

					if (uLength < 90000)
					{
						uLength = SqrtData[uLength];
						sinV = 1280000 * pY / uLength;

						if (sinV > 10000)
							sinV = 10000;

						// x2 : 삼각함수 array의 비율 조정, x1.2 : 실제 보정수치에 가중 적용
						if (pY <= 0)
							tiltR = ArcSinData[-sinV] * 2;
						else
							tiltR = -ArcSinData[sinV] * 2;

						ProtoTabletCenterInfo[0] = realCenterPosition;
						ProtoTabletCenterInfo[1] = AdjustThreeD_Data[MAX_IMAGE_WIDTH * realCenterPosition + x];

						if (tiltR < 0)
						{
							tempX = (ProtoTabletCenterInfo[1] - CenterHeight) * SinData[-tiltR] / 1024 + ProtoTabletCenterInfo[0];
						}
						else
						{
							tempX = -(ProtoTabletCenterInfo[1] - CenterHeight) * SinData[tiltR] / 1024 + ProtoTabletCenterInfo[0];
						}

						adjustHeightArry[x] = tempX;
						realCenterPosition = tempX;

						zeroYZAngle += tiltR;
						zeroYZAngleCheckCount++;
					}
				}
				else
				{
					zeroYZAngle += 0;
					zeroYZAngleCheckCount++;
				}

				CenterOfGravity = splitValue = 0;
				for (y = minY; y <= maxY; y += 2)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
					{
						CenterOfGravity += y * AdjustThreeD_Data[tempAddress];
						splitValue += AdjustThreeD_Data[tempAddress];
					}
				}

				if (splitValue)
				{
					CenterOfGravity /= splitValue;

					AvgShiftCOGY += (realCenterPosition - CenterOfGravity);
					tempCount++;
				}
			}
		}
	}

	if (zeroYZAngleCheckCount)
	{
		zeroYZAngle /= zeroYZAngleCheckCount;
	}

	if (tempCount)
	{
		AvgShiftCOGY /= tempCount;
	}

	memset(centerHisto, 0, MAX_IMAGE_WIDTH * sizeof(int));
	tempCount = 0;
	for (x = 0; x < MAX_IMAGE_WIDTH; x += 2)
	{
		if (adjustHeightArry[x])
		{
			centerHisto[adjustHeightArry[x]]++;
			tempCount++;
		}
	}

	if (tempCount)
	{
		tempCount = 0;
		for (m = 1; m < MAX_IMAGE_WIDTH - 1; m++)
		{
			if (tempCount < centerHisto[m] + centerHisto[m - 1] + centerHisto[m + 1])
			{
				tempCount = centerHisto[m] + centerHisto[m - 1] + centerHisto[m + 1];
				AdjustTabletCenterInfo[1] = m;
			}
		}
	}	
}
//---------------------------------------------------------------------------
void calcCOGX(int startX, int endX, int startY, int endY, int ROIMinX, int ROIMaxX, int &zeroXZAngle, int &AvgShiftCOGX)
{
	int x, y;
	int tempAddress;
	int minLength;
	int zeroXZAngleCheckCount;
	int tempCount;
	int minX, maxX;
	int tempLength, heightDiff, CenterHeight, realCenterPosition;
	int upperPos[2];
	int lowerPos[2];
	int tempX;
	int pX, pY, uLength, sinV;
	int tiltR;
	int ProtoTabletCenterInfo[2];
	int CenterOfGravity, splitValue;
	int centerHisto[MAX_IMAGE_WIDTH];
	int adjustHeightArry[MAX_IMAGE_WIDTH];
	int m;
	
	minLength = abs(ROIMinX - ROIMaxX) / 2;

	memset(adjustHeightArry, 0, sizeof(int) * MAX_IMAGE_WIDTH);
	zeroXZAngle = zeroXZAngleCheckCount = 0;
	AvgShiftCOGX = tempCount = 0;
	for (y = startY; y < endY; y += 2)
	{
		minX = MAX_IMAGE_WIDTH;
		maxX = 0;

		for (x = startX; x < endX; x += 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
			{
				minX = x;
				break;
			}
		}

		for (x = endX - 1; x >= startX; x -= 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
			{
				maxX = x;
				break;
			}
		}

		if (minX != MAX_IMAGE_WIDTH && maxX != 0)
		{
			tempLength = abs(minX - maxX);

			if (tempLength > minLength)
			{
				upperPos[0] = minX;
				upperPos[1] = AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + minX];

				lowerPos[0] = maxX;
				lowerPos[1] = AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + maxX];

				heightDiff = abs(upperPos[1] - lowerPos[1]);
				CenterHeight = (upperPos[1] + lowerPos[1]) / 2;

				realCenterPosition = (minX + maxX) / 2; // 초기값 설정, heightdiff 가 존재하지 않거나 length에 에러가 발생한 경우 minX, maxX의 중간 지점이 real center
				if (heightDiff > 0)						// 틸팅이 발생하는 경우 정제의 위 아래의 최소 높이차
				{
					pX = upperPos[0] - lowerPos[0];
					pY = upperPos[1] - lowerPos[1];

					uLength = pX * pX + pY * pY;

					if (uLength < 90000)
					{
						uLength = SqrtData[uLength];
						sinV = 1280000 * pY / uLength;

						if (sinV > 10000)
							sinV = 10000;

						// x2 : 삼각함수 array의 비율 조정, x1.2 : 실제 보정수치에 가중 적용
						if (pY <= 0)
							tiltR = ArcSinData[-sinV] * 2;
						else
							tiltR = -ArcSinData[sinV] * 2;

						ProtoTabletCenterInfo[0] = realCenterPosition;
						ProtoTabletCenterInfo[1] = AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + realCenterPosition];

						if (tiltR < 0)
						{
							tempX = (ProtoTabletCenterInfo[1] - CenterHeight) * SinData[-tiltR] / 1024 + ProtoTabletCenterInfo[0];
						}
						else
						{
							tempX = -(ProtoTabletCenterInfo[1] - CenterHeight) * SinData[tiltR] / 1024 + ProtoTabletCenterInfo[0];
						}

						adjustHeightArry[y] = tempX;
						realCenterPosition = tempX;

						zeroXZAngle += tiltR;
						zeroXZAngleCheckCount++;
					}
				}
				else
				{
					zeroXZAngle += 0;
					zeroXZAngleCheckCount++;
				}

				CenterOfGravity = splitValue = 0;
				for (x = minX; x <= maxX; x += 2)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					if (AdjustThreeD_Data[tempAddress] && ThreeDOutlineImage[tempAddress] == 0)
					{
						CenterOfGravity += x * AdjustThreeD_Data[tempAddress];
						splitValue += AdjustThreeD_Data[tempAddress];
					}
				}

				if (splitValue)
				{
					CenterOfGravity /= splitValue;

					AvgShiftCOGX += (realCenterPosition - CenterOfGravity);
					tempCount++;
				}
			}
		}
	}

	if (zeroXZAngleCheckCount)
	{
		zeroXZAngle /= zeroXZAngleCheckCount;
	}

	if (tempCount)
	{
		AvgShiftCOGX /= tempCount;
	}

	memset(centerHisto, 0, MAX_IMAGE_WIDTH * sizeof(int));
	tempCount = 0;
	for (x = 0; x < MAX_IMAGE_WIDTH; x += 2)
	{
		if (adjustHeightArry[x])
		{
			centerHisto[adjustHeightArry[x]]++;
			tempCount++;
		}
	}

	if (tempCount)
	{
		tempCount = 0;
		for (m = 1; m < MAX_IMAGE_WIDTH - 1; m++)
		{
			if (tempCount < centerHisto[m] + centerHisto[m - 1] + centerHisto[m + 1])
			{
				tempCount = centerHisto[m] + centerHisto[m - 1] + centerHisto[m + 1];
				AdjustTabletCenterInfo[0] = m;
			}
		}
	}	
}
//---------------------------------------------------------------------------
void calcRotateHeight(int startX, int endX, int startY, int endY, int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int &minHeight, int &maxHeight, int &RotateMinHeight, int &RotateMaxHeight)
{
	int x, y, z;
	int tempAddress;
	int rX2, rY2, rZ2;
	for (x = startX; x < endX; x += 4)
	{
		for (y = startY; y < endY; y += 4)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			z = AdjustThreeD_Data[tempAddress];

			if (z)
			{
				if (minHeight > z)
					minHeight = z;
				if (maxHeight < z)
					maxHeight = z;

				rX2 = x;
				rY2 = (((y - protoCenterY) * yzCosValue - (z - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
				rZ2 = (((y - protoCenterY) * yzSinValue + (z - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

				if (rZ2 < 0)
					rZ2 = 0;
				if (rZ2 > 255)
					rZ2 = 255;

				if (rX2 > 0 && rX2 < MAX_IMAGE_WIDTH && rY2 > 0 && rY2 < MAX_IMAGE_HEIGHT)
				{
					if (RotateMinHeight > rZ2)
						RotateMinHeight = rZ2;
					if (RotateMaxHeight < rZ2)
						RotateMaxHeight = rZ2;
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void makeTempOutlineInfoTable(int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4])
{
	int angle;
	int tempX, tempY, tempZ;
	int expectedRotationHeight;
	for (angle = 0; angle < 360; angle++)
	{
		if (outlineInfoTable[angle][0])
		{
			tempZ = outlineInfoTable[angle][1];
			tempX = outlineInfoTable[angle][2];
			tempY = outlineInfoTable[angle][3];

			expectedRotationHeight = (((tempY - protoCenterY) * yzSinValue + (tempZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

			/*if (expectedRotationHeight < 0)
				rZ2 = 0;
			if (expectedRotationHeight > 255)
				rZ2 = 255;*/

			tempOutlineInfoTable[angle][1] = expectedRotationHeight;
		}
	}	
}
//---------------------------------------------------------------------------
void calcRatationAngleFirst(int maxXZRange, int zeroYZAngle, int yzAngleStartR, int yzAngleEndR, int protoCenterX, int protoCenterY, int protoCenterZ, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int &xzRotAngle, int &yzRotAngle, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4])
{
	int minHeightDiff;
	int xzValue, yzValue;
	int xzCosValue, xzSinValue;
	int yzCosValue, yzSinValue;
	int heightDiffSum;
	int angle;
	int SymmetryAngle1, SymmetryAngle2, SymmetryAngle3;
	int expectedRotationHeight;
	int tempX, tempY, tempZ;
	int rX, rY, rZ, rX2, rY2, rZ2;
	int p1RotationHeight;
	int p2RotationHeight;
	int p3RotationHeight;	

	minHeightDiff = 0x0FFFFFFF;
	for (xzValue = -maxXZRange; xzValue <= maxXZRange; xzValue += 4)
	{
		xzCosValue = CosData[(xzValue + 720) % 720];
		xzSinValue = SinData[(xzValue + 720) % 720];

		for (yzValue = yzAngleStartR + zeroYZAngle; yzValue <= yzAngleEndR + zeroYZAngle; yzValue += 4)
		{
			yzCosValue = CosData[(yzValue + 720) % 720];
			yzSinValue = SinData[(yzValue + 720) % 720];

			heightDiffSum = 0;
			for (angle = 0; angle < 120; angle += 12)
			{
				SymmetryAngle1 = angle;
				SymmetryAngle2 = (angle + 120) % 360;
				SymmetryAngle3 = (angle + 240) % 360;
				if (outlineInfoTable[SymmetryAngle1][1] && outlineInfoTable[SymmetryAngle2][1] && outlineInfoTable[SymmetryAngle3][1])
				{
					// first point
					expectedRotationHeight = tempOutlineInfoTable[SymmetryAngle1][1];

					tempZ = outlineInfoTable[SymmetryAngle1][1];
					if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle1][2] + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
						tempY = outlineInfoTable[SymmetryAngle1][3] + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					}
					else if (expectedRotationHeight < RotateMinHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle1][2];
						tempY = outlineInfoTable[SymmetryAngle1][3];
					}
					else
					{
						tempX = outlineInfoTable[SymmetryAngle1][2] + AvgShiftCOGX;
						tempY = outlineInfoTable[SymmetryAngle1][3] + AvgShiftCOGY;
					}

					rX = (((tempX - protoCenterX) * xzCosValue + (tempZ - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (tempZ - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					p1RotationHeight = rZ2;

					// second point
					expectedRotationHeight = tempOutlineInfoTable[SymmetryAngle2][1];

					tempZ = outlineInfoTable[SymmetryAngle2][1];
					if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle2][2] + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
						tempY = outlineInfoTable[SymmetryAngle2][3] + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					}
					else if (expectedRotationHeight < RotateMinHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle2][2];
						tempY = outlineInfoTable[SymmetryAngle2][3];
					}
					else
					{
						tempX = outlineInfoTable[SymmetryAngle2][2] + AvgShiftCOGX;
						tempY = outlineInfoTable[SymmetryAngle2][3] + AvgShiftCOGY;
					}

					rX = (((tempX - protoCenterX) * xzCosValue + (tempZ - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (tempZ - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					p2RotationHeight = rZ2;

					// third point
					expectedRotationHeight = tempOutlineInfoTable[SymmetryAngle3][1];

					tempZ = outlineInfoTable[SymmetryAngle3][1];
					if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle3][2] + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
						tempY = outlineInfoTable[SymmetryAngle3][3] + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					}
					else if (expectedRotationHeight < RotateMinHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle3][2];
						tempY = outlineInfoTable[SymmetryAngle3][3];
					}
					else
					{
						tempX = outlineInfoTable[SymmetryAngle3][2] + AvgShiftCOGX;
						tempY = outlineInfoTable[SymmetryAngle3][3] + AvgShiftCOGY;
					}

					rX = (((tempX - protoCenterX) * xzCosValue + (tempZ - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (tempZ - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					p3RotationHeight = rZ2;

					heightDiffSum += abs(p1RotationHeight - p2RotationHeight) + abs(p1RotationHeight - p3RotationHeight) + abs(p2RotationHeight - p3RotationHeight);
				}
			}

			if (minHeightDiff > heightDiffSum)
			{
				minHeightDiff = heightDiffSum;

				xzRotAngle = xzValue;
				yzRotAngle = yzValue;
			}
		}
	}	
}
//---------------------------------------------------------------------------
void calcRatationAngleSecond(int txzRotAngle, int maxXZRange, int tyzRotAngle, int maxYZRange, int protoCenterX, int protoCenterY, int protoCenterZ, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int &xzRotAngle, int &yzRotAngle, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4])
{
	int minHeightDiff;
	int xzValue, yzValue;
	int xzCosValue, xzSinValue;
	int yzCosValue, yzSinValue;
	int heightDiffSum;
	int angle;
	int SymmetryAngle1, SymmetryAngle2, SymmetryAngle3;
	int expectedRotationHeight;
	int tempX, tempY, tempZ;
	int rX, rY, rZ, rX2, rY2, rZ2;
	int p1RotationHeight;
	int p2RotationHeight;
	int p3RotationHeight;	
	
	minHeightDiff = 0x0FFFFFFF;
	for (xzValue = txzRotAngle - maxXZRange; xzValue <= txzRotAngle + maxXZRange; xzValue += 2)
	{
		xzCosValue = CosData[(xzValue + 720) % 720];
		xzSinValue = SinData[(xzValue + 720) % 720];

		for (yzValue = tyzRotAngle - maxYZRange; yzValue <= tyzRotAngle + maxYZRange; yzValue += 2)
		{
			yzCosValue = CosData[(yzValue + 720) % 720];
			yzSinValue = SinData[(yzValue + 720) % 720];

			heightDiffSum = 0;
			for (angle = 0; angle < 120; angle += 6)
			{
				SymmetryAngle1 = angle;
				SymmetryAngle2 = (angle + 120) % 360;
				SymmetryAngle3 = (angle + 240) % 360;
				if (outlineInfoTable[SymmetryAngle1][1] && outlineInfoTable[SymmetryAngle2][1] && outlineInfoTable[SymmetryAngle3][1])
				{
					// first point
					expectedRotationHeight = tempOutlineInfoTable[SymmetryAngle1][1];

					tempZ = outlineInfoTable[SymmetryAngle1][1];
					if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle1][2] + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
						tempY = outlineInfoTable[SymmetryAngle1][3] + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					}
					else if (expectedRotationHeight < RotateMinHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle1][2];
						tempY = outlineInfoTable[SymmetryAngle1][3];
					}
					else
					{
						tempX = outlineInfoTable[SymmetryAngle1][2] + AvgShiftCOGX;
						tempY = outlineInfoTable[SymmetryAngle1][3] + AvgShiftCOGY;
					}

					rX = (((tempX - protoCenterX) * xzCosValue + (tempZ - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (tempZ - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					p1RotationHeight = rZ2;

					// second point
					expectedRotationHeight = tempOutlineInfoTable[SymmetryAngle2][1];

					tempZ = outlineInfoTable[SymmetryAngle2][1];
					if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle2][2] + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
						tempY = outlineInfoTable[SymmetryAngle2][3] + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					}
					else if (expectedRotationHeight < RotateMinHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle2][2];
						tempY = outlineInfoTable[SymmetryAngle2][3];
					}
					else
					{
						tempX = outlineInfoTable[SymmetryAngle2][2] + AvgShiftCOGX;
						tempY = outlineInfoTable[SymmetryAngle2][3] + AvgShiftCOGY;
					}

					rX = (((tempX - protoCenterX) * xzCosValue + (tempZ - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (tempZ - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					p2RotationHeight = rZ2;

					// third point
					expectedRotationHeight = tempOutlineInfoTable[SymmetryAngle3][1];

					tempZ = outlineInfoTable[SymmetryAngle3][1];
					if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle3][2] + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
						tempY = outlineInfoTable[SymmetryAngle3][3] + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					}
					else if (expectedRotationHeight < RotateMinHeight)
					{
						tempX = outlineInfoTable[SymmetryAngle3][2];
						tempY = outlineInfoTable[SymmetryAngle3][3];
					}
					else
					{
						tempX = outlineInfoTable[SymmetryAngle3][2] + AvgShiftCOGX;
						tempY = outlineInfoTable[SymmetryAngle3][3] + AvgShiftCOGY;
					}

					rX = (((tempX - protoCenterX) * xzCosValue + (tempZ - protoCenterZ) * xzSinValue) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (tempZ - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					p3RotationHeight = rZ2;

					heightDiffSum += abs(p1RotationHeight - p2RotationHeight) + abs(p1RotationHeight - p3RotationHeight) + abs(p2RotationHeight - p3RotationHeight);
				}
			}

			if (minHeightDiff > heightDiffSum)
			{
				minHeightDiff = heightDiffSum;

				xzRotAngle = xzValue;
				yzRotAngle = yzValue;
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeRotationThreeDImage(int startX, int endX, int startY, int endY, int protoCenterX, int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int xzSinValue, int xzCosValue, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int RealsizeScaleFactor)
{
	int x, y, z;
	int tempAddress, tempAddress2;
	int expectedRotationHeight;
	int tempX, tempY, tempX2, tempY2;
	int rX, rY, rZ, rX2, rY2, rZ2;

	for (y = startY; y < endY; y += 2)
	{
		for (x = startX; x < endX; x += 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			z = AdjustThreeD_Data[tempAddress];

			if (z > 0)
			{
				expectedRotationHeight = (((y - protoCenterY) * yzSinValue + (z - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

				if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
				{
					tempX = x + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
					tempY = y + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
				}
				else if (expectedRotationHeight < RotateMinHeight)
				{
					tempX = x;
					tempY = y;
				}
				else
				{
					tempX = x + AvgShiftCOGX;
					tempY = y + AvgShiftCOGY;
				}

				if (tempX > 0 && tempX < MAX_IMAGE_WIDTH && tempY > 0 && tempY < MAX_IMAGE_HEIGHT)
				{
					rX = (((tempX - protoCenterX) * xzCosValue + (z - protoCenterZ) * xzSinValue * RealsizeScaleFactor / 700) / 1024) + protoCenterX;
					rY = tempY;
					rZ = ((-(tempX - protoCenterX) * xzSinValue + (z - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

					if (rZ < 0)
						rZ = 0;
					else if (rZ > 255)
						rZ = 255;

					rX2 = rX;
					rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue * RealsizeScaleFactor / 700) / 1024) + protoCenterY;
					rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

					if (rZ2 < 0)
						rZ2 = 0;
					else if (rZ2 > 255)
						rZ2 = 255;

					if (rX2 > 0 && rX2 < MAX_IMAGE_WIDTH && rY2 > 0 && rY2 < MAX_IMAGE_HEIGHT)
					{
						for (tempY = 0; tempY <= 1; tempY++)
						{
							for (tempX = 0; tempX <= 1; tempX++)
							{
								tempX2 = rX2 + tempX;
								tempY2 = rY2 + tempY;

								tempAddress2 = MAX_IMAGE_WIDTH * tempY2 + tempX2;

								ThreeDRotationShiftArray[tempAddress2][0] = x + tempX;
								ThreeDRotationShiftArray[tempAddress2][1] = y + tempY;
								if (RotationThreeDImage[tempAddress2] < rZ2)
								{
									RotationThreeDImage[tempAddress2] = rZ2;
								}

								tempAddress = MAX_IMAGE_WIDTH * (y + tempY) + (x + tempX);
								RealThreeDShiftArray[tempAddress][0] = rX2;
								RealThreeDShiftArray[tempAddress][1] = rY2;

								if (tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] >= tempX2)
									tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] = tempX2;
								if (tabletAreaInfo[AREA_INFO_END_X_ADDRESS] <= tempX2)
									tabletAreaInfo[AREA_INFO_END_X_ADDRESS] = tempX2;
								if (tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] >= tempY2)
									tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] = tempY2;
								if (tabletAreaInfo[AREA_INFO_END_Y_ADDRESS] <= tempY2)
									tabletAreaInfo[AREA_INFO_END_Y_ADDRESS] = tempY2;
							}
						}
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void removeRotationThreeDImageHole()
{
	int x, y, z;
	int tempAddress;
	int upBinarySW, downBinarySW, leftBinarySW, rightBinarySW;
	int upThreeDHeight, downThreeDHeight, leftThreeDHeight, rightThreeDHeight;
	int tempCount;
	int r;

	memcpy(TempImage, RotationThreeDImage, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (y = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS]; y <= tabletAreaInfo[AREA_INFO_END_Y_ADDRESS]; y++)
	{
		for (x = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS]; x <= tabletAreaInfo[AREA_INFO_END_X_ADDRESS]; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			z = TempImage[tempAddress];

			if (z == 0)
			{
				upBinarySW = downBinarySW = leftBinarySW = rightBinarySW = 0;
				upThreeDHeight = downThreeDHeight = leftThreeDHeight = rightThreeDHeight = 0;

				tempCount = 0;
				for (r = 7; r >= 1; r -= 2)
				{
					if (TempImage[tempAddress - r])
					{
						leftBinarySW = 1;
						leftThreeDHeight += TempImage[tempAddress - r];
						tempCount++;
					}
				}

				if (tempCount)
				{
					leftThreeDHeight /= tempCount;
				}

				if (leftBinarySW)
				{
					tempCount = 0;
					for (r = 7; r >= 1; r -= 2)
					{
						if (TempImage[tempAddress + r])
						{
							rightBinarySW = 1;
							rightThreeDHeight += TempImage[tempAddress + r];
							tempCount++;
						}
					}

					if (tempCount)
					{
						rightThreeDHeight /= tempCount;
					}
				}

				tempCount = 0;
				for (r = 7; r >= 1; r -= 2)
				{
					if (TempImage[tempAddress - r * MAX_IMAGE_WIDTH])
					{
						upBinarySW = 1;
						upThreeDHeight += TempImage[tempAddress - r * MAX_IMAGE_WIDTH];
						tempCount++;
					}
				}

				if (tempCount)
				{
					upThreeDHeight /= tempCount;
				}

				if (upBinarySW)
				{
					tempCount = 0;
					for (r = 7; r >= 1; r -= 2)
					{
						if (TempImage[tempAddress + r * MAX_IMAGE_WIDTH])
						{
							downBinarySW = 1;
							downThreeDHeight += TempImage[tempAddress + r * MAX_IMAGE_WIDTH];
							tempCount++;
						}
					}

					if (tempCount)
					{
						downThreeDHeight /= tempCount;
					}
				}

				if (upBinarySW && downBinarySW)
				{
					RotationThreeDImage[tempAddress] = (upThreeDHeight + downThreeDHeight) / 2;
				}
				else if (leftBinarySW && rightBinarySW)
				{
					RotationThreeDImage[tempAddress] = (leftThreeDHeight + rightThreeDHeight) / 2;
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
int accumAdjustTabletCenter()
{
	int x, y;
	int tempAddress;
	int tempCount = 0;	
	for (y = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS]; y <= tabletAreaInfo[AREA_INFO_END_Y_ADDRESS]; y++)
	{
		for (x = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS]; x <= tabletAreaInfo[AREA_INFO_END_X_ADDRESS]; x++)
		{
			tempAddress = y * MAX_IMAGE_WIDTH + x;
			if (RotationThreeDImage[tempAddress])
			{
				AdjustTabletCenterX += x;
				AdjustTabletCenterY += y;
				AdjustTabletCenterZ += RotationThreeDImage[tempAddress];
				tempCount++;
			}
		}
	}
	return tempCount;
}
//---------------------------------------------------------------------------
void makeOutlineShapeBinaryImage(int protoCenterX, int protoCenterY, int protoCenterZ, int yzSinValue, int yzCosValue, int xzSinValue, int xzCosValue, int RotateMinHeight, int RotateMaxHeight, int AvgShiftCOGX, int AvgShiftCOGY, int RealsizeScaleFactor, int RESTRICT_ARRAY(outlineInfoTable)[4], int RESTRICT_ARRAY(tempOutlineInfoTable)[4])
{
	int angle;
	int x, y, z;
	int tempAddress;
	int expectedRotationHeight;
	int tempX, tempY;
	int rX, rY, rZ, rX2, rY2, rZ2;

	for (angle = 0; angle < 360; angle += 4)
	{
		if (outlineInfoTable[angle][1] && tempOutlineInfoTable[angle][1])
		{
			z = outlineInfoTable[angle][1];
			x = outlineInfoTable[angle][2];
			y = outlineInfoTable[angle][3];

			expectedRotationHeight = tempOutlineInfoTable[angle][1];

			if (expectedRotationHeight >= RotateMinHeight && expectedRotationHeight <= RotateMaxHeight)
			{
				tempX = x + (AvgShiftCOGX * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
				tempY = y + (AvgShiftCOGY * (expectedRotationHeight - RotateMinHeight)) / (RotateMaxHeight - RotateMinHeight);
			}
			else if (expectedRotationHeight < RotateMinHeight)
			{
				tempX = x;
				tempY = y;
			}
			else
			{
				tempX = x + AvgShiftCOGX;
				tempY = y + AvgShiftCOGY;
			}

			if (tempX > 0 && tempX < MAX_IMAGE_WIDTH && tempY > 0 && tempY < MAX_IMAGE_HEIGHT)
			{
				rX = (((tempX - protoCenterX) * xzCosValue + (z - protoCenterZ) * xzSinValue * RealsizeScaleFactor / 700) / 1024) + protoCenterX;
				rY = tempY;
				rZ = ((-(tempX - protoCenterX) * xzSinValue + (z - protoCenterZ) * xzCosValue) / 1024) + protoCenterZ;

				if (rZ < 0)
					rZ = 0;
				else if (rZ > 255)
					rZ = 255;

				rX2 = rX;
				rY2 = (((rY - protoCenterY) * yzCosValue - (rZ - protoCenterZ) * yzSinValue * RealsizeScaleFactor / 700) / 1024) + protoCenterY;
				rZ2 = (((rY - protoCenterY) * yzSinValue + (rZ - protoCenterZ) * yzCosValue) / 1024) + protoCenterZ;

				if (rZ2 < 0)
					rZ2 = 0;
				else if (rZ2 > 255)
					rZ2 = 255;

				if (rX2 > 0 && rX2 < MAX_IMAGE_WIDTH && rY2 > 0 && rY2 < MAX_IMAGE_HEIGHT)
				{
					for (tempY = rY2 - 6; tempY <= rY2 + 6; tempY++)
					{
						for (tempX = rX2 - 6; tempX <= rX2 + 6; tempX++)
						{
							tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;

							if (RotationThreeDImage[tempAddress])
							{
								ShapeBinaryImage[tempAddress] = 1;
							}
						}
					}
				}
			}
		}
	}	
}
//---------------------------------------------------------------------------
void makeGlobalRotationOutlineInfoTable(int RESTRICT_ARRAY(outlineInfoTable)[4])
{
	int x, y;
	int tempAddress;
	int angle;
	int pX, pY, uLength, sinV;	

	for (angle = 0; angle < 360; angle++)
	{
		GlobalRotationOutlineInfoTable[angle][0] = -1;
	}

	for (y = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS]; y <= tabletAreaInfo[AREA_INFO_END_Y_ADDRESS]; y++)
	{
		for (x = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS]; x <= tabletAreaInfo[AREA_INFO_END_X_ADDRESS]; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (ShapeBinaryImage[tempAddress])
			{
				if (RotationThreeDImage[tempAddress])
				{
					if (RotationThreeDImage[tempAddress - 2] == 0 ||
						RotationThreeDImage[tempAddress + 2] == 0 ||
						RotationThreeDImage[tempAddress - 2 * MAX_IMAGE_WIDTH] == 0 ||
						RotationThreeDImage[tempAddress + 2 * MAX_IMAGE_WIDTH] == 0)
					{
						pX = x - AdjustTabletCenterX;
						pY = y - AdjustTabletCenterY;
						uLength = pX * pX + pY * pY;
						if (uLength < 90000)
						{
							uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
							sinV = 1280000 * pX / uLength;
							if (sinV > 10000)
								sinV = 10000;

							if (pX >= 0 && pY <= 0)
								angle = ArcSinData[sinV];
							else if (pX >= 0 && pY > 0)
								angle = 180 - ArcSinData[sinV];
							else if (pX < 0 && pY > 0)
								angle = 180 + ArcSinData[-sinV];
							else
								angle = 360 - ArcSinData[-sinV];

							if (angle >= 0 && angle < 360)
							{
								if (GlobalRotationOutlineInfoTable[angle][0] < uLength)
								{
									GlobalRotationOutlineInfoTable[angle][0] = uLength;
									GlobalRotationOutlineInfoTable[angle][1] = RotationThreeDImage[tempAddress];
									GlobalRotationOutlineInfoTable[angle][2] = x;
									GlobalRotationOutlineInfoTable[angle][3] = y;
								}
							}
						}
					}
				}
			}
		}
	}

	// 예외처리코드, xy 위치에 따라 오차에 의해 비어있는 angle 이 발생할 수 있음
	// rotation image
	int tempX, tempY, tempZ;
	int tempCount;
	int tempAngle;	
	for (angle = 0; angle < 360; angle++)
	{
		if (GlobalRotationOutlineInfoTable[angle][0] == -1)
		{
			uLength = 0;
			tempX = tempY = tempZ = 0;
			tempCount = 0;
			for (tempAngle = angle - 10; tempAngle <= angle + 10; tempAngle += 2)
			{
				tempAddress = (tempAngle + 360) % 360;

				if (GlobalRotationOutlineInfoTable[tempAddress][0] > -1)
				{
					uLength += GlobalRotationOutlineInfoTable[tempAddress][0];
					tempZ += GlobalRotationOutlineInfoTable[tempAddress][1];
					tempX += GlobalRotationOutlineInfoTable[tempAddress][2];
					tempY += GlobalRotationOutlineInfoTable[tempAddress][3];
					tempCount++;
				}
			}

			if (tempCount)
			{
				GlobalRotationOutlineInfoTable[angle][0] = uLength / tempCount;
				GlobalRotationOutlineInfoTable[angle][1] = tempZ / tempCount;
				GlobalRotationOutlineInfoTable[angle][2] = tempX / tempCount;
				GlobalRotationOutlineInfoTable[angle][3] = tempY / tempCount;
			}
		}
	}	

	for (angle = 0; angle < 360; angle++)
	{
		if (GlobalRotationOutlineInfoTable[angle][0] > -1)
		{
			if (EdgeLineCountForThreeDImage < MAX_THREED_EDGE_POINT_COUNT)
			{
				EdgeLineForThreeDImage[EdgeLineCountForThreeDImage][0] = GlobalRotationOutlineInfoTable[angle][2];
				EdgeLineForThreeDImage[EdgeLineCountForThreeDImage][1] = GlobalRotationOutlineInfoTable[angle][3];
				EdgeLineCountForThreeDImage++;
			}
		}
	}

	// 위에서 구한 GlobalRotationOutlineInfoTable는 사실 EdgeLineForThreeDImage를 구하기 위한 임시 변수였음.
	// 아래에서 outlineInfoTable를 보정하여 GlobalRotationOutlineInfoTable를 구함.

	// 예외처리코드, xy 위치에 따라 오차에 의해 비어있는 angle 이 발생할 수 있음
	// real image	
	int neighborAngle1, neighborAngle2;	
	memcpy(GlobalRotationOutlineInfoTable, outlineInfoTable, sizeof(int) * 361 * 4);
	for (angle = 0; angle < 360; angle++)
	{
		for (tempAngle = 8; tempAngle >= 4; tempAngle -= 4)
		{
			neighborAngle1 = ((angle - tempAngle) + 360) % 360;
			neighborAngle2 = ((angle + tempAngle) + 360) % 360;

			if ((GlobalRotationOutlineInfoTable[neighborAngle1][0] - GlobalRotationOutlineInfoTable[angle][0]) / 128 >= 8 &&
				(GlobalRotationOutlineInfoTable[neighborAngle2][0] - GlobalRotationOutlineInfoTable[angle][0]) / 128 >= 8)
			{
				GlobalRotationOutlineInfoTable[angle][0] = 0;
				GlobalRotationOutlineInfoTable[angle][1] = 0;
				GlobalRotationOutlineInfoTable[angle][2] = 0;
				GlobalRotationOutlineInfoTable[angle][3] = 0;

				break;
			}
		}
	}

	for (angle = 0; angle < 360; angle++)
	{
		if (GlobalRotationOutlineInfoTable[angle][0] < 10)
		{
			uLength = 0;
			tempX = tempY = tempZ = 0;
			tempCount = 0;
			for (tempAngle = angle - 10; tempAngle <= angle + 10; tempAngle += 2)
			{
				tempAddress = (tempAngle + 360) % 360;

				if (GlobalRotationOutlineInfoTable[tempAddress][0] >= 10)
				{
					uLength += GlobalRotationOutlineInfoTable[tempAddress][0];
					tempZ += GlobalRotationOutlineInfoTable[tempAddress][1];
					tempX += GlobalRotationOutlineInfoTable[tempAddress][2];
					tempY += GlobalRotationOutlineInfoTable[tempAddress][3];
					tempCount++;
				}
			}

			if (tempCount)
			{
				GlobalRotationOutlineInfoTable[angle][0] = uLength / tempCount;
				GlobalRotationOutlineInfoTable[angle][1] = tempZ / tempCount;
				GlobalRotationOutlineInfoTable[angle][2] = tempX / tempCount;
				GlobalRotationOutlineInfoTable[angle][3] = tempY / tempCount;
			}
		}
	}		
}
//---------------------------------------------------------------------------
void calcMax3DHeight(int &Max3DHeight)
{
	int x, y, z;
	int tempAddress;
	int tempHeight;
	int maxHeightPos[2];
	int histoHeight[256];
	int tempStartX, tempEndX, tempStartY, tempEndY;	
	int tempCount;
	int maxValue;
	int m;

	if (TabletCharacter.kind != SUGARCOATING)
	{
		for (y = AdjustTabletCenterY - 15; y <= AdjustTabletCenterY + 15; y += 2)
		{
			for (x = AdjustTabletCenterX - 15; x <= AdjustTabletCenterX + 15; x += 2)
			{
				tempAddress = y * MAX_IMAGE_WIDTH + x;

				tempHeight = (RotationThreeDImage[tempAddress] + RotationThreeDImage[tempAddress - 1] + RotationThreeDImage[tempAddress - MAX_IMAGE_WIDTH] + RotationThreeDImage[tempAddress - MAX_IMAGE_WIDTH - 1]) / 4;
				if (Max3DHeight < tempHeight)
				{
					Max3DHeight = tempHeight;
				}
			}
		}
	}
	else
	{
		for (y = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS]; y < tabletAreaInfo[AREA_INFO_END_Y_ADDRESS]; y += 2)
		{
			for (x = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS]; x < tabletAreaInfo[AREA_INFO_END_X_ADDRESS]; x += 2)
			{
				tempAddress = y * MAX_IMAGE_WIDTH + x;
				z = RotationThreeDImage[tempAddress];
				if (z && Max3DHeight < z)
				{
					Max3DHeight = z;
					maxHeightPos[0] = x;
					maxHeightPos[1] = y;
				}
			}
		}

		memset(histoHeight, 0, sizeof(int) * 256);

		tempStartX = maxHeightPos[0] - 10;
		tempEndX = maxHeightPos[0] + 10;
		tempStartY = maxHeightPos[1] - 10;
		tempEndY = maxHeightPos[1] + 10;

		tempCount = 0;
		for (y = tempStartY; y < tempEndY; y++)
		{
			for (x = tempStartX; x < tempEndX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;
				tempHeight = RotationThreeDImage[tempAddress];

				if (tempHeight >= 60 && tempHeight <= 255)
				{
					histoHeight[tempHeight]++;
					tempCount++;
				}
			}
		}

		if (tempCount)
		{
			maxValue = 0;
			for (m = 0; m < 256; m++)
			{
				if (histoHeight[m] > maxValue)
				{
					maxValue = histoHeight[m];
					Max3DHeight = m;
				}
			}
		}
		else
		{
			tempCount = 0;
			if (Max3DHeight)
			{
				tempCount++;

				if (RotationThreeDImage[MAX_IMAGE_WIDTH * (maxHeightPos[1]) + (maxHeightPos[0])])
				{
					Max3DHeight += RotationThreeDImage[MAX_IMAGE_WIDTH * (maxHeightPos[1] - 1) + (maxHeightPos[0])];
					tempCount++;
				}
				if (RotationThreeDImage[MAX_IMAGE_WIDTH * (maxHeightPos[1]) + (maxHeightPos[0] - 1)])
				{
					Max3DHeight += RotationThreeDImage[MAX_IMAGE_WIDTH * (maxHeightPos[1]) + (maxHeightPos[0] - 1)];
					tempCount++;
				}
				if (RotationThreeDImage[MAX_IMAGE_WIDTH * (maxHeightPos[1] - 1) + (maxHeightPos[0] - 1)])
				{
					Max3DHeight += RotationThreeDImage[MAX_IMAGE_WIDTH * (maxHeightPos[1] - 1) + (maxHeightPos[0] - 1)];
					tempCount++;
				}

				Max3DHeight /= tempCount;
			}
		}
	}
}
//---------------------------------------------------------------------------
void SetUpDataTransFormForThreeD(int option, int SDNum)
{
	int m, x, y;
	int tempCountPrint[4];
	int tempX, tempY;
	int tempAddress;
	int r;
	int rotateX;
	int rotateY;
	int addStep;
	int SuctionDiskNum;
	short Position_SmallPrintCoreData1For3D[3000][3];
	short Position_SmallPrintCoreData2For3D[3000][3];
	unsigned char *circleMaskImage;
	unsigned char *splitLine1;
	unsigned char *splitLine2;
	int radial;
	int tempx, tempy;
	int tempCount;
	int printNumber;
	int diskNumber;
	int n;
	unsigned char *tempSplitImage;

	const int SUCTION_DISK_1_NUM = SUCTION_DISK_NUM1;
	const int SUCTION_DISK_2_NUM = SUCTION_DISK_NUM2;

	circleMaskImage = TempImage;
	splitLine1 = TempImage2;
	splitLine2 = TempImage3;

	memset(circleMaskDataCnt, 0, sizeof(int) * MAX_RADIAL_CASE_COUNT);
	memset(circleMaskData, 0, sizeof(short) * MAX_RADIAL_CASE_COUNT * MAX_ROUND_LENGTH * 2);

	memset(Position_SmallPrintCoreData1For3D, 0, sizeof(short) * 3000 * 3);
	memset(Position_SmallPrintCoreData2For3D, 0, sizeof(short) * 3000 * 3);

	tempCount = 0;
	for (radial = 8; radial <= 40; radial += 8)
	{
		memset(circleMaskImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

		for (r = 0; r < 720; r += 2)
		{
			tempx = radial * CosData[r] / 1024 + HALF_IMAGE_WIDTH;
			tempy = radial * SinData[r] / 1024 + HALF_IMAGE_HEIGHT;

			if (circleMaskImage[MAX_IMAGE_WIDTH * tempy + tempx] == 0)
			{
				circleMaskImage[MAX_IMAGE_WIDTH * tempy + tempx] = 1;

				if (circleMaskDataCnt[tempCount] < MAX_ROUND_LENGTH)
				{
					circleMaskData[tempCount][circleMaskDataCnt[tempCount] * 2 + 0] = tempx - HALF_IMAGE_WIDTH;
					circleMaskData[tempCount][circleMaskDataCnt[tempCount] * 2 + 1] = tempy - HALF_IMAGE_HEIGHT;
					circleMaskDataCnt[tempCount]++;
				}
			}
		}

		tempCount++;
	}

	OnePrintIncludeOtherPrintSWFor3D = 0;

	TabletAreaSize = 0;
	for (y = 0; y < MAX_IMAGE_HEIGHT; ++y)
	{
		for (x = 0; x < MAX_IMAGE_WIDTH; ++x)
		{
			if (TabletSetupData.FrontshapeAreaData[(y / 2) * HALF_IMAGE_WIDTH + (x / 2)])
			{
				TabletAreaSize++;
			}
		}
	}

	TabletShapeMinX = HALF_IMAGE_WIDTH;
	TabletShapeMaxX = 0;
	TabletShapeMinY = HALF_IMAGE_HEIGHT;
	TabletShapeMaxY = 0;

	memset(TabletShapeData, 0, HALF_IMAGE_WIDTH * HALF_IMAGE_HEIGHT);
	for (y = 0; y < HALF_IMAGE_HEIGHT; y++)
	{
		for (x = 0; x < HALF_IMAGE_WIDTH; x++)
		{
			if (TabletSetupData.FrontshapeAreaData2[HALF_IMAGE_WIDTH * y + x])
			{
				if (SDNum == SUCTION_DISK_1_NUM)
				{
					tempX = (x - HALF_IMAGE_WIDTH / 2) * 95 / 100 + HALF_IMAGE_WIDTH / 2;
					tempY = (y - HALF_IMAGE_HEIGHT / 2) * 95 / 100 + HALF_IMAGE_HEIGHT / 2;
				}
				else
				{
					tempX = (x - HALF_IMAGE_WIDTH / 2) * 95 / 100 + HALF_IMAGE_WIDTH / 2;
					tempY = (y - HALF_IMAGE_HEIGHT / 2) * 95 / 100 + HALF_IMAGE_HEIGHT / 2;
				}

				TabletShapeData[tempY * HALF_IMAGE_WIDTH + tempX] = TabletSetupData.FrontshapeAreaData[y * HALF_IMAGE_WIDTH + x];
				TabletShapeData[(tempY + 1) * HALF_IMAGE_WIDTH + tempX] = TabletSetupData.FrontshapeAreaData[y * HALF_IMAGE_WIDTH + x];
				TabletShapeData[tempY * HALF_IMAGE_WIDTH + tempX] = TabletSetupData.FrontshapeAreaData[y * HALF_IMAGE_WIDTH + x];
				TabletShapeData[tempY * HALF_IMAGE_WIDTH + tempX + 1] = TabletSetupData.FrontshapeAreaData[y * HALF_IMAGE_WIDTH + x];

				if (TabletShapeMinX >= tempX)
					TabletShapeMinX = tempX;
				if (TabletShapeMaxX <= tempX)
					TabletShapeMaxX = tempX;
				if (TabletShapeMinY >= tempY)
					TabletShapeMinY = tempY;
				if (TabletShapeMaxY <= tempY)
					TabletShapeMaxY = tempY;
			}
		}
	}

	// CPB가 연결되는 SPB에 따라 Initial하는 변수의 값을 서로 달리한다.
	if (SDNum == SUCTION_DISK_1_NUM)
	{
		SuctionDiskNum = SUCTION_DISK_1_NUM;
	}
	else
	{
		SuctionDiskNum = SUCTION_DISK_2_NUM;
	}

	if (SuctionDiskNum == SUCTION_DISK_1_NUM)
	{
		if (TabletCharacter.discriminationDisplay_num == TWO_FACE_DIFF)
		{
			if (Tablet3DSetupData.Disk1printData1Count > 6000 || Tablet3DSetupData.Disk1printData2Count > 6000)
				addStep = 3;
			else if (Tablet3DSetupData.Disk1printData1Count > 1000 || Tablet3DSetupData.Disk1printData2Count > 1000)
				addStep = 2;
			else
				addStep = 1;

			corePrintDataAddStepForCam5 = addStep;
		}
	}
	else if (SuctionDiskNum == SUCTION_DISK_2_NUM)
	{
		if (TabletCharacter.discriminationDisplay_num == TWO_FACE_DIFF)
		{
			if (Tablet3DSetupData.Disk2printData1Count > 6000 || Tablet3DSetupData.Disk2printData2Count > 6000)
				addStep = 3;
			else if (Tablet3DSetupData.Disk2printData1Count > 1000 || Tablet3DSetupData.Disk2printData2Count > 1000)
				addStep = 2;
			else
				addStep = 1;

			corePrintDataAddStepForCam13 = addStep;
		}
	}

	// Global
	memset(TempImage, 0, MAX_IMAGE_HEIGHT * MAX_IMAGE_WIDTH);
	memset(splitLine1, 0, MAX_IMAGE_HEIGHT * MAX_IMAGE_WIDTH);
	SmallPrintCoreData1CountFor3D = 0;

	tempCountPrint[0] = 0;
	tempCountPrint[1] = 0;
	tempCountPrint[2] = 0;
	tempCountPrint[3] = 0;

	if (TabletCharacter.shape != ROUND)
	{
		if (SuctionDiskNum == SUCTION_DISK_1_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk1printData1Count; m++)
			{
				tempX = Tablet3DSetupData.Disk1position_printData1[m][0];
				tempY = Tablet3DSetupData.Disk1position_printData1[m][1];
				for (y = tempY - 1; y < tempY + 1; y++)
				{
					for (x = tempX - 1; x < tempX + 1; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk1slice_printData1Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk1slice_printData1Count; m++)
				{
					tempX = Tablet3DSetupData.Disk1position_slice_printData1[m][0];
					tempY = Tablet3DSetupData.Disk1position_slice_printData1[m][1];
					for (y = tempY - 1; y < tempY + 1; y++)
					{
						for (x = tempX - 1; x < tempX + 1; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine1[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
		else if (SuctionDiskNum == SUCTION_DISK_2_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk2printData1Count; m++)
			{
				tempX = Tablet3DSetupData.Disk2position_printData1[m][0];
				tempY = Tablet3DSetupData.Disk2position_printData1[m][1];
				for (y = tempY - 1; y < tempY + 1; y++)
				{
					for (x = tempX - 1; x < tempX + 1; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk2slice_printData1Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk2slice_printData1Count; m++)
				{
					tempX = Tablet3DSetupData.Disk2position_slice_printData1[m][0];
					tempY = Tablet3DSetupData.Disk2position_slice_printData1[m][1];
					for (y = tempY - 1; y < tempY + 1; y++)
					{
						for (x = tempX - 1; x < tempX + 1; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine1[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
	}
	else
	{
		if (SuctionDiskNum == SUCTION_DISK_1_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk1printData1Count; m++)
			{
				tempX = Tablet3DSetupData.Disk1position_printData1[m][0];
				tempY = Tablet3DSetupData.Disk1position_printData1[m][1];
				for (y = tempY - 2; y < tempY + 2; y++)
				{
					for (x = tempX - 2; x < tempX + 2; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk1slice_printData1Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk1slice_printData1Count; m++)
				{
					tempX = Tablet3DSetupData.Disk1position_slice_printData1[m][0];
					tempY = Tablet3DSetupData.Disk1position_slice_printData1[m][1];
					for (y = tempY - 2; y < tempY + 2; y++)
					{
						for (x = tempX - 2; x < tempX + 2; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine1[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
		else if (SuctionDiskNum == SUCTION_DISK_2_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk2printData1Count; m++)
			{
				tempX = Tablet3DSetupData.Disk2position_printData1[m][0];
				tempY = Tablet3DSetupData.Disk2position_printData1[m][1];
				for (y = tempY - 2; y < tempY + 2; y++)
				{
					for (x = tempX - 2; x < tempX + 2; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk2slice_printData1Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk2slice_printData1Count; m++)
				{
					tempX = Tablet3DSetupData.Disk2position_slice_printData1[m][0];
					tempY = Tablet3DSetupData.Disk2position_slice_printData1[m][1];
					for (y = tempY - 2; y < tempY + 2; y++)
					{
						for (x = tempX - 2; x < tempX + 2; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine1[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
	}

	for (y = 0; y < MAX_IMAGE_HEIGHT; y++)
	{
		for (x = 0; x < MAX_IMAGE_WIDTH; x++)
		{
			if (TempImage[MAX_IMAGE_WIDTH * y + x] == 1)
			{
				tempCountPrint[0]++;
			}
			else if (TempImage[MAX_IMAGE_WIDTH * y + x] == 2)
			{
				tempCountPrint[1]++;
			}
			else if (TempImage[MAX_IMAGE_WIDTH * y + x] == 3)
			{
				tempCountPrint[2]++;
			}
			else if (TempImage[MAX_IMAGE_WIDTH * y + x] == 4)
			{
				tempCountPrint[3]++;
			}
		}
	}

	if (TabletCharacter.discriminationDisplay_num != TWO_FACE_DIFF)
	{
		if (tempCountPrint[1] + tempCountPrint[3] < 1500)
			addStep = 1;
		else if (tempCountPrint[1] + tempCountPrint[3] < 4000) // 8000
			addStep = 2;
		else
			addStep = 3;
	}

	for (y = 0; y < MAX_IMAGE_HEIGHT; y += addStep)
	{
		for (x = 0; x < MAX_IMAGE_WIDTH; x += addStep)
		{
			if (TempImage[MAX_IMAGE_WIDTH * y + x] == 2)
			{
				if (SmallPrintCoreData1CountFor3D < 2500)
				{
					Position_SmallPrintCoreData1For3D[SmallPrintCoreData1CountFor3D][0] = x;
					Position_SmallPrintCoreData1For3D[SmallPrintCoreData1CountFor3D][1] = y;

					if (splitLine1[MAX_IMAGE_WIDTH * y + x])
					{
						Position_SmallPrintCoreData1For3D[SmallPrintCoreData1CountFor3D][2] = 1;
					}
					else
					{
						Position_SmallPrintCoreData1For3D[SmallPrintCoreData1CountFor3D][2] = 0;
					}

					SmallPrintCoreData1CountFor3D++;
				}
			}
		}
	}

	if (TabletCharacter.discriminationDisplay_num != TWO_FACE_DIFF)
	{
		if (tempCountPrint[3] < 1500)
			addStep = 1;
		else if (tempCountPrint[3] < 4000)
			addStep = 2;
		else
			addStep = 3;
	}

	// global
	memset(TempImage, 0, MAX_IMAGE_HEIGHT * MAX_IMAGE_WIDTH);
	memset(splitLine2, 0, MAX_IMAGE_HEIGHT * MAX_IMAGE_WIDTH);
	SmallPrintCoreData2CountFor3D = 0;

	tempCountPrint[0] = 0;
	tempCountPrint[1] = 0;
	tempCountPrint[2] = 0;
	tempCountPrint[3] = 0;

	if (TabletCharacter.shape != ROUND)
	{
		if (SuctionDiskNum == SUCTION_DISK_1_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk1printData2Count; m++)
			{
				tempX = Tablet3DSetupData.Disk1position_printData2[m][0];
				tempY = Tablet3DSetupData.Disk1position_printData2[m][1];
				for (y = tempY - 1; y < tempY + 1; y++)
				{
					for (x = tempX - 1; x < tempX + 1; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk1slice_printData2Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk1slice_printData2Count; m++)
				{
					tempX = Tablet3DSetupData.Disk1position_slice_printData2[m][0];
					tempY = Tablet3DSetupData.Disk1position_slice_printData2[m][1];
					for (y = tempY - 1; y < tempY + 1; y++)
					{
						for (x = tempX - 1; x < tempX + 1; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine2[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
		else if (SuctionDiskNum == SUCTION_DISK_2_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk2printData2Count; m++)
			{
				tempX = Tablet3DSetupData.Disk2position_printData2[m][0];
				tempY = Tablet3DSetupData.Disk2position_printData2[m][1];
				for (y = tempY - 1; y < tempY + 1; y++)
				{
					for (x = tempX - 1; x < tempX + 1; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk2slice_printData2Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk2slice_printData2Count; m++)
				{
					tempX = Tablet3DSetupData.Disk2position_slice_printData2[m][0];
					tempY = Tablet3DSetupData.Disk2position_slice_printData2[m][1];
					for (y = tempY - 1; y < tempY + 1; y++)
					{
						for (x = tempX - 1; x < tempX + 1; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine2[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
	}
	else
	{
		if (SuctionDiskNum == SUCTION_DISK_1_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk1printData2Count; m++)
			{
				tempX = Tablet3DSetupData.Disk1position_printData2[m][0];
				tempY = Tablet3DSetupData.Disk1position_printData2[m][1];
				for (y = tempY - 2; y < tempY + 2; y++)
				{
					for (x = tempX - 2; x < tempX + 2; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk1slice_printData2Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk1slice_printData2Count; m++)
				{
					tempX = Tablet3DSetupData.Disk1position_slice_printData2[m][0];
					tempY = Tablet3DSetupData.Disk1position_slice_printData2[m][1];
					for (y = tempY - 2; y < tempY + 2; y++)
					{
						for (x = tempX - 2; x < tempX + 2; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine2[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
		else if (SuctionDiskNum == SUCTION_DISK_2_NUM)
		{
			for (m = 0; m < Tablet3DSetupData.Disk2printData2Count; m++)
			{
				tempX = Tablet3DSetupData.Disk2position_printData2[m][0];
				tempY = Tablet3DSetupData.Disk2position_printData2[m][1];
				for (y = tempY - 2; y < tempY + 2; y++)
				{
					for (x = tempX - 2; x < tempX + 2; x++)
					{
						TempImage[MAX_IMAGE_WIDTH * y + x] = 2;
					}
				}
			}

			if (Tablet3DSetupData.Disk2slice_printData2Count)
			{
				for (m = 0; m < Tablet3DSetupData.Disk2slice_printData2Count; m++)
				{
					tempX = Tablet3DSetupData.Disk2position_slice_printData2[m][0];
					tempY = Tablet3DSetupData.Disk2position_slice_printData2[m][1];
					for (y = tempY - 2; y < tempY + 2; y++)
					{
						for (x = tempX - 2; x < tempX + 2; x++)
						{
							if (TempImage[MAX_IMAGE_WIDTH * y + x])
							{
								splitLine2[MAX_IMAGE_WIDTH * y + x] = 1;
							}
						}
					}
				}
			}
		}
	}

	for (y = 0; y < MAX_IMAGE_HEIGHT; y++)
	{
		for (x = 0; x < MAX_IMAGE_WIDTH; x++)
		{
			if (TempImage[MAX_IMAGE_WIDTH * y + x] == 1)
			{
				tempCountPrint[0]++;
			}
			else if (TempImage[MAX_IMAGE_WIDTH * y + x] == 2)
			{
				tempCountPrint[1]++;
			}
			else if (TempImage[MAX_IMAGE_WIDTH * y + x] == 3)
			{
				tempCountPrint[2]++;
			}
			else if (TempImage[MAX_IMAGE_WIDTH * y + x] == 4)
			{
				tempCountPrint[3]++;
			}
		}
	}

	if (TabletCharacter.discriminationDisplay_num != TWO_FACE_DIFF)
	{
		if (tempCountPrint[1] + tempCountPrint[3] < 1500)
			addStep = 1;
		else if (tempCountPrint[1] + tempCountPrint[3] < 4000)
			addStep = 2;
		else
			addStep = 3;
	}

	for (y = 0; y < MAX_IMAGE_HEIGHT; y += addStep)
	{
		for (x = 0; x < MAX_IMAGE_WIDTH; x += addStep)
		{
			if (TempImage[MAX_IMAGE_WIDTH * y + x] == 2)
			{
				if (SmallPrintCoreData2CountFor3D < 2500)
				{
					Position_SmallPrintCoreData2For3D[SmallPrintCoreData2CountFor3D][0] = x;
					Position_SmallPrintCoreData2For3D[SmallPrintCoreData2CountFor3D][1] = y;

					if (splitLine2[MAX_IMAGE_WIDTH * y + x])
					{
						Position_SmallPrintCoreData2For3D[SmallPrintCoreData2CountFor3D][2] = 1;
					}
					else
					{
						Position_SmallPrintCoreData2For3D[SmallPrintCoreData2CountFor3D][2] = 0;
					}

					SmallPrintCoreData2CountFor3D++;
				}
			}
		}
	}

	if (TabletCharacter.discriminationDisplay_num != TWO_FACE_DIFF)
	{
		if (tempCountPrint[3] < 1500)
			addStep = 1;
		else if (tempCountPrint[3] < 4000)
			addStep = 2;
		else
			addStep = 3;
	}

	memset(ThreeDSplitLineData1, 0, 2500);
	memset(ThreeDSplitLineData2, 0, 2500);

	for (m = 0; m < SmallPrintCoreData1CountFor3D; m++)
	{
		if (Position_SmallPrintCoreData1For3D[m][2])
		{
			ThreeDSplitLineData1[m] = 1;
		}
	}

	for (m = 0; m < SmallPrintCoreData2CountFor3D; m++)
	{
		if (Position_SmallPrintCoreData2For3D[m][2])
		{
			ThreeDSplitLineData2[m] = 1;
		}
	}

	for (r = 0; r < 720; r++)
	{
		for (m = 0; m < SmallPrintCoreData1CountFor3D; m++)
		{
			tempX = Position_SmallPrintCoreData1For3D[m][0];
			tempY = Position_SmallPrintCoreData1For3D[m][1];
			rotateX = ((tempX - 320) * CosData[r] - (tempY - 240) * SinData[r]) / 1024 + 320;
			rotateY = ((tempX - 320) * SinData[r] + (tempY - 240) * CosData[r]) / 1024 + 240;

			if (rotateX < 4)
				rotateX = 4;
			if (rotateX > MAX_IMAGE_WIDTH - 4)
				rotateX = MAX_IMAGE_WIDTH - 4;
			if (rotateY < 4)
				rotateY = 4;
			if (rotateY > MAX_IMAGE_HEIGHT - 4)
				rotateY = MAX_IMAGE_HEIGHT - 4;

			tempAddress = rotateY * MAX_IMAGE_WIDTH + rotateX;
			detailedCorePrintRotationAddress1For3D[r][m] = tempAddress;
		}
		for (m = 0; m < SmallPrintCoreData2CountFor3D; m++)
		{
			tempX = Position_SmallPrintCoreData2For3D[m][0];
			tempY = Position_SmallPrintCoreData2For3D[m][1];
			rotateX = ((tempX - 320) * CosData[r] - (tempY - 240) * SinData[r]) / 1024 + 320;
			rotateY = ((tempX - 320) * SinData[r] + (tempY - 240) * CosData[r]) / 1024 + 240;

			if (rotateX < 4)
				rotateX = 4;
			if (rotateX > MAX_IMAGE_WIDTH - 4)
				rotateX = MAX_IMAGE_WIDTH - 4;
			if (rotateY < 4)
				rotateY = 4;
			if (rotateY > MAX_IMAGE_HEIGHT - 4)
				rotateY = MAX_IMAGE_HEIGHT - 4;

			tempAddress = rotateY * MAX_IMAGE_WIDTH + rotateX;
			detailedCorePrintRotationAddress2For3D[r][m] = tempAddress;
		}
	}

	memset(ThreeDSplitLineLabel, 0, sizeof(int) * 2 * 2 * THREED_PRINT_MAX_LABEL_COUNT);
	if (SDNum == SUCTION_DISK_1_NUM)
	{
		diskNumber = 0;
	}
	else
	{
		diskNumber = 1;
	}

	int aaaa[2][20];
	for (printNumber = 0; printNumber < 2; printNumber++)
	{
		if (printNumber == 0)
		{
			tempSplitImage = splitLine1;
		}
		else
		{
			tempSplitImage = splitLine2;
		}

		for (n = 0; n < Tablet3DSetupData.printLabelCount[diskNumber][printNumber]; n++)
		{
			tempCount = 0;
			for (m = 0; m < Tablet3DSetupData.printLabelDataCnt[diskNumber][printNumber][n]; m++)
			{
				tempX = Tablet3DSetupData.printLabelData[diskNumber][printNumber][n][m][0];
				tempY = Tablet3DSetupData.printLabelData[diskNumber][printNumber][n][m][1];

				tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;
				if (tempSplitImage[tempAddress])
				{
					tempCount++;
				}
			}

			if (Tablet3DSetupData.printLabelDataCnt[diskNumber][printNumber][n])
			{
				if (tempCount * 100 / Tablet3DSetupData.printLabelDataCnt[diskNumber][printNumber][n] > 90)
				{
					ThreeDSplitLineLabel[diskNumber][printNumber][n] = 1;
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void FrontFace3DShapeEdgeAreaSetting(int cameraIndex)
{
	int i, j, m;
	int tempX;
	int tempY;
	int edgeUnInspectionRange;
	int tempAddress;
	int edgeNeighborRange;
	int r;
	int x, y;
	int uLength;
	int pX, pY;
	int sinV;
	int angle;
	int startX, endX, startY, endY;
	int tempZ, tempAngle, tempCount;
	int alpha;

	startX = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	if (TabletCharacter.kind == SUGARCOATING)
	{
		alpha = 2;
	}
	else
	{
		alpha = 1;
	}

	edgeNeighborRange = 12 / alpha + TabletGradeData.threeDEdgeAreaExtend;
	if (edgeNeighborRange >= 29)
		edgeNeighborRange = 29;

	for (angle = 0; angle < 360; angle += 2)
	{
		if (GlobalRotationOutlineInfoTable[angle][0])
		{
			tempX = GlobalRotationOutlineInfoTable[angle][2];
			tempY = GlobalRotationOutlineInfoTable[angle][3];

			for (i = tempY - edgeNeighborRange; i <= tempY + edgeNeighborRange; i++)
			{
				for (j = tempX - edgeNeighborRange; j <= tempX + edgeNeighborRange; j++)
				{
					tempAddress = i * MAX_IMAGE_WIDTH + j;
					if (AdjustThreeD_Data[tempAddress] && InspectionArea[tempAddress] == 0)
					{
						InspectionArea[tempAddress] = FRONT_SHAPE_EDGE_NEIGHBOR;
					}
				}
			}
		}
	}

	RealEdgeLineCountForThreeDImage = 0;
	edgeUnInspectionRange = 2 + TabletGradeData.front_unInspectionIntensity_ForOptionEdge;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && InspectionArea[tempAddress] == FRONT_SHAPE_EDGE_NEIGHBOR)
			{
				if (AdjustThreeD_Data[tempAddress - edgeUnInspectionRange] == 0 ||
					AdjustThreeD_Data[tempAddress + edgeUnInspectionRange] == 0 ||
					AdjustThreeD_Data[tempAddress - edgeUnInspectionRange * MAX_IMAGE_WIDTH] == 0 ||
					AdjustThreeD_Data[tempAddress + edgeUnInspectionRange * MAX_IMAGE_WIDTH] == 0)
				{
					InspectionArea[tempAddress] = FRONT_SHAPE_EDGE;

					if (AdjustThreeD_Data[tempAddress - 2] == 0 ||
						AdjustThreeD_Data[tempAddress + 2] == 0 ||
						AdjustThreeD_Data[tempAddress - 2 * MAX_IMAGE_WIDTH] == 0 ||
						AdjustThreeD_Data[tempAddress + 2 * MAX_IMAGE_WIDTH] == 0)
					{
						pX = x - ProtoTabletCenterX;
						pY = y - ProtoTabletCenterY;
						uLength = pX * pX + pY * pY;
						if (uLength < 90000)
						{
							uLength = SqrtData[uLength]; // sqrtdata는 128이 곱해 진 상태.
							sinV = 1280000 * pX / uLength;
							if (sinV > 10000)
								sinV = 10000;

							if (pX >= 0 && pY <= 0)
								angle = ArcSinData[sinV];
							else if (pX >= 0 && pY > 0)
								angle = 180 - ArcSinData[sinV];
							else if (pX < 0 && pY > 0)
								angle = 180 + ArcSinData[-sinV];
							else
								angle = 360 - ArcSinData[-sinV];

							if (angle >= 0 && angle < 360)
							{
								if (GlobalRotationOutlineInfoTable[angle][0] < uLength)
								{
									GlobalRotationOutlineInfoTable[angle][0] = uLength;
									GlobalRotationOutlineInfoTable[angle][1] = AdjustThreeD_Data[tempAddress];
									GlobalRotationOutlineInfoTable[angle][2] = x;
									GlobalRotationOutlineInfoTable[angle][3] = y;
								}

								if (RealEdgeLineCountForThreeDImage < MAX_THREED_EDGE_POINT_COUNT)
								{
									RealEdgeLineForThreeDImage[RealEdgeLineCountForThreeDImage][0] = x;
									RealEdgeLineForThreeDImage[RealEdgeLineCountForThreeDImage][1] = y;
									RealEdgeLineForThreeDImage[RealEdgeLineCountForThreeDImage][2] = angle;
									RealEdgeLineCountForThreeDImage++;
								}
							}
						}
					}
				}
			}
		}
	}

	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && InspectionArea[tempAddress] == 0)
			{
				InspectionArea[tempAddress] = NORMAL_INPECTION_AREA;
			}
		}
	}
}
//------------------------------------------------------------------------------------------------------
void DefectSearchFor3DImage(int cameraIndex)
{
	int i, j, m, r;
	int maxDx, maxDy;
	int tempAddress, tempAddress2, tempAddress3, tempAddress4;
	int areaValue;
	int defectPointDataCnt;
	int edge_defectPointDataCnt1;
	int edge_defectPointDataCnt2;
	int defectGroupN;
	int tMax;
	int maxPixelDefectLabelN;
	short defectPointData[MAX_DEFECT_POINT_COUNT * 3];
	short edge_defectPointData1[MAX_DEFECT_POINT_COUNT * 3];
	short edge_defectPointData2[MAX_DEFECT_POINT_COUNT * 3];
	short *label_Image;
	int intensityThresholdForEachArea[20];
	int sizeThresholdForHigherValue;
	int searchRange;
	short selfPositionValue;
	int yShift;
	int tempCount;
	int heightDiff;
	int thresholdEdgeHeightDiff1;
	int thresholdEdgeHeightDiff2;
	int defectCheckSW;
	int x, y;
	int shellN;
	int angle;
	int pX, pY;
	int uLength;
	int sinV;
	int tempAngle;
	int maxHeightDiff;
	int forwardSW, reverseSW;
	int srcHeight[24];
	int modelingHeight[24];
	int srcLabelN;
	int startX, endX, startY, endY;
	int left;
	int right;
	int up;
	int down;
	int upleft;
	int upright;
	int downleft;
	int downright;
	int length;
	int maxHeight;
	int tempNeighborHeight[4];
	int n, l;
	int lengthTable[10];
	int startL, endL;
	int tempDentedDepth;
	int upValue;
	int downValue;
	int leftValue;
	int rightValue;
	int upleftValue;
	int downrightValue;
	int uprightValue;
	int downleftValue;
	int tempDiff;
	int addStepX, addStepY;
	int selfValue;
	int tempX, tempY;
	int maskSize;
	int topSW, bottomSW, rightSW, leftSW, topLeftSW, topRightSW, bottomLeftSW, bottomRightSW;
	int topValue, bottomValue, topleftValue, toprightValue, bottomleftValue, bottomRightValue;
	int maxValue, maxValue2;
	int value;
	int ContactPointShellN;
	int normalVector[360][2];
	int EndPoint[2];
	int addStep;
	int neighborAngle1, neighborAngle2;
	int firstContactPoint[2];
	int secondContactPoint[2];
	int tempSqr;
	int cosV;
	int selectedPointX, selectedPointY;
	int leftDefectSW, rightDefectSW;
	int shiftX, shiftY;
	int contactHeight[4];
	int leftContactSW, rightContactSW;
	int tempHeight;
	int upperValue;
	int lowerValue;
	int weight;
	int firstContactSW, secondContactSW;
	int searchingOK;
	int EdgeofEdgeDefectSW;
	int EdgeNeighborDefectSW;
	int averageWeight[360][42];
	int tempAverageWeight[360][42];
	int maxRadialRange;
	int radial;
	int comparePointR;
	int roundCheckResult;
	int areaCheckCount;
	int expectedDefectCount;
	int edgeDefectMaxValue;
	int edgeNeighborDefectMaxValue;
	int maxEdgeRange;
	int alpha;
	int edgeNeighborRange;

	startX = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	label_Image = RotationEdgeImage;

	defectPointDataCnt = 0;
	edge_defectPointDataCnt1 = 0;
	edge_defectPointDataCnt2 = 0;

	thresholdEdgeHeightDiff1 = TabletGradeData.option_front_edgeBreaking_Intensity_Type1 / 10;
	thresholdEdgeHeightDiff2 = (TabletGradeData.option_front_edgeBreaking_Intensity_Type2 / 10) * 2;

	for (i = 0; i < 20; i++)
	{
		intensityThresholdForEachArea[i] = TabletGradeData.option_Breaking_Intensity / 10;
	}

	// for small wave defect
	if (TabletCharacter.CurvedSurfaceTablet == 0)
	{
		// 표면이 굴곡진 정제가 아닌 경우
		for (y = startY; y < endY; y += 2)
		{
			for (x = startX; x < endX; x += 2)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
				{
					topSW = bottomSW = rightSW = leftSW = topLeftSW = topRightSW = bottomLeftSW = bottomRightSW = 0;
					leftValue = rightValue = topValue = bottomValue = topleftValue = toprightValue = bottomleftValue = bottomRightValue = 0;				
					maxValue = 0;
					for (r = 12; r >= 4; r -= 4)
					{
						if (x - r > startX)
						{
							tempAddress2 = tempAddress - r;
							if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
							{
								value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
								if (value >= 1)
								{
									leftSW = 1;

									if (leftValue < value)
									{
										leftValue = value;
									}
								}
							}
						}
					}

					for (r = 12; r >= 4; r -= 4)
					{
						if (y - r > startY)
						{
							tempAddress2 = tempAddress - r * MAX_IMAGE_WIDTH;
							if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
							{
								value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
								if (value >= 1)
								{
									topSW = 1;

									if (topValue < value)
									{
										topValue = value;
									}
								}
							}
						}
					}

					for (r = 12; r >= 4; r -= 4)
					{
						if (x - r > startX && y - r > startY)
						{
							tempAddress2 = tempAddress - r * MAX_IMAGE_WIDTH - r;
							if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
							{
								value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
								if (value >= 1)
								{
									topLeftSW = 1;

									if (topleftValue < value)
									{
										topleftValue = value;
									}
								}
							}
						}
					}

					for (r = 12; r >= 4; r -= 4)
					{
						if (x - r > startX && y + r < endY)
						{
							tempAddress2 = tempAddress + r * MAX_IMAGE_WIDTH - r;
							if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
							{
								value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
								if (value >= 1)
								{
									bottomLeftSW = 1;

									if (bottomleftValue < value)
									{
										bottomleftValue = value;
									}
								}
							}
						}
					}

					if (leftSW)
					{
						for (r = 12; r >= 4; r -= 4)
						{
							if (x + r < endX)
							{
								tempAddress2 = tempAddress + r;
								if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
								{
									value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
									if (value >= 1)
									{
										rightSW = 1;

										if (rightValue < value)
										{
											rightValue = value;
										}
									}
								}
							}
						}
					}

					if (topSW)
					{
						for (r = 12; r >= 4; r -= 4)
						{
							if (y + r < endY)
							{
								tempAddress2 = tempAddress + r * MAX_IMAGE_WIDTH;
								if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
								{
									value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
									if (value >= 1)
									{
										bottomSW = 1;

										if (bottomValue < value)
										{
											bottomValue = value;
										}
									}
								}
							}
						}
					}

					if (bottomLeftSW)
					{
						for (r = 12; r >= 4; r -= 4)
						{
							if (x + r < endX && y - r > startY)
							{
								tempAddress2 = tempAddress - r * MAX_IMAGE_WIDTH + r;
								if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
								{
									value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
									if (value >= 1)
									{
										topRightSW = 1;

										if (toprightValue < value)
										{
											toprightValue = value;
										}
									}
								}
							}
						}
					}

					if (topLeftSW)
					{
						for (r = 12; r >= 4; r -= 4)
						{
							if (x + r < endX && y + r < endY)
							{
								tempAddress2 = tempAddress + r * MAX_IMAGE_WIDTH + r;
								if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
								{
									value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
									if (value >= 1)
									{
										bottomRightSW = 1;

										if (bottomRightValue < value)
										{
											bottomRightValue = value;
										}
									}
								}
							}
						}
					}

					value = tempCount = 0;
					if (leftSW && rightSW)
					{
						value += leftValue + rightValue;
						tempCount++;
					}

					if (topSW && bottomSW)
					{
						value += topValue + bottomValue;
						tempCount++;
					}

					if (topLeftSW && bottomRightSW)
					{
						value += topleftValue + bottomRightValue;
						tempCount++;
					}

					if (bottomLeftSW && topRightSW)
					{
						value += bottomleftValue + toprightValue;
						tempCount++;
					}

					if (tempCount)
					{
						value /= 2 * tempCount;
						ExpectedThreeDDefectArea[tempAddress] = value;
						ExpectedThreeDDefectArea[tempAddress + 1] = value;
						ExpectedThreeDDefectArea[tempAddress + MAX_IMAGE_WIDTH] = value;
						ExpectedThreeDDefectArea[tempAddress + 1 + MAX_IMAGE_WIDTH] = value;
					}
				}
			}
		}
	}
	else
	{
		// 표면이 굴곡진 정제의 경우.
		for (y = startY; y < endY; y += 2)
		{
			for (x = startX; x < endX; x += 2)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
				{
					maxDx = maxDy = maxValue = 0;
					tempAddress2 = tempAddress - 4;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = -1;
							maxDy = 0;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress - 4 * MAX_IMAGE_WIDTH;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = 0;
							maxDy = -1;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress - 4 * MAX_IMAGE_WIDTH - 4;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = -1;
							maxDy = -1;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress + 4 * MAX_IMAGE_WIDTH - 4;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = -1;
							maxDy = 1;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress + 4;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = 1;
							maxDy = 0;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress + 4 * MAX_IMAGE_WIDTH;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = 0;
							maxDy = 1;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress - 4 * MAX_IMAGE_WIDTH + 4;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = 1;
							maxDy = -1;
							maxValue = value;
						}
					}

					tempAddress2 = tempAddress + 4 * MAX_IMAGE_WIDTH + 4;
					if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						value = AdjustThreeD_Data[tempAddress2] - AdjustThreeD_Data[tempAddress];
						if (maxValue < value)
						{
							maxDx = 1;
							maxDy = 1;
							maxValue = value;
						}
					}

					if (maxValue)
					{
						maxValue = min(maxValue, 40); // 과검을 줄이기 위하여 발산하는 것을 제한해줌.

						// 불량 후보다. 대칭성을 체크하자.
						// 이때 굴곡은 원점 대칭이라고 가정한다. 이상한 모양이라든지 삼각형이라든지 이런 것은 안된다.
						tempAddress2 = MAX_IMAGE_WIDTH * (ProtoTabletCenterY * 2 - y) + ProtoTabletCenterX * 2 - x; // 중심에서 대칭되는 점.
						if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress2] == FRONT_SHAPE_EDGE_NEIGHBOR)
						{
							maxValue2 = 0;
							for (r = -12; r <= 12; r += 2) // 대칭이 잘 안맞는 것을 고려하여 앞뒤로 검색.
							{
								tempAddress3 = tempAddress2 + MAX_IMAGE_WIDTH * maxDy * r + maxDx * r; // 부호 무관. 대칭.
								tempAddress4 = tempAddress3 - MAX_IMAGE_WIDTH * maxDy * 4 - maxDx * 4; // 변동 위치를 반대로 적용해줌 (중심점에 대한 대칭이므로)
								if ((InspectionArea[tempAddress3] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress3] == FRONT_SHAPE_EDGE_NEIGHBOR) &&
									(InspectionArea[tempAddress4] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress4] == FRONT_SHAPE_EDGE_NEIGHBOR))
								{
									value = AdjustThreeD_Data[tempAddress4] - AdjustThreeD_Data[tempAddress3];
									if (maxValue2 < value)
									{
										maxValue2 = value;
									}
								}
							}

							// 대칭이 되는 위치가 정상적이라면 그 위치의 높이차 만큼을 상쇄시킴.
							if (maxValue2 > 3)
							{
								maxValue = 0; // 기울기가 대칭이고 일정 이상이라면 대칭으로 간주하여 무조건 상쇄.
							}
							else
							{								
								maxValue -= maxValue2; // 그외 차감하여 상쇄.
							}							

							if (maxValue > 0)
							{
								ExpectedThreeDDefectArea[tempAddress] = maxValue;
								ExpectedThreeDDefectArea[tempAddress + 1] = maxValue;
								ExpectedThreeDDefectArea[tempAddress + MAX_IMAGE_WIDTH] = maxValue;
								ExpectedThreeDDefectArea[tempAddress + 1 + MAX_IMAGE_WIDTH] = maxValue;
							}
						}
						else
						{
							// 대칭되는 위치가 각인 영역일 경우. 무시.
						}
					}
				}
			}
		}		
	}

	// for big wave defect
	if (TabletCharacter.CurvedSurfaceTablet == 0) // 표면이 굴곡진 정제가 아닌 경우에만 큰 모양의 defect를 검사.
	{
		if ((TabletCharacter.shape == ROUND || TabletCharacter.shape == ETC) && TabletCharacter.kind != SUGARCOATING)
		{
			if (TabletCharacter.length < 9000 / 37)
			{ // 9mm이하인 정제에 대해
				lengthTable[0] = 8 * TabletCharacter.length / (9000 / 37);
				lengthTable[1] = 13 * TabletCharacter.length / (9000 / 37);
				lengthTable[2] = 20 * TabletCharacter.length / (9000 / 37);
				lengthTable[3] = 30 * TabletCharacter.length / (9000 / 37);
				lengthTable[4] = 45 * TabletCharacter.length / (9000 / 37);
				lengthTable[5] = 45 * TabletCharacter.length / (9000 / 37);
				lengthTable[6] = 60 * TabletCharacter.length / (9000 / 37);
			}
			else
			{
				lengthTable[0] = 8;
				lengthTable[1] = 13;
				lengthTable[2] = 20;
				lengthTable[3] = 30;
				lengthTable[4] = 45;
				lengthTable[5] = 45;
				lengthTable[6] = 60;
			}
		}
		else
		{
			lengthTable[0] = 8;
			lengthTable[1] = 13;
			lengthTable[2] = 20;
			lengthTable[3] = 30;
			lengthTable[4] = 45;
			lengthTable[5] = 45;
			lengthTable[6] = 60;
		}

		startL = 1;
		endL = 4;

		if (TabletCharacter.length < 9000 / 37)
		{
			addStepX = 1;
			addStepY = 1;
		}
		else
		{
			addStepX = 2;
			addStepY = 2;
		}

		for (l = startL; l < endL; l++)
		{
			length = lengthTable[l];
			up = -MAX_IMAGE_WIDTH * length;
			down = MAX_IMAGE_WIDTH * length;
			left = -1 * length;
			right = 1 * length;
			upleft = (-MAX_IMAGE_WIDTH - 1) * length;
			upright = (-MAX_IMAGE_WIDTH + 1) * length;
			downleft = (MAX_IMAGE_WIDTH - 1) * length;
			downright = (MAX_IMAGE_WIDTH + 1) * length;

			for (y = startY; y < endY; y += addStepY)
			{
				for (x = startX; x < endX; x += addStepX)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					if (AdjustThreeD_Data[tempAddress] && (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress] == FRONT_SHAPE_EDGE_NEIGHBOR))
					{
						upValue = downValue = leftValue = rightValue = upleftValue = downrightValue = uprightValue = downleftValue = 0;

						maxHeight = 0;
						tempNeighborHeight[0] = 0;
						tempNeighborHeight[1] = 0;
						tempNeighborHeight[2] = 0;
						tempNeighborHeight[3] = 0;

						tempAddress2 = tempAddress + up;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + up] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + up] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							upValue = AdjustThreeD_Data[tempAddress + up];
						}

						tempAddress2 = tempAddress + down;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + down] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + down] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							downValue = AdjustThreeD_Data[tempAddress + down];
						}

						tempAddress2 = tempAddress + left;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + left] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + left] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							leftValue = AdjustThreeD_Data[tempAddress + left];
						}

						tempAddress2 = tempAddress + right;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + right] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + right] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							rightValue = AdjustThreeD_Data[tempAddress + right];
						}

						tempAddress2 = tempAddress + upleft;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + upleft] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + upleft] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							upleftValue = AdjustThreeD_Data[tempAddress + upleft];
						}

						tempAddress2 = tempAddress + downright;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + downright] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + downright] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							downrightValue = AdjustThreeD_Data[tempAddress + downright];
						}

						tempAddress2 = tempAddress + upright;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + upright] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + upright] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							uprightValue = AdjustThreeD_Data[tempAddress + upright];
						}

						tempAddress2 = tempAddress + downleft;
						if (tempAddress2 > MAX_IMAGE_WIDTH * 4 + 4 && tempAddress2 < MAX_IMAGE_WIDTH * (MAX_IMAGE_HEIGHT - 4) + MAX_IMAGE_WIDTH - 4 &&
							(InspectionArea[tempAddress + downleft] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress + downleft] == FRONT_SHAPE_EDGE_NEIGHBOR))
						{
							downleftValue = AdjustThreeD_Data[tempAddress + downleft];
						}

						if (upValue && downValue)
						{
							tempNeighborHeight[0] = upValue + downValue;
						}
						if (leftValue && rightValue)
						{
							tempNeighborHeight[1] = leftValue + rightValue;
						}
						if (upleftValue && downrightValue)
						{
							tempNeighborHeight[2] = upleftValue + downrightValue;
						}
						if (uprightValue && downleftValue)
						{
							tempNeighborHeight[3] = uprightValue + downleftValue;
						}

						for (n = 0; n < 4; n++)
						{
							maxHeight = max(maxHeight, tempNeighborHeight[n]);
						}

						if (maxHeight)
						{
							tempDiff = maxHeight / 2 - AdjustThreeD_Data[tempAddress];
							if (tempDiff > 0)
							{
								ExpectedThreeDDefectArea[tempAddress] = tempDiff;
							}
						}
					}
				}
			}
		}		
	}

	memcpy(TempImage, ExpectedThreeDDefectArea, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	// 채우기
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (AdjustThreeD_Data[tempAddress] && (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA || InspectionArea[tempAddress] == FRONT_SHAPE_EDGE_NEIGHBOR))
			{
				if (TempImage[tempAddress] == 0)
				{
					tempCount = 0;
					tempDentedDepth = 0;

					// 3x3... for루프 풀어놓자
					for (i = y - 1; i <= y + 1; i++)
					{
						for (j = x - 1; j <= x + 1; j++)
						{
							if (j > 2 && j < MAX_IMAGE_WIDTH - 2 && i > 2 && i < MAX_IMAGE_HEIGHT - 2)
							{
								selfValue = TempImage[MAX_IMAGE_WIDTH * i + j];

								if (selfValue)
								{
									tempCount++;
									tempDentedDepth += selfValue;
								}
							}
						}
					}
					if (tempCount >= 4)
					{
						ExpectedThreeDDefectArea[tempAddress] = tempDentedDepth / tempCount;
					}
				}
			}
		}
	}

	memset(normalVector, 0, sizeof(short) * 360 * 2);
	for (angle = 0; angle < 360; angle++)
	{
		neighborAngle1 = -1;
		for (tempAngle = 10; tempAngle <= 20; tempAngle += 2)
		{
			if (GlobalRotationOutlineInfoTable[((angle - tempAngle) + 360) % 360][0] > 0)
			{
				neighborAngle1 = ((angle - tempAngle) + 360) % 360;
				break;
			}
		}

		neighborAngle2 = -1;
		for (tempAngle = 10; tempAngle <= 20; tempAngle += 2)
		{
			if (GlobalRotationOutlineInfoTable[((angle + tempAngle) + 360) % 360][0] > 0)
			{
				neighborAngle2 = ((angle + tempAngle) + 360) % 360;
				break;
			}
		}

		if (GlobalRotationOutlineInfoTable[angle][0] > -1 && neighborAngle1 >= 0 && neighborAngle2 >= 0)
		{
			tempX = GlobalRotationOutlineInfoTable[angle][2];
			tempY = GlobalRotationOutlineInfoTable[angle][3];

			firstContactPoint[0] = GlobalRotationOutlineInfoTable[neighborAngle1][2];
			firstContactPoint[1] = GlobalRotationOutlineInfoTable[neighborAngle1][3];

			secondContactPoint[0] = GlobalRotationOutlineInfoTable[neighborAngle2][2];
			secondContactPoint[1] = GlobalRotationOutlineInfoTable[neighborAngle2][3];

			pX = secondContactPoint[0] - firstContactPoint[0];
			pY = secondContactPoint[1] - firstContactPoint[1];

			tempSqr = pX * pX + pY * pY;
			if (tempSqr < 90000)
			{
				tempSqr = SqrtData[tempSqr];
				// 해당 포인트의 법선 ///
				cosV = -128 * 1024 * (pY) / tempSqr; // cos(a+90) = -sin(a)
				sinV = 128 * 1024 * (pX) / tempSqr;	 // sin(a+90) = cos(a)

				normalVector[angle][0] = cosV;
				normalVector[angle][1] = sinV;
			}
		}
	}

	memset(outlineSectionInfo, 0, sizeof(short) * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	if (TabletCharacter.kind == SUGARCOATING)
	{
		alpha = 2;
	}
	else
	{
		alpha = 1;
	}
	edgeNeighborRange = 12 / alpha + TabletGradeData.threeDEdgeAreaExtend;
	maxEdgeRange = sqrt(edgeNeighborRange * edgeNeighborRange + edgeNeighborRange * edgeNeighborRange) * 140 / 100;

	for (m = 0; m < RealEdgeLineCountForThreeDImage; m++)
	{
		tempX = RealEdgeLineForThreeDImage[m][0];
		tempY = RealEdgeLineForThreeDImage[m][1];
		angle = RealEdgeLineForThreeDImage[m][2];

		if (GlobalRotationOutlineInfoTable[angle][0] > -1)
		{
			cosV = normalVector[angle][0];
			sinV = normalVector[angle][1];

			for (r = 0; r < maxEdgeRange; r++)
			{
				selectedPointX = r * cosV / 1024 + tempX;
				selectedPointY = r * sinV / 1024 + tempY;

				if (selectedPointX > 10 && selectedPointX < MAX_IMAGE_WIDTH - 10 && selectedPointY > 10 && selectedPointY < MAX_IMAGE_HEIGHT - 10)
				{
					tempAddress = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
					threeDShellImage[tempAddress] = r + 1;
					outlineSectionInfo[tempAddress] = angle + 1;
				}
			}
		}
	}

	memset(averageWeight, 0, sizeof(int) * 360 * 42);
	for (i = startY; i < endY; i++)
	{
		for (j = startX; j < endX; j++)
		{
			tempAddress = MAX_IMAGE_WIDTH * i + j;

			areaValue = InspectionArea[tempAddress];

			if (areaValue == FRONT_SHAPE_EDGE_NEIGHBOR)
			{
				angle = outlineSectionInfo[tempAddress];
				r = threeDShellImage[tempAddress] - 1;

				selfPositionValue = AdjustThreeD_Data[tempAddress];

				if (angle && selfPositionValue)
				{
					angle = ((angle - 1) + 360) % 360;

					if (r >= 6)
					{
						if (GlobalRotationOutlineInfoTable[angle][0] > 0)
						{
							contactHeight[0] = contactHeight[1] = 0;

							EndPoint[0] = GlobalRotationOutlineInfoTable[angle][2];
							EndPoint[1] = GlobalRotationOutlineInfoTable[angle][3];

							cosV = normalVector[angle][0];
							sinV = normalVector[angle][1];

							ContactPointShellN = 4 + r;
							selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
							selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];
							tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;

							if (InspectionArea[tempAddress2])
							{
								contactHeight[0] = AdjustThreeD_Data[tempAddress2];
							}

							ContactPointShellN = -4 + r;
							selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
							selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];
							tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;

							if (InspectionArea[tempAddress2])
							{
								contactHeight[1] = AdjustThreeD_Data[tempAddress2];
							}

							if (contactHeight[0] && contactHeight[1])
							{
								if (contactHeight[0] < selfPositionValue)
									contactHeight[0] = selfPositionValue;
								if (contactHeight[1] > selfPositionValue)
									contactHeight[1] = selfPositionValue;

								upperValue = abs(contactHeight[0] - selfPositionValue);
								lowerValue = abs(contactHeight[1] - selfPositionValue);

								if (upperValue + lowerValue)
								{
									weight = lowerValue * 1000 / (upperValue + lowerValue);
								}
								else
								{
									weight = 500;
								}

								averageWeight[angle][r] = weight;
							}
						}
					}
				}
			}
		}
	}

	memcpy(tempAverageWeight, averageWeight, sizeof(int) * 360 * 42);

	for (r = 0; r < maxEdgeRange; r++)
	{
		for (angle = 0; angle < 360; angle++)
		{
			weight = tempCount = 0;
			for (tempAngle = 12; tempAngle >= 2; tempAngle -= 2)
			{
				neighborAngle1 = ((angle - tempAngle) + 360) % 360;
				if (tempAverageWeight[neighborAngle1][r])
				{
					weight += tempAverageWeight[neighborAngle1][r];
					tempCount++;
				}

				neighborAngle2 = ((angle + tempAngle) + 360) % 360;
				if (tempAverageWeight[neighborAngle2][r])
				{
					weight += tempAverageWeight[neighborAngle2][r];
					tempCount++;
				}
			}

			if (tempCount)
			{
				averageWeight[angle][r] = weight / tempCount;
			}
		}
	}

	maxRadialRange = 24 / 8;

	for (i = startY; i < endY; i++)
	{
		for (j = startX; j < endX; j++)
		{
			tempAddress = MAX_IMAGE_WIDTH * i + j;

			areaValue = InspectionArea[tempAddress];

			if (areaValue == FRONT_SHAPE_EDGE_NEIGHBOR)
			{
				edgeDefectMaxValue = edgeNeighborDefectMaxValue = 0;

				angle = outlineSectionInfo[tempAddress];
				r = threeDShellImage[tempAddress] - 1;

				if (angle == 0)
				{
					// 모서리 모양에 따라 angle 값이 존재하지 않을 수 있음
					for (tempY = i - 1; tempY <= i + 1; tempY++)
					{
						for (tempX = j - 1; tempX <= j + 1; tempX++)
						{
							tempAddress2 = MAX_IMAGE_WIDTH * tempY + tempX;
							if (outlineSectionInfo[tempAddress2])
							{
								angle = outlineSectionInfo[tempAddress2];
								r = threeDShellImage[tempAddress2] - 1;
								searchingOK = 1;
								break;
							}
						}

						if (searchingOK)
							break;
					}

					if (!searchingOK)
					{
						// 유효데이터가 존재하지 않으므로 skip
						continue;
					}
				}

				selfPositionValue = AdjustThreeD_Data[tempAddress];
				EdgeofEdgeDefectSW = EdgeNeighborDefectSW = 0;
				if (selfPositionValue)
				{
					angle = ((angle - 1) + 360) % 360;

					if (r >= 1 && r <= 8)
					{
						// 모서리와 붙어 있는 파손 불량만 검출이 가능
						for (tempAngle = 24; tempAngle >= 8; tempAngle -= 4)
						{
							neighborAngle1 = ((angle - tempAngle) + 360) % 360;

							leftContactSW = 0;
							leftDefectSW = 1;

							if (GlobalRotationOutlineInfoTable[neighborAngle1][0] > 0)
							{
								EndPoint[0] = GlobalRotationOutlineInfoTable[neighborAngle1][2];
								EndPoint[1] = GlobalRotationOutlineInfoTable[neighborAngle1][3];

								for (ContactPointShellN = 1; ContactPointShellN <= r; ContactPointShellN *= 2)
								{
									cosV = normalVector[neighborAngle1][0];
									sinV = normalVector[neighborAngle1][1];

									selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
									selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];

									tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
									contactHeight[0] = AdjustThreeD_Data[tempAddress2];

									if (contactHeight[0])
									{
										leftContactSW = 1;
										heightDiff = contactHeight[0] - selfPositionValue;

										if (edgeDefectMaxValue < heightDiff)
										{
											edgeDefectMaxValue = heightDiff;
										}

										if (heightDiff < thresholdEdgeHeightDiff1)
										{
											leftDefectSW = 0;
											break;
										}
									}
								}
							}

							if (leftDefectSW && leftContactSW)
								break;
						}

						if (leftDefectSW && leftContactSW)
						{
							for (tempAngle = 24; tempAngle >= 8; tempAngle -= 4)
							{
								neighborAngle2 = ((angle + tempAngle) + 360) % 360;

								rightContactSW = 0;
								rightDefectSW = 1;

								if (GlobalRotationOutlineInfoTable[neighborAngle2][0] > 0)
								{
									EndPoint[0] = GlobalRotationOutlineInfoTable[neighborAngle2][2];
									EndPoint[1] = GlobalRotationOutlineInfoTable[neighborAngle2][3];

									for (ContactPointShellN = 1; ContactPointShellN <= r; ContactPointShellN *= 2)
									{
										cosV = normalVector[neighborAngle2][0];
										sinV = normalVector[neighborAngle2][1];

										selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
										selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];

										tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
										contactHeight[0] = AdjustThreeD_Data[tempAddress2];

										if (contactHeight[0])
										{
											rightContactSW = 1;
											heightDiff = contactHeight[0] - selfPositionValue;

											if (edgeDefectMaxValue < heightDiff)
											{
												edgeDefectMaxValue = heightDiff;
											}

											if (heightDiff < thresholdEdgeHeightDiff1)
											{
												rightDefectSW = 0;
												break;
											}
										}
									}

									if (rightDefectSW && rightContactSW)
										break;
								}
							}
						}

						if (leftContactSW && rightContactSW && leftDefectSW && rightDefectSW)
						{
							EdgeofEdgeDefectSW = 1;
						}
					}

					if (r >= 6 && ExpectedThreeDDefectArea[tempAddress] >= thresholdEdgeHeightDiff2)
					{
						// 대상 픽셀 기준 대각선 방향 상하좌우 비교적용
						for (tempAngle = 12; tempAngle >= 4; tempAngle -= 4)
						{
							neighborAngle1 = ((angle - tempAngle) + 360) % 360;
							if (GlobalRotationOutlineInfoTable[neighborAngle1][0] > 0)
							{
								EndPoint[0] = GlobalRotationOutlineInfoTable[neighborAngle1][2];
								EndPoint[1] = GlobalRotationOutlineInfoTable[neighborAngle1][3];

								cosV = normalVector[neighborAngle1][0];
								sinV = normalVector[neighborAngle1][1];

								ContactPointShellN = 4 + r;
								selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
								selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];
								tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
								contactHeight[0] = AdjustThreeD_Data[tempAddress2];

								ContactPointShellN = -4 + r;
								selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
								selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];
								tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
								contactHeight[1] = AdjustThreeD_Data[tempAddress2];
							}

							neighborAngle2 = ((angle + tempAngle) + 360) % 360;
							if (GlobalRotationOutlineInfoTable[neighborAngle2][0] > 0)
							{
								EndPoint[0] = GlobalRotationOutlineInfoTable[neighborAngle2][2];
								EndPoint[1] = GlobalRotationOutlineInfoTable[neighborAngle2][3];

								cosV = normalVector[neighborAngle2][0];
								sinV = normalVector[neighborAngle2][1];

								ContactPointShellN = 4 + r;
								selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
								selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];
								tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
								contactHeight[2] = AdjustThreeD_Data[tempAddress2];

								ContactPointShellN = -4 + r;
								selectedPointX = ContactPointShellN * cosV / 1024 + EndPoint[0];
								selectedPointY = ContactPointShellN * sinV / 1024 + EndPoint[1];
								tempAddress2 = MAX_IMAGE_WIDTH * selectedPointY + selectedPointX;
								contactHeight[3] = AdjustThreeD_Data[tempAddress2];
							}

							if (contactHeight[0] && contactHeight[1] && contactHeight[2] && contactHeight[3])
							{
								upperValue = (contactHeight[0] + contactHeight[2]) / 2;
								lowerValue = (contactHeight[1] + contactHeight[3]) / 2;

								if (averageWeight[angle][r])
								{
									weight = averageWeight[angle][r];
								}
								else
								{
									weight = 500;
								}

								tempHeight = upperValue * weight / 1000 + lowerValue * (1000 - weight) / 1000;

								if (tempHeight - selfPositionValue >= thresholdEdgeHeightDiff2)
								{
									if (edgeNeighborDefectMaxValue < tempHeight - selfPositionValue)
									{
										edgeNeighborDefectMaxValue = tempHeight - selfPositionValue;
									}

									EdgeNeighborDefectSW = 1;
									break;
								}
							}
						}
					}
				}

				if (EdgeofEdgeDefectSW && edge_defectPointDataCnt1 < MAX_DEFECT_POINT_COUNT)
				{
					edge_defectPointData1[edge_defectPointDataCnt1 * 3] = j;
					edge_defectPointData1[edge_defectPointDataCnt1 * 3 + 1] = i;
					edge_defectPointData1[edge_defectPointDataCnt1 * 3 + 2] = edgeDefectMaxValue;

					edge_defectPointDataCnt1++;
				}

				if (EdgeNeighborDefectSW && edge_defectPointDataCnt2 < MAX_DEFECT_POINT_COUNT)
				{
					edge_defectPointData2[edge_defectPointDataCnt2 * 3] = j;
					edge_defectPointData2[edge_defectPointDataCnt2 * 3 + 1] = i;
					edge_defectPointData2[edge_defectPointDataCnt2 * 3 + 2] = edgeNeighborDefectMaxValue;

					edge_defectPointDataCnt2++;
				}
			}
			else if (areaValue == NORMAL_INPECTION_AREA)
			{
				if (ExpectedThreeDDefectArea[tempAddress] >= intensityThresholdForEachArea[areaValue] && defectPointDataCnt < MAX_DEFECT_POINT_COUNT)
				{
					defectPointData[defectPointDataCnt * 3] = j;
					defectPointData[defectPointDataCnt * 3 + 1] = i;
					defectPointData[defectPointDataCnt * 3 + 2] = ExpectedThreeDDefectArea[tempAddress];
					defectPointDataCnt++;
				}
			}
		}
	}

	defectGroupN = DefectLabeling(label_Image, defectPointData, defectPointDataCnt, cameraIndex);

	if (ProcessingModeGlobal == INSPECTION_MODE)
	{
		sizeThresholdForHigherValue = TabletGradeData.option_Breaking_Size * (TabletGradeData.option_Breaking_Intensity % 10) / 10;
		if (sizeThresholdForHigherValue == 0)
			sizeThresholdForHigherValue = TabletGradeData.option_Breaking_Size;

		tMax = 1;
		maxPixelDefectLabelN = -1;
		for (m = 1; m <= defectGroupN; m++)
		{
			if (Pixel_N_Of_Label[m] > tMax)
			{
				tMax = Pixel_N_Of_Label[m];
				maxPixelDefectLabelN = m;
			}
		}
		if (maxPixelDefectLabelN > 0)
		{
			if (Pixel_N_Of_Label[maxPixelDefectLabelN] > TabletGradeData.option_Breaking_Size) // && Higher_Pixel_N_Of_Label[maxPixelDefectLabelN] > sizeThresholdForHigherValue)
			{
				DefectSW = 1;
				DefectInformationWrite(DEFECT_3D_BREAKING_FOR_NORMAL_AREA, cameraIndex, StartX_Label[maxPixelDefectLabelN],
									   EndX_Label[maxPixelDefectLabelN], StartY_Label[maxPixelDefectLabelN], EndY_Label[maxPixelDefectLabelN], Pixel_N_Of_Label[maxPixelDefectLabelN]);
			}
		}

#ifdef PC_SIM
		memset(ExpectedThreeDDefectArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		if (DefectSW)
		{
			for (m = 0; m < defectPointDataCnt; m++)
			{
				x = defectPointData[m * 3 + 0];
				y = defectPointData[m * 3 + 1];
				tempAddress = MAX_IMAGE_WIDTH * y + x;
				ExpectedThreeDDefectArea[tempAddress] = defectPointData[m * 3 + 2];
			}
		}
#endif

		if (DefectSW == 0)
		{
			defectPointDataCnt = edge_defectPointDataCnt1;

			for (m = 0; m < defectPointDataCnt; m++)
			{
				defectPointData[3 * m] = edge_defectPointData1[3 * m];
				defectPointData[3 * m + 1] = edge_defectPointData1[3 * m + 1];
				defectPointData[3 * m + 2] = edge_defectPointData1[3 * m + 2];
			}
			defectGroupN = DefectLabeling(label_Image, defectPointData, defectPointDataCnt, cameraIndex);

			tMax = 1;
			maxPixelDefectLabelN = -1;
			for (m = 1; m <= defectGroupN; m++)
			{
				if (Pixel_N_Of_Label[m] > tMax)
				{
					tMax = Pixel_N_Of_Label[m];
					maxPixelDefectLabelN = m;
				}
			}
			if (maxPixelDefectLabelN > 0)
			{
				if (Pixel_N_Of_Label[maxPixelDefectLabelN] > TabletGradeData.option_front_edgeBreaking_Size_Type1)
				{
					DefectSW = 1;
					DefectInformationWrite(DEFECT_3D_BREAKING_FOR_EDGE_AREA, cameraIndex, StartX_Label[maxPixelDefectLabelN],
										   EndX_Label[maxPixelDefectLabelN], StartY_Label[maxPixelDefectLabelN], EndY_Label[maxPixelDefectLabelN], Pixel_N_Of_Label[maxPixelDefectLabelN]);
				}
			}

#ifdef PC_SIM
			memset(ExpectedThreeDDefectArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			if (DefectSW)
			{
				for (m = 0; m < defectPointDataCnt; m++)
				{
					x = defectPointData[m * 3 + 0];
					y = defectPointData[m * 3 + 1];
					tempAddress = MAX_IMAGE_WIDTH * y + x;
					ExpectedThreeDDefectArea[tempAddress] = defectPointData[m * 3 + 2];
				}
			}
#endif
		}

		if (DefectSW == 0)
		{
			defectPointDataCnt = edge_defectPointDataCnt2;

			for (m = 0; m < defectPointDataCnt; m++)
			{
				defectPointData[3 * m] = edge_defectPointData2[3 * m];
				defectPointData[3 * m + 1] = edge_defectPointData2[3 * m + 1];
				defectPointData[3 * m + 2] = edge_defectPointData2[3 * m + 2];
			}
			defectGroupN = DefectLabeling(label_Image, defectPointData, defectPointDataCnt, cameraIndex);

			tMax = 1;
			maxPixelDefectLabelN = -1;
			for (m = 1; m <= defectGroupN; m++)
			{
				if (Pixel_N_Of_Label[m] > tMax)
				{
					tMax = Pixel_N_Of_Label[m];
					maxPixelDefectLabelN = m;
				}
			}
			if (maxPixelDefectLabelN > 0)
			{
				if (Pixel_N_Of_Label[maxPixelDefectLabelN] > TabletGradeData.option_front_edgeBreaking_Size_Type2)
				{
					DefectSW = 1;
					DefectInformationWrite(DEFECT_3D_BREAKING_FOR_EDGE_NEIGHBOR, cameraIndex, StartX_Label[maxPixelDefectLabelN],
										   EndX_Label[maxPixelDefectLabelN], StartY_Label[maxPixelDefectLabelN], EndY_Label[maxPixelDefectLabelN], Pixel_N_Of_Label[maxPixelDefectLabelN]);
				}
			}

#ifdef PC_SIM
			memset(ExpectedThreeDDefectArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			if (DefectSW)
			{
				for (m = 0; m < defectPointDataCnt; m++)
				{
					x = defectPointData[m * 3 + 0];
					y = defectPointData[m * 3 + 1];
					tempAddress = MAX_IMAGE_WIDTH * y + x;
					ExpectedThreeDDefectArea[tempAddress] = defectPointData[m * 3 + 2];
				}
			}
#endif
		}

		if (DefectSW == 0)
		{
			defectPointDataCnt = 0;
			for (i = startY; i < endY; i++)
			{
				for (j = startX; j < endX; j++)
				{
					tempAddress = MAX_IMAGE_WIDTH * i + j;

					areaValue = InspectionArea[tempAddress];

					if (areaValue == FRONT_SHAPE_EDGE_NEIGHBOR)
					{
						edgeDefectMaxValue = 0;
						selfPositionValue = AdjustThreeD_Data[tempAddress];

						if (selfPositionValue)
						{
							EdgeNeighborDefectSW = 0;
							r = threeDShellImage[tempAddress] - 1;

							if (r >= 3)
							{
								for (radial = 1; radial < maxRadialRange; radial++)
								{
									areaCheckCount = expectedDefectCount = 0;
									roundCheckResult = 1;
									for (m = 0; m < circleMaskDataCnt[radial]; m++)
									{
										tempX = circleMaskData[radial][m * 2 + 0] + j;
										tempY = circleMaskData[radial][m * 2 + 1] + i;

										tempAddress2 = MAX_IMAGE_WIDTH * tempY + tempX;

										if (InspectionArea[tempAddress2])
										{
											comparePointR = threeDShellImage[tempAddress2] - 1;

											if (comparePointR >= r - 1)
											{
												areaCheckCount++;
												// 현재 위치와 동일 선상에서의 오차를 고려하기 위해 -1 적용
												tempHeight = AdjustThreeD_Data[tempAddress2] - selfPositionValue;
												if (tempHeight >= thresholdEdgeHeightDiff2)
												{
													if (edgeDefectMaxValue < tempHeight)
													{
														edgeDefectMaxValue = tempHeight;
													}

													expectedDefectCount++;
												}
												else
												{
													roundCheckResult = 0;
													break;
												}
											}
										}
									}

									if (roundCheckResult && areaCheckCount == expectedDefectCount)
									{
										EdgeNeighborDefectSW = 1;
										break;
									}
								}
							}

							if (EdgeNeighborDefectSW && defectPointDataCnt < MAX_DEFECT_POINT_COUNT)
							{
								defectPointData[defectPointDataCnt * 3] = j;
								defectPointData[defectPointDataCnt * 3 + 1] = i;
								defectPointData[defectPointDataCnt * 3 + 2] = edgeDefectMaxValue;

								defectPointDataCnt++;
							}
						}
					}
				}
			}

			defectGroupN = DefectLabeling(label_Image, defectPointData, defectPointDataCnt, cameraIndex);

			tMax = 1;
			maxPixelDefectLabelN = -1;
			for (m = 1; m <= defectGroupN; m++)
			{
				if (Pixel_N_Of_Label[m] > tMax)
				{
					tMax = Pixel_N_Of_Label[m];
					maxPixelDefectLabelN = m;
				}
			}
			if (maxPixelDefectLabelN > 0)
			{
				if (Pixel_N_Of_Label[maxPixelDefectLabelN] > TabletGradeData.option_front_edgeBreaking_Size_Type2)
				{
					DefectSW = 1;
					DefectInformationWrite(DEFECT_3D_BREAKING_FOR_EDGE_NEIGHBOR, cameraIndex, StartX_Label[maxPixelDefectLabelN],
										   EndX_Label[maxPixelDefectLabelN], StartY_Label[maxPixelDefectLabelN], EndY_Label[maxPixelDefectLabelN], Pixel_N_Of_Label[maxPixelDefectLabelN]);
				}
			}

#ifdef PC_SIM
			memset(ExpectedThreeDDefectArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
			if (DefectSW)
			{
				for (m = 0; m < defectPointDataCnt; m++)
				{
					x = defectPointData[m * 3 + 0];
					y = defectPointData[m * 3 + 1];
					tempAddress = MAX_IMAGE_WIDTH * y + x;
					ExpectedThreeDDefectArea[tempAddress] = defectPointData[m * 3 + 2];
				}
			}
#endif
		}
	}

#ifdef PC_SIM
	memcpy(LabelImage_Virtual, label_Image, sizeof(short) * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif
}
//---------------------------------------------------------------------------
void CheckTabletShapeForSugarCoated(int cameraIndex)
{
	unsigned char SliceImage[MAX_IMAGE_WIDTH * 256];
	unsigned char ModelingResult[MAX_IMAGE_WIDTH * 256];
	int x, y;
	int tempAddress;
	int startX, endX, startY, endY;
	int areaValue;
	int SelectedX, SelectedY;
	int CheckPointX1, CheckPointY1;
	int CheckPointX2, CheckPointY2;
	int FstCheckSW, SecCheckSW;
	int z;
	int minHeight, maxHeight;
	int diff;
	int tempX, tempY;
	long long slope1, slope2;
	long long cX, cY;
	int distance;
	int radial;
	unsigned char *TabletModelingResult;
	unsigned char *tempTabletModelingResult;
	int minPos;
	int defectPointDataCnt;
	int defectGroupN;
	int tMax;
	int maxPixelDefectLabelN;
	short defectPointData[MAX_DEFECT_POINT_COUNT * 3];
	short *label_Image;
	int heightDiff;
	int defectHeightLevel;
	int defectHeightSize;
	int m;
	int resultCheckLength1, resultCheckLength2;
	const int compareMaxRange = 50;
	const int compareMinRange = 20;
	int minHeightDiff;
	int adjustHeight;
	int z1, z2;
	int tempAddress2;
	int leftLevel, rightLevel;
	int tempLevel;
	int leftSW, rightSW, topSW, bottomSW;
	int tempCount;
	int tempValue;

	label_Image = RotationEdgeImage;
	TabletModelingResult = TempImage;
	tempTabletModelingResult = TempImage2;

	startX = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	memcpy(TempImage, AdjustThreeD_Data, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (TempImage[tempAddress])
			{
				leftSW = rightSW = topSW = bottomSW = 0;
				tempCount = tempValue = 0;
				for (tempY = y - 4; tempY <= y + 4; tempY += 2)
				{
					for (tempX = x - 4; tempX <= x + 4; tempX += 2)
					{
						tempAddress2 = MAX_IMAGE_WIDTH * tempY + tempX;
						if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
						{
							if (TempImage[tempAddress2])
							{
								if (TempImage[tempAddress2] - AdjustThreeD_Data[tempAddress] > 1)
								{
									if (tempX < x)
										leftSW = 1;
									if (tempX > x)
										rightSW = 1;
									if (tempY < y)
										topSW = 1;
									if (tempY > y)
										bottomSW = 1;

									tempValue += TempImage[tempAddress2];
									tempCount++;
								}
							}
						}
					}
				}

				if (leftSW && rightSW && bottomSW && topSW)
				{
					AdjustThreeD_Data[tempAddress] = tempValue / tempCount;
				}
			}
		}
	}

	memset(TabletModelingResult, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	memset(tempTabletModelingResult, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	// y축 scanning
	for (x = startX; x < endX; x += 2)
	{
		memset(SliceImage, 0, sizeof(unsigned char) * MAX_IMAGE_WIDTH * 256);

		minHeight = 256;
		maxHeight = 0;

		for (y = startY; y < endY; y++)
		{
			z = AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + x];

			if (z)
			{
				SliceImage[256 * y + z] = 1;

				if (minHeight >= z)
					minHeight = z;
				if (maxHeight <= z)
					maxHeight = z;
			}
		}

		if (minHeight != 256 && maxHeight != 0)
		{
			memset(ModelingResult, 0, sizeof(unsigned char) * MAX_IMAGE_WIDTH * 256);

			// valid area
			for (y = startY; y < endY; y += 4)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				areaValue = InspectionArea[tempAddress];

				if (areaValue)
				{
					SelectedX = AdjustThreeD_Data[tempAddress];
					SelectedY = y;

					FstCheckSW = SecCheckSW = 0;

					for (tempY = SelectedY - compareMaxRange; tempY <= SelectedY - compareMinRange; tempY += 2)
					{
						for (tempX = minHeight; tempX <= maxHeight; tempX++)
						{
							tempAddress = 256 * tempY + tempX;

							if (tempY > startY && tempY < endY)
							{
								diff = abs(tempX - SelectedX);
								if (SliceImage[tempAddress] && diff)
								{
									CheckPointX1 = tempX;
									CheckPointY1 = tempY;

									FstCheckSW = 1;
									break;
								}
							}
						}

						if (FstCheckSW)
						{
							break;
						}
					}

					if (FstCheckSW)
					{
						for (tempY = SelectedY + compareMaxRange; tempY >= SelectedY + compareMinRange; tempY -= 2)
						{
							for (tempX = minHeight; tempX <= maxHeight; tempX++)
							{
								tempAddress = 256 * tempY + tempX;

								if (tempY > startY && tempY < endY)
								{
									diff = abs(tempX - SelectedX);
									if (SliceImage[tempAddress] && diff)
									{
										CheckPointX2 = tempX;
										CheckPointY2 = tempY;

										SecCheckSW = 1;
										break;
									}
								}
							}

							if (SecCheckSW)
							{
								break;
							}
						}
					}

					if (FstCheckSW && SecCheckSW)
					{
						if (SelectedY - CheckPointY1)
						{
							slope1 = ((SelectedX - CheckPointX1) * 100000) / (SelectedY - CheckPointY1);
						}
						else
						{
							slope1 = 0;
						}

						if (CheckPointY2 - SelectedY)
						{
							slope2 = ((CheckPointX2 - SelectedX) * 100000) / (CheckPointY2 - SelectedY);
						}
						else
						{
							slope2 = 0;
						}

						if (slope2 - slope1)
						{
							cX = ((CheckPointY2 - CheckPointY1) * 100000 + (SelectedX + CheckPointX2) * slope2 - (SelectedX + CheckPointX1) * slope1) * 100 / (2 * (slope2 - slope1));
							cY = (-slope1 * (cX - ((CheckPointX1 + SelectedX) / 2) * 100)) / 10000000 + (SelectedY + CheckPointY1) / 2;

							cX /= 100;

							distance = (CheckPointX1 - cX) * (CheckPointX1 - cX) + (CheckPointY1 - cY) * (CheckPointY1 - cY);

							if (distance < MAX_SHAPE_RADIAL * MAX_SHAPE_RADIAL)
							{
								radial = sqrtTable[distance] / 1000;

								resultCheckLength1 = radial * radial - (CheckPointY1 - cY) * (CheckPointY1 - cY);
								resultCheckLength2 = radial * radial - (CheckPointY2 - cY) * (CheckPointY2 - cY);

								if (resultCheckLength1 <= distance && resultCheckLength2 <= distance)
								{
									for (tempY = CheckPointY1; tempY <= CheckPointY2; tempY++)
									{
										if (tempY > 0 && tempY < MAX_IMAGE_HEIGHT)
										{
											if (radial * radial >= (tempY - cY) * (tempY - cY))
											{
												distance = radial * radial - (tempY - cY) * (tempY - cY);

												tempX = sqrtTable[distance] / 1000 + cX;

												if (tempX > 0 && tempX < 256)
												{
													tempAddress = 256 * tempY + tempX;
													ModelingResult[tempAddress] = 1;
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			for (tempY = startY; tempY < endY; tempY++)
			{
				minPos = 0xFFFF;
				for (tempX = 0; tempX < 256; tempX++)
				{
					tempAddress = 256 * tempY + tempX;

					if (ModelingResult[tempAddress])
					{
						if (minPos > tempX)
						{
							minPos = tempX;
						}
					}
				}

				if (minPos != 0xFFFF)
				{
					z = minPos;

					tempAddress = MAX_IMAGE_WIDTH * tempY + x;
					if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
					{
						TabletModelingResult[tempAddress] = z;
					}
				}
			}
		}
	}

	// shift
	for (y = startY; y < endY; y++)
	{
		adjustHeight = -1;
		for (z = 0; z <= 4; z++)
		{
			minHeightDiff = 0xFFFFFF;
			for (x = startX; x < endX; x += 4)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
				{
					if (TabletModelingResult[tempAddress])
					{
						heightDiff += abs(AdjustThreeD_Data[tempAddress] - (TabletModelingResult[tempAddress] + z));
					}
				}
			}

			if (minHeightDiff >= heightDiff)
			{
				adjustHeight = z;
			}
		}

		if (adjustHeight != -1)
		{
			for (x = startX; x < endX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (TabletModelingResult[tempAddress])
				{
					TabletModelingResult[tempAddress] += adjustHeight;
				}
			}
		}
	}

	// x축 scanning
	for (y = startY; y < endY; y += 2)
	{
		memset(SliceImage, 0, sizeof(unsigned char) * MAX_IMAGE_WIDTH * 256);

		minHeight = 256;
		maxHeight = 0;

		for (x = startX; x < endX; x++)
		{
			z = AdjustThreeD_Data[MAX_IMAGE_WIDTH * y + x];

			if (z)
			{
				SliceImage[MAX_IMAGE_WIDTH * z + x] = 1;

				if (minHeight >= z)
					minHeight = z;
				if (maxHeight <= z)
					maxHeight = z;
			}
		}

		if (minHeight != 256 && maxHeight != 0)
		{
			memset(ModelingResult, 0, sizeof(unsigned char) * MAX_IMAGE_WIDTH * 256);

			// valid area
			for (x = startX; x < endX; x += 4)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				areaValue = InspectionArea[tempAddress];

				if (areaValue)
				{
					SelectedX = x;
					SelectedY = AdjustThreeD_Data[tempAddress];

					FstCheckSW = SecCheckSW = 0;

					for (tempX = SelectedX - compareMaxRange; tempX <= SelectedX - compareMinRange; tempX += 2)
					{
						for (tempY = minHeight; tempY <= maxHeight; tempY++)
						{
							tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;

							if (tempX > startX && tempX < endX)
							{
								diff = abs(tempY - SelectedY);
								if (SliceImage[tempAddress] && diff)
								{
									CheckPointX1 = tempX;
									CheckPointY1 = tempY;

									FstCheckSW = 1;
									break;
								}
							}
						}

						if (FstCheckSW)
						{
							break;
						}
					}

					if (FstCheckSW)
					{
						for (tempX = SelectedX + compareMaxRange; tempX >= SelectedX + compareMinRange; tempX -= 2)
						{
							for (tempY = minHeight; tempY <= maxHeight; tempY++)
							{
								tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;

								if (tempX > startX && tempX < endX)
								{
									diff = abs(tempY - SelectedY);
									if (SliceImage[tempAddress] && diff)
									{
										CheckPointX2 = tempX;
										CheckPointY2 = tempY;

										SecCheckSW = 1;
										break;
									}
								}
							}

							if (SecCheckSW)
							{
								break;
							}
						}
					}

					if (FstCheckSW && SecCheckSW)
					{
						if (SelectedY - CheckPointY1)
						{
							slope1 = ((SelectedX - CheckPointX1) * 100000) / (SelectedY - CheckPointY1);
						}
						else
						{
							slope1 = 0;
						}

						if (CheckPointY2 - SelectedY)
						{
							slope2 = ((CheckPointX2 - SelectedX) * 100000) / (CheckPointY2 - SelectedY);
						}
						else
						{
							slope2 = 0;
						}

						if (slope2 - slope1)
						{
							cX = ((CheckPointY2 - CheckPointY1) * 100000 + (SelectedX + CheckPointX2) * slope2 - (SelectedX + CheckPointX1) * slope1) * 100 / (2 * (slope2 - slope1));
							cY = (-slope1 * (cX - ((CheckPointX1 + SelectedX) / 2) * 100)) / 10000000 + (SelectedY + CheckPointY1) / 2;

							cX /= 100;

							distance = (CheckPointX1 - cX) * (CheckPointX1 - cX) + (CheckPointY1 - cY) * (CheckPointY1 - cY);

							if (distance < MAX_SHAPE_RADIAL * MAX_SHAPE_RADIAL)
							{
								radial = sqrtTable[distance] / 1000;

								resultCheckLength1 = radial * radial - (CheckPointX1 - cX) * (CheckPointX1 - cX);
								resultCheckLength2 = radial * radial - (CheckPointX2 - cX) * (CheckPointX2 - cX);

								if (resultCheckLength1 <= distance && resultCheckLength2 <= distance)
								{
									for (tempX = CheckPointX1; tempX <= CheckPointX2; tempX++)
									{
										if (tempX > 0 && tempX < MAX_IMAGE_WIDTH)
										{
											if (radial * radial >= (tempX - cX) * (tempX - cX))
											{
												distance = radial * radial - (tempX - cX) * (tempX - cX);

												tempY = sqrtTable[distance] / 1000 + cY;

												if (tempY > 0 && tempY < 256)
												{
													tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;
													ModelingResult[tempAddress] = 1;
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}

			for (tempX = startX; tempX < endX; tempX++)
			{
				minPos = 0xFFFF;
				for (tempY = 0; tempY < 256; tempY++)
				{
					tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;

					if (ModelingResult[tempAddress])
					{
						if (minPos > tempY)
						{
							minPos = tempY;
						}
					}
				}

				if (minPos != 0xFFFF)
				{
					z = minPos;

					tempAddress = MAX_IMAGE_WIDTH * y + tempX;
					if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
					{
						tempTabletModelingResult[tempAddress] = z;
					}
				}
			}
		}
	}

	// shift
	for (x = startX; x < endX; x++)
	{
		adjustHeight = -1;
		for (z = 0; z <= 4; z++)
		{
			minHeightDiff = 0xFFFFFF;
			for (y = startY; y < endY; y += 4)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (InspectionArea[tempAddress])
				{
					if (tempTabletModelingResult[tempAddress])
					{
						heightDiff += abs(AdjustThreeD_Data[tempAddress] - (tempTabletModelingResult[tempAddress] + z));
					}
				}
			}

			if (minHeightDiff >= heightDiff)
			{
				adjustHeight = z;
			}
		}

		if (adjustHeight != -1)
		{
			for (y = startY; y < endY; y++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (tempTabletModelingResult[tempAddress])
				{
					tempTabletModelingResult[tempAddress] += adjustHeight;
				}
			}
		}
	}

	// average
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
			{
				z1 = TabletModelingResult[tempAddress];
				z2 = tempTabletModelingResult[tempAddress];

				if (z1 && z2)
				{
					TabletModelingResult[tempAddress] = (z1 + z2) / 2;
				}
				else
				{
					TabletModelingResult[tempAddress] = 0;
				}
			}
		}
	}

	defectPointDataCnt = 0;
	defectHeightLevel = TabletGradeData.ThreeD_CoatedTablet_ShapeError_Height / 10;
	defectHeightSize = TabletGradeData.ThreeD_CoatedTablet_ShapeError_Size;

	// 3D line scan 각도에 의한 영향인지(디스크 회전방향으로 정제가 기울어지는게 원인을 수 있음)는 모르겠으나 정제의 센터기준 우측 영역이 높이차에 보다 민감하게 반응함
	// 장비마다 결과가 다를수도있음.. 일단 테스트 결과에 따라 등급값에 가중치적용함
	// 사이트 적용 후 결과에 따라 수정되어야할 수 있음
	// 우선 모양에 관계없이 동일한 방식적용

	if (TabletCharacter.shape == ROUND)
	{
		leftLevel = defectHeightLevel;
		rightLevel = defectHeightLevel + 1;
	}
	else
	{
		leftLevel = defectHeightLevel;
		rightLevel = defectHeightLevel + 1;
	}

	// defect check
	for (y = startY; y < endY; y += 2)
	{
		for (x = startX; x < endX; x += 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
			{
				if (TabletModelingResult[tempAddress])
				{
					heightDiff = AdjustThreeD_Data[tempAddress] - TabletModelingResult[tempAddress];

					if (x > ProtoTabletCenterX)
					{
						tempLevel = rightLevel;
					}
					else
					{
						tempLevel = leftLevel;
					}

					if (heightDiff >= tempLevel)
					{
						for (tempY = y; tempY <= y + 1; tempY++)
						{
							for (tempX = x; tempX <= x + 1; tempX++)
							{
								tempAddress2 = MAX_IMAGE_WIDTH * tempY + tempX;
								if (InspectionArea[tempAddress2] == NORMAL_INPECTION_AREA && defectPointDataCnt < MAX_DEFECT_POINT_COUNT)
								{
									defectPointData[defectPointDataCnt * 3 + 0] = tempX;
									defectPointData[defectPointDataCnt * 3 + 1] = tempY;
									defectPointData[defectPointDataCnt * 3 + 2] = heightDiff;
									defectPointDataCnt++;
								}
							}
						}
					}
				}
			}
		}
	}

	memset(label_Image, 0, sizeof(short) * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	defectGroupN = DefectLabeling(label_Image, defectPointData, defectPointDataCnt, cameraIndex);

	tMax = 1;
	maxPixelDefectLabelN = -1;
	for (m = 1; m <= defectGroupN; m++)
	{
		if (Pixel_N_Of_Label[m] > tMax)
		{
			tMax = Pixel_N_Of_Label[m];
			maxPixelDefectLabelN = m;
		}
	}
	if (maxPixelDefectLabelN > 0)
	{
		if (Pixel_N_Of_Label[maxPixelDefectLabelN] > defectHeightSize)
		{
			DefectSW = 1;
			DefectInformationWrite(DEFECT_3D_SUGAR_SHAPE_DEFORMATION, cameraIndex, StartX_Label[maxPixelDefectLabelN],
								   EndX_Label[maxPixelDefectLabelN], StartY_Label[maxPixelDefectLabelN], EndY_Label[maxPixelDefectLabelN], Pixel_N_Of_Label[maxPixelDefectLabelN]);
		}
	}

#ifdef PC_SIM
	memset(ExpectedThreeDDefectArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	if (DefectSW)
	{
		for (m = 0; m < defectPointDataCnt; m++)
		{
			x = defectPointData[m * 3 + 0];
			y = defectPointData[m * 3 + 1];
			tempAddress = MAX_IMAGE_WIDTH * y + x;
			ExpectedThreeDDefectArea[tempAddress] = defectPointData[m * 3 + 2];
		}
	}

	memcpy(LabelImage_Virtual, label_Image, sizeof(short) * MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif
}
//---------------------------------------------------------------------------