첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

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> pointList = new List<PointF>();

        /// <summary>
        /// 근접점 1
        /// </summary>
        private PointF closePoint1;
        
        /// <summary>
        /// 근접점 2
        /// </summary>
        private PointF closePoint2;

        /// <summary>
        /// 처리 여부
        /// </summary>
        private bool isProcessed = false;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            #region 이벤트를 설정한다.

            MouseClick += Form_MouseClick;
            Paint      += Form_Paint;

            #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)
        {
            if(this.pointList.Count == 4)
            {
                this.pointList = new List<PointF>();

                this.isProcessed = false;
            }

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

            if(this.pointList.Count == 4)
            {
                FindShortestDistance
                (
                    this.pointList[0],
                    this.pointList[1],
                    this.pointList[2],
                    this.pointList[3],
                    out this.closePoint1,
                    out this.closePoint2
                );

                this.isProcessed = true;
            }

            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.SmoothingMode = SmoothingMode.AntiAlias;

            if(this.pointList.Count == 4)
            {
                e.Graphics.DrawLine(Pens.Green, this.pointList[2], this.pointList[3]);
            }

            if(this.pointList.Count >= 2)
            {
                e.Graphics.DrawLine(Pens.Blue, this.pointList[0], this.pointList[1]);
            }

            if(this.isProcessed)
            {
                using(Pen dashPen = new Pen(Color.Red))
                {
                    dashPen.DashStyle   = DashStyle.Custom;
                    dashPen.DashPattern = new float[] { 4, 4 };

                    e.Graphics.DrawLine(dashPen, this.closePoint1, this.closePoint2);
                }
            }

            foreach(PointF point in this.pointList)
            {
                DrawPoint(e.Graphics, point, Brushes.White, Pens.Black);
            }

            if(this.isProcessed)
            {
                DrawPoint(e.Graphics, this.closePoint1, Brushes.LightBlue, Pens.Blue);
                DrawPoint(e.Graphics, this.closePoint2, Brushes.LightBlue, Pens.Blue);
            }
        }

        #endregion

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

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

        /// <summary>
        /// 포인트 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="point">포인트</param>
        /// <param name="brush">브러시</param>
        /// <param name="pen">펜</param>
        private void DrawPoint(Graphics graphics, PointF point, Brush brush, Pen pen)
        {
            const int RADIUS = 3;

            graphics.FillEllipse(brush, point.X - RADIUS, point.Y - RADIUS, 2 * RADIUS, 2 * RADIUS);

            graphics.DrawEllipse(pen, point.X - RADIUS, point.Y - RADIUS, 2 * RADIUS, 2 * RADIUS);
        }

        #endregion
        #region 교차점 구하기 - FindIntersection(p1, p2, p3, p4, lineIntersect, segmentIntersect, intersectPoint, closePoint1, closePoint2)

        /// <summary>
        /// 교차점 구하기
        /// </summary>
        /// <param name="p1">직선 1 포인트 1</param>
        /// <param name="p2">직선 1 포인트 2</param>
        /// <param name="p3">직선 2 포인트 1</param>
        /// <param name="p4">직선 2 포인트 2</param>
        /// <param name="lineIntersect">직선 교차 여부</param>
        /// <param name="segmentIntersect">세그먼트 교차 여부</param>
        /// <param name="intersectPoint">교차점</param>
        /// <param name="closePoint1">근접점 1</param>
        /// <param name="closePoint2">근접점 2</param>
        private void FindIntersection
        (
            PointF     p1,
            PointF     p2,
            PointF     p3,
            PointF     p4,
            out bool   lineIntersect,
            out bool   segmentIntersect,
            out PointF intersectPoint,
            out PointF closePoint1,
            out PointF closePoint2
        )
        {
            float deltaX21 = p2.X - p1.X;
            float deltaY21 = p2.Y - p1.Y;
            float deltaX43 = p4.X - p3.X;
            float deltaY43 = p4.Y - p3.Y;

            float denominator = (deltaY21 * deltaX43 - deltaX21 * deltaY43);

            float t1;

            try
            {
                t1 = ((p1.X - p3.X) * deltaY43 + (p3.Y - p1.Y) * deltaX43) / denominator;
            }
            catch
            {
                lineIntersect    = false;
                segmentIntersect = false;

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

                return;
            }

            lineIntersect = true;

            float t2 = ((p3.X - p1.X) * deltaY21 + (p1.Y - p3.Y) * deltaX21) / -denominator;

            intersectPoint = new PointF(p1.X + deltaX21 * t1, p1.Y + deltaY21 * 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(p1.X + deltaX21 * t1, p1.Y + deltaY21 * t1);
            closePoint2 = new PointF(p3.X + deltaX43 * t2, p3.Y + deltaY43 * t2);
        }

        #endregion
        #region 세그먼트 거리 찾기 - FindDistanceToSegment(point, p1, p2, closestPoint)

        /// <summary>
        /// 세그먼트 거리 찾기
        /// </summary>
        /// <param name="point">포인트</param>
        /// <param name="p1">직선 포인트 1</param>
        /// <param name="p2">직선 포인트 2</param>
        /// <param name="closestPoint">최근접점</param>
        /// <returns>거리</returns>
        private double FindDistanceToSegment(PointF point, PointF p1, PointF p2, out PointF closestPoint)
        {
            float deltaX = p2.X - p1.X;
            float deltaY = p2.Y - p1.Y;

            if((deltaX == 0) && (deltaY == 0))
            {
                closestPoint = p1;

                deltaX = point.X - p1.X;
                deltaY = point.Y - p1.Y;

                return Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
            }

            float t = ((point.X - p1.X) * deltaX + (point.Y - p1.Y) * deltaY) / (deltaX * deltaX + deltaY * deltaY);

            if(t < 0)
            {
                closestPoint = new PointF(p1.X, p1.Y);

                deltaX = point.X - p1.X;
                deltaY = point.Y - p1.Y;
            }
            else if(t > 1)
            {
                closestPoint = new PointF(p2.X, p2.Y);

                deltaX = point.X - p2.X;
                deltaY = point.Y - p2.Y;
            }
            else
            {
                closestPoint = new PointF(p1.X + t * deltaX, p1.Y + t * deltaY);

                deltaX = point.X - closestPoint.X;
                deltaY = point.Y - closestPoint.Y;
            }

            return Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
        }

        #endregion
        #region 최단거리 구하기 - FindShortestDistance(p1, p2, p3, p4, closePoint1, closePoint2)

        /// <summary>
        /// 최단거리 구하기
        /// </summary>
        /// <param name="p1">직선 1 포인트 1</param>
        /// <param name="p2">직선 1 포인트 2</param>
        /// <param name="p3">직선 2 포인트 1</param>
        /// <param name="p4">직선 2 포인트 2</param>
        /// <param name="closePoint1">근접점 1</param>
        /// <param name="closePoint2">근접점 2</param>
        /// <returns>최단거리</returns>
        private double FindShortestDistance
        (
            PointF     p1,
            PointF     p2,
            PointF     p3,
            PointF     p4,
            out PointF closePoint1,
            out PointF closePoint2
        )
        {
            bool   lineIntersect;
            bool   segmentIntersect;
            PointF intersectPoint;

            FindIntersection
            (
                p1,
                p2,
                p3,
                p4,
                out lineIntersect,
                out segmentIntersect,
                out intersectPoint,
                out closePoint1,
                out closePoint2
            );

            if(segmentIntersect)
            {
                closePoint1 = intersectPoint;
                closePoint2 = intersectPoint;

                return 0;
            }

            PointF closestPoint;

            double shortestDistance = double.MaxValue;
            double temporaryDistance;

            temporaryDistance = FindDistanceToSegment(p1, p3, p4, out closestPoint);

            if(temporaryDistance < shortestDistance)
            {
                shortestDistance = temporaryDistance;

                closePoint1 = p1;
                closePoint2 = closestPoint;
            }

            temporaryDistance = FindDistanceToSegment(p2, p3, p4, out closestPoint);

            if(temporaryDistance < shortestDistance)
            {
                shortestDistance = temporaryDistance;

                closePoint1 = p2;
                closePoint2 = closestPoint;
            }

            temporaryDistance = FindDistanceToSegment(p3, p1, p2, out closestPoint);

            if(temporaryDistance < shortestDistance)
            {
                shortestDistance = temporaryDistance;

                closePoint1 = closestPoint;
                closePoint2 = p3;
            }

            temporaryDistance = FindDistanceToSegment(p4, p1, p2, out closestPoint);

            if(temporaryDistance < shortestDistance)
            {
                shortestDistance = temporaryDistance;

                closePoint1 = closestPoint;
                closePoint2 = p4;
            }

            return shortestDistance;
        }

        #endregion
    }
}
728x90
반응형
그리드형
Posted by 사용자 icodebroker

댓글을 달아 주세요