Вы находитесь на странице: 1из 29

Введение

Понятия фрактал, фрактальная геометрия и фрактальная графика, появились


в конце 70-х. Слово фрактал образовано от латинского fractus и в переводе
означает «состоящий из фрагментов». Оно было предложено математиком
Бенуа Мандельбротом в 1975 году для обозначения нерегулярных, но
самоподобных структур. Рождение фрактальной геометрии принято
связывать с выходом в 1977 году книги Мандельброта «The Fractal Geometry
of Nature». В его работах использованы научные результаты других ученых,
работавших в 1875 – 1925 годах в той же области (Пуанкаре, Фату, Жюлиа,
Кантор, Хаусдорф). Но только в наше время удалось объединить их работы в
единую систему. Определение фрактала, данное Мандельбротом: фракталом
называется структура, состоящая из частей, которые в каком-то смысле
подобны целому. Самоподобие – одно из основных свойств фракталов.
Объект называют самоподобным, когда увеличенные части объекта походят
на сам объект и друг на друга.

Геометрические фракталы.

Фракталы этого класса самые наглядные. В двухмерном случае их получают


с помощью некоторой ломаной (или поверхности в трехмерном случае),
называемой генератором. За один шаг алгоритма каждый из отрезков,
составляющих ломаную, заменяется на ломаную-генератор, в
соответствующем масштабе. В результате бесконечного повторения этой
процедуры, получается геометрический фрактал.

Алгебраические фракталы.

Если геометрические фракталы встречаются в пространственно-


геометрических приложениях (длина береговой линии, капилляры

1
сосудистой системы), то алгебраические фракталы, или аттракторы, связаны
с процессами, протекающими во времени и кажущиеся на первый взгляд
хаотическими.

Теория хаоса необычайно популярна в последнее время: при ближайшем


рассмотрении оказывается, что хаос несет в себе элементы
упорядоченности, но они определяются таким количеством
взаимосвязанных факторов, что «отследить» их можно только описательно, с
помощью весьма общих рассуждений. Самый характерный пример такого
хаоса – атмосферные процессы. С одной стороны, все факторы, влияющие
на погоду, известны, однако их так много и они настолько сложно
взаимосвязаны, что экстраполирование даже некоторых параметров в
настоящее время пока не поддается математикам.

2
Рекурсивные алгоритмы.

Для построения фрактальных кривых используются рекурсивные алгоритмы.


Алгоритм называется рекурсивным (или рекурсией), когда он вызывает себя в
качестве вспомогательного. Рекурсивными бывают подпрограммы-процедуры и
подпрограммы-функции. В рекурсивных подпрограммах в теле процедуры или в
функции имеется вызов этих же подпрограмм. Процесс обращения к себе
самому может длиться бесконечно. Для обеспечения остановки устанавливают
барьер в виде некоторого условия.

Рекурсия – это мощный метод программирования, который позволяет делить


задачу на части все меньшего и меньшего размера до тех пор, пока они не станут
настолько малы, что решение этих подзадач сведется к набору простых
операций. Рекурсия встречается достаточно часто, но есть ситуации, когда ее
использование неуместно и даже вредно.

При работе с рекурсивными алгоритмами нужно избегать трех основных


опасностей:

1) бесконечная рекурсия. Нужно убедиться, что алгоритм имеет надежное

условие остановки;
2) глубокая рекурсия. Если алгоритм вызывает слишком глубокую рекурсию,

он исчерпает всю память стека.


3) неуместная рекурсия. Обычно это происходит, когда алгоритм, много раз

вычисляет одни и те же промежуточные значения.

Рекурсия полезна при решении задач, которые могут быть разложены на


несколько подзадач. Таким образом, применение рекурсии целесообразно при
построении фрактальных кривых, так как они обладают таким свойством как
самоподобие. Алгоритмы построения фрактальных кривых рекурсивны по
своей природе, и их гораздо проще понять в рекурсивном представлении.

3
Рекурсивное построение кривых Серпинского.

Кривые Серпинского – это самоподобные кривые, которые обычно


определяются рекурсивно. На рисунке 1.2

изображены кривые Серпинского с глубиной 1, 2 и 3.

Кривые Серпинского проще строить с помощью четырех отдельных


