첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

■ 다각형 처리하기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip


Triangle.cs

 

 

using System.Drawing;

 

namespace TestProject

{

    /// <summary>

    /// 삼각형

    /// </summary>

    public class Triangle : Polygon

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - Triangle(p1, p2, p3)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="p1">포인트 1</param>

        /// <param name="p2">포인트 2</param>

        /// <param name="p3">포인트 3</param>

        public Triangle(PointF p1, PointF p2, PointF p3)

        {

            PointArray = new PointF[] { p1, p2, p3 };

        }

 

        #endregion

    }

}

 

 

Polygon.cs

 

 

using System;

using System.Collections.Generic;

using System.Diagnostics;

using System.Drawing;

 

namespace TestProject

{

    /// <summary>

    /// 다각형

    /// </summary>

    public class Polygon

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Field

 

        /// <summary>

        /// 포인트 배열

        /// </summary>

        public PointF[] PointArray;

 

 

        /// <summary>

        /// 포인트 카운트

        /// </summary>

        private int pointCount = 0;

 

        /// <summary>

        /// 컨트롤 포인트 배열

        /// </summary>

        private int[] controlPointArray = new int[4];

 

        /// <summary>

        /// 엣지 체크 배열

        /// </summary>

        private bool[] edgeCheckArray;

 

        /// <summary>

        /// 현재 컨트롤 포인트

        /// </summary>

        private int currentControlPoint = -1;

 

        /// <summary>

        /// 현재 영역 1

        /// </summary>

        private float currentArea1 = float.MaxValue;

 

        /// <summary>

        /// 현재 영역 2

        /// </summary>

        private float currentArea2 = float.MaxValue;

 

        /// <summary>

        /// 현재 사각형 포인트 배열 1

        /// </summary>

        private PointF[] currentRectanglePointArray1 = null;

 

        /// <summary>

        /// 현재 사각형 포인트 배열 2

        /// </summary>

        private PointF[] currentRectanglePointArray2 = null;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - Polygon()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public Polygon()

        {

        }

 

        #endregion

        #region 생성자 - Polygon(pointArray)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="pointArray">포인트 배열</param>

        public Polygon(PointF[] pointArray)

