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

■ 별점(Star Rating)을 그리는 방법을 보여준다.

TestProject.zip
0.01MB

▶ MainForm.cs

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

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

        #region 생성자 - MainForm()

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

        #endregion

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

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

        /// <summary>
        /// 그리기 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void drawButton_Click(object sender, EventArgs e)
        {
            int fillStarCount = int.Parse(this.fillStarCountTextBox.Text);
            int starHeight    = int.Parse(this.starHeightTextBox.Text);

            double radius    = starHeight / (1 + Math.Sin(GetRadian(54)));
            double starWidth = 2 * radius * Math.Cos(GetRadian(18));
            double margin    = starWidth * 0.1;
            double x         = margin;
            double minmumY   = margin;

            Rectangle rectangle = new Rectangle
            (
                (int)x,
                (int)minmumY,
                (int)(2 * radius),
                (int)(2 * radius)
            );

            int bitmapWidth  = (int)(5 * starWidth + 6 * margin);
            int bitmapHeight = (int)(   starHeight + 2 * margin);

            Bitmap bitmap = new Bitmap(bitmapWidth, bitmapHeight);

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.Clear(Color.Transparent);

                graphics.SmoothingMode = SmoothingMode.AntiAlias;

                for(int i = 0; i < 5; i++)
                {
                    PointF[] pointArray = GetStarPointArray(-Math.PI / 2, 5, 2, rectangle);

                    if(i < fillStarCount)
                    {
                        graphics.FillPolygon(Brushes.Yellow, pointArray);
                    }
                    else
                    {
                        graphics.FillPolygon(Brushes.White, pointArray);
                    }

                    graphics.DrawPolygon(Pens.Black, pointArray);

                    x += starWidth + margin;

                    rectangle.X = (int)x;
                }
            }

            this.pictureBox.Image   = bitmap;
            this.pictureBox.Visible = true;

            string filePath = fillStarCount.ToString() + "stars.png";

            SaveImage(bitmap, filePath);
        }

        #endregion

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

        #region 라디안 구하기 - GetRadian(degree)

        /// <summary>
        /// 라디안 구하기
        /// </summary>
        /// <param name="degree">도</param>
        /// <returns>라디안</returns>
        private double GetRadian(double degree)
        {
            return degree * Math.PI / 180;
        }

        #endregion
        #region 교차점 찾기 - FindIntersectionPoint(startPoint1, endPoint1, startPoint2, endPoint2,
            linesIntersect, segmentsIntersect, intersectionPoint, closePoint1, 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="linesIntersect">선 교차 여부</param>
        /// <param name="segmentsIntersect">세그먼트 교차 여부</param>
        /// <param name="intersectionPoint">교차점</param>
        /// <param name="closePoint1">근접 포인트 1</param>
        /// <param name="closePoint2">근접 포인트 2</param>
        private void FindIntersectionPoint
        (
            PointF     startPoint1,
            PointF     endPoint1,
            PointF     startPoint2,
            PointF     endPoint2,
            out bool   linesIntersect,
            out bool   segmentsIntersect,
            out PointF intersectionPoint,
            out PointF closePoint1,
            out PointF closePoint2
        )
        {
            float deltaX1 = endPoint1.X - startPoint1.X;
            float deltaY1 = endPoint1.Y - startPoint1.Y;
            float deltaX2 = endPoint2.X - startPoint2.X;
            float deltaY2 = endPoint2.Y - startPoint2.Y;

            float denominator = (deltaY1 * deltaX2 - deltaX1 * deltaY2);

            float t1 = ((startPoint1.X - startPoint2.X) * deltaY2 + (startPoint2.Y - startPoint1.Y) * deltaX2) / denominator;

            if(float.IsInfinity(t1))
            {
                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 t2 = ((startPoint2.X - startPoint1.X) * deltaY1 + (startPoint1.Y - startPoint2.Y) * deltaX1) / -denominator;

            intersectionPoint = new PointF(startPoint1.X + deltaX1 * t1, startPoint1.Y + deltaY1 * t1);

            segmentsIntersect = ((t1 >= 0) && (t1 <= 1) && (t2 >= 0) && (t2 <= 1));

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

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

            closePoint1 = new PointF(startPoint1.X + deltaX1 * t1, startPoint1.Y + deltaY1 * t1);
            closePoint2 = new PointF(startPoint2.X + deltaX2 * t2, startPoint2.Y + deltaY2 * t2);
        }

        #endregion
        #region 오목 반경 계산하기 - CalculateConcaveRadius(pointCount, skipCount)

        /// <summary>
        /// 오목 반경 계산하기
        /// </summary>
        /// <param name="pointCount">포인트 카운트</param>
        /// <param name="skipCount">스킵 카운트</param>
        /// <returns>오목 반경</returns>
        private double CalculateConcaveRadius(int pointCount, int skipCount)
        {
            if(pointCount < 5)
            {
                return 0.33f;
            }

            double deltaTheta = 2 * Math.PI / pointCount;
            double theta00    = -Math.PI / 2;
            double theta01    = theta00 + deltaTheta * skipCount;
            double theta10    = theta00 + deltaTheta;
            double theta11    = theta10 - deltaTheta * skipCount;

            PointF point00 = new PointF
            (
                (float)Math.Cos(theta00),
                (float)Math.Sin(theta00)
            );

            PointF point01 = new PointF
            (
                (float)Math.Cos(theta01),
                (float)Math.Sin(theta01)
            );

            PointF point10 = new PointF
            (
                (float)Math.Cos(theta10),
                (float)Math.Sin(theta10)
            );

            PointF point11 = new PointF
            (
                (float)Math.Cos(theta11),
                (float)Math.Sin(theta11)
            );

            bool   linesIntersect;
            bool   segmentsIntersect;
            PointF intersectionPoint;
            PointF closePoint1;
            PointF closePoint2;

            FindIntersectionPoint
            (
                point00,
                point01,
                point10,
                point11,
                out linesIntersect,
                out segmentsIntersect,
                out intersectionPoint,
                out closePoint1,
                out closePoint2
            );

            return Math.Sqrt(intersectionPoint.X * intersectionPoint.X + intersectionPoint.Y * intersectionPoint.Y);
        }

        #endregion
        #region 별 포인트 배열 구하기 - GetStarPointArray(startTheta, pointCount, skipCount, rectangle)

        /// <summary>
        /// 별 포인트 배열 구하기
        /// </summary>
        /// <param name="startTheta">시작 세타</param>
        /// <param name="pointCount">포인트 수</param>
        /// <param name="skipCount">건너뛰기 수</param>
        /// <param name="rectangle">사각형</param>
        /// <returns>별 포인트 배열</returns>
        private PointF[] GetStarPointArray(double startTheta, int pointCount, int skipCount, Rectangle rectangle)
        {
            double theta;
            double deltaTheta;

            PointF[] pointArray;

            float rx = rectangle.Width / 2f;
            float ry = rectangle.Height / 2f;
            float cx = rectangle.X + rx;
            float cy = rectangle.Y + ry;

            if(skipCount == 1)
            {
                pointArray = new PointF[pointCount];
                theta      = startTheta;
                deltaTheta = 2 * Math.PI / pointCount;

                for(int i = 0; i < pointCount; i++)
                {
                    pointArray[i] = new PointF
                    (
                        (float)(cx + rx * Math.Cos(theta)),
                        (float)(cy + ry * Math.Sin(theta))
                    );

                    theta += deltaTheta;
                }

                return pointArray;
            }

            double concaveRadius = CalculateConcaveRadius(pointCount, skipCount);

            pointArray = new PointF[2 * pointCount];
            theta      = startTheta;
            deltaTheta = Math.PI / pointCount;

            for(int i = 0; i < pointCount; i++)
            {
                pointArray[2 * i] = new PointF
                (
                    (float)(cx + rx * Math.Cos(theta)),
                    (float)(cy + ry * Math.Sin(theta))
                );

                theta += deltaTheta;

                pointArray[2 * i + 1] = new PointF
                (
                    (float)(cx + rx * Math.Cos(theta) * concaveRadius),
                    (float)(cy + ry * Math.Sin(theta) * concaveRadius)
                );

                theta += deltaTheta;
            }

            return pointArray;
        }

        #endregion
        #region 이미지 저장하기 - SaveImage(image, filePath)

        /// <summary>
        /// 이미지 저장하기
        /// </summary>
        /// <param name="image">이미지</param>
        /// <param name="filePath">파일 경로</param>
        private void SaveImage(Image image, string filePath)
        {
            string fileExtension = Path.GetExtension(filePath);

            switch(fileExtension.ToLower())
            {
                case ".bmp"  : image.Save(filePath, ImageFormat.Bmp ); break;
                case ".exif" : image.Save(filePath, ImageFormat.Exif); break;
                case ".gif"  : image.Save(filePath, ImageFormat.Gif ); break;
                case ".jpg"  :
                case ".jpeg" : image.Save(filePath, ImageFormat.Jpeg); break;
                case ".png"  : image.Save(filePath, ImageFormat.Png ); break;
                case ".tif"  :
                case ".tiff" : image.Save(filePath, ImageFormat.Tiff); break;
                default      : throw new NotSupportedException($"알 수 없는 파일 확장자 :{fileExtension}");
            }
        }

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

댓글을 달아 주세요