Листинг 18д. Реализация обработчика кнопки.
⇐ ПредыдущаяСтр 5 из 5 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 - 2025 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|