процедур, работающих совместно, - SierpA, SierpB, SierpC и SierpD. Эти
процедуры косвенно рекурсивные – каждая из них вызывает другие, которые
после этого вызывают первоначальную процедуру. Они выводят верхнюю,
левую, нижнюю и правую части кривой Серпинского соответственно.

На рисунке 1.3 показано, как эти процедуры образуют кривую глубины 1.

4
Отрезки, составляющие кривую, изображены со стрелками, которые
указывают направление их рисования. Сегменты, используемые для
соединения частей, представлены пунктирными линиями.

Каждая из четырех основных кривых составлена из линий диагонального


сегмента, вертикального или горизонтального и еще одного диагонального
сегмента. При глубине рекурсии больше 1 необходимо разложить каждую
кривую на меньшие части. Это можно сделать, разбивая каждую их линий
диагональных сегментов на две подкривые.

Например, чтобы разбить кривую типа А, первый диагональный отрезок


делится на кривую типа А, за которой следует кривая типа В. Затем без
изменения выводиться линия горизонтального сегмента так же, как и в
исходной кривой типа А. И наконец, второй диагональный отрезок
разбивается на кривую типа D, за которой следует кривая типа А. На
рисунке 1.4 изображен процесс построения кривой 2-го порядка,
сформированной из кривых 1-го порядка. Подкривые показаны жирными
линиями.

5
На рисунке 1.5 показано, как из четырех кривых 1-го порядка формируется
полная кривая Серпинского 2-го порядка. Каждая из подкривых обведена
пунктирными линиями.

6
С помощью стрелок типа → и ←, отображающих типы линий, которые
соединяют части кривых между собой (тонкие линии на рисунке 1.4), можно
перечислить рекурсивные зависимости между четырьмя типами кривых, как
показано на схеме ниже.

A: A B← D A

B: B C A B

C: C D→B C

D: D A C D

Код Delphi для рисования кривых Серпинского приведен ниже.

Листинг 1. Рисование кривых Серпинского.

type
TCerpin = class(TForm) //имя формы – “Cerpin”
procedure SierpA(depth, dist:Integer; canvas: TCanvas);
procedure SierpB(depth, dist:Integer;canvas: TCanvas);
procedure SierpC(depth, dist:Integer;canvas: TCanvas);
procedure SierpD(depth, dist:Integer;canvas: TCanvas);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

Var

Cerpin: TCerpin;

dist: integer=7; // задаем длину отрезков линии


depth: integer; // порядок кривой
implementation
{$R *.dfm}
procedure TCerpin.SierpA(depth,dist: Integer;Canvas:TCanvas); // строим кривую типа А
begin
if (depth=1) then begin // строим кривую типа А первого порядка
Cerpin.Canvas.Pen.Color:=clYellow; //задаем цвет для рисования
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+dist);

7
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+0);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y-dist);
end else begin // если порядок кривой больше 1, то составляем кривую из меньших
кривых, как показано на рисунке 1.5
Cerpin.Canvas.Pen.Color:=clYellow;
SierpA(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+dist);
SierpB(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+0);
SierpD(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y-dist);
SierpA(depth-1,dist,canvas);
end;
end;

procedure TCerpin.SierpB(depth, dist: Integer;Canvas:TCanvas);


begin
if (depth=1) then begin
Cerpin.Canvas.Pen.Color:=clred;
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+dist);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+0,Cerpin.Canvas.PenPos.Y+dist);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+dist); end else
begin
Cerpin.Canvas.Pen.Color:=clred;
SierpB(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+dist);
SierpC(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+0,Cerpin.Canvas.PenPos.Y+dist);
SierpA(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+dist);
SierpB(depth-1,dist,canvas);
end;
end;

procedure TCerpin.SierpC(depth, dist: Integer;Canvas:TCanvas);


begin
if (depth=1) then begin
Cerpin.Canvas.Pen.Color:=clLime;
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y-dist);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+0);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+dist);
end else begin
Cerpin.Canvas.Pen.Color:=clLime;
SierpC(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y-dist);
SierpD(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+0);
SierpB(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+dist);
SierpC(depth-1,dist,canvas);
end;
end;

