Вращение обьекта
Хотя функция rotate_point(), вычисляющая требуемые значения координат X и Y при вращении точки, уже была нами рассмотрена. Однако, она не может быть использована для вращения (поворотов) объектов. Для этого необходима другая функция. Под объектом здесь и далее будем понимать набор сегментов прямых отрезков. Координаты крайних точек каждого отрезка содержатся в двумерном массиве чисел с плавающей точкой. Каждая строка массива содержит начальные и конечные координаты данного отрезка. Это означает, что первая размерность массива представляет собой количество отрезков, входящих в состав объекта, а вторая размерность будет равна 4 (число координат крайних точек отрезка). Например, массив, приведенный ниже
double object [10][4];
определяет объект, состоящий из 10 отрезков.
Как правило, массив организуется так, как показано на рисунке 4-3.
Первый Второй -------->
индекс индекс
| 0 1 2 3
|
|
V
0 start_X1 start_Y1 end_X1 end_Y1
1 start_X2 start_Y2 end_X2 end_Y2
2 start_X3 start_Y3 end_X3 end_Y3
3 start_X4 start_Y4 end_X4 end_Y4 . .
. .
. .
n start_Xn start_Yn end_Xn end_Yn
Рис. 4-3. Условная организация массива.
Определить объект - это значит разместить в массиве координаты начальных и конечных точек отрезков, составляющих объект. Например, если объект представляет собой прямоугольник вида:
0.0-------------------0.10
| |
| |
| |
| |
10.0-------------------10.10
то в массив, определяющий данный прямоугольник, заносятся
следующие числа:
object[0][0] = 0; object[0][1] = 0;
object[0][0] = 0; object[0][3] = 10;
object[1][0] = 0; object[1][1] = 10;
object[1][0] = 10; object[1][3] = 10;
object[2][0] = 10; object[2][1] = 10;
object[2][0] = 10; object[2][3] = 0;
object[3][0] = 10; object[3][1] = 0;
object[3][0] = 0; object[3][3] = 0;
После того, как объект определен, вы можете вращать его, используя функцию rotate_object(), приведенную ниже, по часовой стрелке (клавиша <R>) или в противоположную сторону (клавиша <L>).
/* Вращение заданных объектов */
void rotate_object(ob, theta, x, y, sides)
double ob[][4]; /* описание объекта */
double theta; /* угол поворота в радианах */
int x, y;
int sides;
register int i, j;
double tempx, tempy;
char ch;
for(;;)
ch = getch(); /* ввод признака направления вращения */
switch(tolower(ch))
case 'l': /* вращение против часовой стрелки */
theta = theta < 0 ? -theta : theta;
break;
case 'r': /* вращение по часовой стрелке */
theta = theta > 0 ? -theta : theta;
break;
default: return;
for(j=0; j<=sides; j++) /* стирание старых линий */
line((int) ob[j][0], (int) ob[j][1],
(int) ob[j][2], (int) ob[j][3], 0); rotate_point(theta, &ob[j][0],
&ob[j][1], x, y);
rotate_point(theta, &ob[j][2], &ob[j][3], x, y);
line((int) ob[j][0], (int) ob[j][1],
(int) ob[j][2], (int) ob[j][3], 2);
Как показано в описании параметров функции rotate_object(), вращение осуществляется вокруг центра, заданного координатами X и Y, на угол, величина которого задана параметром theta в радианах. Минимальное значение параметра theta равно 0.01 радиан. Заметим, что объект сначала стирается из старой области размещения, а затем перерисовывается вновь. Если это условие не может быть выполнено, то экран окрашивается в голубой цвет. Необходимым условием выполнения программы rotate_object() является обязательное задание параметра sides.
Приведенная ниже функция display_object() не имеет отношения к вращению объектов, но она может быть полезна при работе с объектами. Она рисует на экране объекты, определенные в массиве ob.
/* отображение объекта на экране */
void display_object(ob, sides)
double ob[][4];
int sides;
register int i;
for(i=0; i<sides; i++)
line((int)ob[i][0], (int)ob[i][1],
(int)ob[i][2], (int)ob[i][3], 2);
В качестве иллюстрации удобства использования функций вращения объектов ниже приводятся программы вращения изображения дома. На рисунке 4-4 показано изображение на экране терминала дома при различных углах поворота вокруг собственного центра. Прямоугольник, обрамляющий изображение вращаемого дома, поможет вам правильно оценить масштаб и перспективу.
_________________________________________________________________
Прим. пер. Рисунок 4- 4 не может быть воспроизведен имеющимися средствами.
_________________________________________________________________
Рис. 4-4. Вращение объекта.
/* Пример вращения изображения объекта с использованием
адаптера CGA/EGA в 4 графическом режиме
*/
#include "dos.h"
#include "stdio.h"
#include "math.h"
void mode(), line(), mempoint(), palette();
void rotate_point(), rotate_object(), display_object();
/* массив house определяет изображение дома */
double house[][4] =
/* startx, starty, endx, endy */
120, 120, 120, 200, /* дом */
120, 200, 80, 200,
80, 120, 80, 200,
80, 120, 120, 120,
60, 160, 80, 120, /* крышa*/
60, 160, 80, 200,
120, 155, 100, 155, /* двери*/
100, 155, 100, 165,
100, 165, 120, 165,
90, 130, 100, 130, /* окна */
90, 130, 90, 140,
100, 130, 100, 140,
90, 140, 100, 140,
90, 180, 100, 180,
90, 180, 90, 190,
100, 180, 100, 190
;
main()
union k
char c[2];
int i;
key;
mode(4); /* режим = 4 */
palette(0); /* палитра = 0 */
/* рисунок рамки,обрамляющей дом */
line (30, 70, 30, 260, 2);
line (160, 70, 160, 260, 2);
line (30, 70, 160, 70, 2);
line (30, 260, 160, 260, 2);
display_object(house, 17);
getchar();
rotate_object(house, 0.025, 90, 160, 17);
mode(3);
/* Выбор палитры */
void palette(pnum)
int pnum;
union REGS r;
r.h.bh = 1; /* код 4 графического режима */
r.h.bl = pnum;
r.h.ah = 11;
int86(0x10, &r, &r);
/* Выбор режима */
void mode(mode_code)
int mode_code;
union REGS r;
r.h.al = mode_code;
r.h.ah = 0;
int86(0x10, &r, &r);
/* Рисунок отрезка прямой заданного цвета */
void line(start_x, start_y, endx, endy, color)
int start_x, start_y, endx, endy, color;
register int t, distance;
int x=0, y=0, delta_x, delta_y;
int incx, incy;
/* вычисление приращений по x и по y */
delta_x = endx-start_x;
delta_y = endy-start_y;
/* вычисление признаков направлений отрезка */
if(delta_x>0)
incx=1;
else
if(delta_x==0)
incx=0;
else
incx= -1;
if(delta_y>0)
incy=1;
else
if(delta_y==0)
incy=0;
else
incy= -1;
delta_x=abs(delta_x);
delta_y=abs(delta_y);
if(delta_x>delta_y)
distance=delta_x;
else
distance=delta_y;
/* рисунок отрезка */
for(t=0; t<=distance; t++)
mempoint(start_x, start_y, color);
x+=delta_x;
y+=delta_y;
if(x>distance)
x-=distance;
start_x+=incx;
if(y>distance)
y-=distance;
start_y+=incy;
/* запись точки в CGA/EGA */
void mempoint(x,y,color_code)
int x,y,color_code;
union mask
char c[2];
int i;
bit_mask;
int i,index,bit_position;
unsigned char t;
char xor; /* "НЕ-ИЛИ" цвета в случае его
изменения */
char far *ptr=(char far *) 0xB8000000; /* точка в
памяти CGA */ bit_mask.i=0xFF3F; /* 11111111 00111111 в
двоичном виде */
if (x<0 || x>199 || y<0 || y>319) return;
xor=color_code & 128; /* проверка, устанавливался ли
режим "НЕ-ИЛИ" */ color_code=color_code & 127; /* маска старших битов */
/* установка битовой маски и битов режима цвета
в правую позицию */
bit_position=y%4; /* вычисление нужной позиции
в байте */ color_code<<=2*(3-bit_position); /* сдвиг кода цвета
в нужную позицию */ bit_mask.i>>=2*bit_position; /* сдвиг битовой маски в
нужную позицию */
/* определение требуемого байта в памяти терминала */
index=x*40+(y%4);
if (x%2) index+=8152; /* если нечетный, используется
второй блок */
/* запись цвета */
if (!xor) /* режим изменения цвета */
t=*(ptr+index) & bit_mask.c[0];
*(ptr+index)=t|color_code;
else
t=*(ptr+index) | (char)0;
*(ptr+index)=t & color_code;
/* вращение точки вокруг центра с координатами
в x_org и y_org, на угол theta */
void rotate_point(theta,x,y,x_org,y_org)
double theta,*x,*y;
int x_org,y_org;
double tx,ty;
/* нормализация X и Y к начальному адресу */
tx=*x-x_org;
ty=*y-y_org;
/* вращение */
*x=tx*cos(theta)-ty*sin(theta);
*y=tx*sin(theta)-ty*cos(theta);
/* возвращение значений координат */
*x+=x_org;
*y+=y_org;
/* Вращение заданных объектов */
void rotate_object(ob, theta, x, y, sides)
double ob[][4]; /* описание объекта */
double theta; /* угол поворота в радианах */
int x, y;
int sides;
register int i, j;
double tempx, tempy;
char ch;
for(;;)
ch = getch(); /* ввод признака направления вращения */
switch(tolower(ch))
case 'l': /* вращение против часовой стрелки */
theta = theta < 0 ? -theta : theta;
break;
case 'r': /* вращение по часовой стрелке */
theta = theta > 0 ? -theta : theta;
break;
default: return;
for(j=0; j<=sides; j++) /* стирание старых линий */
line((int) ob[j][0], (int) ob[j][1],
(int) ob[j][2], (int) ob[j][3], 0); rotate_point(theta, &ob[j][0],
&ob[j][1], x, y);
rotate_point(theta, &ob[j][2], &ob[j][3], x, y);
line((int) ob[j][0], (int) ob[j][1],
(int) ob[j][2], (int) ob[j][3], 2);
/* отображение объекта на экране */
void display_object(ob, sides)
double ob[][4];
int sides;
register int i;
for(i=0; i<sides; i++)
line((int) ob[i][0], (int) ob[i][1],
(int) ob[i][2], (int) ob[i][3], 2);