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

728x90
반응형
728x170

TestProject.zip
다운로드

▶ IntersectionPoint.cs

using System.Drawing;

namespace TestProject
{
    /// <summary>
    /// 교차점
    /// </summary>
    public class IntersectionPoint
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 위치
        /// </summary>
        public PointF Location;

        /// <summary>
        /// 원 배열
        /// </summary>
        public Circle[] CircleArray = null;

        /// <summary>
        /// 상위 원
        /// </summary>
        public Circle CircleOnTop = null;

        #endregion

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

        #region 생성자 - IntersectionPoint(location, circle1, circle2)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="location">위치</param>
        /// <param name="circle1">원 1</param>
        /// <param name="circle2">원 2</param>
        public IntersectionPoint(PointF location, Circle circle1, Circle circle2)
        {
            Location    = location;
            CircleArray = new Circle[] { circle1, circle2 };
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 문자열 구하기 - ToString()

        /// <summary>
        /// 문자열 구하기
        /// </summary>
        /// <returns>문자열</returns>
        public override string ToString()
        {
            return string.Format("({0}/{1}", CircleArray[0], CircleArray[1]);
        }

        #endregion
        #region 다른 원 구하기 - GetOtherCircle(circle)

        /// <summary>
        /// 다른 원 구하기
        /// </summary>
        /// <param name="circle">원</param>
        /// <returns>다른 원</returns>
        public Circle GetOtherCircle(Circle circle)
        {
            if(CircleArray[0] != circle)
            {
                return CircleArray[0];
            }

            return CircleArray[1];
        }

        #endregion
        #region 하위 원 구하기 - GetCircleOnBottom()

        /// <summary>
        /// 하위 원 구하기
        /// </summary>
        /// <returns>하위 원</returns>
        public Circle GetCircleOnBottom()
        {
            if(CircleOnTop == null)
            {
                return null;
            }

            return GetOtherCircle(CircleOnTop);
        }

        #endregion
    }
}

 

728x90

 

▶ Circle.cs

using System;
using System.Collections.Generic;
using System.Drawing;

namespace TestProject
{
    /// <summary>
    /// 원
    /// </summary>
    public class Circle
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 중심 포인트
        /// </summary>
        public PointF CenterPoint;

        /// <summary>
        /// 반경
        /// </summary>
        public float Radius;
        
        /// <summary>
        /// 원 두께
        /// </summary>
        public float CircleThickness;

        /// <summary>
        /// 윤곽선 두께
        /// </summary>
        public float OutlineThickness;

        /// <summary>
        /// 채우기 색상
        /// </summary>
        public Color FillColor;
        
        /// <summary>
        /// 윤곽선 색상
        /// </summary>
        public Color OutlineColor;

        /// <summary>
        /// 교차점 리스트
        /// </summary>
        public List<IntersectionPoint> IntersectionPointList = new List<IntersectionPoint>();

        /// <summary>
        /// 각도 리스트
        /// </summary>
        public List<float> AngleList = new List<float>();

        /// <summary>
        /// 할당 카운트
        /// </summary>
        public int AssignedCount = 0;

        #endregion

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