8
procedure TCerpin.SierpD(depth, dist: Integer;Canvas:TCanvas);
begin
if (depth=1) then begin
Cerpin.Canvas.Pen.Color:=clTeal;
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y-dist);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+0,Cerpin.Canvas.PenPos.Y-dist);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y-dist);
end else begin
Cerpin.Canvas.Pen.Color:=clTeal;
SierpD(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y-dist);
SierpA(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+0,Cerpin.Canvas.PenPos.Y-dist);
SierpC(depth-1,dist,canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y-dist);
SierpD(depth-1,dist,canvas);
end;
end;
procedure TCerpin.FormPaint(Sender: TObject); // выводим изображение кривых
Серпинского на канву формы с помощью события OnPaint.
begin
Cerpin.Canvas.MoveTo(dist,dist);
SierpB(depth,dist,Cerpin.Canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y+dist);
//соединяем кривые разных типов (смотри рисунок 1.4)
SierpC(depth,dist,Cerpin.Canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X+dist,Cerpin.Canvas.PenPos.Y-dist);
SierpD(depth,dist,Cerpin.Canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y-dist);
SierpA(depth,dist,Cerpin.Canvas);
Cerpin.Canvas.LineTo(Cerpin.Canvas.PenPos.X-dist,Cerpin.Canvas.PenPos.Y+dist);
end;

Примечания.

Канва – область, на которой можно рисовать чертежными инструментами –


пером, кистью и шрифтом. Канву имеют такие
компоненты как, например, форма и компонент
Image. В приведенном выше листинге программы,
мы выводим изображение кривых непосредственно
на форму.

Для этого мы используем событие формы OnPaint,


которое возникает при необходимости прорисовки

9
окна. Событие не прописывают вручную. Поступают следующим образом, в
инспекторе объектов находят это событие и в поле, которое находится рядом
с ним, щелкают два раза мышкой. После этого активизируется окно кода
программы, где уже будет прописана заготовка для процедуры обработчика
события OnPaint.

10
Рекурсивное построение кривых Гильберта.

Рассмотрим рекурсивное построение кривых Гильберта. На рисунке 1.6


изображены кривые Гильберта 1-го, 2-го и 3-го порядка.

Построение кривых Гильберта аналогично построению кривых Серпинского.


Ниже приведен листинг программы.

type
TForm1 = class(TForm)
procedure a(i:integer; canvas: Tcanvas);
procedure b(i:integer; canvas: Tcanvas);
procedure c(i:integer; canvas: Tcanvas);
procedure d(i:integer; canvas: Tcanvas);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
p: integer=11;
u: integer=13;
implementation
{$R *.dfm}
procedure Tform1.a(i: integer; canvas: Tcanvas);
begin
if i>0 then begin
d(i-1, canvas);
canvas.LineTo(canvas.PenPos.X+u, canvas.PenPos.Y);
a(i-1, canvas);

11
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y+u);
a(i-1, canvas);
canvas.LineTo(canvas.PenPos.X-u, canvas.PenPos.Y);
c(i-1,canvas);
end;
end;

procedure Tform1.b(i: integer; canvas: Tcanvas);


begin
if i>0 then begin
c(i-1, canvas);
canvas.LineTo(canvas.PenPos.X-u, canvas.PenPos.Y);
b(i-1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y-u);
b(i-1, canvas);
canvas.LineTo(canvas.PenPos.X+u, canvas.PenPos.Y);
d(i-1,canvas);
end;
end;

procedure Tform1.c(i: integer; canvas: Tcanvas);


begin
if i>0 then begin
b(i-1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y-u);
c(i-1, canvas);
canvas.LineTo(canvas.PenPos.X-u, canvas.PenPos.Y);
c(i-1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y+u);
a(i-1,canvas);
end;
end;

procedure Tform1.d(i: integer; canvas: Tcanvas);


begin
if i>0 then begin
a(i-1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y+u);
d(i-1, canvas);
canvas.LineTo(canvas.PenPos.X+u, canvas.PenPos.Y);
d(i-1, canvas);
canvas.LineTo(canvas.PenPos.X, canvas.PenPos.Y-u);
b(i-1,canvas);
end;
end;

procedure TForm1.FormPaint(Sender: TObject);


begin
Form1.Canvas.MoveTo(u,u);
a(5,Form1.Canvas);
end;
end.

12
Рекурсивное построение кривой «Снежинка»

