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

TestProject.zip
다운로드

▶ Car.cs

using System.Drawing;

namespace TestProject
{
    /// <summary>
    /// 자동차
    /// </summary>
    public class Car
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 명칭
        /// </summary>
        public string Name;

        /// <summary>
        /// 색상
        /// </summary>
        public Color Color;

        /// <summary>
        /// 값 배열
        /// </summary>
        public float[] ValueArray;

        /// <summary>
        /// 포인트 배열
        /// </summary>
        public PointF[] PointArray = null;

        #endregion

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

        #region 생성자 - Car(name, color, valueArray)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="name">명칭</param>
        /// <param name="color">색상</param>
        /// <param name="valueArray">값 배열</param>
        public Car(string name, Color color, params float[] valueArray)
        {
            Name       = name;
            Color      = color;
            ValueArray = (float[])valueArray.Clone();
        }

        #endregion
    }
}

 

728x90

 

▶ Axis.cs

namespace TestProject
{
    /// <summary>
    /// 축
    /// </summary>
    public class Axis
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 명칭
        /// </summary>
        public string Name;
        
        /// <summary>
        /// 포맷 문자열
        /// </summary>
        public string FormatString;

        /// <summary>
        /// 최소값
        /// </summary>
        public float Minimum;
        
        /// <summary>
        /// 최대값
        /// </summary>
        public float Maximum;

        #endregion

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

        #region 생성자 - Axis(name, formatString, minimum, maximum)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="name">명칭</param>
        /// <param name="formatString">포맷 문자열</param>
        /// <param name="minimum">최소값</param>
        /// <param name="maximum">최대값</param>
        public Axis(string name, string formatString, float minimum, float maximum)
        {
            Name         = name;
            FormatString = formatString;
            Minimum      = minimum;
            Maximum      = maximum;
        }

        #endregion
    }
}

 

300x250

 

