Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Семинары N7 и N8 МАШИННАЯ ГРАФИКА-студ.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
218.11 Кб
Скачать

Формирование движущихся изображений

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

ПЕРЕМЕЩЕНИЕ. Для перемещения точки из позиции с координатами (X,Y) в позицию с координатами (X1,Y1) надо к координате X добавить dX пикселов, а к координате Y - dY пикселов:

X1=X+dX; (1)

Y1=Y+dY.

Положительное значение dX означает перемещение точки по горизонтали вправо; отрицательное - по горизонтали влево; положительное значение dY - перемещение вниз по вертикали, отрицательное - вверх по вертикали.

МАСШТАБИРОВАНИЕ. Для выполнения масштабирования необходимо указать коэффициенты масштабирования вдоль координатных осей и координаты точки, относительно которой производится масштабирование. Масштабирование может осуществляться относительно любой точки (центральной точки рисунка; точки, лежащей на границе объекта; относительно точки, лежащей вне объекта или даже вне экрана).

Масштабирование может быть однородным (коэффициенты масштабирования по горизонтали и вертикали одинаковы и пропорции объекта сохраняются) и неоднородным (коэффициенты масштабирования неодинаковы по горизонтали и вертикали и пропорции объекта не сохраняются). Координаты (Х1,Y1) промасштабированной точки рисунка определяются по формулам:

X1=E(KX*X +XM*(1-KX)); (2)

Y1=E(KY*Y +YM*(1-KY)),

где (X,Y) - координаты точки исходного рисунка;

(XM,YM) - координаты центра масштабирования;

KX, KY - коэффициенты масштабирования по горизонтали и вертикали,

E - округление до ближайшего целого.

Округление необходимо, так как коэффициенты масштабирования и координаты центра масштабирования в общем случае являются действительными величинами. При KX>1 и KY>1 рисунок увеличивается в размерах и удаляется от центра масштабирования; при KX<1 и KY<1 рисунок уменьшается в размерах и приближается к центру масштабирования. При KX=KY угол наклона масштабируемого отрезка не меняется.

ВРАЩЕНИЕ. При перемещении точки А с координатами (X,Y) по дуге окружности с центром в точке С с координатами (XC,YC) в точку В с координатами (X1,Y1) новые координаты определяются по формулам:

X1=XC+E((X-XC)*COS(Θ)+(Y-YC)*SIN(Θ)) (3)

Y1=YC+E((Y-YC)*COS(Θ)-(X-XC)*SIN(Θ)),

где Θ - угол поворота (положительное направление поворота против часовой стрелки, отрицательное - по часовой стрелке);

E - округление до ближайшего целого (оно необходимо, т.к. результат вычисления синуса, косинуса - действительный).

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

ПРЕОБРАЗОВАНИЕ ИЗОБРАЖЕНИЯ будет заключаться в вычислении координат всех точек нового рисунка в соответствии с выражениями (1) - для переноса, (2) - при масштабировании, (3) - при повороте и соединении полученных точек отрезками прямых.

Однако, не всегда требуется вычислять координаты всех точек нового рисунка. Например, при вычерчивании окружности достаточно вычислить новые координаты ее центра, а в качестве радиуса взять величину K*R, где K - коэффициент масштабирования, R - радиус исходной окружности.

Все программы, позволяющие воспроизводить движущееся изображение, имеют следующую особенность. Поскольку человеческий глаз обладает определенной инерционностью восприятия, то нельзя нарисовать изображение, затем сразу же стереть его и нарисовать новое изображение. Перед стиранием изображения необходимо предусмотреть задержку. Интервал времени, в течение которого высвечивается изображение, должен быть больше, чем интервал времени, в течение которого изображение отсутствует.

Прямолинейное движение получается при многократном применении операции перемещения изображения. При этом на каждом шаге перемещения координаты точек изображения получают приращения dx, dy вдоль координатных осей. При dx=0 движение осуществляется вдоль оси ординат; при dy=0 - вдоль оси абсцисс. При движении в произвольном направлении оба приращения координат dx, dy должны быть отличны от нуля. Отношение приращений dx/dy определяет коэффициент наклона траектории. Скорость движения изображения определяется значениями dx и dy (а также продолжительностью задержки). Если вдоль одной из осей приращение больше, то и скорость движения вдоль этой оси будет больше.

Перемещение изображения по известной криволинейной траектории выполняется путем вычисления текущего положения точки на основании уравнения траектории.

Вращение изображения относительно произвольной точки создается путем многократного повторения операции поворота.

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

