Что такое матричное программирование

Эта глава из книги

Использование матричных вычислений в преобразованиях

В предыдущем разделе вы должны были использовать вычисления поворота и преобразования для просмотра формы треугольника. Графические программы часто выполняют все виды вычислений на вершинах объекта. Прежде чем, наконец. Нарисовать этот объект на экране. Преобразование. Масштабирование и вращение могут быть выполнены на одной фигуре. Просто вызвав функции Translate(), Scale ()и Rotate() с вершинами фигуры. Однако выполнение такого количества вычислений на многих вершинах может занять много времени. Поэтому графические программисты часто используют матричную математику для преобразования фигур.

Матрица-это просто таблица чисел. Расположенных в строках и столбцах. Подобно массивам в программировании. Размер матрицы определяется количеством строк и столбцов. Которые она имеет. Например, это матрица 4×4, которая имеет четыре строки и четыре столбца:

4 3 2 1 5 4 2 8 3 7 0 5 9 3 6 1

С другой стороны, ниже приведена матрица 3х4, которая имеет три строки и четыре столбца:

4 7 2 4 4 6 7 3 4 5 2 2

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

Матрица 3х4 может быть представлена в программе следующим образом:

int matrix[3][4] = 

Преимущество матриц в графическом программировании заключается в том. Что вы можете представить любое количество преобразований с помощью одной матрицы. Например, одна матрица может содержать все значения. Необходимые для одновременного преобразования. Масштабирования и поворота фигуры. Для этого вы заполняете матрицу соответствующими значениями. А затем умножаете матрицу на все вершины фигуры. Конечно, весь фокус в том. Чтобы знать. Какие значения поместить в матрицу. Вам также нужно знать. Как умножать матрицы. Вы узнаете об обоих трюках в следующих разделах.

Использование матричных типов данных для 2D-графики

Во-первых, вам нужны типы данных для матриц. Которые вы будете использовать в своих программах. Программы, которые имеют дело с 2D-графикой. Обычно используют два типа матриц: 1×3 и 3×3. Матрица 1×3 — это особый тип матрицы. Известный как вектор. Векторы могут представлять вершину в форме. Удерживая значения X. Y и W вершины. Что такое Хотя Direct3D иногда специально использует это дополнительное значение. W действительно используется чаще всего для упрощения операций с матрицей. В большинстве случаев W равно 1, что означает. Что вектор. Представляющий вершину в форме. Имеет такую форму:

X Y 1

Таким образом. Тип данных для вектора выглядит следующим образом:

typedef struct vector { int x. Y } ВЕКТОР;

Матрица 3×3 будет содержать значения. Необходимые для преобразования вершины. Которая будет храниться в ВЕКТОРНОМ типе данных (который также является матрицей). Тип данных для матрицы 3×3 выглядит следующим образом:

typedef double MATRIX3X3[3][3];

Использование Матриц Преобразования

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

1 0 0 0 1 0 xTrans yTrans 1

Точно так же. Как когда вы использовали формулу для перевода вершин фигуры. В предыдущей матрице переменные xTrans и yTrans представляют собой количество вертикальных и горизонтальных единиц соответственно. Которые вы хотите перевести в форму. В программе вы бы инициализировали эту матрицу следующим образом:

МАТРИЦА 3Х3 м; m[0][0] = 1.0; m[0][1] = 0.0; m[0][2] = 0.0; m[1][0] = 0.0; m[1][1] = 1.0; m[1][2] = 0.0; m[2][0] = xTrans; m[2][1] = yTrans; m[2][2] = 1.0;

Матрица для масштабирования фигуры выглядит следующим образом:

xScaleFactor 0 0 0 yScaleFactor 0 0 0 1

Здесь переменная xScaleFactor-это то. Насколько вы хотите масштабировать фигуру по горизонтали. Тогда как yScaleFactor-это то. Насколько масштабировать ее по вертикали. В программе вы бы инициализировали масштабирующую матрицу следующим образом:

МАТРИЦА 3Х3 м; m[0][0] = xScaleFactor; m[0][1] = 0.0; m[0][2] = 0.0; m[1][0] = 0.0; m[1][1] = yScaleFactor; m[1][2] = 0.0; m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0;

Наконец, матрица для вращения фигуры выглядит следующим образом:

cos(radians) sin(radians) 0 -sin(radians) cos(radians) 0 0 0 1

Здесь переменная радиана-это угол поворота в радианах. В программе вы бы инициализировали матрицу вращения следующим образом:

МАТРИЦА 3Х3 м; m[0][0] = cos(радианы); m[0][1] = sin(радианы); m[0][2] = 0.0; m[1][0] = -sin(радианы); m[1][1] = cos(радианы); m[1][2] = 0.0; m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0;

Составление преобразований

Ранее я говорил. Что вы можете хранить в матрице все значения. Необходимые для выполнения перевода. Масштабирования и поворота одновременно. В предыдущем разделе вы видели. Как выглядит каждое преобразование. Когда оно хранится отдельно в матрице. Теперь вы узнаете о составлении преобразований. Которые представляют собой процесс объединения матриц преобразования. Масштабирования и вращения в одну основную матрицу преобразования.

