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

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>
        /// 포인트 리스트 1
        /// </summary>
        private List<PointF> pointList1 = new List<PointF>();

        /// <summary>
        /// 포인트 리스트 2
        /// </summary>
        private List<PointF> pointList2 = new List<PointF>();

        #endregion

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

        #region 생성자 - MainForm()

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

            MouseClick += Form_MouseClick;
            Paint      += Form_Paint;
        }

        #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(e.Button == MouseButtons.Left)
            {
                if(this.pointList1.Count == 2)
                {
                    this.pointList1 = new List<PointF>();
                }

                this.pointList1.Add(e.Location);

                Refresh();
            }
            else
            {
                if(this.pointList2.Count == 2)
                {
                    this.pointList2 = new List<PointF>();
                }

                this.pointList2.Add(e.Location);

                Refresh();
            }
        }

        #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((pointList1.Count == 2) && (pointList2.Count == 2))
            {
                RectangleF rectangle;
                float      startAngle;
                float      sweepAngle;
                PointF     farPoint1;
                PointF     closePoint1;
                PointF     farPoint2;
                PointF     closePoint2;

                FindArcFromSegments
                (
                    this.pointList1[0],
                    this.pointList1[1],
                    this.pointList2[0],
                    this.pointList2[1],
                    out rectangle,
                    out startAngle,
                    out sweepAngle,
                    out farPoint1,
                    out closePoint1,
                    out farPoint2,
                    out closePoint2
                );

                using(Pen thickPen = new Pen(Color.Green, 2))
                {
                    e.Graphics.DrawLine(thickPen, farPoint1, closePoint1);
                    e.Graphics.DrawLine(thickPen, farPoint2, closePoint2);

                    thickPen.Color = Color.Red;

                    e.Graphics.DrawArc(thickPen, rectangle, startAngle, sweepAngle);

                    e.Graphics.FillPoint(Brushes.Red, farPoint1  , 5);
                    e.Graphics.FillPoint(Brushes.Red, closePoint1, 5);
                    e.Graphics.FillPoint(Brushes.Red, farPoint2  , 5);
                    e.Graphics.FillPoint(Brushes.Red, closePoint2, 5);
                }
            }
            else
            {
                using(Pen thickPen = new Pen(Color.Green, 2))
                {
                    if(pointList1.Count == 2)
                    {
                        e.Graphics.DrawLine(thickPen, pointList1[0], pointList1[1]);
                    }

                    if(pointList2.Count == 2)
                    {
                        e.Graphics.DrawLine(thickPen, pointList2[0], pointList2[1]);
                    }
                }
            }

            foreach(PointF point in pointList1)
            {
                e.Graphics.FillPoint(Brushes.Green, point, 5);
            }

            foreach(PointF point in pointList2)
            {
                e.Graphics.FillPoint(Brushes.Green, point, 5);
            }
        }

        #endregion

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

        #region 교차 찾기 - FindIntersection(point1, point2, point3, point4, linesIntersect, segmentsIntersect,
            intersectionPoint, closePoint1, closePoint2)

        /// <summary>
        /// 교차 찾기
        /// </summary>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <param name="point3">포인트 3</param>
        /// <param name="point4">포인트 4</param>
        /// <param name="linesIntersect">선 교차 여부</param>
        /// <param name="segmentsIntersect">세그먼트 교차 여부</param>
        /// <param name="intersectionPoint">교차 포인트</param>
        /// <param name="closePoint1">근접 포인트 1</param>
        /// <param name="closePoint2">근접 포인트 2</param>
        private void FindIntersection
        (
            PointF     point1,
            PointF     point2,
            PointF     point3,
            PointF     point4,
            out bool   linesIntersect,
            out bool   segmentsIntersect,
            out PointF intersectionPoint,
            out PointF closePoint1,
            out PointF closePoint2
        )
        {
            float deltaX12 = point2.X - point1.X;
            float deltaY12 = point2.Y - point1.Y;
            float deltaX34 = point4.X - point3.X;
            float deltaY34 = point4.Y - point3.Y;

            float denominator = (deltaY12 * deltaX34 - deltaX12 * deltaY34);

            float temporary1 = ((point1.X - point3.X) * deltaY34 + (point3.Y - point1.Y) * deltaX34) / denominator;

            if(float.IsInfinity(temporary1))
            {
                linesIntersect    = false;
                segmentsIntersect = false;
                intersectionPoint = new PointF(float.NaN, float.NaN);
                closePoint1       = new PointF(float.NaN, float.NaN);
                closePoint2       = new PointF(float.NaN, float.NaN);

                return;
            }

            linesIntersect = true;

            float temporary2 = ((point3.X - point1.X) * deltaY12 + (point1.Y - point3.Y) * deltaX12) / -denominator;

            intersectionPoint = new PointF(point1.X + deltaX12 * temporary1, point1.Y + deltaY12 * temporary1);

            segmentsIntersect = ((temporary1 >= 0) && (temporary1 <= 1) && (temporary2 >= 0) && (temporary2 <= 1));

            if(temporary1 < 0)
            {
                temporary1 = 0;
            }
            else if(temporary1 > 1)
            {
                temporary1 = 1;
            }

            if(temporary2 < 0)
            {
                temporary2 = 0;
            }
            else if (temporary2 > 1)
            {
                temporary2 = 1;
            }

            closePoint1 = new PointF(point1.X + deltaX12 * temporary1, point1.Y + deltaY12 * temporary1);
            closePoint2 = new PointF(point3.X + deltaX34 * temporary2, point3.Y + deltaY34 * temporary2);
        }

        #endregion
        #region 거리에서 포인트 구하기 - GetPointAtDistance(point1, point2, distance)

        /// <summary>
        /// 거리에서 포인트 구하기
        /// </summary>
        /// <param name="point1">포이트 1</param>
        /// <param name="point2">포이트 2</param>
        /// <param name="distance">거리</param>
        /// <returns>거리에서 포인트</returns>
        private PointF GetPointAtDistance(PointF point1, PointF point2, float distance)
        {
            float deltaX = point2.X - point1.X;
            float deltaY = point2.Y - point1.Y;

            float distanceBetweenPoint1AndPoint2 = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);

            return new PointF
            (
                point1.X + deltaX / distanceBetweenPoint1AndPoint2 * distance,
                point1.Y + deltaY / distanceBetweenPoint1AndPoint2 * distance
            );
        }

        #endregion
        #region 탄젠트에서 호 찾기 - FindArcFromTangents(closePoint1, farPoint1, closePoint2, farPoint2,
            rectangle, startAngle, sweepAngle)

        /// <summary>
        /// 탄젠트에서 호 찾기
        /// </summary>
        /// <param name="closePoint1">근접 포인트 1</param>
        /// <param name="farPoint1">원거리 포인트 1</param>
        /// <param name="closePoint2">근접 포인트 2</param>
        /// <param name="farPoint2">원거리 포인트 2</param>
        /// <param name="rectangle">사각형</param>
        /// <param name="startAngle">시작 각도</param>
        /// <param name="sweepAngle">스윕 각도</param>
        private void FindArcFromTangents
        (
            PointF         closePoint1,
            PointF         farPoint1,
            PointF         closePoint2,
            PointF         farPoint2,
            out RectangleF rectangle,
            out float      startAngle,
            out float      sweepAngle
        )
        {
            float deltaX1 = closePoint1.X - farPoint1.X;
            float deltaY1 = closePoint1.Y - farPoint1.Y;

            PointF temporaryPoint1 = new PointF(closePoint1.X - deltaY1, closePoint1.Y + deltaX1);

            float deltaX2 = closePoint2.X - farPoint2.X;
            float deltaY2 = closePoint2.Y - farPoint2.Y;

            PointF temporaryPoint2 = new PointF(closePoint2.X + deltaY2, closePoint2.Y - deltaX2);

            bool   linesIntersect;
            bool   segmentsIntersect;
            PointF point1;
            PointF temporaryClosePoint1;
            PointF temporaryClosePoint2;

            FindIntersection
            (
                closePoint1,
                temporaryPoint1,
                closePoint2,
                temporaryPoint2,
                out linesIntersect,
                out segmentsIntersect,
                out point1,
                out temporaryClosePoint1,
                out temporaryClosePoint2
            );

            float deltaX = closePoint1.X - point1.X;
            float deltaY = closePoint1.Y - point1.Y;
            float radius = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);

            rectangle = new RectangleF
            (
                point1.X - radius,
                point1.Y - radius,
                2 * radius,
                2 * radius
            );

            startAngle = (float)(Math.Atan2(deltaY, deltaX) * 180 / Math.PI);

            deltaX = closePoint2.X - point1.X;
            deltaY = closePoint2.Y - point1.Y;

            float endAngle = (float)(Math.Atan2(deltaY, deltaX) * 180 / Math.PI);

            sweepAngle = endAngle - startAngle;

            if(sweepAngle > 180)
            {
                sweepAngle = sweepAngle - 360;
            }

            if(sweepAngle < -180)
            {
                sweepAngle = 360 + sweepAngle;
            }
        }

        #endregion
        #region 세그먼트들에서 호 찾기 - FindArcFromSegments(startPoint1, endPoint1, startPoint2, endPoint2, rectangle,
            startAngle, sweepAngle, farPoint1, closePoint1, farPoint2, closePoint2)

        /// <summary>
        /// 세그먼트들에서 호 찾기
        /// </summary>
        /// <param name="startPoint1">시작 포인트 1</param>
        /// <param name="endPoint1">종료 포인트 1</param>
        /// <param name="startPoint2">시작 포인트 2</param>
        /// <param name="endPoint2">종료 포인트 2</param>
        /// <param name="rectangle">사각형</param>
        /// <param name="startAngle">시작 각도</param>
        /// <param name="sweepAngle">스윕 각도</param>
        /// <param name="farPoint1">원거리 포인트 1</param>
        /// <param name="closePoint1">근접 포인트 1</param>
        /// <param name="farPoint2">원거리 포인트 2</param>
        /// <param name="closePoint2">근접 포인트 2</param>
        private void FindArcFromSegments
        (
            PointF         startPoint1,
            PointF         endPoint1,
            PointF         startPoint2,
            PointF         endPoint2,
            out RectangleF rectangle,
            out float      startAngle,
            out float      sweepAngle,
            out PointF     farPoint1,
            out PointF     closePoint1,
            out PointF     farPoint2,
            out PointF     closePoint2
        )
        {
            PointF point;
            bool   linesIntersect;
            bool   segmentsIntersect;
            PointF temporaryClosePoint1;
            PointF temporaryClosePoint2;

            FindIntersection
            (
                startPoint1,
                endPoint1,
                startPoint2,
                endPoint2,
                out linesIntersect,
                out segmentsIntersect,
                out point,
                out temporaryClosePoint1,
                out temporaryClosePoint2
            );

            if(!linesIntersect)
            {
                throw new NotImplementedException("The segments are parallel.");
            }

            float closeDistance1;
            float closeDistance2;
            float farDistance1;
            float farDistance2;

            if(startPoint1.GetDistance(point) < endPoint1.GetDistance(point))
            {
                closePoint1    = startPoint1;
                farPoint1      = endPoint1;
                closeDistance1 = startPoint1.GetDistance(point);
                farDistance1   = endPoint1.GetDistance(point);
            }
            else
            {
                closePoint1    = endPoint1;
                farPoint1      = startPoint1;
                closeDistance1 = endPoint1.GetDistance(point);
                farDistance1   = startPoint1.GetDistance(point);
            }

            if(startPoint2.GetDistance(point) < endPoint2.GetDistance(point))
            {
                closePoint2    = startPoint2;
                farPoint2      = endPoint2;
                closeDistance2 = startPoint2.GetDistance(point);
                farDistance2   = endPoint1.GetDistance(point);
            }
            else
            {
                closePoint2    = endPoint2;
                farPoint2      = startPoint2;
                closeDistance2 = endPoint2.GetDistance(point);
                farDistance2   = startPoint1.GetDistance(point);
            }

            if(closeDistance1 < closeDistance2)
            {
                closePoint2 = GetPointAtDistance(point, farPoint2, closeDistance1);

                closeDistance2 = closeDistance1;
            }
            else
            {
                closePoint1 = GetPointAtDistance(point, farPoint1, closeDistance2);

                closeDistance1 = closeDistance2;
            }

            FindArcFromTangents
            (
                closePoint1,
                farPoint1,
                closePoint2,
                farPoint2,
                out rectangle,
                out startAngle,
                out sweepAngle
            );
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요