Разработать программу, которая обеспечивает рисование снежинки, число


звеньев и число ветвей которой задаются пользователем.

На рисунке изображена снежинка, состоящая из трех звеньев и восьми


ветвей.

Сделаем расчет длина каждого звена по известному значению n – количеству


звеньев и величине экрана. Будем считать, что снежинка строится в квадрате
400х400 точек. Центр снежинки совпадает с центром квадрата, а длина отрезка

13
l l
l + + 2 = 200
4 4
каждого очередного звена в четыре раза меньше предыдущего. Если через l
обозначить длину первого звена, то справедливо следующее равенство:

1
1−
l× 4 n = 200
1 (сумма членов геометрической прогрессии)
1−
4
то есть

4 n −1
l = 200 × 3 × n
4 −1
Пусть снежинка состоит из одного звена и р ветвей, тогда соответствующая
программа проста. Основная ее часть ее имеет вид:

for i:=1 to p do
begin
x1:=trunc(x+L[k]*cos(2*pi*(i-1)/p));
y1:=trunc(y+L[k]*sin(2*pi*(i-1)/p));
{координаты конца очередного звена}
Flake.Canvas.MoveTo(x,y); {рисование звена}
Flake.Canvas.LineTo(x1,y1);
End;

Здесь х, у – координаты точки центра снежинки. Снежинка должна


рисоваться как бы много раз, при этом длины звеньев изменяются, поэтому
их надо просчитать один раз и запомнить в массиве L из n элементов. Длина
каждого звена уменьшается в четыре раза по отношению к предыдущему.
Длина первого звена определяется из того, что в квадрате из 400х400
необходимо построить снежинку из n звеньев. Если мы дошли до этапа
рисования самого маленького звена (самой маленькой снежинки), то наша
программа должна работать как приведенный набросок.

Итак, логика решения задачи.

14
Начинаем рисовать из центра, точки А, нарисовали отрезок АВ(a), если это
не последнее звено, то будем рисовать отрезок следующего звена ВС, звено
не последнее, поэтому продолжим. Предположим, что нарисовали СЕ, это
первая часть самой маленькой снежинки, наша программа должна работать
как набросок, то есть рисовать все ветви этой снежинки. После этого мы
должны вернуться в точку В. Так как это не последняя ветвь этой снежинки,
то мы снова должны нарисовать следующую ветвь – отрезок ВД(б), а затем
снова полностью самую маленькую снежинку. Что необходимо для
реализации этой логики? Пусть значение переменной k будет равно
текущему номеру звена снежинки, в начальный момент оно равно n. Тогда
при переходе из точек С, Д к точке В мы должны «вспомнить» координаты
точки В и номер ветви снежинки, рисуемой из точки В. При переходе от
точки В к А мы должны «вспомнить» координаты точки А и номер ветви
снежинки, рисуемой из точки А. Эта логика реализуется с помощью
рекурсии.

Листинг программы в Delphi:

type
TFlake = class(TForm) //имя формы- “Flake”
procedure Snow(x,y,k: Integer);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
p=8;
var
Flake: TFlake;
x,y,i: integer;
n: integer;
L: array[1..8] of integer;
implementation
{$R *.dfm}
procedure TFlake.Snow(x,y,k: Integer);
var x1, i,y1: integer;
begin
if k>0 then

15
begin
for i:=1 to p do
begin
x1:=trunc(x+L[k]*cos(2*pi*(i-1)/p));
y1:=trunc(y+L[k]*sin(2*pi*(i-1)/p));
Flake.Canvas.MoveTo(x,y);
Flake.Canvas.LineTo(x1,y1);
snow(x1,y1,k-1); end; end;
end;
procedure TFlake.FormPaint(Sender: TObject);
begin
Flake.Canvas.Pen.Color:=clWhite;
L[n]:=trunc(200*3*exp((n-1)*ln(4))/(exp(n*ln(4))-1));
for i:=2 to n do
L[n-i+1]:=trunc(L[n]/exp((i-1)*ln(4)));
x:=300; y:=200;
Snow(x,y,n);
end;
end.

Рекурсивное построение треугольника Серпинского.

На рисунке 1.7 изображен треугольник Серпинского 1-го и 2-го порядка.