В программах создания движущегося изображения для моделирования задержки используется невизуальный (во время работы программы не отображается на форме) компонент Timer, который расположен на странице System палитры компонентов Delphi. Компонент Timer добавляется к форме обычным образом, но, поскольку, он не отображается на форме, то его значок можно поместить в любое место формы.

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

Основное свойство таймера Interval определяет интервал времени в миллисекундах, который должен пройти от включения таймера до наступления события OnTimer. Другое свойство таймера Enabled определяет нахождение его во включенном (значение true) или выключенном состоянии (false). Включенный таймер периодически порождает событие OnTimer. Для прекращения генерации этого события свойству Enabled надо присвоить значение false. Таким образом, программист должен написать обработчик события OnTimer, в котором и будут предусмотрены действия по стиранию старого и выводу нового изображения.

В рассматриваемом примере в начальный момент времени Timer не используется (его свойство Enabled имеет значение False). Кнопка "Движение" активизирует этот компонент (Timer.Enabled := True), и начинается движение. Кнопка "Останов" меняет значение свойства Timer.Enabled на False, и движение прекращается.

В рассматриваемых примерах создание анимации демонстрируется на примере движения домика, рассмотренного в качестве примера построения плоского изображения.

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

В зависимости от вводимых скоростей можно промоделировать только один вид движения. К ранее использовавшимся кнопкам добавлены кнопки “Ввод скоростей”, “Движение”, “Останов”. Кнопка “Ввод скоростей” добавлена для того, чтобы можно было изменить скорости движения во время движения, кнопка “Останов” позволяет остановить движение объекта, например, для изменения скоростей движения (когда пользователь не успевает этого сделать в процессе движения и объект выходит за пределы формы).

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

unit domik;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Edit1: TEdit;

Label1: TLabel;

Edit2: TEdit;

Label2: TLabel;

Edit3: TEdit;

Label3: TLabel;

Button3: TButton;

Button4: TButton;

Button5: TButton;

Timer1: TTimer;