Чтобы составить два преобразования. Вы умножаете их матрицы вместе. Получая третью главную матрицу. Затем вы можете составить еще одно преобразование. Умножив новую матрицу на еще одну матрицу преобразования. Эту композицию матриц можно повторять так часто. Как это необходимо. На рис. 3.11 показан пример композиции матрицы. На рис. 3.12 показан другой взгляд на эту матричную композицию. На рис. 3.12результаты каждой композиции не показаны.

Рис. 3.11Рис. 3.11 Состав матрицы.

Рисунок 3.12Рис. 3.12 Другой вид композиции матрицы.

Ах, если бы вы только знали. Как умножать матрицы! Матрица может быть умножена на любую другую матрицу до тех пор. Пока первая матрица имеет то же количество столбцов. Что и вторая матрица имеет строки. Таким образом, матрицу 1х3 можно умножить на матрицу 3х3, и это очень удачно. Потому что именно это вам нужно сделать. Чтобы умножить матрицу на вектор в 2D-графических программах. Кроме того, матрицу 3х3 можно умножить на матрицу 3х3, что еще нужно сделать в 2D-графической программе для составления преобразований. Вы посмотрите на умножение векторов a немного позже в этой главе. Но листинг 3.7-это функция. Которая умножает две матрицы 3×3.

Листинг 3.7 Умножение матриц 3х3