В треугольнике проводятся все три средние линии. В результате он


разбивается на 4 новых треугольника. К трем из них, примыкающим к
вершинам первоначального треугольника, применяется та же процедура.

В Delphi это реализуется следующим образом:

type
TTringle = class(TForm)
Panel1: TPanel;

16
Label3: TLabel;
Label2: TLabel;
Label1: TLabel;
Button1: TButton;
Button2: TButton;
procedure Triangle(xa, ya, xb, yb, xc, yc, n: Integer);
procedure FormPaint(Sender: TObject);
procedure FormShow(Sender: TObject);

private
{ Private declarations }
public
{ Public declarations }
end;
var
Tringle: TTringle;
n,xa, ya, xb, yb, xc, yc,q : Integer;
w:string;
a: array[0..10]of TColor; //задаем массив цветов.
implementation

{$R *.dfm}

procedure TTringle.Triangle(xa, ya, xb, yb, xc, yc, n: Integer);


var xp, xq, xr, yp, yq, yr: Integer;
begin
if n>0 then
begin //вычисление координат середин сторон треугольника
xp:=(xb+xc) div 2;
yp:=(yb+yc) div 2;
xq:=(xa+xc) div 2;
yq:=(ya+yc) div 2;
xr:=(xb+xa) div 2;
yr:=(yb+ya) div 2;
a[0]:=clMaroon; //определяем значение каждого элемента массива
a[1]:=clPurple;
a[2]:=clFuchsia;
a[3]:=clAqua;
a[4]:=clBlue;
a[5]:=clBlue;
a[6]:=clMaroon;
a[7]:=clPurple;
a[8]:=clFuchsia;
a[9]:=clAqua;
a[10]:=clBlue;
for q:=0 to n do //задаем цикл для раскрашивания треугольника
Tringle.Canvas.Pen.Color:=a[q];
Tringle.Canvas.MoveTo(xp,yp); //Изображение средних линий треугольника
Tringle.Canvas.LineTo(xq,yq);
Tringle.Canvas.MoveTo(xq,yq);
Tringle.Canvas.LineTo(xr,yr);
Tringle.Canvas.MoveTo(xp,yp);

17
Tringle.Canvas.LineTo(xr,yr);
Triangle(xa, ya, xr, yr, xq, yq, n-1);
Triangle(xb, yb, xp, yp, xr, yr, n-1);
Triangle(xc, yc, xq, yq, xp, yp, n-1);
end;
end;
procedure TTringle.FormPaint(Sender: TObject);
begin
Tringle.Canvas.Pen.Color:=clPurple;
xc:=300; yc:=0; xb:=600; yb:=400; xa:=0; ya:=400;// задание начальных значений
Tringle.Canvas.MoveTo(xa,ya);
Tringle.Canvas.LineTo(xb,yb);
Tringle.Canvas.MoveTo(xb,yb);
Tringle.Canvas.LineTo(xc,yc);
Tringle.Canvas.MoveTo(xa,ya);
Tringle.Canvas.LineTo(xc,yc);
Triangle(xa,ya,xb,yb,xc,yc,StrToInt(w));
end;

Фрактальные изображения выглядят особенно красиво, если их раскрасить.


Это предусмотрено в программе для построения треугольника Серпинского.
Составляющие основной треугольники меняют цвета в зависимости от их
размера. Треугольники одинакового размера имеют одинаковый цвет. Самый
большой треугольник всегда окрашен в фиолетовый цвет.

Это организовано следующим образом. В разделе описания глобальных


переменных var мы задаем массив из набора цветов (a: array[0..10]of TColor).

18
Затем определяем значение каждого элемента массива, то есть каждому из
элемента массива присваиваем определенный цвет. Для того, чтобы при
каждом последующем повторении программа использовала другой цвет
организуем цикл с параметром for. Этот цикл имеет следующую структуру:

For <параметр_цикла>:=<начальное_значение> to <конечное_значение>

Do <оператор>;

Начальное значение цикла будет равно 1 и цикл должен выполняться до тех


пор, пока не будет начерчена кривая наибольшей глубины, то есть до n.

a[0]:=clMaroon; a[1]:=clPurple; a[2]:=clFuchsia; a[3]:=clAqua; a[4]:=clBlue; a[5]:=clBlue;