        {

            PointArray = pointArray;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 다각형 면적 구하기 - GetPolygonArea()

 

        /// <summary>

        /// 다각형 면적 구하기

        /// </summary>

        /// <returns>다각형 면적</returns>

        public float GetPolygonArea()

        {

            return Math.Abs(GetSignedPolygonArea());

        }

 

        #endregion

        #region 중심점 구하기 - GetCentroid()

 

        /// <summary>

        /// 중심점 구하기

        /// </summary>

        /// <returns>중심점</returns>

        public PointF GetCentroid()

        {

            int pointCount = PointArray.Length;

 

            PointF[] temporaryArray = new PointF[pointCount + 1];

 

            PointArray.CopyTo(temporaryArray, 0);

 

            temporaryArray[pointCount] = PointArray[0];

 

            float x = 0;

            float y = 0;

            float secondFactor;

 

            for(int i = 0; i < pointCount; i++)

            {

                secondFactor = temporaryArray[i].X * temporaryArray[i + 1].Y -

                               temporaryArray[i + 1].X * temporaryArray[i].Y;

 

                x += (temporaryArray[i].X + temporaryArray[i + 1].X) * secondFactor;

                y += (temporaryArray[i].Y + temporaryArray[i + 1].Y) * secondFactor;

            }

 

            float polygonArea = GetPolygonArea();

 

            x /= (6 * polygonArea);

            y /= (6 * polygonArea);

 

            if(x < 0)

            {

                x = -x;

                y = -y;

            }

 

            return new PointF(x, y);

        }

 

        #endregion

        #region 포인트 포함 여부 구하기 - IncludePoint(x, y)

 

        /// <summary>

        /// 포인트 포함 여부 구하기

        /// </summary>

        /// <param name="x">X 좌표</param>

        /// <param name="y">Y 좌표</param>

        /// <returns>포인트 포함 여부</returns>

        public bool IncludePoint(float x, float y)

        {

            int maximumPoint = PointArray.Length - 1;

 

            float totalAngle = GetAngle

            (

                PointArray[maximumPoint].X,

                PointArray[maximumPoint].Y,

                x,

                y,

                PointArray[0].X,

                PointArray[0].Y

            );

 

            for(int i = 0; i < maximumPoint; i++)

            {

                totalAngle += GetAngle

                (

                    PointArray[i].X,

                    PointArray[i].Y,

                    x,

                    y,

                    PointArray[i + 1].X,

                    PointArray[i + 1].Y

                );

            }

 

            return (Math.Abs(totalAngle) > 0.000001);

        }

 

        #endregion

        #region 시계 방향 여부 구하기 - IsOrientedClockwise()

 

        /// <summary>

        /// 시계 방향 여부 구하기

        /// </summary>

        /// <returns>시계 방향 여부</returns>

        public bool IsOrientedClockwise()

        {

            return (GetSignedPolygonArea() < 0);

        }

 

        #endregion

        #region 볼록 여부 구하기 - IsConvex()

 

        /// <summary>

        /// 볼록 여부 구하기

        /// </summary>

        /// <returns>볼록 여부</returns>

        public bool IsConvex()

        {

            bool isNegative = false;

            bool isPositive = false;

            int  pointCount = PointArray.Length;

            int  b;

            int  c;

 

            for(int a = 0; a < pointCount; a++)

            {

                b = (a + 1) % pointCount;

                c = (b + 1) % pointCount;

 

                float crossProduct = GetCrossProduct

                (

                    PointArray[a].X,

                    PointArray[a].Y,

                    PointArray[b].X,

                    PointArray[b].Y,

                    PointArray[c].X,

                    PointArray[c].Y

                );

 

                if(crossProduct < 0)

                {

                    isNegative = true;

                }

                else if(crossProduct > 0)

                {

                    isPositive = true;

                }

 

                if(isNegative && isPositive)

                {

                    return false;

                }

            }

 

            return true;

        }

 

        #endregion

        #region 삼각형 리스트 구하기 - GetTriangleList()

 

        /// <summary>

        /// 삼각형 리스트 구하기

        /// </summary>

        /// <returns>삼각형 리스트</returns>

        public List<Triangle> GetTriangleList()

        {

            PointF[] temporaryArray = new PointF[PointArray.Length];

 

            Array.Copy(PointArray, temporaryArray, PointArray.Length);

 

            Polygon polygon = new Polygon(temporaryArray);

 

            polygon.OrientPolygonClockwise();

 

            List<Triangle> triangleList = new List<Triangle>();

 

            while(polygon.PointArray.Length > 3)

            {

                polygon.RemoveEar(triangleList);

            }

 

            triangleList.Add

            (

                new Triangle

                (

                    polygon.PointArray[0],

                    polygon.PointArray[1],

                    polygon.PointArray[2]

                )

            );

 

            return triangleList;

        }

 

        #endregion

        #region 가장 작은 테두리 사각형 구하기 - GetSmallestBoundingRectangle()

 

        /// <summary>

        /// 가장 작은 테두리 사각형 구하기

        /// </summary>

        /// <returns>가장 작은 테두리 사각형 포인트 배열</returns>

        public PointF[] GetSmallestBoundingRectangle()

        {

            Debug.Assert(!IsOrientedClockwise());

 

            ResetBoundingRectangle();

 

            for(int i = 0; i < PointArray.Length; i++)

            {

                CheckNextRectangle();

            }

 

            return this.currentRectanglePointArray2;

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region 부호 있는 다각형 영역 구하기 - GetSignedPolygonArea()

 

        /// <summary>

        /// 부호 있는 다각형 영역 구하기

        /// </summary>

        /// <returns>부호 있는 다각형 영역</returns>

        /// <remarks>다각형이 시계 방향으로 향한 경우 음수값을 반환한다.</remarks>

        private float GetSignedPolygonArea()

        {

            int pointCount = PointArray.Length;

 

            PointF[] temporaryArray = new PointF[pointCount + 1];

 

            PointArray.CopyTo(temporaryArray, 0);

 

            temporaryArray[pointCount] = PointArray[0];

 

            float area = 0;

 

            for(int i = 0; i < pointCount; i++)

            {

                area += (temporaryArray[i + 1].X - temporaryArray[i].X) * (temporaryArray[i + 1].Y + temporaryArray[i].Y) / 2;

            }

 

            return area;

        }

 

        #endregion

        #region 내적 구하기 - GetDotProduct(aX, aY, bX, bY, cX, cY)

 

        /// <summary>

        /// 내적 구하기

        /// </summary>

        /// <param name="aX">A 포인트 X 좌표</param>

        /// <param name="aY">A 포인트 Y 좌표</param>

        /// <param name="bX">B 포인트 X 좌표</param>

        /// <param name="bY">B 포인트 Y 좌표</param>

        /// <param name="cX">C 포인트 X 좌표</param>

        /// <param name="cY">C 포인트 Y 좌표</param>

        /// <returns>내적</returns>

        private static float GetDotProduct(float aX, float aY, float bX, float bY, float cX, float cY)

        {

            float baX = aX - bX;

            float baY = aY - bY;

            float bcX = cX - bX;

            float bcY = cY - bY;

 

            return (baX * bcX + baY * bcY);

        }

 

        #endregion

        #region 외적 구하기 - GetCrossProduct(aX, aY, bX, bY, cX, cY)

 

        /// <summary>

        /// 외적 구하기

        /// </summary>

        /// <param name="aX">A 포인트 X 좌표</param>

        /// <param name="aY">A 포인트 Y 좌표</param>

        /// <param name="bX">B 포인트 X 좌표</param>

        /// <param name="bY">B 포인트 Y 좌표</param>

        /// <param name="cX">C 포인트 X 좌표</param>

        /// <param name="cY">C 포인트 Y 좌표</param>

        /// <returns>외적</returns>

        public static float GetCrossProduct(float aX, float aY, float bX, float bY, float cX, float cY)

        {

            float baX = aX - bX;

            float baY = aY - bY;

            float bcX = cX - bX;

            float bcY = cY - bY;

 

            return (baX * bcY - baY * bcX);

        }

 

        #endregion

        #region 각도 구하기 - GetAngle(aX, aY, bX, bY, cX, cY)

 

        /// <summary>

        /// 각도 구하기

        /// </summary>

        /// <param name="aX">A 포인트 X 좌표</param>

        /// <param name="aY">A 포인트 Y 좌표</param>

        /// <param name="bX">B 포인트 X 좌표</param>

        /// <param name="bY">B 포인트 Y 좌표</param>

        /// <param name="cX">C 포인트 X 좌표</param>

        /// <param name="cY">C 포인트 Y 좌표</param>

        /// <returns>각도</returns>

        public static float GetAngle(float aX, float aY, float bX, float bY, float cX, float cY)

        {

            float dotProduct = GetDotProduct(aX, aY, bX, bY, cX, cY);

 

            float crossProduct = GetCrossProduct(aX, aY, bX, bY, cX, cY);

 

            return (float)Math.Atan2(crossProduct, dotProduct);

        }

 

        #endregion

        #region 3개의 점이 귀의 형태를 구성하는지 여부 구하기 - FormsEar(pointArray, a, b, c)

 

        /// <summary>

        /// 3개의 점이 귀의 형태를 구성하는지 여부 구하기

        /// </summary>

        /// <param name="pointArray">포인트 배열</param>

        /// <param name="a">A 포인트 인덱스</param>

        /// <param name="b">B 포인트 인덱스</param>

        /// <param name="c">C 포인트 인덱스</param>

        /// <returns>3개의 점이 귀의 형태를 구성하는지 여부</returns>

        private static bool FormsEar(PointF[] pointArray, int a, int b, int c)

        {

            float angle = GetAngle

            (

                pointArray[a].X,

                pointArray[a].Y,

                pointArray[b].X,

                pointArray[b].Y,

                pointArray[c].X,

                pointArray[c].Y

            );

 

            if(angle > 0)

            {

                return false;

            }

 

            Triangle triangle = new Triangle(pointArray[a], pointArray[b], pointArray[c]);

 

            for(int i = 0; i < pointArray.Length; i++)

            {

                if((i != a) && (i != b) && (i != c))

                {

                    if(triangle.IncludePoint(pointArray[i].X, pointArray[i].Y))

                    {

                        return false;

                    }

                }

            }

 

            return true;

        }

 

        #endregion

        #region 귀를 구성하는 3개의 점의 인덱스 찾기 - FindEar(a, b, c)

 

        /// <summary>

        /// 귀를 구성하는 3개의 점의 인덱스 찾기

        /// </summary>

        /// <param name="a">A 포인트 인덱스</param>

        /// <param name="b">B 포인트 인덱스</param>

        /// <param name="c">C 포인트 인덱스</param>

        private void FindEar(ref int a, ref int b, ref int c)

        {

            int pointCount = PointArray.Length;

 

            for(a = 0; a < pointCount; a++)

            {

                b = (a + 1) % pointCount;

                c = (b + 1) % pointCount;

 

                if(FormsEar(PointArray, a, b, c))

                {

                    return;

                }

            }

 

            Debug.Assert(false);

        }

 

        #endregion

        #region 배열에서 포인트 제거하기 - RemovePointFromArray(target)

 

        /// <summary>

        /// 배열에서 포인트 제거하기

        /// </summary>

        /// <param name="target">타겟 인덱스</param>

        private void RemovePointFromArray(int target)

        {

            PointF[] temporaryArray = new PointF[PointArray.Length - 1];

 

            Array.Copy(PointArray, 0, temporaryArray, 0, target);

 

            Array.Copy(PointArray, target + 1, temporaryArray, target, PointArray.Length - target - 1);

 

            PointArray = temporaryArray;

        }

 

        #endregion

        #region 다각형에서 귀를 제거하고 삼각형 리스트 추가하기 - RemoveEar(triangleList)

 

        /// <summary>

        /// 다각형에서 귀를 제거하고 삼각형 리스트 추가하기

        /// </summary>

        /// <param name="triangleList">삼각형 리스트</param>

        private void RemoveEar(List<Triangle> triangleList)

        {

            int a = 0;

            int b = 0;

            int c = 0;

 

            FindEar(ref a, ref b, ref c);

 

            triangleList.Add(new Triangle(PointArray[a], PointArray[b], PointArray[c]));

 

            RemovePointFromArray(b);

        }

 

        #endregion

        #region 초기 컨트롤 포인트 배열 체크하기 - CheckInitialControlPointArray(index)

 

        /// <summary>

        /// 초기 컨트롤 포인트 배열 체크하기

        /// </summary>

        /// <param name="index">인덱스</param>

        /// <returns>체크 결과</returns>

        private bool CheckInitialControlPointArray(int index)

        {

            int nextIndex = (index + 1) % this.pointCount;

 

            float xGap1 = PointArray[nextIndex].X - PointArray[index].X;

            float yGap1 = PointArray[nextIndex].Y - PointArray[index].Y;

 

            for(int i = 0; i < 4; i++)

            {

                this.controlPointArray[i] = index;

            }

 

            for(int i = 1; i < pointCount; i++)

            {

                int j     = (index - i + pointCount) % pointCount;

                int nextJ = (j + 1) % pointCount;

 

                float xGap2 = PointArray[nextJ].X - PointArray[j].X;

                float yGap2 = PointArray[nextJ].Y - PointArray[j].Y;

 

                float dotProduct = xGap1 * xGap2 + yGap1 * yGap2;

 

                if(dotProduct < 0)

                {

                    this.controlPointArray[0] = nextJ;

 

                    break;

                }

            }

 

            if(this.controlPointArray[0] == index)

            {

                return false;

            }

 

            for(int i = 1; i < pointCount; i++)

            {

                int j     = (index + i) % pointCount;

                int nextJ = (j + 1) % pointCount;

 

                float xGap3 = PointArray[nextJ].X - PointArray[j].X;

                float yGap3 = PointArray[nextJ].Y - PointArray[j].Y;

 

                float dotProduct = xGap1 * xGap3 + yGap1 * yGap3;

 

                if(dotProduct <= 0)

                {

                    this.controlPointArray[2] = j;

 

                    break;

                }

            }

 

            if(this.controlPointArray[2] == index)

            {

                return false;

            }

 

            index = this.controlPointArray[2] - 1;

 

            float temporary = xGap1;

 

            xGap1 = yGap1;

            yGap1 = -temporary;

 

            for(int i = 1; i < pointCount; i++)

            {

                int j     = (index + i) % pointCount;

                int nextJ = (j + 1) % pointCount;

 

                float xGap4 = PointArray[nextJ].X - PointArray[j].X;

                float yGap4 = PointArray[nextJ].Y - PointArray[j].Y;

 

                float dotProduct = xGap1 * xGap4 + yGap1 * yGap4;

 

                if(dotProduct <= 0)

                {

                    this.controlPointArray[3] = j;

 

                    break;

                }

            }

 

            if(this.controlPointArray[0] == index)

            {

                return false;

            }

 

            return true;

        }

 

        #endregion

        #region 초기 컨트롤 포인트 배열 찾기 - FindInitialControlPointArray()

 

        /// <summary>

        /// 초기 컨트롤 포인트 배열 찾기

        /// </summary>

        private void FindInitialControlPointArray()

        {

            for(int i = 0; i < pointCount; i++)

            {

                if(CheckInitialControlPointArray(i))

                {

                    return;

                }

            }

 

            Debug.Assert(false, "초기 컨트롤 포인트 배열을 찾을 수 없습니다.");

        }

 

        #endregion

        #region  다각형을 시계 방향으로 만들기 - OrientPolygonClockwise()

 

        /// <summary>

        /// 다각형을 시계 방향으로 만들기

        /// </summary>

        private void OrientPolygonClockwise()

        {

            if(!IsOrientedClockwise())

            {

                Array.Reverse(PointArray);

            }

        }

 

        #endregion

        #region 교차점 구하기 - FindIntersection(x1, y1, x2, y2, a1, b1, a2, b2, intersect)

 

        /// <summary>

        /// 교차점 구하기

        /// </summary>

        /// <param name="x1">X1</param>

        /// <param name="y1">Y1</param>

        /// <param name="x2">X2</param>

        /// <param name="y2">Y2</param>

        /// <param name="a1">A1</param>

        /// <param name="b1">B1</param>

        /// <param name="a2">A2</param>

        /// <param name="b2">B2</param>

        /// <param name="intersect">교차점</param>

        /// <returns>처리 결과</returns>

        private bool FindIntersection(float x1, float y1, float x2, float y2, float a1, float b1, float a2, float b2, ref PointF intersect)

        {

            float deltaX = x2 - x1;

            float deltaY = y2 - y1;

            float deltaA = a2 - a1;

            float deltaB = b2 - b1;

            float s;

            float t;

 

            if(Math.Abs(deltaA * deltaY - deltaB * deltaX) < 0.001)

            {

                return false;

            }

 

            s = (deltaX * (b1 - y1) + deltaY * (x1 - a1)) / (deltaA * deltaY - deltaB * deltaX);

            t = (deltaA * (y1 - b1) + deltaB * (a1 - x1)) / (deltaB * deltaX - deltaA * deltaY);

 

            intersect = new PointF(x1 + t * deltaX, y1 + t * deltaY);

 

            return true;

        }

 

        #endregion

        #region 테두리 사각형 찾기 - FindBoundingRectangle()

 

        /// <summary>

        /// 테두리 사각형 찾기

        /// </summary>

        private void FindBoundingRectangle()

        {

            int index1 = this.controlPointArray[this.currentControlPoint];

            int index2 = (index1 + 1) % this.pointCount;

 

            float deltaX = PointArray[index2].X - PointArray[index1].X;

            float deltaY = PointArray[index2].Y - PointArray[index1].Y;

 

            switch(this.currentControlPoint)

            {

                case 0 :

 

                    break;

 

                case 1 :

 

                    float temporary1 = deltaX;

 

                    deltaX = -deltaY;

                    deltaY = temporary1;

 

                    break;

 

                case 2 :

 

                    deltaX = -deltaX;

                    deltaY = -deltaY;

 

                    break;

 

                case 3 :

 

                    float temporary2 = deltaX;

 

                    deltaX = deltaY;

                    deltaY = -temporary2;

 

                    break;

            }

 

            float px0 = PointArray[this.controlPointArray[0]].X;

            float py0 = PointArray[this.controlPointArray[0]].Y;

            float dx0 = deltaX;

            float dy0 = deltaY;

            float px1 = PointArray[this.controlPointArray[1]].X;

            float py1 = PointArray[this.controlPointArray[1]].Y;

            float dx1 = deltaY;

            float dy1 = -deltaX;

            float px2 = PointArray[this.controlPointArray[2]].X;

            float py2 = PointArray[this.controlPointArray[2]].Y;

            float dx2 = -deltaX;

            float dy2 = -deltaY;

            float px3 = PointArray[this.controlPointArray[3]].X;

            float py3 = PointArray[this.controlPointArray[3]].Y;

            float dx3 = -deltaY;

            float dy3 = deltaX;

 

            this.currentRectanglePointArray1 = new PointF[4];

 

            FindIntersection(px0, py0, px0 + dx0, py0 + dy0, px1, py1, px1 + dx1, py1 + dy1, ref this.currentRectanglePointArray1[0]);

            FindIntersection(px1, py1, px1 + dx1, py1 + dy1, px2, py2, px2 + dx2, py2 + dy2, ref this.currentRectanglePointArray1[1]);

            FindIntersection(px2, py2, px2 + dx2, py2 + dy2, px3, py3, px3 + dx3, py3 + dy3, ref this.currentRectanglePointArray1[2]);

            FindIntersection(px3, py3, px3 + dx3, py3 + dy3, px0, py0, px0 + dx0, py0 + dy0, ref this.currentRectanglePointArray1[3]);

 

            float xGap1   = this.currentRectanglePointArray1[0].X - this.currentRectanglePointArray1[1].X;

            float yGap1   = this.currentRectanglePointArray1[0].Y - this.currentRectanglePointArray1[1].Y;

            float length1 = (float)Math.Sqrt(xGap1 * xGap1 + yGap1 * yGap1);

 

            float xGap2   = this.currentRectanglePointArray1[1].X - this.currentRectanglePointArray1[2].X;

            float yGap2   = this.currentRectanglePointArray1[1].Y - this.currentRectanglePointArray1[2].Y;

            float length2 = (float)Math.Sqrt(xGap2 * xGap2 + yGap2 * yGap2);

 

            this.currentArea2 = length1 * length2;

 

            if(this.currentArea2 < this.currentArea1)

            {

                this.currentArea1 = this.currentArea2;

 

                this.currentRectanglePointArray2 = this.currentRectanglePointArray1;

            }

        }

 

        #endregion

        #region 엣지 기울시 찾기 - FindEdgeSlope(deltaX, deltaY, index)

 

        /// <summary>

        /// 엣지 기울시 찾기

        /// </summary>

        /// <param name="deltaX">델타 X</param>

        /// <param name="deltaY">델타 Y</param>

        /// <param name="index">인덱스</param>

        private void FindEdgeSlope(out float deltaX, out float deltaY, int index)

        {

            int nextIndex = (index + 1) % pointCount;

 

            deltaX = PointArray[nextIndex].X - PointArray[index].X;

            deltaY = PointArray[nextIndex].Y - PointArray[index].Y;

        }

 

        #endregion

        #region 다음 사각형 체크하기 - CheckNextRectangle()

 

        /// <summary>

        /// 다음 사각형 체크하기

        /// </summary>

        private void CheckNextRectangle()

        {

            if(this.currentControlPoint >= 0)

            {

                this.controlPointArray[this.currentControlPoint] = (this.controlPointArray[this.currentControlPoint] + 1) % this.pointCount;

            }

 

            float deltaX0;

            float deltaY0;

            float deltaX1;

            float deltaY1;

            float deltaX2;

            float deltaY2;

            float deltaX3;

            float deltaY3;

 

            FindEdgeSlope(out deltaX0, out deltaY0, this.controlPointArray[0]);

            FindEdgeSlope(out deltaX1, out deltaY1, this.controlPointArray[1]);

            FindEdgeSlope(out deltaX2, out deltaY2, this.controlPointArray[2]);

            FindEdgeSlope(out deltaX3, out deltaY3, this.controlPointArray[3]);

 

            float opposite0 =  deltaX0;

            float adjust0   =  deltaY0;

            float opposite1 = -deltaY1;

            float adjust1   =  deltaX1;

            float opposite2 = -deltaX2;

            float adjust2   = -deltaY2;

            float opposite3 =  deltaY3;

            float adjust3   = -deltaX3;

 

            float currentOpposite = opposite0;

            float currentAdjust   = adjust0;

 

            int currentControlPoint = 0;

 

            if(opposite1 * currentAdjust < currentOpposite * adjust1)

            {

                currentOpposite = opposite1;

                currentAdjust   = adjust1;

 

                currentControlPoint = 1;

            }

 

            if(opposite2 * currentAdjust < currentOpposite * adjust2)

            {

                currentOpposite = opposite2;

                currentAdjust   = adjust2;

 

                currentControlPoint = 2;

            }

 

            if(opposite3 * currentAdjust < currentOpposite * adjust3)

            {

                currentOpposite = opposite3;

                currentAdjust   = adjust3;

 

                currentControlPoint = 3;

            }

 

            this.currentControlPoint = currentControlPoint;

 

            this.edgeCheckArray[this.controlPointArray[this.currentControlPoint]] = true;

 

            FindBoundingRectangle();

        }

 

        #endregion

        #region 테두리 사각형 리셋하기 - ResetBoundingRectangle()

 

        /// <summary>

        /// 테두리 사각형 리셋하기

        /// </summary>

        private void ResetBoundingRectangle()

        {

            this.pointCount = PointArray.Length;

 

            FindInitialControlPointArray();

 

            this.edgeCheckArray = new bool[pointCount];

 

            this.currentControlPoint = 1;

 

            this.currentArea1 = float.MaxValue;

 

            FindBoundingRectangle();

 

            this.edgeCheckArray[this.controlPointArray[this.currentControlPoint]] = true;

        }

 

        #endregion

    }

}

 

 

MainForm.cs

 

 

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// PT_RAD