▶ MainForm.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 자동차 리스트
        /// </summary>
        private List<Car> carList = null;

        /// <summary>
        /// 축 리스트
        /// </summary>
        private List<Axis> axisList = null;

        #endregion

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

        #region 생성자 - MainForm()

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

            Load                                 += Form_Load;
            this.fillAreaCheckBox.CheckedChanged += fillAreaCheckBox_CheckedChanged;
            this.pictureBox.Resize               += pictureBox_Resize;
            this.pictureBox.MouseMove            += pictureBox_MouseMove;
            this.pictureBox.Paint                += pictureBox_Paint;
        }

        #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.carList = new List<Car>();

            this.carList.Add(new Car("Audi e-tron"  , Color.Red  , 69850, 80900, 8.4f, 218, 100f / 44));
            this.carList.Add(new Car("Jaguar I-PACE", Color.Green, 39090, 44590, 8.2f, 234, 100f / 30));
            this.carList.Add(new Car("Polestar 2"   , Color.Blue , 59900, 59900, 8.2f, 275, 100f / 27));

            this.axisList = new List<Axis>();

            this.axisList.Add(new Axis("PriceLow" , "c"   , 90000, 30000));
            this.axisList.Add(new Axis("PriceHigh", "c"   , 90000, 30000));
            this.axisList.Add(new Axis("Rating"   , "0.0" , 0    , 10   ));
            this.axisList.Add(new Axis("Range"    , "0"   , 0    , 300  ));
            this.axisList.Add(new Axis("Miles/kWh", "0.00", 0    , 5    ));
        }

        #endregion
        #region 영역 채우기 체크 박스 체크 변경시 처리하기 - fillAreaCheckBox_CheckedChanged(sender, e)

        /// <summary>
        /// 영역 채우기 체크 박스 체크 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void fillAreaCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            this.pictureBox.Refresh();
        }

        #endregion
        #region 픽처 박스 크기 변경시 처리하기 - pictureBox_Resize(sender, e)

        /// <summary>
        /// 픽처 박스 크기 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_Resize(object sender, EventArgs e)
        {
            this.pictureBox.Refresh();
        }

        #endregion
        #region 픽처 박스 마우스 이동시 처리하기 - pictureBox_MouseMove(sender, e)

        /// <summary>
        /// 픽처 박스 마우스 이동시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            string tipText = string.Empty;

            foreach(Car car in this.carList)
            {
                if(car.PointArray == null)
                {
                    continue;
                }

                for(int i = 0; i < car.ValueArray.Length; i++)
                {
                    if(IsPointClose(e.Location, car.PointArray[i], 8))
                    {
                        tipText = car.Name + " " +
                                  this.axisList[i].Name + " : " +
                                  car.ValueArray[i].ToString(this.axisList[i].FormatString);

                        break;
                    }

                    if(tipText != string.Empty)
                    {
                        break;
                    }
                }
            }

            if(tipText != this.pointToolTip.GetToolTip(this.pictureBox))
            {
                this.pointToolTip.SetToolTip(this.pictureBox, tipText);
            }
        }

        #endregion
        #region 픽처 박스 페인트시 처리하기 - pictureBox_Paint(sender, e)

        /// <summary>
        /// 픽처 박스 페인트시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode     = SmoothingMode.AntiAlias;
            e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;

            e.Graphics.Clear(this.pictureBox.BackColor);

            DrawChart(e.Graphics, this.fillAreaCheckBox.Checked);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Event

        #region 포인트 밀접 여부 구하기 - IsPointClose(point1, point2, radius)

        /// <summary>
        /// 포인트 밀접 여부 구하기
        /// </summary>
        /// <param name="point1">포인트 1</param>
        /// <param name="point2">포인트 2</param>
        /// <param name="radius">반경</param>
        /// <returns>포인트 밀접 여부</returns>
        private bool IsPointClose(PointF point1, PointF point2, float radius)
        {
            float deltaX = point1.X - point2.X;
            float deltaY = point1.Y - point2.Y;

            return (deltaX * deltaX + deltaY * deltaY) < (radius * radius);
        }

        #endregion
        #region 회전된 텍스트 그리기 - DrawRotatedText(graphics, font, brush, text, x, y, theta)

        /// <summary>
        /// 회전된 텍스트 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="font">폰트</param>
        /// <param name="brush">브러시</param>
        /// <param name="text">텍스트</param>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        /// <param name="theta">세타</param>
        private void DrawRotatedText(Graphics graphics, Font font, Brush brush, string text, double x, double y, double theta)
        {
            GraphicsState state = graphics.Save();

            graphics.ResetTransform();

            graphics.RotateTransform((float)(theta * 180 / Math.PI));

            graphics.TranslateTransform((float)x, (float)y, MatrixOrder.Append);

            using(StringFormat stringFormat = new StringFormat())
            {
                stringFormat.Alignment     = StringAlignment.Center;
                stringFormat.LineAlignment = StringAlignment.Center;

                graphics.DrawString(text, font, brush, 0, 0, stringFormat);
            }

            graphics.Restore(state);
        }

        #endregion
        #region 축 그리기 - DrawAxis(graphics, centerX, centerY, radiusX, radiusY, deltaTheta)

        /// <summary>
        /// 축 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="centerX">중심 X</param>
        /// <param name="centerY">중심 Y</param>
        /// <param name="radiusX">반경 X</param>
        /// <param name="radiusY">반경 Y</param>
        /// <param name="deltaTheta">델타 세타</param>
        private void DrawAxis(Graphics graphics, float centerX, float centerY, float radiusX, float radiusY, double deltaTheta)
        {
            double theta = -Math.PI / 2;

            using(Font font = new Font("나눔고딕코딩", 12))
            {
                for(int i = 0; i < this.axisList.Count; i++)
                {
                    double x = centerX + radiusX * Math.Cos(theta);
                    double y = centerY + radiusY * Math.Sin(theta);

                    graphics.DrawLine(Pens.Black, centerX, centerY, (float)x, (float)y);

                    x = centerX + (radiusX + 10) * Math.Cos(theta);
                    y = centerY + (radiusY + 10) * Math.Sin(theta);

                    DrawRotatedText
                    (
                        graphics,
                        font,
                        Brushes.Black,
                        this.axisList[i].Name,
                        x,
                        y,
                        theta + Math.PI / 2
                    );

                    theta += deltaTheta;
                }
            }
        }

        #endregion
        #region 레벨 그리기 - DrawLevel(graphics, centerX, centerY, radiusX, radiusY, deltaTheta)

        /// <summary>
        /// 레벨 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="centerX">중심 X</param>
        /// <param name="centerY">중심 Y</param>
        /// <param name="radiusX">반경 X</param>
        /// <param name="radiusY">반경 Y</param>
        /// <param name="deltaTheta">델타 세타</param>
        private void DrawLevel(Graphics graphics, float centerX, float centerY, float radiusX, float radiusY, double deltaTheta)
        {
            double   theta         = -Math.PI / 2;
            int      levelCount    = 5;
            double   deltaFraction = 1.0 / levelCount;
            double   fraction      = deltaFraction;
            PointF[] pointArray    = new PointF[this.axisList.Count];

            for(int level = 0; level < levelCount; level++)
            {
                for(int i = 0; i < this.axisList.Count; i++)
                {
                    double x = centerX + fraction * radiusX * Math.Cos(theta);
                    double y = centerY + fraction * radiusY * Math.Sin(theta);

                    pointArray[i] = new PointF((float)x, (float)y);

                    theta += deltaTheta;
                }

                graphics.DrawPolygon(Pens.Black, pointArray);

                fraction += deltaFraction;
            }
        }

        #endregion
        #region 레이더 차트 그리기 - DrawRadarChart(car, graphics, fillArea, centerX, centerY, radiusX, radiusY, deltaTheta)

        /// <summary>
        /// 레이더 차트 그리기
        /// </summary>
        /// <param name="car">자동차</param>
        /// <param name="graphics">그래픽스</param>
        /// <param name="fillArea">영역 채우기 여부</param>
        /// <param name="centerX">중심 X</param>
        /// <param name="centerY">중심 Y</param>
        /// <param name="radiusX">반경 X</param>
        /// <param name="radiusY">반경 Y</param>
        /// <param name="deltaTheta">델타 세타</param>
        private void DrawRadarChart
        (
            Car      car,
            Graphics graphics,
            bool     fillArea,
            float    centerX,
            float    centerY,
            float    radiusX,
            float    radiusY,
            double   deltaTheta
        )
        {
            PointF[] pointArray = new PointF[this.axisList.Count];

            double theta = -Math.PI / 2;

            for(int i = 0; i < this.axisList.Count; i++)
            {
                double fraction = (car.ValueArray[i] - this.axisList[i].Minimum) / (this.axisList[i].Maximum - this.axisList[i].Minimum);

                double x = centerX + fraction * radiusX * Math.Cos(theta);
                double y = centerY + fraction * radiusY * Math.Sin(theta);

                pointArray[i] = new PointF((float)x, (float)y);

                theta += deltaTheta;
            }

            car.PointArray = pointArray;

            if(fillArea)
            {
                Color color = Color.FromArgb(64, car.Color);

                using(Brush brush = new SolidBrush(color))
                {
                    graphics.FillPolygon(brush, pointArray);
                }
            }

            using(Pen pen = new Pen(car.Color, 3))
            {
                graphics.DrawPolygon(pen, pointArray);
            }
        }
        
        #endregion
        #region 레이더 차트 그리기 - DrawRadarChart(graphics, fillArea, centerX, centerY, radiusX, radiusY, deltaTheta)

        /// <summary>
        /// 레이더 차트 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="fillArea">영역 채우기 여부</param>
        /// <param name="centerX">중심 X</param>
        /// <param name="centerY">중심 Y</param>
        /// <param name="radiusX">반경 X</param>
        /// <param name="radiusY">반경 Y</param>
        /// <param name="deltaTheta">델타 세타</param>
        private void DrawRadarChart
        (
            Graphics graphics,
            bool     fillArea,
            float    centerX,
            float    centerY,
            float    radiusX,
            float    radiusY,
            double   deltaTheta
        )
        {
            foreach(Car car in this.carList)
            {
                DrawRadarChart(car, graphics, fillArea, centerX, centerY, radiusX, radiusY, deltaTheta);
            }
        }

        #endregion
        #region 차트 그리기 - DrawChart(graphics, fillArea)

        /// <summary>
        /// 차트 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="fillArea">영역 채우기</param>
        private void DrawChart(Graphics graphics, bool fillArea)
        {
            float centerX = this.pictureBox.ClientSize.Width  / 2f;
            float centerY = this.pictureBox.ClientSize.Height / 2f;

            float radiusX = centerX - 20;
            float radiusY = centerY - 20;

            double deltaTheta = 2 * Math.PI / this.axisList.Count;

            DrawAxis(graphics, centerX, centerY, radiusX, radiusY, deltaTheta);

            DrawLevel(graphics, centerX, centerY, radiusX, radiusY, deltaTheta);

            DrawRadarChart(graphics, fillArea, centerX, centerY, radiusX, radiusY, deltaTheta);
        }

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

댓글을 달아 주세요