procedure Button2Click(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure Button3Click(Sender: TObject);

procedure Button4Click(Sender: TObject);

procedure Button5Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

xmax,ymax:integer;

hd, ld, ldo, hdo, rdo, hkr, hram :real;

const kl = 1.0 / 6.0;

kh = 1.0 / 8.0;

kkr = 1.0 / 24.0;

klo = 2.0 / 3.0;

kho = 2.0 / 3.0;

kro = 1.0 / 4.0;

khram = 1.0 / 4.0;

nn=14;

cris=clRed;

cf=clWhite;

k=pi/180;

var x,y:array[1..nn] of real;

dx,dy,f:integer; // скорости движения

xc,yc:real; //координаты центра вращения

implementation

{$R *.dfm}

//Рисование домика

procedure ris;

var i:integer;

begin

With Form1.Canvas do

begin

MoveTo(round(x[1]),round(y[1]));

for I := 2 to 5 do

LineTo(round(x[i]),round(y[i]));

LineTo(round(x[1]),round(y[1]));

MoveTo(round(x[2]),round(y[2]));

LineTo(round(x[4]),round(y[4]));

MoveTo(round(x[2]),round(y[2]));

LineTo(round(x[3]),round(y[3]));

LineTo(round(x[4]),round(y[4]));

//рисование окна

MoveTo(round(x[6]),round(y[6]));

for I := 7 to 9 do

LineTo(round(x[i]),round(y[i]));

LineTo(round(x[6]),round(y[6]));

MoveTo(round(x[10]),round(y[10]));

LineTo(round(x[11]),round(y[11]));

MoveTo(round(x[13]),round(y[13]));

LineTo(round(x[12]),round(y[12]));

//рисование окна на чердаке

Ellipse(round(x[14]-rdo),round(y[14]-rdo),round(x[14]+rdo),

round(y[14]+rdo));

end;

end;

//процедура вычисления координат точек при переносе

procedure perenos;

var i:integer;

begin

for i:=1 to nn do

begin

x[i]:=x[i]+dx;

y[i]:=y[i]+dy;

end;

end;

//процедура вычисления координат точек при повороте

procedure povorot;

var i:integer;

tp:real;

begin

xc:=(x[2]+x[4])/2;

yc:=(y[2]+y[4])/2;

for I := 1 to nn do

begin

tp:=x[i];

x[i]:=(xc+(x[i]-xc)*cos(f*k)+(y[i]-yc)*sin(f*k));

y[i]:=(yc-(tp-xc)*sin(f*k)+(y[i]-yc)*cos(f*k));

end;

end;

//обработчик события – щелчок на первой кнопке (рисование)

procedure TForm1.Button1Click(Sender: TObject);

begin

with Canvas do

begin

Brush.Color:=cf;

Brush.Style:=bsSolid;

Rectangle(0,0,xmax,Label1.Top);

Pen.Color:=cris;

ris();

end;

end;

//обработчик события – щелчок на второй кнопке (выход)

procedure TForm1.Button2Click(Sender: TObject);

begin

Close();

end;

//обработчик события – щелчок на третьей кнопке

//( ввод скоростей)

procedure TForm1.Button3Click(Sender: TObject);

begin

dx:=StrToInt(Edit1.Text);

dy:=StrToInt(Edit2.Text);

f:=StrToInt(Edit3.Text);

end;

// обработчик события – щелчок на четвертой кнопке (движение)

procedure TForm1.Button4Click(Sender: TObject);

begin

Timer1.Enabled:=true;

end;

//обработчик события – щелчок на пятой кнопке (останов)

procedure TForm1.Button5Click(Sender: TObject);

begin

Timer1.Enabled:=false;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

xmax:=ClientWidth;

ymax:=ClientHeight;

hd := kh * ymax;

ld := kl * xmax;

ldo := klo * ld;

hdo := kho * hd;

hkr := kkr * ymax;

rdo := kro * hkr;

hram := khram * hdo;

//расчет координат вершин домика

x[1]:=(xmax/2-ld/2); y[1]:=(ymax/2+hd/2);

x[2]:=x[1]; y[2]:=(y[1]-hd);

x[3]:=(x[2]+ld/2); y[3]:=(y[2]-hkr);

x[4]:=(x[2]+ld); y[4]:=y[2];

x[5]:=x[4]; y[5]:=y[1];

//расчет координат вершин окна

x[6]:=(x[1]+(ld-ldo)/2); y[6]:=(y[1]-(hd-hdo)/2);

x[7]:=x[6]; y[7]:=(y[6]-hdo);

x[8]:=(x[7]+ldo); y[8]:=y[7];

x[9]:=x[8]; y[9]:=y[6];

x[10]:=x[6]; y[10]:=(y[7]+hram);

x[11]:=x[8]; y[11]:=y[10];

x[12]:=x[3]; y[12]:=y[10];

x[13]:=x[12]; y[13]:=y[6];

x[14]:=x[12]; y[14]:=(y[3]+hkr/2);

Timer1.Enabled:=false;

Timer1.Interval:=40;

Edit1.Text:='3';

Edit2.Text:='2';

Edit3.Text:='10';

end;

//обработчик события – сигнал от таймера

procedure TForm1.Timer1Timer(Sender: TObject);

begin

With Canvas do

begin

Pen.Color:=cf;

ris();

perenos();

povorot;

Pen.Color:=cris;;

ris();

end;

end;

end.

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

unit domik;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Edit1: TEdit;

Label1: TLabel;

Edit2: TEdit;

Label2: TLabel;

Edit3: TEdit;

Label3: TLabel;

Button3: TButton;

Button4: TButton;

Button5: TButton;

Timer1: TTimer;

procedure Button2Click(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure Button3Click(Sender: TObject);

procedure Button4Click(Sender: TObject);

procedure Button5Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

xmax,ymax:integer;

hd, ld, ldo, hdo, rdox,rdoy, hkr, hram :real;

const kl = 1.0 / 6.0;

kh = 1.0 / 8.0;

kkr = 1.0 / 24.0;

klo = 2.0 / 3.0;

kho = 2.0 / 3.0;

kro = 1.0 / 4.0;

khram = 1.0 / 4.0;

nn=14;

cris=clRed;

cf=clWhite;

k=pi/180;

var x,y:array[1..nn] of real;

f:integer; kx,ky,xm,ym,xc,yc:real;

implementation

{$R *.dfm}

//Рисование домика

procedure ris;

var i:integer;

begin

With Form1.Canvas do

begin

//рисование домика

//Rectangle(x[2],y[2],x[5],y[5]);

MoveTo(round(x[1]),round(y[1]));

for I := 2 to 5 do

LineTo(round(x[i]),round(y[i]));

LineTo(round(x[1]),round(y[1]));

MoveTo(round(x[2]),round(y[2]));

LineTo(round(x[4]),round(y[4]));

MoveTo(round(x[2]),round(y[2]));

LineTo(round(x[3]),round(y[3]));

LineTo(round(x[4]),round(y[4]));

//рисование окна

//Rectangle(x[7],y[7],x[9],y[9]);

MoveTo(round(x[6]),round(y[6]));

for I := 7 to 9 do

LineTo(round(x[i]),round(y[i]));

LineTo(round(x[6]),round(y[6]));

MoveTo(round(x[10]),round(y[10]));

LineTo(round(x[11]),round(y[11]));

MoveTo(round(x[13]),round(y[13]));

LineTo(round(x[12]),round(y[12]));

//рисование окна на чердаке

Ellipse(round(x[14]-rdox),round(y[14]-rdoy),round(x[14]+rdox),

round(y[14]+rdoy));

end;

end;

procedure masst;

var i:integer;

begin

for i:=1 to nn do

begin

x[i]:=x[i]*kx+(1-kx)*xm;

y[i]:=y[i]*ky+(1-ky)*ym;

end;

rdox:=kx*rdox;

rdoy:=ky*rdoy;

end;

procedure povorot;

var i:integer;

tp:real;

begin

xc:=(x[2]+x[4])/2;

yc:=(y[2]+y[4])/2;

for I := 1 to nn do

begin

tp:=x[i];

x[i]:=(xc+(x[i]-xc)*cos(f*k)+(y[i]-yc)*sin(f*k));

y[i]:=(yc-(tp-xc)*sin(f*k)+(y[i]-yc)*cos(f*k));

end;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

with Canvas do

begin

Brush.Color:=cf;

Brush.Style:=bsSolid;

Rectangle(0,0,xmax,Label1.Top);

Pen.Color:=cris;

ris();

end;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

Close();

end;

//ввод скоростей

procedure TForm1.Button3Click(Sender: TObject);

begin

kx:=StrToFloat(Edit1.Text);

ky:=StrToFloat(Edit2.Text);

f:=StrToInt(Edit3.Text);

end;

//движение

procedure TForm1.Button4Click(Sender: TObject);

begin

Timer1.Enabled:=true;

end;

//останов

procedure TForm1.Button5Click(Sender: TObject);

begin

Timer1.Enabled:=false;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

xmax:=ClientWidth;

ymax:=ClientHeight;

hd := kh * ymax;

ld := kl * xmax;

ldo := klo * ld;

hdo := kho * hd;

hkr := kkr * ymax;

rdox := kro * hkr;

rdoy:=rdox;

hram := khram * hdo;

//расчет координат верштн домика

x[1]:=round(xmax/2-ld/2); y[1]:=round(ymax/2+hd/2);

x[2]:=x[1]; y[2]:=(y[1]-hd);

x[3]:=(x[2]+ld/2); y[3]:=(y[2]-hkr);

x[4]:=(x[2]+ld); y[4]:=y[2];

x[5]:=x[4]; y[5]:=y[1];

//расчет координат вершин окна

x[6]:=(x[1]+(ld-ldo)/2); y[6]:=(y[1]-(hd-hdo)/2);

x[7]:=x[6]; y[7]:=(y[6]-hdo);

x[8]:=(x[7]+ldo); y[8]:=y[7];

x[9]:=x[8]; y[9]:=y[6];

x[10]:=x[6]; y[10]:=(y[7]+hram);

x[11]:=x[8]; y[11]:=y[10];

x[12]:=x[3]; y[12]:=y[10];

x[13]:=x[12]; y[13]:=y[6];

x[14]:=x[12]; y[14]:=(y[3]+hkr/2);

Timer1.Enabled:=false;

Timer1.Interval:=40;

Edit1.Text:='0,98';

Edit2.Text:='0,98';

Edit3.Text:='5';

xm:=0;ym:=0;

end;

procedure TForm1.Timer1Timer(Sender: TObject);

begin

With Canvas do

begin

Pen.Color:=cf;

ris();

masst();

povorot;

Pen.Color:=cris;;

ris();

end;

end;

end.

Семинар N7

Построение графика функции

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

Для построения графика по заданному аналитическому описанию функции необходимо вычислить массив значений аргумента и массив соответствующих значений функции. Шаг изменения аргумента зависит от выбранного диапазона изменения аргумента и количества точек и вычисляется по формуле:

dx=(xmax-xmin)/(k-1), (1)

где dx - шаг изменения аргумента;

xmax - максимальное значение аргумента;

xmin - минимальное значение аргумента;

k - выбранное количество точек графика.

Параллельно с вычислением массивов значений аргумента х и функции y нужно определить минимальное и максимальное значения функции, что необходимо при проведении масштабирования.

Для рисования графика на канве выбирается прямоугольный участок, определяемый координатами левого верхнего угла (xn, yn) и правого нижнего угла (xk, yk). При этом надо иметь в виду то, что левее и ниже поля вывода будут располагаться надписи (выводятся соответствующие значения аргумента и функции).

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

Номер позиции в строке (абсцисса) точки графика определяется из выражения:

kx(i)=E((x(i)-xmin)*mx/rx) +xn, (2)

где kx(i) - номер позиции в строке для i-oй точки;