        /// </summary>

        private const int PT_RAD = 2;

 

        /// <summary>

        /// PT_WID

        /// </summary>

        private const int PT_WID = PT_RAD * 2 + 1;

 

        /// <summary>

        /// 포인트 리스트

        /// </summary>

        private List<PointF> pointList = new List<PointF>();

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            MouseClick                           += Form_MouseClick;

            Paint                                += Form_Paint;

            this.clearMenuItem.Click             += clearMenuItem_Click;

            this.convexMenuItem.Click            += convexMenuItem_Click;

            this.includePointMenuItem.Click      += includePointMenuItem_Click;

            this.areaMenuItem.Click              += areaMenuItem_Click;

            this.centroidMenuItem.Click          += centroidMenuItem_Click;

            this.orientationMenuItem.Click       += orientationMenuItem_Click;

            this.reverseMenuItem.Click           += reverseMenuItem_Click;

            this.triangulateMenuItem.Click       += triangulateMenuItem_Click;

            this.boundingRectangleMenuItem.Click += boundingRectangleMenuItem_Click;

 

            #endregion

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 폼 마우스 클릭시 처리하기 - Form_MouseClick(sender, e)

 

        /// <summary>

        /// 폼 마우스 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void Form_MouseClick(object sender, MouseEventArgs e)

