Наступна програма викреслює в діалоговому вікні криву Гільберта. На рис. 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 означають процедури.