a[6]:=clMaroon; a[7]:=clPurple; a[8]:=clFuchsia; a[9]:=clAqua; a[10]:=clBlue;
//каждому элементу массива присваиваем определенный цвет
for q:=0 to n do //задаем цикл для раскрашивания треугольника
Tringle.Canvas.Pen.Color:=a[q];//рисование треугольников текущим цветом
Tringle.Canvas.MoveTo(xp,yp);
Tringle.Canvas.MoveTo(xq,yq);
Tringle.Canvas.LineTo(xr,yr);
Tringle.Canvas.MoveTo(xp,yp);
Tringle.Canvas.LineTo(xr,yr);
Triangle(xa, ya, xr, yr, xq, yq, n-1);
Triangle(xb, yb, xp, yp, xr, yr, n-1);
Triangle(xc, yc, xq, yq, xp, yp, n-1); end;

Построение треугольника Серпинского в среде Visual Basic практически не


отличается от его построения в Delphi. Ниже приведен листинг программы.

Public xa As Integer, ya As Integer, xb As Integer, yb As Integer, xc As Integer, yc As Integer, n


As Integer // объявление глобальных переменных
Private Sub triangle(xa As Integer, ya As Integer, xb As Integer, yb As Integer, xc As Integer, yc
As Integer, n As Integer) //описываем процедуру
Dim xp As Integer, xq As Integer, xr As Integer, yp As Integer, yq As Integer, yr As Integer
// объявление локальных переменных
If n > 0 Then
xp = Int((xb + xc) / 2) //Нахождение координат средних линий треугольника
yp = Int((yb + yc) / 2)
xq = Int((xa + xc) / 2)
yq = Int((ya + yc) / 2)
xr = Int((xb + xa) / 2)
yr = Int((yb + ya) / 2)
Line (xp, yp)-(xq, yq), RGB(90, 10, 500)
Line (xq, yq)-(xr, yr), RGB(190, 10, 100)

19
Line (xp, yp)-(xr, yr), RGB(190, 190, 300)
Call triangle(xc, yc, xq, yq, xp, yp, n - 1)
Call triangle(xa, ya, xr, yr, xq, yq, n - 1)
Call triangle(xb, yb, xp, yp, xr, yr, n - 1)
End If End Sub
Private Sub Command1_Click()
treug.Scale (-50, -50)-(800, 600) // Задаем маштаб
treug.Cls //предварительно очищаем форму от предыдущего рисунка
n = Val(Text1.Text)
Line (xa, ya)-(xb, yb), RGB(90, 190, 300)
Line (xb, yb)-(xc, yc), RGB(90, 190, 300)
Line (xa, ya)-(xc, yc), RGB(90, 190, 300)
Call triangle(xa, ya, xb, yb, xc, yc, n)
End Sub
Private Sub Form_Load()
n = Val(Text1.Text)
xc = 300
yc = 0
xb = 600
yb = 400
xa = 0 ya = 400
End Sub

При построении фрактальной кривой используется графический метод Line.

Метод Line позволяет:

1) чертить на объекте, к которому этот метод применяется, отрезки прямых


линий;

2) изображать на этом объекте закрашенные или не закрашенные


прямоугольники.

Метод Line имеет следующий синтаксис:

[ИмяОбъекта.] Line (Х1, Y1) — (Х2, Y2) [, Цвет [, Флаг ]]

Вслед за ключевым словом Line через запятую следуют так называемые


параметры метода.

20
Имя Объекта может отсутствовать. В этом случае по умолчанию метод
применяется к экранной форме.

Параметр (XI, Y1) — (Х2, Y2) — это координаты границ отрезка или двух
углов прямоугольника — левого верхнего и правого нижнего (или наоборот
— правого нижнего и левого верхнего).

Параметр Цвет — это выражение, значением которого является число типа


Long, которым в Visual Basic кодируется тот или иной цвет.

Параметр Цвет может отсутствовать. Тогда по умолчанию он будет


совпадать со значением свойства ForeColor объекта.

В Visual Basic встроена функция RGB, которая рассчитывает код оттенков


красного, зеленого, синего цветов. Данная функция имеет следующий
синтаксис:

