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

TestProject.zip
다운로드

▶ MainForm.cs

using System;
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>
        /// 파란 막대 앵커 X
        /// </summary>
        private float blueRodAnchorX = 200;

        /// <summary>
        /// 파란 막대 앵커 Y
        /// </summary>
        private float blueRodAnchorY = 250;

        /// <summary>
        /// 바퀴 중심 X
        /// </summary>
        private float wheelCenterX = 300;

        /// <summary>
        /// 바퀴 중심 Y
        /// </summary>
        private float wheelCenterY = 250;

        /// <summary>
        /// 파란 막대의 앵커와 바퀴 중심 사이 거리
        /// </summary>
        private float distance = 200;

        /// <summary>
        /// 녹색 막대 길이
        /// </summary>
        private float greenRodLength = 200;

        /// <summary>
        /// 파란 막대 길이
        /// </summary>
        private float blueRodLength = 220;

        /// <summary>
        /// 파란 막대 나머지 부분 길이
        /// </summary>
        private float blueRodOtherPartLength = 120;

        /// <summary>
        /// 바퀴 반지름
        /// </summary>
        private float wheelRadius = 60;

        /// <summary>
        /// 델타 세타
        /// </summary>
        private const double DELTA_THETA = Math.PI / 10;

        /// <summary>
        /// 세타
        /// </summary>
        private double theta = 0;

        /// <summary>
        /// 비트맵
        /// </summary>
        private Bitmap bitmap = null;

        /// <summary>
        /// 그래픽스
        /// </summary>
        private Graphics graphics;

        #endregion

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

        #region 생성자 - MainForm()

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

            #region 이벤트를 설정한다.

            this.startButton.Click += startButton_Click;
            this.timer.Tick        += timer_Tick;

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 시작하기 버튼 클릭시 처리하기 - startButton_Click(sender, e)

        /// <summary>
        /// 시작하기 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void startButton_Click(object sender, EventArgs e)
        {
            if(this.startButton.Text == "시작하기")
            {
                this.startButton.Text = "중단하기";

                this.distance               = float.Parse(this.distanceTextBox.Text);
                this.greenRodLength         = float.Parse(this.greenRodLengthTextBox.Text);
                this.blueRodLength          = float.Parse(this.blueRodLengthTextBox.Text);
                this.blueRodOtherPartLength = float.Parse(this.blueRodOtherPartLengthTextBox.Text);
                this.wheelRadius            = float.Parse(this.wheelRadiusTextBox.Text);

                this.wheelCenterX = this.blueRodAnchorX + this.distance;

                this.theta = 0;

                this.bitmap = new Bitmap
                (
                    this.canvasPictureBox.ClientSize.Width,
                    this.canvasPictureBox.ClientSize.Height
                );

                this.graphics = Graphics.FromImage(this.bitmap);

                this.canvasPictureBox.Image = this.bitmap;

                this.timer.Enabled = true;
            }
            else
            {
                this.startButton.Text = "시작하기";

                this.timer.Enabled = false;
            }
        }

        #endregion
        #region 타이머 틱 처리하기 - timer_Tick(sender, e)

        /// <summary>
        /// 타이머 틱 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            this.theta += DELTA_THETA;

            Draw();

            this.canvasPictureBox.Refresh();
        }

        #endregion

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

        #region 원과 원의 교차 포인트 찾기 - FindCircleCircleIntersectionPoint(cx1, cy1, radius1, cx2, cy2, radius2, intersectionPoint1, intersectionPoint2)

        /// <summary>
        /// 원과 원의 교차 포인트 찾기
        /// </summary>
        /// <param name="cx1">원 1 중심 X</param>
        /// <param name="cy1">원 1 중심 Y</param>
        /// <param name="radius1">원 1 반지름</param>
        /// <param name="cx2">원 2 중심 X</param>
        /// <param name="cy2">원 2 중심 Y</param>
        /// <param name="radius2">원 2 반지름</param>
        /// <param name="intersectionPoint1">교차 포인트 1</param>
        /// <param name="intersectionPoint2">교차 포인트 2</param>
        /// <returns>교차 포인트 수</returns>
        private int FindCircleCircleIntersectionPoint(float cx1, float cy1, float radius1, float cx2, float cy2, float radius2,
            out PointF intersectionPoint1, out PointF intersectionPoint2)
        {
            float dx = cx1 - cx2;
            float dy = cy1 - cy2;

            double distance = Math.Sqrt(dx * dx + dy * dy);

            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 cx3 = cx1 + a * (cx2 - cx1) / distance;
                double cy3 = cy1 + a * (cy2 - cy1) / distance;

                intersectionPoint1 = new PointF
                (
                    (float)(cx3 + h * (cy2 - cy1) / distance),
                    (float)(cy3 - h * (cx2 - cx1) / distance)
                );

                intersectionPoint2 = new PointF
                (
                    (float)(cx3 - h * (cy2 - cy1) / distance),
                    (float)(cy3 + h * (cx2 - cx1) / distance)
                );

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

                return 2;
            }
        }

        #endregion
        #region 그리기 - Draw()

        /// <summary>
        /// 그리기
        /// </summary>
        private void Draw()
        {
            this.graphics.Clear(BackColor);

            this.graphics.SmoothingMode = SmoothingMode.AntiAlias;

            using(Pen pen = new Pen(Color.Blue, 2))
            {
                RectangleF wheelRectangle = new RectangleF
                (
                    this.wheelCenterX - this.wheelRadius,
                    this.wheelCenterY - this.wheelRadius,
                    2 * this.wheelRadius,
                    2 * this.wheelRadius
                );

                this.graphics.FillEllipse(Brushes.LightBlue, wheelRectangle);

                pen.Color = Color.Blue;
                pen.Width = 2;

                this.graphics.DrawEllipse(pen, wheelRectangle);

                float linkageX1 = (float)(this.wheelCenterX + Math.Cos(this.theta) * this.wheelRadius);
                float linkageY1 = (float)(this.wheelCenterY + Math.Sin(this.theta) * this.wheelRadius);

                PointF pt1;
                PointF pt2;

                FindCircleCircleIntersectionPoint
                (
                    this.blueRodAnchorX,
                    this.blueRodAnchorY,
                    this.blueRodLength,
                    linkageX1,
                    linkageY1,
                    this.greenRodLength,
                    out pt1,
                    out pt2
                );

                float linkageX2 = pt1.X;
                float linkageY2 = pt1.Y;

                pen.Color = Color.Green;
                pen.Width = 5;

                this.graphics.DrawLine(pen, linkageX1, linkageY1, linkageX2, linkageY2);

                pen.Color = Color.Lime;
                pen.Width = 2;

                this.graphics.DrawLine(pen, linkageX1, linkageY1, linkageX2, linkageY2);

                pen.Color = Color.Blue;
                pen.Width = 5;

                this.graphics.DrawLine(pen, this.blueRodAnchorX, this.blueRodAnchorY, linkageX2, linkageY2);

                pen.Color = Color.LightBlue;
                pen.Width = 2;

                this.graphics.DrawLine(pen, this.blueRodAnchorX, this.blueRodAnchorY, linkageX2, linkageY2);

                float dx = this.blueRodAnchorX - linkageX2;
                float dy = this.blueRodAnchorY - linkageY2;

                double length = Math.Sqrt(dx * dx + dy * dy);

                float lowerX1 = (float)(this.blueRodAnchorX + dx * this.blueRodOtherPartLength / length);
                float lowerY1 = (float)(this.blueRodAnchorY + dy * this.blueRodOtherPartLength / length);

                pen.Color = Color.Blue;
                pen.Width = 5;

                this.graphics.DrawLine(pen, this.blueRodAnchorX, this.blueRodAnchorY, lowerX1, lowerY1);

                pen.Color = Color.LightBlue;
                pen.Width = 2;

                this.graphics.DrawLine(pen, this.blueRodAnchorX, this.blueRodAnchorY, lowerX1, lowerY1);

                this.graphics.FillEllipse(Brushes.Black, this.wheelCenterX - 4, this.wheelCenterY - 4, 8, 8);

                this.graphics.FillEllipse(Brushes.Green, linkageX1 - 4, linkageY1 - 4, 8, 8);

                this.graphics.FillEllipse(Brushes.Green, linkageX2 - 4, linkageY2 - 4, 8, 8);

                this.graphics.FillEllipse(Brushes.Black, this.blueRodAnchorX - 4, this.blueRodAnchorY - 4, 8, 8);

                this.graphics.FillEllipse(Brushes.Blue, lowerX1 - 4, lowerY1 - 4, 8, 8);
            }
        }

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

댓글을 달아 주세요