Реализация треугольника Серпинского
Треугольник Серпинского — фрактал, один из двумерных аналогов множества Кантора предложенный польским математиком Серпинским в 1915 году. Также известен как «решётка» или «салфетка» Серпинского.
Отображается в виде треугольника, из четырех секций, каждый треугольник имеет половину ширины и высоты оригинала. Центральный треугольник инвертируется и может рассматриваться как отверстие в изображении. Каждый из трех внешних треугольников являются уменьшенной версией целого рисунка с собственными центральными отверстиями. Такая схема повторяется бесконечно.
Алгоритм построения
Существуют различные методы построения Треугольника Серпинского.Одним из них является использование теории хаоса с тремя аттракторами,.расположенными на трех вершинах равностороннего треугольника. При переходе с текущей позиции в направлении аттрактора пройденное расстояние всегда составляет расстояние между текущей точкой и выбранной вершиной.
Рассмотрим шаги для построения фрактала:
- Определить три точки.
- Выбрать стартовую позицию.
- Случайно выбрать один из трех аттракторов.
- Переместить текущую позицию на половину в направлении позиции аттрактора и нарисовать точку.
- Вернуться к шагу 3.
Рисование треугольника Серпинского
Нам понадобиться три метода. Первый для генерации псевдо - случайных чисел, которые будут использоваться для определения направления во время каждой итерации. Во - вторых, нам необходимо сохранить три позиции равностороннего треугольника, для этого будем использовать структуру Point. Наконец, еще одна структура Point понадобиться для хранения текущей позиции. Объявим три переменные:
Random randomiser = new Random();
private Point[] points;
private Point currentLocation;
Размер треугольника должен быть, как можно больше, чтобы рассмотреть детали Чтобы определить наибольший возможный размер, мы должны рассматривать соотношение между высотой и шириной любого равностороннего треугольника. Если предположить, что основание треугольника выравнивается по горизонтали, ширина будет такая же, как длина одной из сторон. Высота будет иметь ширину, умноженная на половину квадратного корня из трех. Добавим метод, который рассчитает соотношение:
private double HeightWidthRatio()
{
return Math.Sqrt(3) / 2;
}
Следующий метод вычисляет размер треугольника, чтобы поместить на форме:
private int SideLength()
{
int height = (int)Math.Min(
(double)ClientSize.Width, ClientSize.Height / HeightWidthRatio());
// отнимаем 2 чтобы треугольник не примыкал к краям.
return (int)height - 2;
}
Следующий метод будет вычислять позиции каждого из трех аттракторов, каждый будет расположен в вершине треугольника. Эти значения будут основаны на размере треугольника и медиане клиентской области формы.
private void SetPointLocations(int sideLength)
{
Point midPoint = new Point(ClientSize.Width / 2, ClientSize.Height / 2);
points = new Point[3];
points[0] = new Point(midPoint.X
, midPoint.Y - (int)(sideLength * HeightWidthRatio() / 2));
points[1] = new Point(midPoint.X - sideLength / 2
, midPoint.Y + (int)(sideLength * HeightWidthRatio() / 2));
points[2] = new Point(midPoint.X + sideLength / 2
, midPoint.Y + (int)(sideLength * HeightWidthRatio() / 2));
}
Следующей задачей является создание метода (точнее двух) для размещения точек.
private void PlotPointLocations(Graphics g)
{
foreach (Point p in _points)
{
PlotPoint(p, g);
}
}
private void PlotPoint(Point p, Graphics g)
{
Brush b = new SolidBrush(Color.Black);
g.FillRectangle(b, p.X, p.Y, 1, 1);
}
Теперь напишем методы, которые используются в цикле алгоритмом для рисования фрактала. Метод DrawNextPoint будет контролировать этот процесс вызывая "MoveTowardsRandomPoint", за которым следует вызов PlotPoint, чтобы поставить точку в текущей позиции. Наконец, вызывается метод Application.DoEvents для обновления формы.
Метод MoveTowardsRandomPoint выбирает одну из трех вершин используя наш объект randomizer.Он как раз и вычисляет половину расстояния между текущей позицией и выбранным аттрактором:
private void DrawNextPoint(Graphics g)
{
MoveTowardsRandomPoint();
PlotPoint(currentLocation, g);
Application.DoEvents();
}
private void MoveTowardsRandomPoint()
{
int moveTowards = randomiser.Next(0,3);
currentLocation.X = (currentLocation.X + points[moveTowards].X) / 2;
currentLocation.Y = (currentLocation.Y + points[moveTowards].Y) / 2;
}
Метод для вызова всего что мы написали:
private void DrawTriangle(Graphics g)
{
int sideLength = SideLength();
SetPointLocations(sideLength);
PlotPointLocations(g);
currentLocation = new Point(points[0].X, points[0].Y);
while (working)
{
DrawNextPoint(g);
}
}
Поместим две кнопки для Старт и Стоп на форму и в обработчике события Click напишем:
private void StopButton_Click(object sender, EventArgs e)
{
StartButton.Enabled = true;
StopButton.Enabled = false;
working = false;
}
private void StartButton_Click(object sender, EventArgs e)
{
StartButton.Enabled = false;
StopButton.Enabled = true;
Graphics g = CreateGraphics();
g.Clear(Color.White);
working = true;
DrawTriangle(g);
}
4
Подписаться на:
Комментарии к сообщению (Atom)
11 сентября 2010 г. в 22:55
Ссылка на демо-проект: http://fileland.ru/file_id-253119
12 сентября 2010 г. в 04:15
DrawTriangle не лучше запускать в отдельном процессе, чтобы форма приложения не тормозила?
12 сентября 2010 г. в 14:31
можно, но целью было просто продемонстрировать построение треугольника
21 мая 2011 г. в 09:40
Спасибо, все очень ясно изложено. Вы очень помогли с практической ;)