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

■ 다각형 합집합 구하기

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


TestProject.zip


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>

        /// 다각형 리스트 배열

        /// </summary>

        private List<PointF>[] polygonListArray =

        {

            new List<PointF>(),

            new List<PointF>(),

        };

 

        /// <summary>

        /// 다각형 색상 배열

        /// </summary>

        private Color[] polygonColorArray =

        {

            Color.Blue,

            Color.Green,

        };

 

        /// <summary>

        /// 현재 포인트

        /// </summary>

        private PointF currentPoint;

 

        /// <summary>

        /// 인덱스 작성 여부

        /// </summary>

        private int makingIndex = -1;

 

        /// <summary>

        /// 다각형 체크 박스 배열

        /// </summary>

        private CheckBox[] polygonCheckboxArray;

 

        #endregion

 

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

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

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            Load                                   += Form_Load;

            this.polygon1CheckBox.CheckedChanged   += checkBox_CheckedChanged;

            this.polygon2CheckBox.CheckedChanged   += checkBox_CheckedChanged;

            this.unionCheckBox.CheckedChanged      += checkBox_CheckedChanged;

            this.canvasPictureBox.Paint            += canvasPictureBox_Paint;

            this.canvasPictureBox.MouseDoubleClick += canvasPictureBox_MouseDoubleClick;

            this.canvasPictureBox.MouseDown        += canvasPictureBox_MouseDown;

            this.canvasPictureBox.MouseMove        += canvasPictureBox_MouseMove;

 

            #endregion

        }

 

        #endregion

 

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

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

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

 

        #region 폼 로드시 처리하기 - Form_Load(sender, e)

 

        /// <summary>

        /// 폼 로드시 처리하기

        /// </summary>

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

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

        private void Form_Load(object sender, EventArgs e)

        {

            const int X     = 30;

            const int Y     = 30;

            const int SCALE = 2;

 

            this.polygonCheckboxArray = new CheckBox[]

            {

                this.polygon1CheckBox,

                this.polygon2CheckBox,

            };

 

            this.polygonListArray[0] = new List<PointF>

            (

                new PointF[]

                {

                    new PointF(136 * SCALE + X,  75 * SCALE + Y),

                    new PointF( 64 * SCALE + X, 125 * SCALE + Y),

                    new PointF(171 * SCALE + X, 181 * SCALE + Y),

                    new PointF(140 * SCALE + X,  97 * SCALE + Y),

                    new PointF(199 * SCALE + X, 102 * SCALE + Y),

                    new PointF(183 * SCALE + X, 158 * SCALE + Y),

                    new PointF(242 * SCALE + X, 127 * SCALE + Y),

                    new PointF(184 * SCALE + X,  44 * SCALE + Y),

                    new PointF( 60 * SCALE + X,  59 * SCALE + Y),

                }

            );

 

            this.polygonListArray[1] = new List<PointF>

            (

                new PointF[]

                {

                    new PointF(115 * SCALE + X,  34 * SCALE + Y),

                    new PointF(146 * SCALE + X, 198 * SCALE + Y),

                    new PointF(181 * SCALE + X, 114 * SCALE + Y),

                    new PointF(217 * SCALE + X, 162 * SCALE + Y),

                    new PointF(249 * SCALE + X,  73 * SCALE + Y),

                    new PointF(179 * SCALE + X,  20 * SCALE + Y),

                    new PointF(146 * SCALE + X,  69 * SCALE + Y),

                }

            );

        }

 

        #endregion

        #region 체크 박스 체크 변경시 처리하기 - checkBox_CheckedChanged(sender, e)

 

        /// <summary>

        /// 체크 박스 체크 변경시 처리하기

        /// </summary>

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

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

        private void checkBox_CheckedChanged(object sender, EventArgs e)

        {

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 DOWN 처리하기 - canvasPictureBox_MouseDown(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 DOWN 처리하기

        /// </summary>

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

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

        private void canvasPictureBox_MouseDown(object sender, MouseEventArgs e)

        {

            if(this.makingIndex < 0)

            {

                if(e.Button == MouseButtons.Left)

                {

                    this.makingIndex = 0;

                }

                else

                {

                    this.makingIndex = 1;

                }

 

                this.polygonListArray[this.makingIndex] = new List<PointF>();

 

                this.polygonListArray[this.makingIndex].Add(e.Location);

 

                this.currentPoint = e.Location;

            }

            else

            {

                if(this.polygonListArray[this.makingIndex][this.polygonListArray[this.makingIndex].Count-1] != e.Location)

                {

                    this.polygonListArray[this.makingIndex].Add(e.Location);

                }

 

                this.currentPoint = e.Location;

            }

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 이동시 처리하기 - canvasPictureBox_MouseMove(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 이동시 처리하기

        /// </summary>

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

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

        private void canvasPictureBox_MouseMove(object sender, MouseEventArgs e)

        {

            if(this.makingIndex < 0)

            {

                return;

            }

 

            this.currentPoint = e.Location;

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 마우스 더블 클릭시 처리하기 - canvasPictureBox_MouseDoubleClick(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 마우스 더블 클릭시 처리하기

        /// </summary>

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

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

        private void canvasPictureBox_MouseDoubleClick(object sender, MouseEventArgs e)

        {

            if(this.polygonListArray[this.makingIndex].Count < 3)

            {

                this.polygonListArray[this.makingIndex] = new List<PointF>();

            }

            else

            {

                ReversePolygonPointListIfCounterclockwise(this.polygonListArray[this.makingIndex]);

            }

 

            this.makingIndex = -1;

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 캔버스 픽처 박스 페인트시 처리하기 - canvasPictureBox_Paint(sender, e)

 

        /// <summary>

        /// 캔버스 픽처 박스 페인트시 처리하기

        /// </summary>

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

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

        private void canvasPictureBox_Paint(object sender, PaintEventArgs e)

        {

            e.Graphics.Clear(this.canvasPictureBox.BackColor);

 

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

 

            if((this.makingIndex < 0) && (this.polygonListArray[0].Count > 2) && (this.polygonListArray[1].Count > 2) &&

                (unionCheckBox.Checked))

            {

                List<PointF> polygonUnionPointList = GetPolygonUnionPointList(this.polygonListArray);

 

                using(Pen pen = new Pen(Color.Black, 10))

                {

                    e.Graphics.DrawPolygon(pen, polygonUnionPointList.ToArray());

                }

            }

 

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

            {

                if(this.polygonCheckboxArray[i].Checked)

                {

                    if(i == this.makingIndex)

                    {

                        if(this.polygonListArray[i].Count > 1)

                        {

                            using(Pen pen = new Pen(this.polygonColorArray[i], 3))

                            {

                                e.Graphics.DrawLines(pen, this.polygonListArray[i].ToArray());

                            }

                        }

 

                        if(this.polygonListArray[i].Count > 0)

                        {

                            PointF point = this.polygonListArray[i][this.polygonListArray[i].Count - 1];

 

                            e.Graphics.DrawLine

                            (

                                Pens.Green,

                                point.X,

                                point.Y,

                                this.currentPoint.X,

                                this.currentPoint.Y

                            );

                        }

                    }

                    else

                    {

                        if(this.polygonListArray[i].Count > 2)

                        {

                            Color fillColor = Color.FromArgb(128, this.polygonColorArray[i]);

 

                            using(Brush brush = new SolidBrush(fillColor))

                            {

                                e.Graphics.FillPolygon(brush, this.polygonListArray[i].ToArray());

                            }

 

                            using(Pen pen = new Pen(this.polygonColorArray[i], 3))

                            {

                                e.Graphics.DrawPolygon(pen, this.polygonListArray[i].ToArray());

                            }

                        }

                    }

                }

            }

        }

 

        #endregion

 

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

 

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

 

        /// <summary>

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

        /// </summary>

        /// <param name="sourcePointList">소스 포인트 리스트</param>

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

        private float GetSignedPolygonArea(List<PointF> sourcePointList)

        {

            int pointCount = sourcePointList.Count;

 

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

 

            sourcePointList.CopyTo(pointArray, 0);

 

            pointArray[pointCount] = sourcePointList[0];

 

            float area = 0;

 

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

            {

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

            }

 

            return area;

        }

 

        #endregion

        #region 다각형 시계 방향 여부 구하기 - IsPolygonOrientedClockwise(sourcePointList)

 

        /// <summary>

        /// 다각형 시계 방향 여부 구하기

        /// </summary>

        /// <param name="sourcePointList">소스 포인트 리스트</param>

        /// <returns>다각형 시계 방향 여부</returns>

        public bool IsPolygonClockwise(List<PointF> sourcePointList)

        {

            return (GetSignedPolygonArea(sourcePointList) < 0);

        }

 

        #endregion

        #region 시계 반대 방형의 다각형 포인트 리스트 반전하기 - ReversePolygonPointListIfCounterclockwise(sourcePointList)

 

        /// <summary>

        /// 시계 반대 방형의 다각형 포인트 리스트 반전하기

        /// </summary>

        /// <param name="sourcePointList">소스 포인트 리스트</param>

        private void ReversePolygonPointListIfCounterclockwise(List<PointF> sourcePointList)

        {

            if(IsPolygonClockwise(sourcePointList))

            {

                sourcePointList.Reverse();

            }

        }

 

        #endregion

 

        #region 직선과 직선의 교차 포인트 찾기 - FindLineLineIntersectionPoint(line1StartPoint, line1EndPoint, line2StartPoint,

            line2EndPoint, lineIntersect, segmentIntersect, intersectionPoint, closePoint1, closePoint2, t1, t2)

 

        /// <summary>

        /// 직선과 직선의 교차 포인트 찾기

        /// </summary>

        /// <param name="line1StartPoint">선 1 시작 포인트</param>

        /// <param name="line1EndPoint">선 1 종료 포인트</param>

        /// <param name="line2StartPoint">선 2 시작 포인트</param>

        /// <param name="line2EndPoint">선 2 종료 포인트</param>

        /// <param name="lineIntersect">선 교차 여부</param>

        /// <param name="segmentIntersect">세그먼트 교차 여부</param>

        /// <param name="intersectionPoint">교차 포인트</param>

        /// <param name="closePoint1">근접 포인트 1</param>

        /// <param name="closePoint2">근접 포인트 2</param>

        /// <param name="t1">T1</param>

        /// <param name="t2">T2</param>

        private void FindLineLineIntersectionPoint(PointF line1StartPoint, PointF line1EndPoint, PointF line2StartPoint,

            PointF line2EndPoint, out bool lineIntersect, out bool segmentIntersect, out PointF intersectionPoint, out PointF closePoint1,

            out PointF closePoint2, out float t1, out float t2)

        {

            float dx12 = line1EndPoint.X - line1StartPoint.X;

            float dy12 = line1EndPoint.Y - line1StartPoint.Y;

            float dx34 = line2EndPoint.X - line2StartPoint.X;

            float dy34 = line2EndPoint.Y - line2StartPoint.Y;

 

            float denominator = (dy12 * dx34 - dx12 * dy34);

 

            t1 = ((line1StartPoint.X - line2StartPoint.X) * dy34 + (line2StartPoint.Y - line1StartPoint.Y) * dx34) / denominator;

 

            if(float.IsInfinity(t1))

            {

                lineIntersect    = false;

                segmentIntersect = false;

 

                intersectionPoint = new PointF(float.NaN, float.NaN);

 

                closePoint1 = new PointF(float.NaN, float.NaN);

                closePoint2 = new PointF(float.NaN, float.NaN);

 

                t2 = float.PositiveInfinity;

 

                return;

            }

 

            lineIntersect = true;

 

            t2 = ((line2StartPoint.X - line1StartPoint.X) * dy12 + (line1StartPoint.Y - line2StartPoint.Y) * dx12) / -denominator;

 

            intersectionPoint = new PointF(line1StartPoint.X + dx12 * t1, line1StartPoint.Y + dy12 * t1);

 

            segmentIntersect = ((t1 >= 0) && (t1 <= 1) && (t2 >= 0) && (t2 <= 1));

 

            if(t1 < 0)

            {

                t1 = 0;

            }

            else if(t1 > 1)

            {

                t1 = 1;

            }

 

            if(t2 < 0)

            {

                t2 = 0;

            }

            else if(t2 > 1)

            {

                t2 = 1;

            }

 

            closePoint1 = new PointF(line1StartPoint.X + dx12 * t1, line1StartPoint.Y + dy12 * t1);

            closePoint2 = new PointF(line2StartPoint.X + dx34 * t2, line2StartPoint.Y + dy34 * t2);

        }

 

        #endregion

        #region 다각형 합집합 포인트 리스트 구하기 - GetPolygonUnionPointList(polygonPointListArray)

 

        /// <summary>

        /// 다각형 합집합 포인트 리스트 구하기

        /// </summary>

        /// <param name="polygonPointListArray">다각형 포인트 리스트 배열</param>

        /// <returns>다각형 합집합 포인트</returns>

        private List<PointF> GetPolygonUnionPointList(List<PointF>[] polygonPointListArray)

        {

            int currentPolygonIndex = 0;

            int currentPointIndex   = 0;

 

            PointF currentPoint = polygonPointListArray[currentPolygonIndex][currentPointIndex];

 

            for(int polygonIndex = 0; polygonIndex < 2; polygonIndex++)

            {

                for(int pointIndex = 0; pointIndex < polygonPointListArray[polygonIndex].Count; pointIndex++)

                {

                    PointF testPoint = polygonPointListArray[polygonIndex][pointIndex];

 

                    if((testPoint.X < currentPoint.X) || ((testPoint.X == currentPoint.X) && (testPoint.Y > currentPoint.Y)))

                    {

                        currentPolygonIndex = polygonIndex;

                        currentPointIndex   = pointIndex;

                        currentPoint        = polygonPointListArray[currentPolygonIndex][currentPointIndex];

                    }

                }

            }

 

            List<PointF> unionPointList = new List<PointF>();

 

            PointF startPoint = currentPoint;

 

            unionPointList.Add(startPoint);

 

            for(;;)

            {

                int nextPointIndex = (currentPointIndex + 1) % polygonPointListArray[currentPolygonIndex].Count;

 

                PointF nextPoint = polygonPointListArray[currentPolygonIndex][nextPointIndex];

 

                int otherPolygonIndex = (currentPolygonIndex + 1) % 2;

 

                PointF bestIntersectionPoint = new PointF(0, 0);

                int    bestIndex             = -1;

                float  bestT                 = 2f;

 

                for(int polygonIndex1 = 0; polygonIndex1 < polygonPointListArray[otherPolygonIndex].Count; polygonIndex1++)

                {

                    int polygonIndex2 = (polygonIndex1 + 1) % polygonPointListArray[otherPolygonIndex].Count;

 

                    PointF point1 = polygonPointListArray[otherPolygonIndex][polygonIndex1];

                    PointF point2 = polygonPointListArray[otherPolygonIndex][polygonIndex2];

 

                    bool lineIntersect;

                    bool segmentIntersect;

 

                    PointF intersectionPoint;

                    PointF closePoint1;

                    PointF closePoint2;

                    float  t1;

                    float  t2;

 

                    FindLineLineIntersectionPoint

                    (

                        currentPoint,

                        nextPoint,

                        point1,

                        point2,

                        out lineIntersect,

                        out segmentIntersect,

                        out intersectionPoint,

                        out closePoint1,

                        out closePoint2,

                        out t1,

                        out t2

                    );

 

                    if((segmentIntersect) && (t1 > 0.001) && (t1 < bestT))

                    {

                        if(t1 < bestT)

                        {

                            bestT                 = t1;

                            bestIndex             = polygonIndex1;

                            bestIntersectionPoint = intersectionPoint;

                        }

                    }

                }

 

                if(bestT < 2f)

                {

                    unionPointList.Add(bestIntersectionPoint);

 

                    currentPolygonIndex = (currentPolygonIndex + 1) % 2;

                    currentPoint        = bestIntersectionPoint;

                    currentPointIndex   = bestIndex;

                }

                else

                {

                    currentPoint      = nextPoint;

                    currentPointIndex = nextPointIndex;

 

                    if(currentPoint == startPoint)

                    {

                        break;

                    }

 

                    unionPointList.Add(currentPoint);

                }

            }

 

            return unionPointList;

        }

 

        #endregion

    }

}

 

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

Posted by 사용자 icodebroker

댓글을 달아 주세요