728x90
반응형
728x170
■ 별점(Star Rating)을 그리는 방법을 보여준다.
▶ 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
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] ElementHost 클래스 : WPF 복합 컨트롤 사용하기 (0) | 2023.01.01 |
---|---|
[C#/WINFORM] RNGCryptoServiceProvider 클래스 : GetBytes 메소드를 사용해 임시 패스워드 생성기 만들기 (0) | 2022.11.05 |
[C#/WINFORM/.NET6] 이미지 RTF 구하기 (0) | 2022.10.30 |
[C#/WINFORM/.NET6] 마우스 이벤트 발생시키기 (0) | 2022.10.22 |
[C#/WINFORM/.NET6] Point 구조체 : 선의 왼쪽 포인트 여부 구하기 (0) | 2022.10.19 |
[C#/WINFORM/.NET6] 비주얼 스튜디오 2022에서 단일 실행 파일 배포하기 (0) | 2022.10.10 |
[C#/WINFORM/.NET6] ISynchronizeInvoke 인터페이스 : InvokeRequired 코드 패턴 자동화하기 (0) | 2022.10.09 |
[C#/WINFORM/.NET6] MethodInvoker 대리자 : InvokeRequired 코드 패턴 자동화하기 (0) | 2022.10.09 |
[C#/WINFORM/.NET6] 화면 돋보기 사용하기 (0) | 2022.10.09 |
[C#/WINFORM/.NET6] TextBox 클래스 : Multiline 속성이 False일 때 ENTER 키 누르는 경우 알림 소리 방지하기 (0) | 2022.10.07 |
댓글을 달아 주세요