Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Листинг 18д. Реализация обработчика кнопки.




procedure TForm1.N1Click(Sender: TObject);

Var t0: TPoint; tc: TRPoint;

Begin

// Центр экрана и центр вращения (пока в центре

// экрана)

t0.X:=Form1.ClientWidth div 2;

t0.Y:=Form1.ClientHeight div 2;

tc.x:=0.0; tc.y:=0.0;

// Инициализация объектов

dec:=cDecart.Init(t0.x, t0.Y, 100, Form1.Canvas);

tr[1]:=cTri.Init(t0.x, t0.Y, 100, Form1.Canvas,

0,0, 1,0, 0,1, tc, clRed);

tr[2]:=cTri.Init(t0.x, t0.Y, 100, Form1.Canvas,

0,0, -1,0, 0,1, tc, clBlue);

tr[3]:=cTri.Init(t0.x, t0.Y, 100, Form1.Canvas,

0,0, -1,0, 0,-1, tc, clGreen);

tr[4]:=cTri.Init(t0.x, t0.Y, 100, Form1.Canvas,

0,0, 1,0, 0,-1, tc, clYellow);

N2.Enabled:=true; // Кнопка таймера доступна

end;

// Кнопка запуска таймера.

procedure TForm1.N2Click(Sender: TObject);

Begin

if Timer1.Enabled then Timer1.Enabled:=false

else Timer1.Enabled:=true;

end;

// Обработчик сигналов таймера.

procedure TForm1.Timer1Timer(Sender: TObject);

Var i: word;

Begin

// Очистка экрана и рисование осей

dec.Draw;

// Рисование

for i:=1 to 4 do tr[i].Draw;

// Пересчет координат объектов

for i:=1 to 4 do tr[i].Rotation(Pi/36);

end;

Класс графика траектории cTrace.

График траектории движения это обычный график функции y=f(x), построенный в интервале изменения аргумента от a до b. Параметры графика являются полями класса cTrace.

Для большей универсальности функцию целесообразно объявить предопределенным процедурным типом.

Листинг 18е. Объявление класса cTrace.

Type

Tf=function(x: real): real;

cTrace=

Class(cDecart)

Private

a, b: real;

f: Tf;

Public

Constructor Init(fx0, fy0: integer;

fmx: real; fcnv: TCanvas;

fa, fb: real; ff: Tf);

Procedure Draw;

end;

Реализация методов класса.

Procedure cTrace.Draw;

Const n=101;

Var dx: real; tr: TRPoint; tp: TPoint; i: word;

Begin

dx:=(b-a)/(n-1);

tr.x:=a; tr.y:=f(tr.x); tp:=tpix(tr);

cnv.Pen.Color:=clBlack;

cnv.MoveTo(tp.X, tp.Y);

for i:=2 to n do

Begin

tr.x:=a+dx*(i-1); tr.y:=f(tr.x);

tp:=tpix(tr);

cnv.LineTo(tp.X, tp.Y);

end;

End;

Constructor cTrace.Init(fx0, fy0: integer;

fmx: real; fcnv: TCanvas;

fa, fb: real; ff: Tf);

Begin

inherited Init(fx0, fy0, fmx, fcnv);

a:=fa; b:=fb; f:=ff;

End;

Траектория (см. свой вариант задания) программируется в том же модуле, что и класс траектории, но она не является членом класса. По правилам оформления модуля заголовок функции должен быть продублирован в интерфейсной части модуля вне объявления класса (обычно перед разделом VAR модуля).

Function MyTrace(x: real): real;

Begin

Result:=1-0.25*x*x;

End;

Инициализация и использование класса.

Переменную trace, объявленную в модуле класса, необходимо проинициализировать в обработчике настроек программы в соответствии с параметрами конструктора. Чтобы не запутаться в порядке инициализации, рекомендую сначала инициализировать объект класса cDecart затем объект класса cTrace, а уже потом другие объекты (треугольники и т.п.).

trace:=cTrace.Init(t0.x, t0.Y, 100, Form1.Canvas,

-2, 2, MyTrace);

Обращение к методу рисования необходимо добавить в обработчик таймера сразу после рисования осей координат.

Проблема связи между объектами.

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

Начальное положение фигуры – точка x=a, y=f(a). Конечное положение - точка x=b, y=f(b).

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

• Передавать параметры траектории через список параметра метода Move(a, b: real; f:Tf);

• Включить эти параметры в данные класса «треугольник» в виде переменной – объекта класса cTrace, либо «россыпью». Естественно, в конструкторе класса надо эти дополнительные поля инициализировать.