RGB(Red (код красного цв.), Green (код зеленого цв.), Blue (код синего цв.)

Наличие данной функции позволяет, например, проследить зависимость


цвета от координат. Рассмотрим это позже на примере аттракторов «Радуга»
и «Космос».

Параметр Флаг — это либо символ В, либо символы BF. В первом случае
рисуется не отрезок, а прямоугольник, во втором — закрашенный
прямоугольник. (Цвет закраски — это значение параметра Цвет.)

Отметим и то, что толщину отрезка или контура прямоугольника можно


увеличить. Для этого, следует увеличить значение свойства DrawWidth (по
умолчанию оно равно 2).

Рекурсивное построение кривой «Квадраты».

21
Построение данной кривой аналогично построению треугольника
Серпинского. Для квадрата находят середины его сторон. Эти точки
соединяются линиями, в результате чего образуется другой квадрат,
вписанный в первоначальный. К последующим квадратам применяется та
же процедура.

Листинг программы построения кривой «Квадраты»

type
TRect = class(TForm)
procedure Rectangle(xa,ya,xb,yb,xc,yc,xd,yd,n:Integer);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Rect: TRect;
xa,ya,xb,yb,xc,yc,xd,yd,n,i,w: integer;

implementation

22
{$R *.dfm}

procedure TRect.Rectangle(xa,ya,xb,yb,xc,yc,xd,yd,n:integer);
var xp,yp,xr,yr,xq,yq,xf,yf: integer;
begin
if n>0 then begin
Rect.Canvas.Pen.Color:=clBlack;
xp:=(xa+xb) div 2; //нахождение середин сторон
yp:=(ya+yb) div 2;
xr:=(xb+xc) div 2;
yr:=(yb+yc) div 2;
xq:=(xc+xd) div 2;
yq:=(yc+yd) div 2;
xf:=(xa+xd) div 2;
yf:=(ya+yd) div 2;
Rect.Canvas.MoveTo(xp,yp);
Rect.Canvas.LineTo(xr,yr);
Rect.Canvas.MoveTo(xr,yr);
Rect.Canvas.LineTo(xq,yq);
Rect.Canvas.MoveTo(xq,yq);
Rect.Canvas.LineTo(xf,yf);
Rect.Canvas.MoveTo(xf,yf);
Rect.Canvas.LineTo(xp,yp);
Rectangle(xp,yp,xr,yr,xq,yq,xf,yf,n-1);
end;
end;
procedure TRect.FormPaint(Sender: TObject);
begin
Rect.Canvas.Pen.Width:=2;
xa:=0; ya:=500; //задание начальных значений
xb:=500; yb:=500;
xc:=500; yc:=50;
xd:=0; yd:=50;
Rectangle(xa,ya,xb,yb,xc,yc,xd,yd,n);
end;

Примечание. Используемые методы класса Canvas.

Метод Описание
FillRect(const Rect: Заполняет текущей кистью прямоугольную область
TRect);* Rect, включая ее левую и верхнюю границу, но не
затрагивая правую и нижнюю границы.
MoveTo(x,y: Integer); Перемещает перо в положение (x,y) без вычерчивания
линий.
TextOut(x,y: integer; Выводит текстовую строку Text так, чтобы левый
const Text: String); верхний угол прямоугольника, охватывающего текст,
располагался в точке (х,у).

23
*Метод FillRect удобно использовать для удаления изображения. Нужно
просто задать цвет заливки таким же, как цвет формы и в скобках указать
всю область формы: Form1.Canvas.FillRect(Form1.ClientRect) ;

24
Листинг программы для построения кривой «Квадраты» в Visual Basic

Public xa As Integer, ya As Integer, xb As Integer, yb As Integer, xc As Integer, yc As Integer, n


As Integer, xd As Integer, yd As Integer

Private Sub triangle(xa As Integer, ya As Integer, xb As Integer, yb As Integer, xc As Integer, yc


As Integer, xd As Integer, yd As Integer, n As Integer)
Dim xp As Integer, yp As Integer, xr As Integer, yr As Integer, xq As Integer, yq As Integer, xf As
Integer, yf As Integer
If n > 0 Then
xp = Int((xa + xb) / 2)
yp = Int((ya + yb) / 2)
xr = Int((xb + xc) / 2)
yr = Int((yb + yc) / 2)
xq = Int((xc + xd) / 2)
yq = Int((yc + yd) / 2)
xf = Int((xa + xd) / 2)
yf = Int((ya + yd) / 2)
Line (xp, yp)-(xr, yr), RGB(90, 10, 500)
Line (xr, yr)-(xq, yq), RGB(10, 30, 200)
Line (xq, yq)-(xf, yf), RGB(190, 0, 300)
Line (xf, yf)-(xp, yp), RGB(0, 0, 500)
Call triangle(xp, yp, xr, yr, xq, yq, xf, yf, n - 1)
End If
End Sub

Private Sub Command1_Click()


kvad.Scale (-10, -10)-(600, 600)
kvad.Cls
n = Val(Text1.Text)
Call triangle(xa, ya, xb, yb, xc, yc, xd, yd, n)
End Sub

Private Sub Form_Load()


n = Val(Text1.Text)
xc = 500
yc = 50
xb = 500
yb = 500
xa = 0
ya = 500
xd = 0
yd = 50
End Sub

25
Вы, наверное, часто видели довольно хитроумные картины, на которых
непонятно что изображено, но все равно необычность их форм завораживает
и приковывает внимание. Как правило, кажется, что эти хитроумные формы
не поддаются какому-либо математическому описанию. Однако такие
картины могут быть порождены многократным применением формулы,
связывающей каким-либо образом координаты x и у. Эти картины
называются аттракторами. Рассмотрим формулу, придуманную Мартином из
Астонского университета:

X n +1 = Yn − b ⋅ X n − c

Yn +1 = a − X n

Здесь новые значения Х и У оригинально связаны со старыми: новый Х


зависит от старого У, а новый У от старого Х, причем чтобы процесс не
пошел «вразнос», при вычислении Х от старого У отнимается Х1/2 . А для
того, чтобы ситуация нами контролировалась введены коэффициенты a,b и с.
Посмотрим, как уложатся точки, если их координаты менять по
приведенной формуле, ─ результат будет необычайно красив.

Листинг программы реализации аттрактора «Звездное небо»

procedure Tpoint.Paint;
var q,i,x,x1,y,a,b,c: integer;
begin
if n>0 then begin
s[0]:=clBlue; //каждому элементу массива задаем определенный цвет
s[1]:=clred;
s[2]:=clyellow;
s[3]:=clAqua;
s[4]:=clPurple;
s[5]:=clGreen;

26
s[6]:=clFuchsia;
s[7]:=clWhite;
repeat
Point.Canvas.MoveTo(x,y);
a:=StrToInt(Edit1.Text); //значения коэффициентов будут вводиться пользователем в
текстовые поля
b:=StrToInt(Edit2.Text);
c:=StrToInt(Edit3.Text);
for i:=1 to 1000 do
begin
for q:=0 to 7 do
begin
Point.Font.Color:=s[q];
p:=10; //искусственно вводим переменную для остановки цикла
x1:=x;
x:=trunc(y-sqrt(abs(b*x1-c)))-100;
y:=a-x1;
Point.Canvas.TextOut(x+300,y+100,'*');
end; end;
end;
until
p=10;
end;
end;
procedure Tpoint.FormPaint(Sender: TObject);
var q:integer;
begin
paint; //рисуем «звездное небо»
end;

Запустив программу и введя наугад три числа 33, 33 и 33, получим узор,
напоминающий цветок.

27
Числа 1, 1, 1000 дадут спираль.

28
Можно вводить любые числа и наблюдать за изменением узора. Особенно
интересные картины получаются, когда меняется одно из трех чисел, а
другие остаются без изменения. Можно добиться, чтобы узор распался в
«звездное небо» или выродился в несколько точек.

Для удобства можно написать программу, которая организует случайный


выбор чисел. В языке Delphi это можно сделать с помощью функции
случайного выбора randomize. Например, можно создать кнопку, после
нажатия на которую в текстовые поля будут занесены случайные числа.
Обработчик события OnClick должен выглядеть следующим образом:

procedure Tpoint.Button2Click(Sender: TObject);


begin
randomize;
Edit1.Text:=IntToStr(random(1000)+11);
Edit2.Text:=IntToStr(random(1000)+11);
Edit3.Text:=IntToStr(random(1000)+11);
end;

29