첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 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 ToolType toolType = ToolType.None;

 

        /// <summary>

        /// 마우스 포인트

        /// </summary>

        private Point mousePoint;

 

        /// <summary>

        /// 직선 포인트 리스트

        /// </summary>

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

 

        /// <summary>

        /// 다각형 포인트 리스트

        /// </summary>

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

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration

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

 

        #region 도구 타입 - ToolType

 

        /// <summary>

        /// 도구 타입

        /// </summary>

        private enum ToolType

        {

            /// <summary>

            /// 해당 무

            /// </summary>

            None,

 

            /// <summary>

            /// 직선

            /// </summary>

            Line,

 

            /// <summary>

            /// 다각형

            /// </summary>

            Polygon,

        }

 

        #endregion

 

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

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

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            this.lineButton.Click           += lineButton_Click;

            this.polygonButton.Click        += polygonButton_Click;

            this.canvasPictureBox.Paint     += canvasPictureBox_Paint;

            this.canvasPictureBox.MouseDown += canvasPictureBox_MouseDown;

            this.canvasPictureBox.MouseMove += canvasPictureBox_MouseMove;

 

            #endregion

        }

 

        #endregion

 

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

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

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

 

        #region 직선 버튼 클릭시 처리하기 - lineButton_Click(sender, e)

 

        /// <summary>

        /// 직선 버튼 클릭시 처리하기

        /// </summary>

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

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

        private void lineButton_Click(object sender, EventArgs e)

        {

            if(this.lineButton.Checked)

            {

                SelectTool(ToolType.None);

            }

            else

            {

                SelectTool(ToolType.Line);

            }

        }

 

        #endregion

        #region 다각형 버튼 클릭시 처리하기 - polygonButton_Click(sender, e)

 

        /// <summary>

        /// 다각형 버튼 클릭시 처리하기

        /// </summary>

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

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

        private void polygonButton_Click(object sender, EventArgs e)

        {

            if(this.polygonButton.Checked)

            {

                SelectTool(ToolType.None);

            }

            else

            {

                SelectTool(ToolType.Polygon);

            }

        }

 

        #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.toolType == ToolType.None)

            {

                return;

            }

 

            if(this.toolType == ToolType.Line)

            {

                this.linePointList.Add(e.Location);

 

                if(this.linePointList.Count == 2)

                {

                    SelectTool(ToolType.None);

                }

            }

            else

            {

                if(e.Button == MouseButtons.Right)

                {

                    SelectTool(ToolType.None);

                }

                else

                {

                    this.polygonPointList.Add(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.toolType != ToolType.None)

            {

                this.mousePoint = e.Location;

 

                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.toolType == ToolType.Polygon)

            {

                if(this.polygonPointList.Count > 1)

                {

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

                }

 

                if(this.polygonPointList.Count > 0)

                {

                    e.Graphics.DrawLine(Pens.Green, this.polygonPointList[this.polygonPointList.Count - 1], this.mousePoint);

                }

            }

            else if(this.polygonPointList.Count > 2)

            {

                e.Graphics.FillPolygon(Brushes.LightBlue, this.polygonPointList.ToArray());

 

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

            }

 

            if((this.toolType == ToolType.None) && (this.polygonPointList.Count > 2) && (this.linePointList.Count == 2))

            {

                bool draw;

 

                PointF[] intersectionPointArray = GetClippingPointArray

                (

                    out draw,

                    this.linePointList[0],

                    this.linePointList[1],

                    this.polygonPointList

                );

 

                using(Pen pen = new Pen(Color.Pink, 5))

                {

                    for(int i = 0; i < intersectionPointArray.Length - 1; i++)

                    {

                        if(draw)

                        {

                            pen.Color = Color.Pink;

                        }

                        else

                        {

                            pen.Color = Color.Yellow;

                        }

 

                        draw = !draw;

 

                        e.Graphics.DrawLine(pen, intersectionPointArray[i], intersectionPointArray[i + 1]);

                    }

 

                    foreach(PointF point in intersectionPointArray)

                    {

                        DrawPoint(e.Graphics, point);

                    }

                }

            }

 

            if(this.linePointList.Count == 2)

            {

                e.Graphics.DrawLine(Pens.Red, this.linePointList[0], this.linePointList[1]);

            }

            else if(this.linePointList.Count == 1)

            {

                e.Graphics.DrawLine(Pens.Green, this.linePointList[0], this.mousePoint);

            }

        }

 

        #endregion

 

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

 

        #region 도구 선택하기 - SelectTool(toolType)

 

        /// <summary>

        /// 도구 선택하기

        /// </summary>

        /// <param name="toolType">도구 타입</param>

        private void SelectTool(ToolType toolType)

        {

            if((this.toolType == ToolType.Line) && (this.linePointList.Count == 1))

            {

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

            }

 

            if((this.toolType == ToolType.Polygon) && (this.polygonPointList.Count < 3))

            {

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

            }

 

            this.toolType = toolType;

 

            switch(this.toolType)

            {

                case ToolType.None :

 

                    Cursor = Cursors.Default;

 

                    this.lineButton.Checked    = false;

                    this.polygonButton.Checked = false;

 

                    break;

 

                case ToolType.Line :

 

                    Cursor = Cursors.Cross;

 

                    this.lineButton.Checked    = true;

                    this.polygonButton.Checked = false;

 

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

 

                    break;

 

                case ToolType.Polygon :

 

                    Cursor = Cursors.Cross;

 

                    this.lineButton.Checked    = false;

                    this.polygonButton.Checked = true;

 

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

 

                    break;

            }

 

            this.canvasPictureBox.Refresh();

        }

 

        #endregion

        #region 포인트 그리기 - DrawPoint(graphics, point)

 

        /// <summary>

        /// 포인트 그리기

        /// </summary>

        /// <param name="graphics">그래픽스</param>

        /// <param name="point">포인트</param>

        private void DrawPoint(Graphics graphics, PointF point)

        {

            RectangleF rectangle = new RectangleF(point.X - 3, point.Y - 3, 6, 6);

 

            graphics.FillEllipse(Brushes.White, rectangle);

 

            graphics.DrawEllipse(Pens.Black, rectangle);

        }

 

        #endregion

 

        #region 내적 구하기 - GetDotProduct(x1, y1, x2, y2, x3, y3)

 

        /// <summary>

        /// 내적 구하기

        /// </summary>

        /// <param name="x1">X 1</param>

        /// <param name="y1">Y 1</param>

        /// <param name="x2">X 2</param>

        /// <param name="y2">Y 2</param>

        /// <param name="x3">X 3</param>

        /// <param name="y3">Y 3</param>

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

        private float GetDotProduct(float x1, float y1, float x2, float y2, float x3, float y3)

        {

            float dx12 = x1 - x2;

            float dy12 = y1 - y2;

            float dx32 = x3 - x2;

            float dy32 = y3 - y2;

 

            return (dx12 * dx32 + dy12 * dy32);

        }

 

        #endregion

        #region 외적 구하기 - GetCrossProduct(x1, y1, x2, y2, x3, y3)

 

        /// <summary>

        /// 외적 구하기

        /// </summary>

        /// <param name="x1">X 1</param>

        /// <param name="y1">Y 1</param>

        /// <param name="x2">X 2</param>

        /// <param name="y2">Y 2</param>

        /// <param name="x3">X 3</param>

        /// <param name="y3">Y 3</param>

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

        public float GetCrossProduct(float x1, float y1, float x2, float y2, float x3, float y3)

        {

            float dx12 = x1 - x2;

            float dy12 = y1 - y2;

            float dx32 = x3 - x2;

            float dy32 = y3 - y2;

 

            return (dx12 * dy32 - dy12 * dx32);

        }

 

        #endregion

        #region 각도 구하기 - GetAngle(x1, y1, x2, y2, x3, y3)

 

        /// <summary>

        /// 각도 구하기

        /// </summary>

        /// <param name="x1">X 1</param>

        /// <param name="y1">Y 1</param>

        /// <param name="x2">X 2</param>

        /// <param name="y2">Y 2</param>

        /// <param name="x3">X 3</param>

        /// <param name="y3">Y 3</param>

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

        public float GetAngle(float x1, float y1, float x2, float y2, float x3, float y3)

        {

            float dotProduct = GetDotProduct(x1, y1, x2, y2, x3, y3);

 

            float crossProduct = GetCrossProduct(x1, y1, x2, y2, x3, y3);

 

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

        }

 

        #endregion

        #region 다각형 내 포인트 여부 구하기 - IsPointInPolygon(x, y, polygonPointList)

 

        /// <summary>

        /// 다각형 내 포인트 여부 구하기

        /// </summary>

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

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

        /// <param name="polygonPointList">다각형 포인트 리스트</param>

        /// <returns>다각형 내 포인트 여부</returns>

        public bool IsPointInPolygon(float x, float y, PointF[] polygonPointList)

        {

            int pointCount = polygonPointList.Length - 1;

 

            float totalAngle = GetAngle

            (

                polygonPointList[pointCount].X,

                polygonPointList[pointCount].Y,

                x,

                y,

                polygonPointList[0].X,

                polygonPointList[0].Y

            );

 

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

            {

                totalAngle += GetAngle

                (

                    polygonPointList[i].X,

                    polygonPointList[i].Y,

                    x,

                    y,

                    polygonPointList[i + 1].X,

                    polygonPointList[i + 1].Y

                );

            }

 

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

        }

 

        #endregion

        #region 교차 포인트 찾기 - FindIntersectionPoint(point1, point2, point3, point4, lineIntersect,

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

 

        /// <summary>

        /// 교차 포인트 찾기

        /// </summary>

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

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

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

        /// <param name="point4">포인트 41</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 FindIntersectionPoint(PointF point1, PointF point2, PointF point3, PointF point4,

            out bool lineIntersect, out bool segmentIntersect, out PointF intersectionPoint,

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

        {

            float dx12 = point2.X - point1.X;

            float dy12 = point2.Y - point1.Y;

            float dx34 = point4.X - point3.X;

            float dy34 = point4.Y - point3.Y;

 

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

 

            t1 = ((point1.X - point3.X) * dy34 + (point3.Y - point1.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 = ((point3.X - point1.X) * dy12 + (point1.Y - point3.Y) * dx12) / -denominator;

 

            intersectionPoint = new PointF(point1.X + dx12 * t1, point1.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(point1.X + dx12 * t1, point1.Y + dy12 * t1);

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

        }

 

        #endregion

        #region 클리핑 포인트 배열 구하기 - GetClippingPointArray(startOutsidePolygon, point1, point2, polygonPointList)

 

        /// <summary>

        /// 클리핑 포인트 배열 구하기

        /// </summary>

        /// <param name="startOutsidePolygon">다각형 외부 시작 여부</param>

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

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

        /// <param name="polygonPointList">다각형 포인트 리스트</param>

        /// <returns>포인트 배열</returns>

        private PointF[] GetClippingPointArray(out bool startOutsidePolygon, PointF point1, PointF point2, List<PointF> polygonPointList)

        {

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

 

            List<float> valueList = new List<float>();

 

            intersectionPointList.Add(point1);

 

            valueList.Add(0f);

 

            startOutsidePolygon = !IsPointInPolygon(point1.X, point1.Y, polygonPointList.ToArray());

 

            for(int i = 0; i < polygonPointList.Count; i++)

            {

                int j = (i + 1) % polygonPointList.Count;

 

                bool lineIntersect;

                bool segmentIntersect;

 

                PointF intersectionPoint;

                PointF closePoint1;

                PointF closePoint2;

                float  value1;

                float  value2;

 

                FindIntersectionPoint

                (

                    point1,

                    point2,

                    polygonPointList[i],

                    polygonPointList[j],

                    out lineIntersect,

                    out segmentIntersect,

                    out intersectionPoint,

                    out closePoint1,

                    out closePoint2,

                    out value1,

                    out value2

                );

 

                if(segmentIntersect)

                {

                    intersectionPointList.Add(intersectionPoint);

 

                    valueList.Add(value1);

                }

            }

 

            intersectionPointList.Add(point2);

 

            valueList.Add(1f);

 

            PointF[] intersectionPointArray = intersectionPointList.ToArray();

 

            float[] valueArray = valueList.ToArray();

 

            Array.Sort(valueArray, intersectionPointArray);

 

            return intersectionPointArray;

        }

 

        #endregion

    }

}

 

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

Posted by 사용자 icodebroker

댓글을 달아 주세요