Как это часто бывает, очевидного решения этой проблемы, на данном этапе разработки, нет. Надо пробовать оба.

Метод Move c параметрами графика.

Листинг 18ж. Метод Move с передачей данных графика через параметры.

Procedure cTri.Move(a, b: real; f: Tf);

Const n=101;

Var dx: real;

Begin

dx:=(b-a)/(n-1);

tc.x:=tc.x+dx;

tc.y:=f(tc.x);

End;

Обращение к методу в обработчике событий таймера рекомендуется выполнять после вызова Rotation

for i:=1 to 4 do tr[i].Rotation(Pi/36);

for i:=1 to 4 do tr[i].Move(-2,2,MyTrace);

Перед инициализацией треугольников надо указать стартовую точку

tc.x:=-2;

tc.y:=MyTrace(-2);

Доработка метода cTri.Draw.

До сих пор центр вращения совпадал с началом координат, и все было ОК. Теперь надо внести изменения в «рисовалку» для учета смещения центра вращения относительно центра экрана.

Procedure cTri.Draw;

Var t: array[1..3] of TPoint; tr: TRPoint;

i: byte;

Begin

// Введена вспомогательная переменная tr для

// вычисления абсолютных реальных координат вершин

// (напомню, вершины apex[i] задаются относительно

// центра вращения tc)

for i:=1 to 3 do

Begin

tr.x:=tc.x+apex[i].x;

tr.y:=tc.y+apex[i].y;

t[i]:=tpix(tr);

end;

// Далее по тексту листинга 18б.

Метод Move с включением переменной – объекта класса cTrace в данные класса cTri.

· В список данных класса необходимо добавить поле trace: cTrace;

· В конструкторе класса теперь, вместо формального параметра ftc: TRPoint; для инициализации начального положения центра вращения, следует предусмотреть ftrace:cTrace;.

· В классе cTrace должны быть предусмотрены функции доступа к закрытым полям класса.

· В реализации конструктора фрагмент инициализации начального положения центра вращения можно оформить следующим образом (обратите внимание на синтаксис)

trace:=ftrace;

tc.x:=trace.GetA;

tc.y:=trace.GetFunc()(tc.x);

Смысл этих изменений заключается в том, что теперь источниками получения значений параметров a, b являются функции доступа GetA и GetB, а траектории – функция GetFunc. Алгоритм пересчета остался прежним.

Листинг 18з. Метод Move с использованием объекта класса cTrace внутри класса cTri.

Procedure cTri.Move;

Const n=101;

Var dx: real;

Begin

dx:=(trace.GetB-trace.GetA)/(n-1);

tc.x:=tc.x+dx;

tc.y:=trace.GetFunc()(tc.x);

End;

Создание контейнера.

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

Понятие контейнера было рассмотрено ранее в Практикуме 6 на примере организации данных в списковую структуру (список и контейнер – здесь рассматриваются как синонимы).

Поскольку в контейнере можно хранить данные только одного типа, то в качестве такого типа выберем базовый класс cDecart. Структура и методы класса остались такими же, как и в примере из Практикума 6.

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

В методы класса необходимо включить, кроме методов добавления в список и рисования списка, еще методы пересчета координат при вращении и перемещении.

Класс cList.

Листинг 18и. Объявление контейнерного класса cList. Используется метод Move из листинга 18з.

Type cList=

Class(cDecart)

Private

n: word;

a: array[1..21] of cDecart;

Public

Constructor Init;

Procedure Add(fa: cDecart);

Procedure Draw;

Procedure Rotation(da: real);

Procedure Move;

end;

// Объект класса.

Var list: cList;

Реализация методов.

Constructor cList.Init;

Begin n:=0; End;

Procedure cList.Add(frp: cDecart);

Begin inc(n); a[n]:=frp; End;

Procedure cList.Draw;

Var i: integer;

Begin

if n=0 then Exit;

for i:=1 to n do a[i].Draw;

End;

Procedure cList.Rotation(da: real);

Var i: word;

Begin

if n=0 then Exit;

for i:=1 to n do a[i].Rotation(Pi/36);

End;

Procedure cList.Move;

Var i: word;

Begin

if n=0 then Exit;

for i:=1 to n do a[i].Move;

End;

Обработчик инициализации.

Структура обработчика для инициализации объектов списка и добавления этих объектов в список не изменилась. Для инициализации должна быть предусмотрена отдельная кнопка меню, возможно и не одна, если возникла необходимость инициализировать список по разному.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...