Крива Гільберта

Наступна програма викреслює в діалоговому вікні криву Гільберта. На рис. 12.7 наведені криві Гільберта першого, другого і третього порядків. Якщо придивитися, то видно, що крива другого порядку виходить шляхом з’єднання прямими лініями чотирьох кривих першого порядку. Аналогічним чином виходить крива третього порядку, але при цьому в якості “цеглинок” використовуються криві другого порядку. Таким чином, щоб намалювати криву третього порядку, треба намалювати чотири кривих другого порядку. У свою чергу, щоб намалювати криву другого порядку, треба намалювати чотири кривих першого порядку. Таким чином, алгоритм креслення кривої Гільберта є рекурсивним.

Діалогове вікно програми Крива Гільберта, в якому знаходиться крива п’ятого порядку, наведено на рис. 12.8, текст програми – в лістингу 12.4.

Рис. 12.7. Криві Гільберта першого, другого і третього порядків

Рис. 12.8. Крива Гільберта п’ятого порядку

Лістинг 12.4. Крива Гільберта

unit gilbert _;

interface

uses

Windows, Messages, SysUtils,
Variants, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls, ComCtrls;

type

TForml = Class (TForm)

procedure FormPaint (Sender: TObject);
private

{ Private declarations}
public

{ Public declarations}
end;

var

Form1: TForm1;

implementation {$ R * .dfm}

var

p: integer = 5; // порядок кривої
u: integer = 7; // довжина штриха

{ Криву Гільберта можна отримати шляхом
з'єднання елементів а, b, с і d.

Кожен елемент будує
відповідна процедура. }
procedure a (i: integer; canvas: TCanvas); forward;
procedure b (i: integer; canvas: TCanvas); forward;
procedure з (i: integer; canvas: TCanvas); forward;
procedure d (i: integer; canvas: TCanvas); forward;

// Елементи кривої

procedure a (i: integer; canvas: TCanvas);
begin

if i > 0 then begin

d (i-l, canvas);
canvas.LineTo (canvas.PenPos.X + u, canvas.PenPos.Y);
a (i-l, canvas);
canvas.LineTo (canvas.PenPos.X, canvas.PenPos.Y + u);
a (i-l, canvas);
canvas.LineTo (canvas.PenPos.Xu, canvas.PenPos.Y);
з (I-1, canvas);
end;
end;

procedure b (i: integer; canvas: TCanvas);

begin

if i > 0 then begin

c (i-l, canvas);
canvas.LineTo (canvas.PenPos.Xu, canvas.PenPos.Y);
b (i-1, canvas);
canvas.LineTo (canvas.PenPos.X, canvas.PenPos.Yu);
b (i-l, canvas);
canvas.LineTo (canvas.PenPos.X + u, canvas.PenPos.Y);
d (i-l, canvas);
end;
end;

procedure c (i: integer; canvas: TCanvas);
begin

if i > 0 then begin

b (i-1, canvas);
canvas.LineTo (canvas.PenPos.X, canvas.PenPos.Yu);
з (I-1, canvas);
canvas.LineTo (canvas.PenPos.Xu, canvas.PenPos.Y);
c (i-1, canvas);
canvas.LineTo (canvas.PenPos.X, canvas.PenPos.Y + u);
a (i-1, canvas);
end;
end;

procedure 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.Yu);
b (i-1, canvas);
end;
end;

procedure TForml.FormPaint (Sender: TObject);
begin

Form1.Canvas.MoveTo (u, u);
a (5, Form1.Canvas); // викреслити криву Гільберта
end;

end.

Слід звернути увагу на таку особливість реалізації програми. Процедура, яка викреслює елемент а, крім самої себе (для креслення елемента а кривої нижчого порядку) викликає процедури d і ь, опис (текст) яких в тексті програми знаходиться після процедури а. Щоб компілятор не вивів повідомлення про помилку, в текст програми вміщено оголошення процедури з ключовим словом forward, що означає, що це тільки оголошення, а опис (реалізація) знаходиться далі. Таким чином, вже в процесі компіляції процедури а, компілятор “знає “, що імена ь і d означають процедури.