첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ ArcPart.cs

namespace TestProject
{
    /// <summary>
    /// 아크 파트
    /// </summary>
    public enum ArcPart
    {
        /// <summary>
        /// 해당 무
        /// </summary>
        None,

        /// <summary>
        /// 시작 포인트
        /// </summary>
        StartPoint,

        /// <summary>
        /// 종료 포인트
        /// </summary>
        EndPoint,

        /// <summary>
        /// 몸체
        /// </summary>
        Body,
    }
}

 

728x90

 

▶ 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 RectangleF arcRectangle;

        /// <summary>
        /// 시작 각도
        /// </summary>
        private float startAngle;
        
        /// <summary>
        /// 스윕 각도
        /// </summary>
        private float sweepAngle;

        /// <summary>
        /// 종료 포인트 배열
        /// </summary>
        private PointF[] endPointArray;

        #endregion

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

        #region 생성자 - MainForm()

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

            Load                  += Form_Load;
            this.canvas.Paint     += canvas_Paint;
            this.canvas.MouseMove += canvas_MouseMove;
        }

        #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)
        {
            this.arcRectangle = new RectangleF
            (
                20,
                20,
                this.canvas.ClientSize.Width  - 40,
                this.canvas.ClientSize.Height - 40
            );

            this.startAngle = 45;
            this.sweepAngle = 255;

            this.endPointArray = FindArcEndPointArray
            (
                this.arcRectangle,
                this.startAngle,
                this.startAngle + this.sweepAngle
            );
        }

        #endregion
        #region 캔버스 페인트시 처리하기 - canvas_Paint(sender, e)

        /// <summary>
        /// 캔버스 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.Clear(this.canvas.BackColor);

            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            using(Pen pen = new Pen(Color.Blue, 6))
            {
                e.Graphics.DrawArc(pen, this.arcRectangle, this.startAngle, this.sweepAngle);
            }

            for(int i = 0; i < 2; i++)
            {
                RectangleF rectangle = new RectangleF
                (
                    this.endPointArray[i].X - 3,
                    this.endPointArray[i].Y - 3,
                    6,
                    6
                );

                e.Graphics.FillRectangle(Brushes.White, rectangle);

                e.Graphics.DrawRectangle(Pens.Black, Rectangle.Round(rectangle));
            }
        }

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

        /// <summary>
        /// 캔버스 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            switch(GetArcPart(arcRectangle, endPointArray[0], endPointArray[1], e.Location, 3))
            {
                case ArcPart.None       : this.canvas.Cursor = Cursors.Default; break;
                case ArcPart.Body       : this.canvas.Cursor = Cursors.SizeAll; break;
                case ArcPart.StartPoint : this.canvas.Cursor = Cursors.PanWest; break;
                case ArcPart.EndPoint   : this.canvas.Cursor = Cursors.PanEast; break;
                default              : throw new Exception("Unknown arc part");
            }
        }

        #endregion

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

        #region 타원 세그먼트 교차 포인트 배열 찾기 - FindEllipseSegmentIntersectionPointArray(rectangle, point1, point2, segmentOnly)

        /// <summary>
        /// 타원 세그먼트 교차 포인트 배열 찾기
        /// </summary>
        /// <param name="rectangle">사각형</param>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <param name="segmentOnly">세그먼트 전용 여부</param>
        /// <returns>타원 세그먼트 교차 포인트 배열</returns>
        private static PointF[] FindEllipseSegmentIntersectionPointArray
        (
            RectangleF rectangle,
            PointF     point1,
            PointF     point2,
            bool       segmentOnly
        )
        {
            if((rectangle.Width == 0) || (rectangle.Height == 0) || ((point1.X == point2.X) && (point1.Y == point2.Y)))
            {
                return new PointF[] { };
            }

            if(rectangle.Width < 0)
            {
                rectangle.X     =  rectangle.Right;
                rectangle.Width = -rectangle.Width;
            }

            if(rectangle.Height < 0)
            {
                rectangle.Y      =  rectangle.Bottom;
                rectangle.Height = -rectangle.Height;
            }

            float centerX = rectangle.Left + rectangle.Width  / 2f;
            float centerY = rectangle.Top  + rectangle.Height / 2f;

            rectangle.X -= centerX;
            rectangle.Y -= centerY;

            point1.X -= centerX;
            point1.Y -= centerY;
            point2.X -= centerX;
            point2.Y -= centerY;

            float a = rectangle.Width  / 2;
            float b = rectangle.Height / 2;

            float A = (point2.X - point1.X) * (point2.X - point1.X) / a / a + (point2.Y - point1.Y) * (point2.Y - point1.Y) / b / b;
            float B = 2 * point1.X * (point2.X - point1.X) / a / a + 2 * point1.Y * (point2.Y - point1.Y) / b / b;
            float C = point1.X * point1.X / a / a + point1.Y * point1.Y / b / b - 1;

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

            float discriminant = B * B - 4 * A * C;

            if(discriminant == 0)
            {
                valueList.Add(-B / 2 / A);
            }
            else if(discriminant > 0)
            {
                valueList.Add((float)((-B + Math.Sqrt(discriminant)) / 2 / A));
                valueList.Add((float)((-B - Math.Sqrt(discriminant)) / 2 / A));
            }

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

            foreach(float value in valueList)
            {
                if(!segmentOnly || ((value >= 0f) && (value <= 1f)))
                {
                    float x = point1.X + (point2.X - point1.X) * value + centerX;
                    float y = point1.Y + (point2.Y - point1.Y) * value + centerY;

                    pointList.Add(new PointF(x, y));
                }
            }

            return pointList.ToArray();
        }

        #endregion
        #region 아크 종단 포인트 배열 찾기 - FindArcEndPointArray(rectangle, degree1, degree2)

        /// <summary>
        /// 아크 종단 포인트 배열 찾기
        /// </summary>
        /// <param name="rectangle">사각형</param>
        /// <param name="degree1">각도 1</param>
        /// <param name="degree2">각도 2</param>
        /// <returns>아크 종단 포인트 배열</returns>
        private PointF[] FindArcEndPointArray(RectangleF rectangle, double degree1, double degree2)
        {
            PointF centerPoint = new PointF
            (
                rectangle.X + rectangle.Width  / 2f,
                rectangle.Y + rectangle.Height / 2f
            );

            float distance = rectangle.Width + rectangle.Height;

            double radian1 = degree1 * Math.PI / 180;

            PointF point1 = new PointF
            (
                (float)(centerPoint.X + distance * Math.Cos(radian1)),
                (float)(centerPoint.Y + distance * Math.Sin(radian1))
            );

            double radian2 = degree2 * Math.PI / 180;

            PointF point2 = new PointF
            (
                (float)(centerPoint.X + distance * Math.Cos(radian2)),
                (float)(centerPoint.Y + distance * Math.Sin(radian2))
            );

            PointF[] intersectionPointArray1 = FindEllipseSegmentIntersectionPointArray
            (
                rectangle,
                centerPoint,
                point1,
                true
            );

            PointF[] intersectionPointArray2 = FindEllipseSegmentIntersectionPointArray
            (
                rectangle,
                centerPoint,
                point2,
                true
            );

            return new PointF[]
            {
                intersectionPointArray1[0],
                intersectionPointArray2[0]
            };
        }

        #endregion
        #region 거리 구하기 - GetDistance(point1, point2)

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

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

        #endregion
        #region 아크 파트 구하기 - GetArcPart(arcRectangle, startPoint, endPoint, targetPoint, radius)

        /// <summary>
        /// 아크 파트 구하기
        /// </summary>
        /// <param name="arcRectangle">아크 사각형</param>
        /// <param name="startPoint">시작 포인트</param>
        /// <param name="endPoint">종료 포인트</param>
        /// <param name="targetPoint">타겟 포인트</param>
        /// <param name="radius">반경</param>
        /// <returns>아크 파트</returns>
        public ArcPart GetArcPart(RectangleF arcRectangle, PointF startPoint, PointF endPoint, Point targetPoint, float radius)
        {
            if(GetDistance(targetPoint, startPoint) < radius)
            {
                return ArcPart.StartPoint;
            }

            if(GetDistance(targetPoint, endPoint) < radius)
            {
                return ArcPart.EndPoint;
            }

            float centerX = (arcRectangle.Left + arcRectangle.Right ) / 2f;
            float centerY = (arcRectangle.Top  + arcRectangle.Bottom) / 2f;

            float deltaX = targetPoint.X - centerX;
            float deltaY = targetPoint.Y - centerY;

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

            while(degree < 0)
            {
                degree += 360;
            }

            if((degree < startAngle) || (degree > startAngle + sweepAngle))
            {
                return ArcPart.None;
            }

            PointF centerPoint = new PointF(centerX, centerY);

            PointF[] intersectionPointArray = FindEllipseSegmentIntersectionPointArray(arcRectangle, centerPoint, targetPoint, false);

            for(int i = 0; i < intersectionPointArray.Length; i++)
            {
                if(GetDistance(targetPoint, intersectionPointArray[i]) < radius)
                {
                    return ArcPart.Body;
                }
            }

            return ArcPart.None;
        }

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

댓글을 달아 주세요