void MultMatrix(MATRIX3X3& product, MATRIX3X3& matrix1, MATRIX3X3& matrix2) { for (int x=0; x) for (int y=0; y

Три параметра функции являются ссылкой на матрицу 3×3, в которой должно содержаться произведение умножения. И ссылки на две матрицы. Которые должны быть умножены. Листинг 3.8 является примером использования функции.

Листинг 3.8 Использование функции MultMatrix()

МАТРИЦА 3X3 m1, m2, m3; m1[0][0] = 1.0; m1[0][1] = 0.0; m1[0][2] = 0.0; m1[1][0] = 0.0; m1[1][1] = 1.0; m1[1][2] = 0.0; m1[2][0] = 0.0; m1[2][1] = 0.0; m1[2][2] = 1.0; м2[0][0] = 9,0; м2[0][1] = 8,0; м2[0][2] = 7,0; м2[1][0] = 6.0; м2[1][1] = 5.0; м2[1][2] = 4.0; м2[2][0] = 3,0; м2[2][1] = 2,0; м2[2][2] = 3,0; Мультматрица(м3, м1, м2); 

Здесь код сначала объявляет три матрицы 3×3, m1, m2 и m3. Далее инициализируются m1 и m2, после чего вызов функции MultMatrix3X3() умножает m1 на m2 и сохраняет результат в m3. Можете ли вы сказать. Что m3 будет содержать после умножения? Ответ заключается в том. Что m3 будет содержать точно такие же значения, как и m2. Почему? Потому что значения , хранящиеся в m1, являются так называемой единичной матрицей, которая для матрицы 3×3 выглядит следующим образом:

1 0 0 0 1 0 0 0 1

Матрица идентичности

Матрица идентичности является своего рода матричным эквивалентом числа 1. Так же. Как любое число, умноженное на 1, равно исходному числу (например, 5 x 1 = 5). Так и любая матрица. Умноженная на матрицу идентичности. Равна исходной матрице (например. M1 x I = m1). Матрица идентичности содержит все нули, за исключением строки 1, которая проходит по диагонали от верхнего левого угла до нижнего правого угла.

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

Выполнение преобразования

После того как вы создадите свои преобразования. У вас будет основная матрица. Содержащая точные значения. Необходимые для одновременного перевода. Масштабирования и поворота фигуры. Чтобы выполнить это преобразование. Вам нужно только умножить основную матрицу преобразования на каждый из векторов фигуры. Для этой операции требуется функция матричного умножения, которая может обрабатывать не только векторы 1х3 и матрицы 3х3, но и применять умножение ко всему списку векторов. Листинг 3.9-это функция. Которая делает именно это.

Листинг 3.9 Преобразования с матрицами

void Transform(SHAPE& shape. MATRIX3X3& m) { int transformedX. TransformedY; for (int x=0; x) { transformedX = (int) (shape.vertices[x].x * m[0][0] + shape.vertices[x].y * m[1][0] + m[2][0]); transformedY = (int) (shape.vertices[x].x * m[0][1] + shape.vertices[x].y * m[1][1] + m[2][1]); shape.vertices[x].x = transformedX; 

Эта функция принимает в качестве параметров ссылку на структуру формы и ссылку на массив MATRIX3X3. Когда эта функция завершится. Вершины в структуре фигуры, shape. Будут преобразованы значениями в матрице преобразования, m.

Использование Некоторых Матричных Функций Полезности

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

Листинг 3.10 Инициализация единичной матрицы

void InitMatrix(MATRIX3X3& m) { m[0][0]=1; m[0][1]=0; m[0][2]=0; m[1][0]=0; m[1][1]=1; m[1][2]=0; 

Функция InitMatrix() принимает в качестве параметра ссылку на массив MATRIX3X3, в который функция загружает значения. Составляющие матрицу идентичности 3×3.

Еще одна вещь. Которую вам нужно будет сделать. — это скопировать матрицу. Функция CopyMatrix() выглядит как листинг 3.11.

Листинг 3.11 Копирование матрицы

void CopyMatrix(MATRIX3X3& dst. MATRIX3X3& src) { для (int i=0; i

Эта функция принимает в качестве параметров ссылки на матрицы назначения и источника. Обе из которых являются матрицами типа MATRIX3X3. Функция копирует матрицу src в матрицу dst.

Использование функций для составления преобразований

Последняя задача при написании функций для 2D-графической программы . Использующей матрицы . Состоит в том. Чтобы переписать функции Translate (), Scale ()и Rotate() так. Чтобы они использовали новые матричные типы данных. Функция Translate() в конечном итоге выглядит как листинг 3.12.

Листинг 3.12 Перевод с матрицами

void Translate(MATRIX3X3& m, int xTrans, int yTrans) { МАТРИЦА 3Х3 м1, м2; m1[0][0]=1; m1[0][1]=0; m1[0][2]=0; m1[1][0]=0; m1[1][1]=1; m1[1][2]=0; m1[2][0]=xTrans; m1[2][1]=yTrans; m1[2][2]=1; MultMatrix(m2, m1, m); 

Эта функция принимает в качестве параметров ссылку на матрицу. Которая содержит текущее состояние преобразования и значения перевода X и Y. Сначала функция загружает локальную матрицу со значениями. Создающими матрицу перевода. После чего она умножает матрицу перевода на основную матрицу преобразования. Результат умножения, хранящийся в локальной матрице м2, затем копируется в матрицу преобразования.

Переписывание функции Scale() для использования с матрицами приводит к листингу 3.13.

Листинг 3.13 Масштабирование с помощью матриц

void Scale(MATRIX3X3& m, double XScale, double yScale) { МАТРИЦА 3Х3 м1, м2; m1[0][0]=XScale; m1[0][1]=0; m1[0][2]=0; m1[1][0]=0; m1[1][1]=yScale; m1[1][2]=0; m1[2][0]=0; m1[2][1]=0; m1[2][2]=1; MultMatrix(m2, m1, m); 

Функция Scale() принимает в качестве параметров ссылку на текущую матрицу преобразования и масштабирующие коэффициенты X и Y. Функция сначала инициализирует локальную матрицу m1 в масштабирующую матрицу. Затем он умножает масштабирующую матрицу на текущую матрицу преобразования. Сохраняя результаты в локальной матрице м2. Программа, наконец. Копирует m2 в матрицу преобразования .

Последняя функция. Которая вам нужна. — это матричная версия Rotate(). Эта функция выглядит как листинг 3.14.

Листинг 3.14 Вращение с матрицами

void Rotate(MATRIX3X3& m, int градусов) { МАТРИЦА 3Х3Х3 м1, м2; if (degrees == 0) return; двойные радианы = 6.283185308 / (360,0 / градусов); двойной c = cos(радианы); double s = sin(радианы); m1[0][0]=c; m1[0][1]=s; m1[0][2]=0; m1[1][0]=-s; m1[1][1]=c; m1[1][2]=0; m1[2][0]=0; m1[2][1]=0; m1[2][2]=1; Мультматрица(м2, м1, м); 

Функция Rotate() принимает в качестве параметров ссылку на текущую матрицу преобразования и количество градусов для поворота фигуры. Функция сначала проверяет. Является ли градусами ноль. Если это так. Функция возвращается немедленно. Чтобы избежать ошибки деления на ноль. Затем функция преобразует градусы в радианы и вычисляет косинус и синус угла. Далее, Rotate() инициализирует матрицу вращения и умножает эту матрицу на текущую матрицу преобразования. Сохраняя результаты в локальной матрице m2. Наконец, m2 копируется в матрицу преобразования.

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

Листинг 3.15 Выполнение преобразований с матричными функциями

МАТРИЦА 3Х3 м; InitMatrix(m); Перевести(m, 10, 15); Шкала(м, 0,5, 0,5); Поворот(м, 45); Преобразование(shape1, m); DrawShape(форма 1);

Сегмент кода сначала объявляет матрицу преобразования 3×3, называемую m. Затем он вызывает функцию InitMatrix() для инициализации m в единичную матрицу. В этот момент м выглядит примерно так:

1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000

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

1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 10.000000000000 15.000000000000 1.0000000000000

После вызова функции Scale () m содержит значения преобразования и масштабирования:

0.5000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.5000000000000 0.0000000000000 10.000000000000 15.000000000000 1.0000000000000

Наконец. Вызов Rotate() оставляет m. Содержащий полное преобразование—преобразование. Масштабирование и вращение—для фигуры:

0.35355339055702 0.35355339062953 0.0000000000000 -0.35355339062953 0.35355339055702 0.0000000000000 10.000000000000 15.000000000000 1.0000000000000

Вызов Transform() применяет матрицу преобразования m ко всем вершинам в shape1, после чего DrawShape() рисует только что преобразованную фигуру на экране.