728x90
728x170
■ 소수 프랙탈(Prime Number Fractal)을 그리는 방법을 보여준다.
▶ MainForm.cs
using System;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace TestProject
{
/// <summary>
/// 메인 폼
/// </summary>
public partial class MainForm : Form
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 너비
/// </summary>
private const int WIDTH = 800;
/// <summary>
/// 높이
/// </summary>
private const int HEIGHT = 800;
/// <summary>
/// 오프셋 X
/// </summary>
private const int X_OFFSET = 150;
/// <summary>
/// 오프셋 Y
/// </summary>
private const int Y_OFFSET = 200;
/// <summary>
/// 히트 배열
/// </summary>
private int[,] hitArray = new int[WIDTH, HEIGHT];
/// <summary>
/// 현재 포인트
/// </summary>
private Point currentPoint = new Point(0, 0);
/// <summary>
/// 현재 소수
/// </summary>
private long currentPrimeNumber = 1;
/// <summary>
/// 실행 여부
/// </summary>
private bool isRunning = false;
/// <summary>
/// 포인트 카운트
/// </summary>
private int pointCount = 0;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary>
/// 생성자
/// </summary>
public MainForm()
{
InitializeComponent();
#region 이벤트를 설정한다.
FormClosing += Form_FormClosing;
this.startMenuItem.Click += startMenuItem_Click;
this.saveAsMenuItem.Click += saveAsMenuItem_Click;
#endregion
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 폼을 닫을 경우 처리하기 - Form_FormClosing(sender, e)
/// <summary>
/// 폼을 닫을 경우 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
this.isRunning = false;
}
#endregion
#region 시작하기 메뉴 항목 클릭시 처리하기 - startMenuItem_Click(sender, e)
/// <summary>
/// 시작하기 메뉴 항목 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void startMenuItem_Click(object sender, EventArgs e)
{
if(this.startMenuItem.Text == "시작하기(&S)")
{
this.isRunning = true;
this.startMenuItem.Text = "중단하기(&S)";
this.drawPictureBox.Visible = true;
DrawPrimeNumberFractal();
this.startMenuItem.Enabled = true;
this.startMenuItem.Text = "시작하기(&S)";
}
else
{
this.isRunning = false;
this.startMenuItem.Enabled = false;
}
}
#endregion
#region 다른 이름으로 저장하기 메뉴 항목 클릭시 처리하기 - saveAsMenuItem_Click(sender, e)
/// <summary>
/// 다른 이름으로 저장하기 메뉴 항목 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void saveAsMenuItem_Click(object sender, EventArgs e)
{
if(this.saveFileDialog.ShowDialog() == DialogResult.OK)
{
Bitmap bitmap = this.drawPictureBox.Image as Bitmap;
SaveImage(bitmap, this.saveFileDialog.FileName);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 소수 여부 구하기 - IsPrimeNumber(value)
/// <summary>
/// 소수 여부 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>소수 여부</returns>
private bool IsPrimeNumber(long value)
{
long stopAt = (long)Math.Sqrt(value);
for(long factor = 3; factor <= stopAt; factor += 2)
{
if(value % factor == 0)
{
return false;
}
}
return true;
}
#endregion
#region 다음 소수 찾기 - FindNextPrimeName(value)
/// <summary>
/// 다음 소수 찾기
/// </summary>
/// <param name="value">값</param>
/// <returns>다음 소수</returns>
private long FindNextPrimeName(long value)
{
for(long i = value + 2; ; i += 2)
{
if(IsPrimeNumber(i))
{
return i;
}
}
}
#endregion
#region 무지개 색상 구하기 - GetRainbowColor(value, redValue, blueValue)
/// <summary>
/// 무지개 색상 구하기
/// </summary>
/// <param name="value">값</param>
/// <param name="redValue">빨강색 값</param>
/// <param name="blueValue">파랑색 값</param>
/// <returns>무지개 색상</returns>
private Color GetRainbowColor(float value, float redValue, float blueValue)
{
int mappingValue = (int)(1023 * (value - redValue) / (blueValue - redValue));
if(mappingValue < 256)
{
return Color.FromArgb(255, mappingValue, 0);
}
else if(mappingValue < 512)
{
mappingValue -= 256;
return Color.FromArgb(255 - mappingValue, 255, 0);
}
else if(mappingValue < 768)
{
mappingValue -= 512;
return Color.FromArgb(0, 255, mappingValue);
}
else
{
mappingValue -= 768;
return Color.FromArgb(0, 255 - mappingValue, 255);
}
}
#endregion
#region 이미지 생성하기 - CreateImage()
/// <summary>
/// 이미지 생성하기
/// </summary>
private void CreateImage()
{
Bitmap bitmap = new Bitmap(WIDTH, HEIGHT);
using(Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Clear(Color.Black);
var query = from int count in this.hitArray
select count;
float maximumValue = (float)query.Max();
for(int x = 0; x < WIDTH; x++)
{
for(int y = 0; y < HEIGHT; y++)
{
if(this.hitArray[x, y] > 0)
{
bitmap.SetPixel(x, y, GetRainbowColor(this.hitArray[x, y], 1, maximumValue));
}
}
}
graphics.DrawLine(Pens.Blue, X_OFFSET, 0, X_OFFSET, HEIGHT);
graphics.DrawLine(Pens.Blue, 0, Y_OFFSET, WIDTH, Y_OFFSET);
}
this.drawPictureBox.Image = bitmap;
}
#endregion
#region 소수 프랙탈 그리기 - DrawPrimeNumberFractal()
/// <summary>
/// 소수 프랙탈 그리기
/// </summary>
private void DrawPrimeNumberFractal()
{
const int pointCountPerLoop = 10000;
while(this.isRunning)
{
for(int i = 0; i < pointCountPerLoop; i++)
{
this.currentPrimeNumber = FindNextPrimeName(this.currentPrimeNumber);
switch(this.currentPrimeNumber % 5)
{
case 1 : this.currentPoint.Y--; break;
case 2 : this.currentPoint.X++; break;
case 3 : this.currentPoint.Y++; break;
case 4 : this.currentPoint.X--; break;
}
int xIndex = this.currentPoint.X + X_OFFSET;
int yIndex = this.currentPoint.Y + Y_OFFSET;
if(xIndex >= 0 && yIndex >= 0 && xIndex < WIDTH && yIndex < HEIGHT)
{
this.hitArray[xIndex, yIndex]++;
}
}
CreateImage();
this.pointCount += pointCountPerLoop;
this.pointCountValueLabel.Text = this.pointCount.ToString();
this.primeNumberValueLabel.Text = this.currentPrimeNumber.ToString();
Application.DoEvents();
}
}
#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] 문자열 JUSTIFY 정렬하기 (0) | 2018.12.16 |
---|---|
[C#/WINFORM] 다각형 에디터 사용하기 (0) | 2018.12.13 |
[C#/WINFORM] 라인 에디터 사용하기 (0) | 2018.12.13 |
[C#/WINFORM] 확대 창 사용하기 (0) | 2018.12.13 |
[C#/WINFORM] 2D/3D 테두리 그리기 (0) | 2018.12.13 |
[C#/WINFORM] PictureBox 클래스 : 이미지 드래그 하기 (0) | 2018.12.11 |
[C#/WINFORM] Region 클래스 : 특정 모양을 갖는 폼 만들기 (0) | 2018.12.10 |
[C#/WINFORM] TextureBrush 클래스 사용하기 (0) | 2018.12.10 |
[C#/WINFORM] 영역 채우기(Flood Fill) (0) | 2018.12.10 |
[C#/WINFORM] 유니코드 문자 조회하기 (0) | 2018.12.09 |