        #region 생성자 - Circle(centerPoint, radius, circleThickness, outlineThickness, fillColor, outlineColor)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="centerPoint">중심 포인트</param>
        /// <param name="radius">반경</param>
        /// <param name="circleThickness">원 두께</param>
        /// <param name="outlineThickness">윤곽선 두께</param>
        /// <param name="fillColor">채우기 색상</param>
        /// <param name="outlineColor">윤곽선 색상</param>
        public Circle(PointF centerPoint, float radius, float circleThickness, float outlineThickness, Color fillColor, Color outlineColor)
        {
            CenterPoint      = centerPoint;
            Radius           = radius;
            CircleThickness  = circleThickness;
            OutlineThickness = outlineThickness;
            FillColor        = fillColor;
            OutlineColor     = outlineColor;

        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 교차점 찾기 - FindIntersectionPoint(circleArray)

        /// <summary>
        /// 교차점 찾기
        /// </summary>
        /// <param name="circleArray">원 배열</param>
        public static void FindIntersectionPoint(Circle[] circleArray)
        {
            for(int i = 0; i < circleArray.Length; i++)
            {
                for(int j = i + 1; j < circleArray.Length; j++)
                {
                    PointF point1;
                    PointF point2;

                    FindCircleCircleIntersectionPoint
                    (
                        circleArray[i].CenterPoint.X,
                        circleArray[i].CenterPoint.Y,
                        circleArray[i].Radius,
                        circleArray[j].CenterPoint.X,
                        circleArray[j].CenterPoint.Y,
                        circleArray[j].Radius,
                        out point1,
                        out point2
                    );

                    if(!float.IsNaN(point1.X))
                    {
                        IntersectionPoint intersectionPoint = new IntersectionPoint
                        (
                            point1,
                            circleArray[i],
                            circleArray[j]
                        );

                        circleArray[i].IntersectionPointList.Add(intersectionPoint);
                        circleArray[j].IntersectionPointList.Add(intersectionPoint);
                    }

                    if(!float.IsNaN(point2.X))
                    {
                        IntersectionPoint intersectionPoint = new IntersectionPoint
                        (
                            point2,
                            circleArray[i],
                            circleArray[j]
                        );

                        circleArray[i].IntersectionPointList.Add(intersectionPoint);
                        circleArray[j].IntersectionPointList.Add(intersectionPoint);
                    }
                }
            }

            foreach(Circle circle in circleArray)
            {
                circle.SortIntersectionPoint();
            }

            List<Circle> circleList = new List<Circle>(circleArray);

            while(circleList.Count > 0)
            {
                List<Circle> partiallyAssignedCircleList = new List<Circle>();

                Circle circle = circleList[0];

                circleList.RemoveAt(0);

                partiallyAssignedCircleList.Add(circle);

                if(circle.IntersectionPointList.Count > 0)
                {
                    circle.IntersectionPointList[0].CircleOnTop = circle;
                }

                while(partiallyAssignedCircleList.Count > 0)
                {
                    circle = partiallyAssignedCircleList[0];

                    partiallyAssignedCircleList.RemoveAt(0);

                    circle.Assign(circleList, partiallyAssignedCircleList);
                }
            }
        }

        #endregion

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

        #region 원과 원 교차점 찾기 - FindCircleCircleIntersectionPoint(centerX1, centerY1, radius1, centerX2, centerY2, radius2, intersection1, intersection2)

        /// <summary>
        /// 원과 원 교차점 찾기
        /// </summary>
        /// <param name="centerX1">중심 X1</param>
        /// <param name="centerY1">중심 Y1</param>
        /// <param name="radius1">반경1</param>
        /// <param name="centerX2">중심 X2</param>
        /// <param name="centerY2">중심 Y2</param>
        /// <param name="radius2">반경2</param>
        /// <param name="intersectionPoint1">교차점 1</param>
        /// <param name="intersectionPoint2">교차점 2</param>
        /// <returns>교차점 수</returns>
        private static int FindCircleCircleIntersectionPoint
        (
            float      centerX1,
            float      centerY1,
            float      radius1,
            float      centerX2,
            float      centerY2,
            float      radius2,
            out PointF intersectionPoint1,
            out PointF intersectionPoint2
        )
        {
            float deltaX = centerX1 - centerX2;
            float deltaY = centerY1 - centerY2;

            double distance = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);

            if(distance > radius1 + radius2)
            {
                intersectionPoint1 = new PointF(float.NaN, float.NaN);
                intersectionPoint2 = new PointF(float.NaN, float.NaN);

                return 0;
            }
            else if(distance < Math.Abs(radius1 - radius2))
            {
                intersectionPoint1 = new PointF(float.NaN, float.NaN);
                intersectionPoint2 = new PointF(float.NaN, float.NaN);

                return 0;
            }
            else if((distance == 0) && (radius1 == radius2))
            {
                intersectionPoint1 = new PointF(float.NaN, float.NaN);
                intersectionPoint2 = new PointF(float.NaN, float.NaN);

                return 0;
            }
            else
            {
                double a = (radius1 * radius1 - radius2 * radius2 + distance * distance) / (2 * distance);
                double h = Math.Sqrt(radius1 * radius1 - a * a);

                double centerX = centerX1 + a * (centerX2 - centerX1) / distance;
                double centerY = centerY1 + a * (centerY2 - centerY1) / distance;

                intersectionPoint1 = new PointF
                (
                    (float)(centerX + h * (centerY2 - centerY1) / distance),
                    (float)(centerY - h * (centerX2 - centerX1) / distance)
                );

                intersectionPoint2 = new PointF
                (
                    (float)(centerX - h * (centerY2 - centerY1) / distance),
                    (float)(centerY + h * (centerX2 - centerX1) / distance)
                );

                if(distance == radius1 + radius2)
                {
                    return 1;
                }

                return 2;
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 문자열 구하기 - ToString()

        /// <summary>
        /// 문자열 구하기
        /// </summary>
        /// <returns>문자열</returns>
        public override string ToString()
        {
            return string.Format("{0}, {1}", CenterPoint.X, CenterPoint.Y);
        }

        #endregion
        #region 그리기 - Draw(graphics)

        /// <summary>
        /// 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        public void Draw(Graphics graphics)
        {
            using(Pen fillPen = new Pen(FillColor, CircleThickness))
            {
                using(Pen outlinePen = new Pen(OutlineColor, OutlineThickness))
                {
                    graphics.DrawThickArc(CenterPoint, Radius, 0, 360, fillPen, outlinePen);
                }
            }
        }

        #endregion
        #region 교차점 그리기 - DrawIntersectionPoint(graphics)

        /// <summary>
        /// 교차점 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        public void DrawIntersectionPoint(Graphics graphics)
        {
            using(Pen fillPen = new Pen(FillColor, CircleThickness))
            {
                using(Pen outlinePen = new Pen(OutlineColor, OutlineThickness))
                {
                    for(int i = 0; i < IntersectionPointList.Count; i++)
                    {
                        if(IntersectionPointList[i].CircleOnTop == this)
                        {
                            const float SWEEP_ANGLE = 30;

                            float startAngle = AngleList[i] - SWEEP_ANGLE / 2f;

                            graphics.DrawThickArc(CenterPoint, Radius, startAngle, SWEEP_ANGLE, fillPen, outlinePen);
                        }
                    }
                }
            }
        }

        #endregion
        #region 모든 교차점 할당 여부 구하기 - IsAllIntersectionPointsAreAssigned()

        /// <summary>
        /// 모든 교차점 할당 여부 구하기
        /// </summary>
        /// <returns>모든 교차점 할당 여부</returns>
        public bool IsAllIntersectionPointsAreAssigned()
        {
            return AssignedCount == IntersectionPointList.Count;
        }

        #endregion

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

        #region 교차점 정렬하기 - SortIntersectionPoint()

        /// <summary>
        /// 교차점 정렬하기
        /// </summary>
        private void SortIntersectionPoint()
        {
            AngleList = new List<float>();

            foreach(IntersectionPoint intersectionPoint in IntersectionPointList)
            {
                float deltaX = intersectionPoint.Location.X - CenterPoint.X;
                float deltaY = intersectionPoint.Location.Y - CenterPoint.Y;

                double radian = Math.Atan2(deltaY, deltaX);

                AngleList.Add((float)(radian / Math.PI * 180));
            }

            IntersectionPoint[] intersectionPointArray = IntersectionPointList.ToArray();

            float[] angleArray = AngleList.ToArray();

            Array.Sort(angleArray, intersectionPointArray);

            IntersectionPointList = new List<IntersectionPoint>(intersectionPointArray);

            AngleList = new List<float>(angleArray);
        }

        #endregion
        #region 할당하기 - Assign(unfinishedCircleList, partiallyAssignedCircleList)

        /// <summary>
        /// 할당하기
        /// </summary>
        /// <param name="unfinishedCircleList">미종료 원 리스트</param>
        /// <param name="partiallyAssignedCircleList">부분 할당 원 리스트</param>
        private void Assign(List<Circle> unfinishedCircleList, List<Circle> partiallyAssignedCircleList)
        {
            if((IntersectionPointList.Count == 0) || IsAllIntersectionPointsAreAssigned())
            {
                partiallyAssignedCircleList.Remove(this);

                return;
            }

            int firstAssigned = -1;

            for(int i = 0; i < IntersectionPointList.Count; i++)
            {
                if(IntersectionPointList[i].CircleOnTop != null)
                {
                    firstAssigned = i;

                    break;
                }
            }

            bool isLast = (IntersectionPointList[firstAssigned].CircleOnTop == this);

            for(int i = 1; i < IntersectionPointList.Count; i++)
            {
                int index = (firstAssigned + i) % IntersectionPointList.Count;

                if(IntersectionPointList[index].CircleOnTop == null)
                {
                    Circle otherCircle = IntersectionPointList[index].GetOtherCircle(this);

                    if(isLast)
                    {
                        IntersectionPointList[index].CircleOnTop = otherCircle;
                    }
                    else
                    {
                        IntersectionPointList[index].CircleOnTop = this;
                    }

                    if(!otherCircle.IsAllIntersectionPointsAreAssigned() && !partiallyAssignedCircleList.Contains(otherCircle))
                    {
                        partiallyAssignedCircleList.Add(otherCircle);
                    }

                    if(unfinishedCircleList.Contains(otherCircle))
                    {
                        unfinishedCircleList.Remove(otherCircle);
                    }

                    AssignedCount++;
                }

                isLast = !isLast;
            }
        }

        #endregion
    }
}

 

300x250

 

▶ 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 Circle[] circleArray = null;