        {

            this.pointList.Add(new PointF(e.X, e.Y));

 

            Invalidate();

        }

 

        #endregion

        #region 폼 페인트시 처리하기 - Form_Paint(sender, e)

 

        /// <summary>

        /// 폼 페인트시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void Form_Paint(object sender, PaintEventArgs e)

        {

            e.Graphics.Clear(BackColor);

 

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

 

            if(this.pointList.Count >= 3)

            {

                e.Graphics.DrawPolygon(Pens.Blue, this.pointList.ToArray());

            }

            else if(this.pointList.Count == 2)

            {

                e.Graphics.DrawLines(Pens.Blue, this.pointList.ToArray());

            }

 

            if(this.pointList.Count > 0)

            {

                foreach(PointF point in this.pointList)

                {

                    e.Graphics.FillRectangle(Brushes.White, point.X - PT_RAD, point.Y - PT_RAD, PT_WID, PT_WID);

 

                    e.Graphics.DrawRectangle(Pens.Black, point.X - PT_RAD, point.Y - PT_RAD, PT_WID, PT_WID);

                }

            }

 

            EnableMenu();

        }

 

        #endregion

        #region 지우기 메뉴 항목 클릭시 처리하기 - clearMenuItem_Click(sender, e)

 

        /// <summary>

