728x90
반응형
728x170
▶ GraphicsExtension.cs
using System;
using System.Collections.Generic;
using System.Drawing;
namespace TestProject
{
/// <summary>
/// 그래픽스 확장
/// </summary>
public static class GraphicsExtension
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 난수기
/// </summary>
private static Random _random = new Random();
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 포인트 그리기 - DrawPoint(graphics, point, brush, pen, radius)
/// <summary>
/// 포인트 그리기
/// </summary>
/// <param name="graphics">그래픽스</param>
/// <param name="point">포인트</param>
/// <param name="brush">브러시</param>
/// <param name="pen">펜</param>
/// <param name="radius">반경</param>
public static void DrawPoint(this Graphics graphics, PointF point, Brush brush, Pen pen, float radius)
{
RectangleF rectangle = new RectangleF
(
point.X - radius,
point.Y - radius,
2 * radius,
2 * radius
);
graphics.FillEllipse(brush, rectangle);
graphics.DrawEllipse(pen, rectangle);
}
#endregion
#region 포인트 리스트 그리기 - DrawPointList(graphics, pointList, brush, pen, radius)
/// <summary>
/// 포인트 리스트 그리기
/// </summary>
/// <param name="graphics">그래픽스</param>
/// <param name="pointList">포인트 리스트</param>
/// <param name="brush">브러시</param>
/// <param name="pen">펜</param>
/// <param name="radius">반경</param>
public static void DrawPointList(this Graphics graphics, List<PointF> pointList, Brush brush, Pen pen, float radius)
{
if(pointList == null)
{
return;
}
foreach(PointF point in pointList)
{
graphics.DrawPoint(point, brush, pen, radius);
}
}
#endregion
#region 사각형 그리기 - DrawRectangle(graphics, pen, rectangle)
/// <summary>
/// 사각형 그리기
/// </summary>
/// <param name="graphics">그래픽스</param>
/// <param name="pen">펜</param>
/// <param name="rectangle">사각형</param>
public static void DrawRectangle(this Graphics graphics, Pen pen, RectangleF rectangle)
{
graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
}
#endregion
#region 사각형 그리기 - DrawRectangle(graphics, point, brush, pen, radius)
/// <summary>
/// 사각형 그리기
/// </summary>
/// <param name="graphics">그래픽스</param>
/// <param name="point">포인트</param>
/// <param name="brush">브로시</param>
/// <param name="pen">펜</param>
/// <param name="radius">반경</param>
public static void DrawRectangle(this Graphics graphics, PointF point, Brush brush, Pen pen, float radius)
{
RectangleF rectangle = new RectangleF
(
point.X - radius,
point.Y - radius,
2 * radius,
2 * radius
);
graphics.FillRectangle(brush, rectangle);
graphics.DrawRectangle(pen, rectangle);
}
#endregion
#region 십자가 그리기 - DrawCross(graphics, pen, point, radius)
/// <summary>
/// 십자가 그리기
/// </summary>
/// <param name="graphics">그래픽스</param>
/// <param name="pen">펜</param>
/// <param name="point">포인트</param>
/// <param name="radius">반경</param>
public static void DrawCross(this Graphics graphics, Pen pen, PointF point, float radius)
{
graphics.DrawLine(pen, point.X - radius, point.Y, point.X + radius, point.Y);
graphics.DrawLine(pen, point.X, point.Y - radius, point.X, point.Y + radius);
}
#endregion
#region 십자가 그리기 - DrawCross(graphics, outerColor, innerColor, point, radius)
/// <summary>
/// 십자가 그리기
/// </summary>
/// <param name="graphics">그래픽스</param>
/// <param name="outerColor">외부 색상</param>
/// <param name="innerColor">내부 색상</param>
/// <param name="point">포인트</param>
/// <param name="radius">반경</param>
public static void DrawCross(this Graphics graphics, Color outerColor, Color innerColor, PointF point, float radius)
{
using(Pen pen = new Pen(outerColor, 3))
{
graphics.DrawLine(pen, point.X - radius - 1, point.Y, point.X + radius + 1, point.Y);
graphics.DrawLine(pen, point.X, point.Y - radius - 1, point.X, point.Y + radius + 1);
}
using(Pen pen = new Pen(innerColor, 1))
{
graphics.DrawLine(pen, point.X - radius, point.Y, point.X + radius, point.Y);
graphics.DrawLine(pen, point.X, point.Y - radius, point.X, point.Y + radius);
}
}
#endregion
#region 난수 구하기 - Random<T>(list)
/// <summary>
/// 난수 구하기
/// </summary>
/// <typeparam name="TItem">항목 타입</typeparam>
/// <param name="list">리스트</param>
/// <returns>난수 값</returns>
public static TItem Random<TItem>(List<TItem> list)
{
return list[_random.Next(list.Count)];
}
#endregion
#region 임의로 섞기 - Randomize<T>(array)
/// <summary>
/// 임의로 섞기
/// </summary>
/// <typeparam name="TItem">항목 타입</typeparam>
/// <param name="array">배열</param>
public static void Randomize<TItem>(this TItem[] array)
{
for(int i = 0; i < array.Length - 1; i++)
{
int j = _random.Next(i, array.Length);
TItem temporaryValue = array[i];
array[i] = array[j];
array[j] = temporaryValue;
}
}
#endregion
#region 임의로 섞기 - Randomize<T>(list)
/// <summary>
/// 임의로 섞기
/// </summary>
/// <typeparam name="TItem">항목 타입</typeparam>
/// <param name="list">리스트</param>
public static void Randomize<TItem>(this List<TItem> list)
{
TItem[] array = list.ToArray();
array.Randomize();
list.Clear();
list.AddRange(array);
}
#endregion
}
}
728x90
▶ PointData.cs
using System.Drawing;
namespace TestProject
{
/// <summary>
/// 포인트 데이터
/// </summary>
public class PointData
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 위치
/// </summary>
public PointF Location;
/// <summary>
/// 클러스터 번호
/// </summary>
public int ClusterNumber;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - PointData(location, clusterNumber)
/// <summary>
/// 생성자
/// </summary>
/// <param name="location">위치</param>
/// <param name="clusterNumber">클러스터 번호</param>
public PointData(PointF location, int clusterNumber)
{
Location = location;
ClusterNumber = clusterNumber;
}
#endregion
#region 생성자 - PointData(x, y, clusterNumber)
/// <summary>
/// 생성자
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="clusterNumber">클러스터 번호</param>
public PointData(float x, float y, int clusterNumber) : this(new PointF(x, y), clusterNumber)
{
}
#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 Pen[] pointPenArray =
{
Pens.Red,
Pens.Green,
Pens.Blue,
Pens.Black,
Pens.Red,
Pens.Green,
Pens.Blue,
Pens.Black
};
/// <summary>
/// 포인트 브러시 배열
/// </summary>
private Brush[] pointBrushArray =
{
Brushes.Pink,
Brushes.LightGreen,
Brushes.LightBlue,
Brushes.Yellow,
Brushes.Orange,
Brushes.Lime,
Brushes.Cyan,
Brushes.White
};
/// <summary>
/// 중심점 브러시 배열
/// </summary>
private Brush[] centroidBrushArray =
{
Brushes.Red,
Brushes.Green,
Brushes.Blue,
Brushes.Yellow,
Brushes.Orange,
Brushes.Lime,
Brushes.Cyan,
Brushes.White
};
/// <summary>
/// 시드 포인트 리스트
/// </summary>
private List<PointF> seedPointList = new List<PointF>();
/// <summary>
/// 중심점 리스트
/// </summary>
private List<PointF> centroidList = new List<PointF>();
/// <summary>
/// 포인트 데이터 리스트
/// </summary>
private List<PointData> pointDataList = new List<PointData>();
/// <summary>
/// 단계 카운트
/// </summary>
private int stepCount = 0;
/// <summary>
/// 최대 클러스터 카운트
/// </summary>
private int maximumClusterCount = 1;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary>
/// 생성자
/// </summary>
public MainForm()
{
InitializeComponent();
Load += Form_Load;
this.createPointButton.Click += createPointButton_Click;
this.createClusterButton.Click += createClusterButton_Click;
this.clearButton.Click += clearButton_Click;
this.pictureBox.MouseClick += pictureBox_MouseClick;
this.pictureBox.Paint += pictureBox_Paint;
this.fpsScrollBar.Scroll += fpsScrollBar_Scroll;
this.timer.Tick += timer_Tick;
}
#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.maximumClusterCount = this.pointBrushArray.Length;
}
#endregion
#region 포인트 생성 버튼 클릭시 처리하기 - createPointButton_Click(sender, e)
/// <summary>
/// 포인트 생성 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void createPointButton_Click(object sender, EventArgs e)
{
AddPointData();
}
#endregion
#region 클러스터 생성 버튼 클릭시 처리하기 - createClusterButton_Click(sender, e)
/// <summary>
/// 클러스터 생성 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void createClusterButton_Click(object sender, EventArgs e)
{
int clusterCount = int.Parse(this.clusterCountTextBox.Text);
if(this.pointDataList.Count < clusterCount)
{
return;
}
this.centroidList = new List<PointF>();
this.pointDataList.Randomize();
for(int i = 0; i < clusterCount; i++)
{
this.centroidList.Add(this.pointDataList[i].Location);
}
foreach(PointData pointData in this.pointDataList)
{
pointData.ClusterNumber = 0;
}
this.stepCount = 0;
this.pictureBox.Refresh();
this.scoreLabel.Text = string.Empty;
Cursor = Cursors.WaitCursor;
this.timer.Enabled = true;
}
#endregion
#region 지우기 버튼 클릭시 처리하기 - clearButton_Click(sender, e)
/// <summary>
/// 지우기 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void clearButton_Click(object sender, EventArgs e)
{
this.seedPointList.Clear();
this.centroidList.Clear();
this.pointDataList.Clear();
this.pictureBox.Refresh();
}
#endregion
#region 픽처 박스 마우스 클릭시 처리하기 - pictureBox_MouseClick(sender, e)
/// <summary>
/// 픽처 박스 마우스 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void pictureBox_MouseClick(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
this.seedPointList.Add(e.Location);
}
else
{
this.pointDataList.Add(new PointData(e.Location, 0));
}
this.centroidList.Clear();
this.pictureBox.Refresh();
}
#endregion
#region 픽처 박스 페인트시 처리하기 - pictureBox_Paint(sender, e)
/// <summary>
/// 픽처 박스 페인트시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
const float RADIUS = 4;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
foreach(PointData pointData in this.pointDataList)
{
e.Graphics.DrawPoint
(
pointData.Location,
this.pointBrushArray[pointData.ClusterNumber % this.maximumClusterCount],
this.pointPenArray[pointData.ClusterNumber % this.maximumClusterCount],
RADIUS
);
}
for(int i = 0; i < this.centroidList.Count; i++)
{
e.Graphics.DrawRectangle
(
this.centroidList[i],
this.centroidBrushArray[i % this.maximumClusterCount],
Pens.Black,
RADIUS
);
}
for(int i = 0; i < this.seedPointList.Count; i++)
{
e.Graphics.DrawCross
(
Color.Black,
Color.White,
this.seedPointList[i],
2 * RADIUS
);
}
}
#endregion
#region FPS 스크롤바 스크롤시 처리하기 - fpsScrollBar_Scroll(sender, e)
/// <summary>
/// FPS 스크롤바 스크롤시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void fpsScrollBar_Scroll(object sender, ScrollEventArgs e)
{
int fps = this.fpsScrollBar.Value / 10;
if(fps < 1)
{
fps = 1;
}
this.fpsValueLabel.Text = fps.ToString();
this.timer.Interval = 1000 / fps;
}
#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.stepCount++;
UpdateSolution();
this.pictureBox.Refresh();
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 포인트 데이터 추가하기 - AddPointData()
/// <summary>
/// 포인트 데이터 추가하기
/// </summary>
private void AddPointData()
{
if(this.seedPointList.Count < 1)
{
MessageBox.Show("먼저 최소한 하나의 시드를 정의해 주시기 바랍니다.");
return;
}
this.centroidList.Clear();
Random random = new Random();
double maximumRadius = Math.Min(this.pictureBox.ClientSize.Width, this.pictureBox.ClientSize.Height) / 6;
int pointCount = int.Parse(this.pointCountTextBox.Text);
for(int i = 0; i < pointCount; i++)
{
int seedNumber = random.Next(this.seedPointList.Count);
double radius = maximumRadius * random.NextDouble() + maximumRadius * random.NextDouble();
double theta = random.Next(360);
float x = this.seedPointList[seedNumber].X + (float)(radius * Math.Cos(theta));
float y = this.seedPointList[seedNumber].Y + (float)(radius * Math.Sin(theta));
this.pointDataList.Add(new PointData(x, y, 0));
}
this.pictureBox.Refresh();
}
#endregion
#region 거리 구하기 - GetDistance(point1, point2)
/// <summary>
/// 거리 구하기
/// </summary>
/// <param name="point1">포인트 1</param>
/// <param name="point2">포인트 2</param>
/// <returns>거리</returns>
private double GetDistance(PointF point1, PointF point2)
{
float deltaX = point1.X - point2.X;
float deltaY = point1.Y - point2.Y;
return Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
}
#endregion
#region 점수 구하기 - GetScore()
/// <summary>
/// 점수 구하기
/// </summary>
/// <returns>점수</returns>
private int GetScore()
{
float score = 0;
foreach(PointData pointData in this.pointDataList)
{
float deltaX = this.centroidList[pointData.ClusterNumber].X - pointData.Location.X;
float deltaY = this.centroidList[pointData.ClusterNumber].Y - pointData.Location.Y;
score += deltaX * deltaX + deltaY * deltaY;
}
return (int)score;
}
#endregion
#region 솔루션 업데이트하기 - UpdateSolution()
/// <summary>
/// 솔루션 업데이트하기
/// </summary>
private void UpdateSolution()
{
int clusterCount = this.centroidList.Count;
PointF[] newCenterPointArray = new PointF[clusterCount];
int[] pointNumberArray = new int[clusterCount];
foreach(PointData pointData in this.pointDataList)
{
double bestDistance = GetDistance(pointData.Location, this.centroidList[0]);
int bestCluster = 0;
for(int i = 1; i < clusterCount; i++)
{
double testDistance = GetDistance(pointData.Location, this.centroidList[i]);
if(testDistance < bestDistance)
{
bestDistance = testDistance;
bestCluster = i;
}
}
pointData.ClusterNumber = bestCluster;
newCenterPointArray[bestCluster].X += pointData.Location.X;
newCenterPointArray[bestCluster].Y += pointData.Location.Y;
pointNumberArray[bestCluster]++;
}
List<PointF> newCentroidList = new List<PointF>();
for(int i = 0; i < clusterCount; i++)
{
newCentroidList.Add
(
new PointF
(
newCenterPointArray[i].X / pointNumberArray[i],
newCenterPointArray[i].Y / pointNumberArray[i]
)
);
}
bool centroidChanged = false;
for(int i = 0; i < clusterCount; i++)
{
const float minimumChange = 0.5f;
if
(
(Math.Abs(this.centroidList[i].X - newCentroidList[i].X) > minimumChange) ||
(Math.Abs(this.centroidList[i].Y - newCentroidList[i].Y) > minimumChange)
)
{
centroidChanged = true;
break;
}
}
if(!centroidChanged)
{
this.timer.Enabled = false;
this.scoreLabel.Text = $"점수 : {GetScore()}, 단계 수 : {this.stepCount}";
Cursor = Cursors.Default;
return;
}
this.centroidList = newCentroidList;
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] 특정 화면에서 전경 윈도우의 전체 화면 모드 여부 구하기 (0) | 2021.04.10 |
---|---|
[C#/WINFORM] 윈도우 텍스트 구하기 (0) | 2021.04.07 |
[C#/WINFORM] 윈도우 위치/크기/상태 구하기 (0) | 2021.04.07 |
[C#/WINFORM] SendKeys 클래스 : 키 코드 사용하기 (0) | 2021.04.03 |
[C#/WINFORM] SendKeys 클래스 : SendWait 정적 메소드를 사용해 ESC 키 누르기 (0) | 2021.04.03 |
[C#/WINFORM] k-평균 클러스터링(k-means clustering) 사용하기 (0) | 2021.03.11 |
[C#/WINFORM] PictureBox 클래스 : 배경 이미지 위에 낙서하기 (0) | 2021.03.10 |
[C#/WINFORM] Bitmap 클래스 : 오버레이 비트맵 혼합하기 (0) | 2021.03.09 |
[C#/WINFORM] Bitmap 클래스 : 색상 대체하기 (0) | 2021.03.08 |
[C#/WINFORM] Bitmap 클래스 : ARGB 색상 채널 교환하기 (0) | 2021.03.08 |
[C#/WINFORM] Bitmap 클래스 : 부분 색상 반전하기 (0) | 2021.02.24 |
댓글을 달아 주세요