        #endregion

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

        #region 생성자 - MainForm()

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

            Load  += Form_Load;
            Paint += Form_Paint;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public
        //////////////////////////////////////////////////////////////////////////////// Event

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

        /// <summary>
        /// 폼 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_Load(object sender, EventArgs e)
        {
          //SetCircleArray1();
          //SetCircleArray2();
          //SetCircleArray3();
          //SetCircleArray4();
          //SetCircleArray5();
          //SetCircleArray6();
            SetCircleArray7();

            Circle.FindIntersectionPoint(this.circleArray);
        }

        #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;

            foreach(Circle circle in circleArray)
            {
                circle.Draw(e.Graphics);
            }

            foreach(Circle circle in circleArray)
            {
                circle.DrawIntersectionPoint(e.Graphics);
            }
        }

        #endregion

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

        #region 원 배열 설정하기 1 - SetCircleArray1()

        /// <summary>
        /// 원 배열 설정하기 1
        /// </summary>
        private void SetCircleArray1()
        {
            this.circleArray = new Circle[]
            {
                new Circle(new PointF(125, 100), 50, 10, 2, Color.Orange, Color.Black),
                new Circle(new PointF(100, 150), 50, 10, 2, Color.Red   , Color.Black),
                new Circle(new PointF(150, 150), 50, 10, 2, Color.Blue  , Color.Black)
            };
        }

        #endregion
        #region 원 배열 설정하기 2 - SetCircleArray2()

        /// <summary>
        /// 원 배열 설정하기 2
        /// </summary>
        private void SetCircleArray2()
        {
            this.circleArray = new Circle[]
            {
                new Circle(new PointF( 60, 100), 30, 10, 2, Color.Orange, Color.Black),
                new Circle(new PointF(100, 100), 30, 10, 2, Color.Red   , Color.Black),
                new Circle(new PointF(140, 100), 30, 10, 2, Color.Green , Color.Black),
                new Circle(new PointF(180, 100), 30, 10, 2, Color.Blue  , Color.Black)
            };
        }

        #endregion
        #region 원 배열 설정하기 3 - SetCircleArray3()

        /// <summary>
        /// 원 배열 설정하기 3
        /// </summary>
        private void SetCircleArray3()
        {
            this.circleArray = new Circle[]
            {
                new Circle(new PointF( 70,  70), 40, 10, 2, Color.Orange, Color.Black),
                new Circle(new PointF(180,  70), 40, 10, 2, Color.Green , Color.Black),
                new Circle(new PointF( 70, 180), 40, 10, 2, Color.Red   , Color.Black),
                new Circle(new PointF(180, 180), 40, 10, 2, Color.Blue  , Color.Black),
                new Circle(new PointF(125, 125), 60, 10, 2, Color.Yellow, Color.Black)
            };
        }

        #endregion
        #region 원 배열 설정하기 4 - SetCircleArray4()

        /// <summary>
        /// 원 배열 설정하기 4
        /// </summary>
        private void SetCircleArray4()
        {
            this.circleArray = new Circle[]
            {
                new Circle(new PointF( 90,  75), 30, 7, 2, Color.Orange, Color.Black),
                new Circle(new PointF( 75, 100), 30, 7, 2, Color.Red   , Color.Black),
                new Circle(new PointF(105, 100), 30, 7, 2, Color.Blue  , Color.Black),

                new Circle(new PointF(170, 155), 30, 7, 2, Color.Orange, Color.Black),
                new Circle(new PointF(155, 180), 30, 7, 2, Color.Red   , Color.Black),
                new Circle(new PointF(185, 180), 30, 7, 2, Color.Blue  , Color.Black)
            };
        }

        #endregion
        #region 원 배열 설정하기 5 - SetCircleArray5()

        /// <summary>
        /// 원 배열 설정하기 5
        /// </summary>
        private void SetCircleArray5()
        {
            this.circleArray = new Circle[]
            {
                new Circle(new PointF(100, 100), 50, 10, 2, Color.Orange, Color.Black),
                new Circle(new PointF(150, 100), 50, 10, 2, Color.Green , Color.Black),
                new Circle(new PointF(100, 150), 50, 10, 2, Color.Red   , Color.Black),
                new Circle(new PointF(150, 150), 50, 10, 2, Color.Blue  , Color.Black)
            };
        }

        #endregion
        #region 원 배열 설정하기 6 - SetCircleArray6()

        /// <summary>
        /// 원 배열 설정하기 6
        /// </summary>
        private void SetCircleArray6()
        {
            float x = 60;
            float y = 100;

            List<Circle> circleList = new List<Circle>();

            circleList.Add(new Circle(new PointF(x, y), 40, 7, 2, Color.SteelBlue, Color.Transparent));

            x += 46;
            y += 46;

            circleList.Add(new Circle(new PointF(x, y), 40, 7, 2, Color.Orange, Color.Transparent));

            x += 46;
            y -= 46;

            circleList.Add(new Circle(new PointF(x, y), 40, 7, 2, Color.Black, Color.Transparent));

            x += 46;
            y += 46;

            circleList.Add(new Circle(new PointF(x, y), 40, 7, 2, Color.Green, Color.Transparent));

            x += 46;
            y -= 46;

            circleList.Add(new Circle(new PointF(x, y), 40, 7, 2, Color.Red, Color.Transparent));

            this.circleArray = circleList.ToArray();
        }

        #endregion
        #region 원 배열 설정하기 7 - SetCircleArray7()

        /// <summary>
        /// 원 배열 설정하기 7
        /// </summary>
        private void SetCircleArray7()
        {
            Color[] colorArray =
            {
                Color.Red,
                Color.Green,
                Color.Orange,
                Color.Yellow,
                Color.Blue,
                Color.Black,
            };

            this.circleArray = new Circle[6];

            float centerX = ClientSize.Width  / 2f;
            float centerY = ClientSize.Height / 2f;

            float radius = 60;

            double theta  = -Math.PI / 2;
            double dtheta = 2 * Math.PI / 5;

            for(int i = 0; i < 5; i++)
            {
                PointF centerPoint = new PointF
                (
                    (float)(centerX + radius * Math.Cos(theta)),
                    (float)(centerY + radius * Math.Sin(theta))
                );

                this.circleArray[i] = new Circle(centerPoint, 45, 8, 2, colorArray[i], Color.Black);

                theta += dtheta;
            }

            this.circleArray[5] = new Circle(new PointF(centerX, centerY), 50, 8, 2, colorArray[5], Color.Black);
        }

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

댓글을 달아 주세요