Книжка Хабы (970988), страница 13
Текст из файла (страница 13)
M = ¦ 0 KY 0 ¦ (5)
¦ 0 0 1 ¦
При подстановке ее в выражение (2) получим:
¦ KX 0 0 ¦
(X1,Y1,1) = (X,Y,1)*¦ 0 KY 0 ¦ или
¦ 0 0 1 ¦
X1 = X*KX, Y1 = Y*KY,
где KX - коэффициент масштабирования по оси абсцисс;
KY - коэффициент масштабирования по оси ординат.
Применяя преобразование (5) ко всем точкам рисунка, получим рисунок промасштабированный относительно начала координат. При этом если KX > 1 и KY > 1, то рисунок увеличивается в размере и удаляется от начала координат; если KX < 1 и KY< 1, то рисунок уменьшается в размерах и приближается к началу координат.
Координаты промасштабированной точки определяются из следующих выражений:
X1=X*KX+(1-KX)*XM
Y1=Y*KY+(1-KY)*YM (6)
где X,Y - координаты исходной точки;
X1,Y1 - координаты промасштабированной точки;
XM,YM - координаты центра масштабирования;
KX,KY - коэффициенты масштабирования.
1.4. Масштабирование рисунка
Для масштабирования рисунка необходимо в соответствии с (6) вычислить новые координаты всех точек нового изображения, а затем полученные точки соединить линиями. При этом размеры рисунка равномерно увеличиваются или уменьшаются, если KX=KY.
Однако не всегда надо вычислять координаты всех точек нового рисунка. Например, при масштабировании окружности, достаточно вычислить новые координаты ее центра, а в качестве радиуса взять величину k*R (k - коэффициент масштабирования, R - радиус исходной окружности). Таким же образом можно поступить при масштабировании эллипсов, прямоугольников.
При вычислении координат точек нового рисунка следует иметь в виду, что коэффициент масштабирования, как правило, величина действительная, а координаты точек на экране должны быть целыми, поэтому в программе необходимо использовать в этом случае операцию округления.
С помощью масштабирования можно растянуть или сжать изображение вдоль одной координатной оси, оставив его без изменения вдоль другой оси. Например, масштабируя квадрат с коэффициентами масштабирования KX = 1, KY =2, получим прямоугольник, у которого большая сторона имеет вертикальное расположение.
Неравномерное масштабирование окружности приводит к тому, что будет изображен эллипс. Но в этом случае программист должен воспользоваться процедурой рисования эллипса, а не окружности. В такой ситуации в целях общности целесообразно далее окружности вычерчивать процедурой рисования эллипса (задавая равные значения полуосей).
1.5. Поворот изображения
Наиболее часто поворот изображения используется при создании движущихся изображений в моделирующих, игровых программах. Однако иногда бывает удобно повернуть на 90о график, гистограмму. В процессе проектирования также необходимо поворачивать изображение создаваемого объекта, чтобы рассмотреть его с разных сторон и избежать возможных ошибок.
1.5.1.Поворот точки
Для выполнения поворота надо указать величину угла, на который необходимо осуществить поворот, и координаты точки, которая берется за центр вращения. Если исходную точку A с координатами (X,Y) по дуге окружности с центром в точке C с координатами (Xс,Yс) поворачивают на угол t, то координаты (X1,Y1) повернутой точки могут быть записаны в следующем виде:
X1 = Xс + (X-Xс) * cost + (Y-Yс) * sint * rx/ry
Y1 = Yс + (Y-Yс)*cost - (X-Xс) * sint * ry/rx, (7)
где rx, ry - разрешающие способности вдоль оси X и Y соответственно.
Значения rx/ry (ry/rx) могут быть получены в Турбо-Паскале с помощью процедуры GetAspectRatio, они являются обратными величинами по отношению к коэффициентам, выдаваемым процедурой:
rx/ry = ya/xa, ry/rx=xa/ya
Если центр вращения совпадает с началом координат (Xc=0, Yс=0), то матрица преобразования имеет вид:
¦ cost -sint 0 ¦
M = ¦ sint cost 0 ¦ (8)
¦ 0 0 1 ¦
Если начало координат расположено в левой верхней точке экрана, то угол поворота измеряется в направлении против часовой стрелки. Если же начало координат лежит в левой нижней точке экрана, то угол поворота должен измеряться в направлении по часовой стрелке.
Центр поворота может быть расположен в любом месте экрана, а также за пределами его границ.
Угол поворота лежит обычно в пределах от 0 до 360 , другие углы поворота также допустимы, однако поворот при этих углах эквивалентен повороту при углах из указанного диапазона.
1.5.2. Поворот рисунка
Для поворота всего рисунка, прежде всего необходимо выбрать центр поворота и определить угол, на который необходимо повернуть исходное изображение. После этого в соответствии с (7) вычислить координаты всех точек рисунка и соединить вновь полученные точки линиями.
Однако не всегда требуется расчет новых координат всех точек изображения: для прямоугольника это только четыре точки - его вершины, для окружности необходимо рассчитать только координаты центра и вычертить ее на новом месте.
При вычислении координат следует иметь в виду, что для хранения новых координат точек нельзя использовать ту же область памяти, где хранятся исходные координаты. Это объясняется тем, что при вычислении координаты Y1 в выражении (7) используется значение исходной координаты X. Таким образом, используя одну область памяти, мы будем затирать нужную нам старую координату X и неправильно вычислять новое значение координаты Y1.
Следует обратить внимание на то, что результат вычисления значений функций синуса и косинуса - действительный, а координаты точек должны быть целыми, поэтому в программе необходимо использовать операцию округления.
Преобразованное изображение (повернутое, промасштабированное или перенесенное) может высвечиваться вместе с исходным. Если же это нежелательно, то перед рисованием нового изображения необходимо произвести очистку экрана.
При выполнении преобразований желательно заранее предвидеть расположение рисунка, т.к. точки, выходящие за границы экрана, не высвечиваются. Если в программе введен контроль значений координат точек, то он только может обеспечить поиск ошибки, но программу в этом случае все равно придется корректировать.
Программа lab_31 строит исходную астроиду с осями, параллельными координатным осям, а затем может построить ее промасштабированное, перенесенное или повернутое на некоторый угол изображение. Поскольку заранее не известно количество точек, аппроксимирующих кривую, то для хранения координат точек исходной астроиды используется динамический список.
1.6. Композиция преобразований
Композиция преобразований представляет собой совокупность последовательного выполнения базовых преобразований: переноса, масштабирования, поворота. Зная матрицы преобразований, можно заранее вычислить результирующую матрицу преобразований как произведение отдельных матриц и использовать уже полученную матрицу для определения итоговых координат точек рисунка, не вычисляя промежуточных координат.
Однако операция умножения матриц достаточно трудоемка, поэтому желательно знать такие варианты преобразований, когда можно не использовать перемножение матриц.
Полезно знать, что перенос и поворот являются аддитивными операциями, а масштабирование - мультипликативной операцией, то есть для нахождения результирующего преобразования в первом случае надо суммировать значения отдельных переносов (в случае поворота итоговый угол равен сумме отдельных углов поворота), во втором случае коэффициенты масштабирования следует перемножить для нахождения результирующего коэффициента масштабирования.
В ряде частных случаев наблюдается коммутативность операций преобразования. В таблице приведены варианты таких преобразований:
М1 | М2 |
Перенос | Перенос |
Масштабирование | Масштабирование |
Поворот | Поворот |
Масштабирование (однородное) | Поворот |
2. Демонстрационные примеры
Пример 10.1.
program lab_31; {Выполнение преобразований на плоскости на примере плоской кривой - астроиды}
uses Graph,Crt;
label 1;
const b=120; {параметр астроиды}
fon=7; {цвет фона}
col=12; {цвет рисования}
type spis=^p;
p=record
x,y:integer;
praw:spis; {метод проверка}
end;
var i, {параметр цикла}
gd,gm, {тип и режим работы адаптера}
xn,yn, {координаты центра астроиды}
x,y, {координаты текущей точки исходной астроиды}
x1,y1, {координаты текущей точки преобразованной астроиды}
xnc,ync, {координаты начальной точки астроиды}
n, {количество точек, аппроксимирующих астроиду}
xc,yc, {координаты центра поворота}
xm,ym, {координаты центра масштабирования}
dx,dy: integer; {величины смещений}
a,c,d,f, {рабочие переменные}
dt, {шаг изменения аргумента при аппроксимации}
t, {текущее значение аргумента}
kx,ky, {коэффициенты масштабирования}
kx1,ky1, {единица минус коэффициент масштабирования}
ug, {угол поворота в градусах}
ur, {угол поворота в радианах}
rx,ry, {отношения коэффициентов, учитывающих разрешающие cпособности}
sur,cur: real; {значение синуса и косинуса угла поворота}
xa,ya:word; {коэффициенты учета разрешающих способностей}
nach, {указатель на начало списка}
kon, {указатель на конец списка}
rab: spis; {текущий указатель}
pr: char; {признак выбираемой функции}
begin
nach:=nil; {указатель на начало списка}
kon:=nil; {указатель на конец списка}
rab:=nil; {текущий указатель}
gd:=detect;
InitGraph(gd,gm,'d:\turbo');
GetASpectRatio(xa,ya);
xn :=GetMaxX div 2;{координаты центра}
yn:=GetMaxY div 2; {кривой}
dt:=2.0/3.0/b; {шаг изменения аргумента}
n:=round(2*pi/dt);
{вычисление координат точек исходной астроиды}
{и формирование списка, хранящего координаты}
for i:=1 to n do
begin
t:=(i-1)*dt;
a:=cos(t);
c:=a*a*a;
x:=xn+round(b*c);
f:=sin(t);
d:=f*f*f;
y:=yn-round(b*d*xa/ya);
new(rab); {создает новую динамическую переменную типа rab}
rab^.x:=x;
rab^.y:=y;
if t=0 then nach:=rab
else kon^.praw:=rab;
kon:=rab;
end;
repeat
RestoreCrtMode; {восстанавливает текстовый режим экрана}
writeln('Выберите нужную функцию');
writeln('Исходное изображение - i(I)');
writeln('Перенесенное изображение - p(P)');
writeln('Промасштабированное изображение - m(M)');
writeln('Повернутое изображение - w(W)');
writeln('Конец работы - e(E)');
pr:=UpCase(ReadKey);
case pr of
'P': begin
writeln('Введите величины смещений (dx,dy)');
readln(dx,dy);
end;
'M': begin
writeln('Введите координаты центра масштабирования');
writeln('(координаты центра астроиды',xn:5,yn:5,')');
readln(xm,ym);
writeln('Введите коэффициенты масштабирования');
readln(kx,ky);
end;
'W': begin
writeln('Введите координаты центра поворота');
writeln('(координаты центра астроиды',xn:5,yn:5,')');