Программа Астероиды с использованием матриц
Я уже устал от разговоров — давайте что-нибудь напишем. К примеру, перепишем нашу программу из Листинга 4.8. Вы должны ее помнить. Мы ее переделаем и используем в ней матрицы. Листинг 4.11 показывает эту программу. Она называется «Супер Астероиды».
Листинг 4.11. Супер Астероиды (FIELD.C).
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ ///////////////////////////////////////////
#include <stdio.h>
#include <graph.h>
#include <math.h>
//ОПРЕДЕЛЕНИЯ ///////////////////////////////////////////////
#define NUM__ASTEROIDS 10
#define ERASE 0
#define DRAW 1
#defineX_COMP 0
#define Y_COMP 1
#define N_COMP 2
// СТРУКТУРЫ ДАННЫХ ////////////////////////////////////
// новая структура, описывающая вершину
typedef struct vertex_typ
{
float p[3]; // единичная точка в двумерном пространстве
//и фактор нормализации
} vertex, *vertex_ptr;
// общая структура матрицы
typedef struct matrix_typ
{
float elem[3] [3]; // массив для хранения элементов
// матрицы
} matrix, *matrix_ptr;
// определение структуры "объект",.
typedef struct object_typ
{
int num_vertices; // количество вершин в объекте
int color; // цвет объекта
float xo,yo; // позиция объекта
float x_velocity; // скорости пермещения по осям Х float y_velocity;
// и Y matrix scale; // матрица масштабирования
matrix rotation; // матрицы поворота и перемещения
vertex vertices[16]; // 16 вершин
} object, *object_ptr;
// ГЛОБАЛЬНЫЕ.ПЕРЕМЕННЫЕ /////////////////////////////////
object asteroids [NUM_ASTEROIDS] ;
// ФУНКЦИИ ////////////////////////////////////////////
void Delay(int t)
{
// функция выполняет некоторую задержку
float x = 1;
while(t—>0)
x=cos(x) ;
} // конец функции
///////////////////////////////////////////////////////
void Make_Identity(matrix_ptr i)
{
// функция формирует единичную матрицу
i->elem[0][0] = i->elem[l][1] = i->elem[2][2] = 1;
i->elem[0][1] = i->elem[l][0] = i->elem[l][2] = 0;
i->elem[2][0] = i->elem[01[2] = i->eiem[2][1] = 0;
} // конец функции ///////////////////////////////////////////////////////
void Clear_Matrix(matrix_ptr m) {
// функция формирует нулевую матрицу
m->е1еm[0][0] = m->elem[l][1] = m->е1еm[2][2] = 0;
m->elem[0][1] = m->elem[l] [0] = m->е1еm[1] [2] = 0;
m->elem[2][0] = m->elem[0] [2] = m->elem[2][1] = 0;
} // конец
функции
///////////////////////////////////////////////////////
void Mat_Mul (vertex_ptr v, matrix_ptr m)
{
// функция выполняет умножение матрицы 1х3 на матрицу 3х3
// элемента. Для скорости каждое действие определяется "вручную",
// без использования циклов. Результат операции - матрица 1х3
float x new, у
new;
x_new=v->p[0]*m->elem[0][0] + v->p[1]*m->elem[1][0] + m->elem[2][0];
y_new=v->p[0]*m->elem[0][1] + v->p[1]*m->elem[1][1] + m->elem[2][1];
// N_COMP - всегда единица
v->p[X_COMP] = x_new;
v->p[Y_COMP] = y_new;
} // конец
функции
///////////////////////////////////////////////////////
void Scale_Object_Mat(object_ptr obj)
{
// функция выполняет масштабирование объекта путем умножения на
// матрицу масштабирования
int index;
for (index=0; index<obj->num_vertices; index++)
{
Mat_Mul ((vertex_ptr) &obj->vertices [index],
(matrix_ptr)&obj->scale) ;
} // конец цикла for
} // конец функции
///////////////////////////////////////////////////////
Rotate_Object_Mat(object_ptr obj)
{
// функция выполняет.поворот объекта путем умножения на
// матрицу поворота
int index;
for (index=0; index<obj->num_yertices; index++)
{
Mat_Mul((vertex_ptr)&obj->vertices[index],(matrix_ptr)&obj->rotation) ;
} // конец цикла for
} // конец функции ///////////////////////////////////////////////////////
void Create_Field(void)
{
int index;
float angle,c,s;
// эта функция создает поле астероидов
for (index=0; index<NUM_ASTEROIDS; index++)
{
asteroids[index].num vertices = 6;
asteroids [index] .color = 1 + rand() % 14; //астероиды
// всегда
видимы
asteroids[index].xo = 41 + rand() % 599;
asteroids[index].yo = 41 + rand() % 439;
asteroids [index].,x_velocity = -10 +rand() % 20;
asteroids[index].y_velocity = -10 + rand() % 20;
// очистить
матрицу
Make_Identity((matrix_ptr)&asteroids[index].rotation) ;
// инициализировать матрицу вращений
angle = (float) (- 50 + (float) (rand ()\ % 100)) / 100;
c=cos(angle);
s=sin(angle);
asteroids[index].rotation.elem[0][0] = с;
asteroids[index].rotation.elem[0][1] = -s;
asteroids[index].rotation.elem[l][0] = s;
asteroids[index].rotation.elem[l][1] = с;
// формируем матрицу масштабирования
// очистить матрицу и установить значения коэффициентов
Make_Identity((matrix ptr)&asteroids[index].scale);
asteroids[index].scale.elem[0][0] = (float) (rand() % 30) / 10;
asteroids[index].scale.elem[1][1] = asteroids[index].scale.elem[0][0];
asteroids[index].vertices[0].p[X_COMP] = 4.0;
asteroids[index].vertices[0].p[Y_COMP] = 3.5;
asteroids[index].vertices[0].p[N_COMP] = l;
asteroids[index].vertices[1].p[X_COMP] = 8.5;
asteroids[index].vertices[l].p[Y_COMP) = -3.0;
asteroids[index].vertices[1].p[N_COMP] = l;
asteroids[index].vertices[2].p[X_COMP] = 6;
asteroids[index].vertices[2].p[Y_COMP] = -5;
asteroids[index].vertices[2].p[N_COMP] = l;
asteroids[index].vertices[3].p[X_COMP] = 2;
asteroids[index].vertices[3].p[Y_COMP] = -3;
asteroids[index].vertices[3].p[N_COMP] = l;
asteroids[index].vertices[4].p[X_COMP] = -4;
asteroids[index].vertices[4].p[Y_COMP] = -6;
asteroids[index].vertices[4].p[N_COMP] = 1;
asteroids[index].vertices[5].p[X_COMP] = -3.5;
asteroids[index].vertices[5].p[Y_COMP] = 5.5;
asteroids[index],vertices[5].p[N_COMP] = 1;
// теперь масштабировать астероиды
Scale_Object_Mat((object_ptr)&asteroids[index]);
} // конец цикла for
} // конец функции
///////////////////////////////////////////////////////
void Draw Asteroids (int erase)
{
int index,vertex;
float xo,yo;
// эта функция в зависимости от переданного флага рисует или стирает
// астероиды
for (index=0; index<NUM_ASTEROIDS; index++)
{
// нарисовать астероид
if (erase==ERASE)
_setcolor(0);
else
_setcolor(asteroids[index].color);
// получаем позицию объекта
xo = asteroids[index].xo;
yo = asteroids [index].yo;
// перемещаемся в позицию первой вершины астероида
_moveto((int)(xo+asteroids[index]-vertices[О].p[X_COMP]), (int)(yo+asteroids[indexl.vertices[0].p[Y_COMP])) ;
for (vertex=l; vertex<asteroids[index].num_vertices; vertex++)
{
_lineto((int)(xo+asteroids[index].vertices[vertex].p[X_COMP]), (int)(yo+asteroids[index].vertices[vertex].p[Y_COMP])) ;
} // конец цикла по вершинам
_lineto((int)(xo+asteroids[index].vertices[0],p[X_COMP]), (int)(yo+asteroids[index].vertices[0].p[Y_COMP]));
} // замкнуть контур объекта
} // конец функции
///////////////////////////////////////////////////////
void Translate_Asteroids(void)
{
// функция перемещает астероиды
int index;
for (index=0; index<NUM_ASTEROIDS; index++)
{
// перемещать текущий астероид
asteroids[index].xo += asteroids[index].x velocity;
asteroids[index].yo += asteroids[index].y_velocity;
// проверка на выход за границы экрана
if (asteroids[index].xo > 600 || asteroids[index].xo < 40)
{
asteroids[index].x_velocity = -asteroids[index].x_velocity;
asteroids[index].xo += asteroids[index].x_velocity;
}
if (asteroids[index].yo > 440 1) asteroids[index].yo < 40)
{
asteroids[index].y_velocity = -asteroids[index].y_velocity;
asteroids[index].yo += asteroids[index].у_velocity;
)
} // конец цикла for
} // конец функции
////////////////////////////////////////////////////////
void Rotate__Asteroids()
{
int index;
for (index=0; index<NUM_ASTEROIDS; index++)
{
// вращать
текущий астероид
Rotate_Object_Mat((object_ptr)&asteroids[index]);
} // конец цикла for
} // конец функции
///////////////////////////////////////////////////////
void main(void)
{
// перевести компьютер в графический режим
_setvideomode(_VRES16COLOR); // 640х480, 16 цветов
// инициализация
Create_Field();
while(!kbhit())
{
// стереть
поле
Draw_Asteroids(ERASE) ;
// преобразовать
поле
Rotate_Asteroids();
Translate_Asteroids() ;
// нарисовать
поле
Draw_Asteroids(DRAW);
// немного подождем...
Delay(500);
)
// перевести компьютер в текстовый режим
_setvideomode(_DEFAULTMODE);
} // конец функции
Вам потребуется время, чтобы изучить эту программу. Уделите внимание способу, которым астероиды масштабируются и поворачиваются. Если вы сравните время исполнения программ из Листингов 4.8 и 4.11, то не найдете никакой разницы. Если же вы потратите время и поместите все повороты, масштабирование и перемещение в одну матрицу, то программа из Листинга 4,11 будет работать значительно быстрее.
Итак, умножение матриц особенно эффективно, когда вы производите множественные трансформации объекта с помощью единой, заранее подготовленной матрицы, включающей в себя все операции преобразования над этим объектом.