[ Миронченко ] Императивное и объектно-ориентированное програмирование на Turbo Pascal и Delphi
.pdf352
type
TPoint = packed record
X:Longint;
Y:Longint;
end;
TRect = packed record case Integer of
0:(Left, Top, Right, Bottom: Integer);
1:(TopLeft, BottomRight: TPoint);
end;
Ключевое слово packed влияет на способ хранения структурированных типов (записей, массивов, классов и т.д.). Можете набрать слово packed в справочной системе, чтобы получить больше информации.
Чтобы нарисовать незакрашенный прямоугольник, можно использовать процедуру
procedure FrameRect(const Rect: TRect);
Но имейте в виду, что граница прямоугольника рисуется в этой процедуре цветом кисти.
Чтобы рисовать пером, надо использовать процедуру Polygon, которая рисует многоугольник по заданному массиву вершин (ребро – это линия между соседними вершинами).
procedure Polygon(Points: array of TPoint);
18.3. Рисование линий
procedure MoveTo(X, Y: Integer);
Устанавливает новую позицию пера. procedure LineTo(X, Y: Integer);
Рисует линию от текущего положения пера до точки (х, у). При этом новая позиция пера становится равной (х, у).
На самом деле в LineTo рисуется не линия, а отрезок, начало которого хранится в свойстве PenPos (позиция пера) канвы.
Кроме линий и прямоугольников в классе TCanvas есть еще довольно много графических примитивов. Вы можете прочитать о них, посмотрев методы класса TCanvas в справочной системе.
Пример 1: Построение правильного многоугольника.
Создайте новый проект, и на форму поместите простую кнопку (Button1). По нажатию этой кнопки на канве будет рисоваться правильный многоугольник.
procedure VielEck(n:integer;x,y:integer;a:integer;fi0:real;C:TCanvas); var
i:integer;
x1,x2,y1,y2:integer;
fi:real; //угол, на который надо повернуть вершину многоуг. begin
x1:=x+round(a*cos(fi0));
353
y1:=y+round(a*sin(fi0));
fi:=fi0+2*Pi/n;
C.MoveTo(x1,y1); //переходим в точку, с которой будут чертиться линии
for i:=0 to n-1 do //чертим все линии begin
x2:=x+round(a*cos(fi)); //координаты следующей вершины y2:=y+round(a*sin(fi));
C.LineTo(x2,y2); //чертит линию от текущего положения до точки
(х2,у2) fi:=fi+2*Pi/n; end;
end;
procedure TForm1.Button1Click(Sender: TObject); begin
with Canvas do begin
Pen.Color:=clWhite;//устанавливаем белый цвет пера Brush.Color:=clWhite;//устанавливаем белый цвет кисти Rectangle(clipRect);//рисуем белый прямоугольник на весь экран Pen.Color:=clRed; //устанавливаем красный цвет пера
VielEck(4,300,300,150,0,Form1.Canvas);
end;
end;
По-немецки многоугольник – das Polygon, но мне кажется, что гораздо красивее было бы назвать его das Vieleck (по аналогии с Viereck – четырехугольник; само слово viel означает много). Поэтому я назвал процедуру VielEck. Ее параметры – количество углов, центр окружности, описанной около многоугольника, радиус описанной окружности и угол относительно положительного направления оси ОХ, на который надо повернуть многоугольник и канва, на которой сам Vieleck будет рисоваться.
Сначала рассмотрим обработчик нажатия кнопки. Работа ведется со свойством Canvas (которое принадлежит классу TForm).
Чтобы очистить экран закрашиваем весь фон белым цветом.
ClipRect, который передается в процедуру рисования прямоугольника – это специальное свойство канвы:
property |
Содержит прямоугольник, который должен быть |
ClipRect:TRect; |
перерисован. |
Когда форма только создается, ClipRect содержит размеры всей канвы.
Если вы еще не запустили программу, то сделайте это. Вы увидите, что кнопка находится поверх канвы, и на ней ничего не рисуется. Теперь измените размеры экрана, перетащите окно так, чтобы оно одним краем вылезло за границы экрана и верните в исходное положение или сверните окно, а затем восстановите его. Вы увидите, что изображение на канве не перерисовывается само по себе, в отличие от кнопок и других компонентов.
354
18.4.Рисуем шашечную доску
Уформы есть специальное событие OnPaint, которое возникает тогда, когда надо перерисовать форму. Если написать обработчик этого события, то мы сможем выполнять специальные действия при перерисовке изображения канвы.
При изменении размеров формы возникает событие OnResize. В следующем примере мы будем рисовать шашечную доску (иногда ее называют шахматной, но исторически раньше появились шашки, поэтому правильнее писать «шашечная доска»). Мы сделаем так, чтобы она перерисовывалась, когда это необходимо и, кроме того, чтобы ее размеры менялись в зависимости от размеров формы.
Пример 2: Шашечная доска.
1 : unit BrettU;
2 :
3 : interface
4 :
5 : uses Graphics,Types,Forms,Math;
6 :
7 : type
8 : Brett = class {das Brett - доска} 9 : private
10:Langex:byte;{количество клеток по горизонтали}
11:Langey:byte;{количество клеток по вертикали}
12:Seite:byte;{длина стороны клетки (в пикселях)}
13:x,y:integer;{координаты верхнего левого угла доски}
14:farbe1:TColor;{цвет "белых" клеток}
15:farbe2:TColor;{цвет "черных" клеток}
16:ErwSeite:integer; //желаемая ширина клетки
17:public
18:procedure FestSt(aLangex,aLangey,aSeite:byte; ax,ay:integer; afarbe1,afarbe2:TColor);
19:constructor Bilde(aLangex,aLangey,aSeite:byte; ax,ay:integer; afarbe1,afarbe2:TColor);
20:procedure Male(C:TCanvas);
21:procedure StellFormat(Frm:TForm);
22:end;
23:
24: implementation 25:
26:constructor Brett.Bilde(aLangex,aLangey,aSeite:byte; ax,ay:integer; afarbe1,afarbe2:TColor);
27:begin
28:FestSt(aLangex,aLangey,aSeite,ax,ay,afarbe1,afarbe2);
29:end;
30:
31:procedure Brett.FestSt(aLangex,aLangey,aSeite:byte; ax,ay:integer; afarbe1,afarbe2:TColor);
32:begin
33:Langex:=aLangex;
34:Langey:=aLangey;
35:Seite:=aSeite;
355
36:x:=ax;
37:y:=ay;
38:farbe1:=afarbe1;
39:farbe2:=afarbe2;
40:ErwSeite:=Seite;
41:end;
42:
43:procedure Brett.StellFormat(Frm:TForm);
44:var
45:EinzugBr,EinzugH:integer;{отступ от краев (по ширине и
высоте)}
46:R:TRect;
47:begin
48:EinzugBr:=15;
49:EinZugH:=15;
50:{Вычисляем текущие координаты клиентской части формы}
51:R.Left:=Frm.Left;
52:R.Top:=Frm.Top;
53:R.Right:=Frm.Left+Frm.ClientWidth;
54:R.Bottom:=Frm.Top+Frm.clientHeight;
55:
56:{Длина и ширина поля}
57:Seite:=min(min(ErwSeite,(R.Right-R.Left-2*EinzugBr) div LangeX), (R.Bottom-R.Top-2*EinzugH) div LangeY);
59:x:=(R.Right-R.Left - Seite*LangeX) div 2;
60:y:=(R.Bottom-R.Top - Seite*LangeY) div 2;
62:end;
64:procedure Brett.Male(C:TCanvas);
65:var
66:Pfrb,Bfrb:TColor;
67:i,j:byte;
68:R:TRect;
69:begin
70:Pfrb:=C.Pen.Color;
71:Bfrb:=C.Brush.Color;
72:C.Brush.Color:=farbe1;
73:C.Pen.Color:=farbe2;
74:{заполняем всю доску "белым" цветом}
75:R.Left:=x;
76:R.Right:=x+Langex*Seite;
77:R.Top:=y;
78:R.Bottom:=y+Langey*Seite;
79:C.Rectangle(R);
80:
81:C.Brush.Color:=farbe2;
82:{Заполняем нужные клетки "черным" цветом}
83:for i:=1 to (Langex div 2) do
84:for j:=1 to (Langey div 2) do
356
85: C.Rectangle(x+(2*i-1)*Seite,y+(2*j- 2)*Seite,x+2*i*Seite,y+(2*j-1) *Seite);
86:
87:for i:=1 to (Langex div 2) do
88:for j:=1 to (Langey div 2) do
89:C.Rectangle(x+(2*i-2)*Seite,y+(2*j-1)*Seite,x+(2*i- 1)*Seite, y+(2*j)*Seite);
91:C.FrameRect(R);
92:C.Pen.Color:=Pfrb;
93:C.Brush.Color:=Bfrb;
94:end;
95:END.
Впроцедуре Male используется FrameRect (см. «рисование прямоугольников»). Сам алгоритм рисования можете разобрать самостоятельно. StellFormat («установи формат») устанавливает размеры доски относительно формы. У доски есть желаемая ширина клетки. Если доска с такой шириной клетки может поместиться на канву, то она рисуется именно с таким размером по центру канвы. Если вся доска не влезает на канву, то размер выбирается так, чтобы она влезла и еще оставалось место до края канвы (EinzugBr – отступ по горизонтали, EinzugH – отступ по вертикали). Теперь рассмотрим основной модуль.
1 : unit haupt;
2 :
3 : interface
4 :
5 : uses
6 : Windows, Messages, SysUtils, Variants, Classes, Graphics,
7 : Controls, Forms,Dialogs,BrettU;
8 :
9 : type
10:TForm1 = class(TForm)
11:procedure FormCreate(Sender: TObject);
12:procedure FormPaint(Sender: TObject);
13:procedure FormResize(Sender: TObject);
14:private
15:B:Brett;
16:public
17:{ Public declarations }
18:end;
19:
20:var
21:Form1: TForm1;
23:implementation
25:{$R *.dfm}
27:procedure TForm1.FormCreate(Sender: TObject);
28:begin
357
29:B:=Brett.Bilde(40,30,15,100,100,clYellow,clBlack);
30:end;
31:
32:procedure TForm1.FormPaint(Sender: TObject);
33:begin
34:B.Male(Canvas);
35:end;
36:
37:procedure TForm1.FormResize(Sender: TObject);
38:begin
39:with Canvas do
40:begin
41:Pen.Color:=Brush.Color;
42:Rectangle(ClipRect);
43:end;
44:B.StellFormat(Form1);
45:B.Male(Canvas);
46:end;
47:end.
Сами видите, что я добавил к форме одно закрытое поле – доску.
При создании формы возникает событие OnCreate, которое обрабатывается методом FormCreate, в которой создается объект В. Сразу после этого форма должна быть перерисована, поэтому возникает событие OnPaint, которое обрабатывается с помощью процедуры FormPaint. FormResize, обработчик события OnResize, вызывается после изменения размеров формы. В нем закрашивается все содержимое формы, устанавливаются новые параметры доски и рисуется ее обновленный вариант.
18.5. Фракталы
Термин «фрактал» ввел Бенуа Мандельброт. Но первые фрактальные множества появились задолго до работ Мандельброта. На рисунке 18.1 вы видите множество Кантора, которое строится на базе простого отрезка (0-я итерация). На первом шаге из отрезка выбрасывается средняя треть. Затем из двух оставшихся третей также выбрасываются центральные трети и т.д. до бесконечности. То, что осталось от отрезка после бесконечного количества выбрасываний и есть множество Кантора ( K ).
Рис. 18.1. Множество Кантора (0, 1, 2, 4-я итерации)
Что в нем такого необычного, спросите вы? Но оказывается, что несмотря на то, что суммарная длина выброшенных отрезков равна длине исходного отрезка, количество точек множества Кантора (т.е. его мощность) равно количеству точек исходного отрезка (это означает, что можно найти функцию, которая каждой точке K поставит в соответствие некоторую точку отрезка, причем разным точкам K