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

728x90
반응형

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
    }
}
728x90
반응형
Posted by 사용자 icodebroker

댓글을 달아 주세요