        /// 지우기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void clearMenuItem_Click(object sender, EventArgs e)

        {

            this.pointList = new List<PointF>();

 

            EnableMenu();

 

            Invalidate();

        }

 

        #endregion

        #region 볼록 여부 구하기 메뉴 항목 클릭시 처리하기 - convexMenuItem_Click(sender, e)

 

        /// <summary>

        /// 볼록 여부 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void convexMenuItem_Click(object sender, EventArgs e)

        {

            Polygon polygon = new Polygon(this.pointList.ToArray());

 

            if(polygon.IsConvex())

            {

                MessageBox.Show("다각형이 볼록입니다.", "확인", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }

            else

            {

                MessageBox.Show("다각형이 볼록이 아닙니다.", "확인", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }

        }

 

        #endregion

        #region 포인트 포함 여부 구하기 메뉴 항목 클릭시 처리하기 - includePointMenuItem_Click(sender, e)

 

        /// <summary>

        /// 포인트 포함 여부 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void includePointMenuItem_Click(object sender, EventArgs e)

        {

            Point point = Cursor.Position;

 

            point = PointToClient(point);

 

            Rectangle rectangle = new Rectangle(point.X - 3, point.Y - 3, 7, 7);

 

            using(Graphics graphics = CreateGraphics())

            {

                Polygon polygon = new Polygon(this.pointList.ToArray());

 

                if(polygon.IncludePoint(point.X, point.Y))

                {

                    graphics.FillEllipse(Brushes.Lime, rectangle);

                }

                else

                {

                    graphics.FillEllipse(Brushes.Red, rectangle);

                }

 

                graphics.DrawEllipse(Pens.Black, rectangle);

            }

        }

 

        #endregion

        #region 면적 구하기 메뉴 항목 클릭시 처리하기 - areaMenuItem_Click(sender, e)

 

        /// <summary>

        /// 면적 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void areaMenuItem_Click(object sender, EventArgs e)

        {

            Polygon polygon = new Polygon(this.pointList.ToArray());

 

            MessageBox.Show("면적 : " + polygon.GetPolygonArea().ToString(), "확인", MessageBoxButtons.OK, MessageBoxIcon.Information);

        }

 

        #endregion

        #region 중심점 구하기 메뉴 항목 클릭시 처리하기 - centroidMenuItem_Click(sender, e)

 

        /// <summary>

        /// 중심점 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void centroidMenuItem_Click(object sender, EventArgs e)

        {

            Polygon polygon = new Polygon(this.pointList.ToArray());

 

            PointF point = polygon.GetCentroid();

 

            Rectangle rectangle = new Rectangle((int)point.X - 3, (int)point.Y - 3, 7, 7);

 

            using(Graphics graphics = CreateGraphics())

            {

                graphics.FillEllipse(Brushes.Yellow, rectangle);

 

                graphics.DrawEllipse(Pens.Black, rectangle);

            }

        }

 

        #endregion

        #region 방향 구하기 메뉴 항목 클릭시 처리하기 - orientationMenuItem_Click(sender, e)

 

        /// <summary>

        /// 방향 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void orientationMenuItem_Click(object sender, EventArgs e)

        {

            Polygon polygon = new Polygon(this.pointList.ToArray());

 

            if(polygon.IsOrientedClockwise())

            {

                MessageBox.Show("시계 방향", "확인", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }

            else

            {

                MessageBox.Show("시계 반대 방향", "확인", MessageBoxButtons.OK, MessageBoxIcon.Information);

            }

        }

 

        #endregion

        #region 반전 시키기 메뉴 항목 클릭시 처리하기 - reverseMenuItem_Click(sender, e)

 

        /// <summary>

        /// 반전 시키기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void reverseMenuItem_Click(object sender, EventArgs e)

        {

            this.pointList.Reverse();

        }

 

        #endregion

        #region 삼각형 리스트 구하기 메뉴 항목 클릭시 처리하기 - triangulateMenuItem_Click(sender, e)

 

        /// <summary>

        /// 삼각형 리스트 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void triangulateMenuItem_Click(object sender, EventArgs e)

        {

            Polygon polygon = new Polygon(this.pointList.ToArray());

 

            List<Triangle> triangleList = polygon.GetTriangleList();

 

            using(Graphics graphics = CreateGraphics())

            {

                foreach(Triangle triangle in triangleList)

                {

                    graphics.DrawPolygon(Pens.Red, triangle.PointArray);

                }

 

                if(this.pointList.Count >= 3)

                {

                    graphics.DrawPolygon(Pens.Blue, this.pointList.ToArray());

                }

            }

        }

 

        #endregion

        #region 테두리 사각형 구하기 메뉴 항목 클릭시 처리하기 - boundingRectangleMenuItem_Click(sender, e)

 

        /// <summary>

        /// 테두리 사각형 구하기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void boundingRectangleMenuItem_Click(object sender, EventArgs e)

        {

            Polygon polygon = new Polygon(this.pointList.ToArray());

 

            if(polygon.IsOrientedClockwise())

            {

                Array.Reverse(polygon.PointArray);

            }

 

            PointF[] pointArray = polygon.GetSmallestBoundingRectangle();

 

            using(Graphics graphics = CreateGraphics())

            {

                graphics.DrawPolygon(Pens.Orange, pointArray);

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 메뉴 활성화 여부 설정하기 - EnableMenu()

 

        /// <summary>

        /// 메뉴 활성화 여부 설정하기

        /// </summary>

        private void EnableMenu()

        {

            bool enabled = (this.pointList.Count >= 3);

 

            this.convexMenuItem.Enabled            = enabled;

            this.includePointMenuItem.Enabled      = enabled;

            this.areaMenuItem.Enabled              = enabled;

            this.centroidMenuItem.Enabled          = enabled;

            this.orientationMenuItem.Enabled       = enabled;

            this.reverseMenuItem.Enabled           = enabled;

            this.triangulateMenuItem.Enabled       = enabled;

            this.boundingRectangleMenuItem.Enabled = enabled;

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker

댓글을 달아 주세요