Компьютерная графика c примерами на С++: Аффинное преобразование
Тема этого выпуска задание аффинного преобразования в матричной форме. Эта
тема, по сути, является обобщением всего, что было сказано ранее.
Определение. Преобразование плоскости называется аффинным,
если
оно взаимно однозначно;
образом любой прямой является прямая.
Преобразование называется взаимно однозначным, если
разные точки переходят в разные;
в каждую точку переходит какая-то
точка.
Прим. Более подробно про аффинные преобразования
можно посмотреть на ru.wikibooks.org
Однородные координаты
Если рассмотреть параллельный перенос, то оказывается, что для его задания
матрицы 2x2 уже недостаточно. Но его можно задать с помощью матрицы размера
3x3. Появляется вопрос, откуда взять третью координату у двумерной точки?
Определение. Однородные координаты ― координаты, обладающие тем свойством,
что определяемый ими объект не меняется при умножении всех координат на одно и
то же число.
Однородными координатами вектора (х, у) является тройка чисел (x', y', h), где х = х' / h, у = y'/h, а h — некоторое
вещественное число (случай,
когда h = 0 является особым).
Прим. Данные координаты не позволяют однозначно
задать точку плоскости. Например, (1, 1, 1) и (2, 2, 2) задают одну и ту же точку (1, 1). Предлагается взять
набор (x, y, 1), который будет описывать все точки
плоскости.
Матрица преобразования для однородных координат имеет размер 3х3. Рассмотрим
некоторые преобразования в однородных координатах.
Сжатие/растяжение
Это преобразование умножает
соответствующие координаты точек на коэффициенты масштабирования по осям: (x, y) -> (ax * x, ay * y). Матрица преобразования запишется следующим образом:
[ax00 ]
[0ay0 ]
[001 ]
Где ax– растяжение по оси x,
ay– растяжение по оси y.
Прим. Можно заметить, что при отрицательных
значениях коэффициентов сжатия/растяжения происходит отражение относительно
соответствующих осей. Этот случай можно включить в данное преобразование, а
можно вынести в отдельное, сказав, что коэффициенты масштабирования принимают
только положительные значения.
Поворот
Матрица поворота 2x2 была подробно
разобрана ранее. Теперь она дополняется строкой и столбцом:
[cos(phi)sin(phi)0]
[ -sin(phi)cos(phi) 0]
[0 01]
Прим.
При угле phi = п эта матрица задает центральную симметрию относительно начала координат,
которая является частным случаем поворота. Можно заметить, что такую симметрию
можно задать с помощью преобразования сжатия/растяжения (допуская отрицательные
коэффициенты масштабирования).
Параллельный перенос
Исходный вектор (x, y) переходит в (x + tx, y + ty).
Матрица преобразования запишется следующим образом:
[100]
[010]
[txty1]
Отражение
Как говорилось в примечании к
преобразованию сжатия/растяжения, отражения получаются следующим образом:
[-100]
[01 0]
[001] отражение относительно оси x
[100]
[0-10]
[001] отражение относительно оси y
Общий вид аффинного преобразования
Вы, наверное, уже догадались, что
произвольная матрица 3x3 задает аффинное преобразование плоскости.
Прим. Так как мы выбрали набор однородных
координат (x, y, 1), то последний столбец не варьируется:
[**0]
[**0]
[**1]
По одному из свойств, аффинное
преобразование можно записать в виде:
f(x) = x * R + t,
гдеR – обратимая матрица 2x2, а t – произвольный вектор. В однородных
координатах это запишется следующим образом:
[ R1,1R1,20 ]
[ R2,1R2,20 ]
[ txty1 ]
Если умножить вектор-строку на эту матрицу
получаем результат преобразования:
[ xy1 ] *[ R1,1R1,20 ]
[ R2,1R2,20 ]
[ txty1 ]
=
[ x’y’1 ]+[ txty 1]
При этом [ x’y’ ]=R*[ xy
]
Прим. Любопытный читатель уже задал себе
вопрос: в чем смысл определителя матрицы R? При аффинном преобразовании площади
всех фигур изменяются в |R|.
(Можно строго доказать это с точки зрения математики, но здесь этот факт
приводится без доказательства.)
Т.о. аффинное преобразование
представляется в виде композиции некоторого преобразования, задаваемого
матрицей R, и параллельного
переноса. Разберем более подробно природу этой матрицы и возможности, которые
она нам дает.
Матрица R определяет новый базис плоскости. Т.е. вектор (1, 0) переходит в (R1,1, R1,2), вектор (0, 1) переходит в (R2,1, R2,2).
Новый базис это строки матрицы R.
Пример.
При отражении относительно оси y, базисный вектор по оси ординат
сохраняется, а по оси абсцисс переходит в (-1, 0). Т.о. матрица R будет выглядеть следующим образом:
[-10 ]
[01] Теперь становится ясно, что кроме
вышеперечисленных преобразований, с помощью аффинного преобразования можно
получить скос:
Выше приведены базовые сведения о таком
мощном инструменте, как аффинное преобразование. Остается много вопросов: какой
подкласс аффинных преобразований сохраняет углы между прямыми? Как можно
представить аффинное преобразование в виде композиции нескольких подклассов? Как
задавать более сложные преобразования, например, осевая симметрия относительно
произвольной прямой?
Ответы на эти вопросы и более детальное
рассмотрение аффинного преобразования будут приведены отдельно, в качестве
раздела курса теоретической геометрии на нашем сайте compgraphics.info.
Остановимся на практической реализации
аффинного преобразования в виде демонстрационной программы. К возможностям
приложения, демонстрирующего поворот плоскости мышью, добавляются функции
параллельного переноса при нажатой клавише CTRL.
Т.к. этот выпуск является завершающим в
данном разделе, код демонстрационного приложения должен быть соответствующим. Давайте
попробуем разобраться, какие блоки нужны в графическом приложении, параллельно
рассматривая, как они реализованы в данной программе:
блок, в котором происходит создание
окна и обрабатываются сообщения операционной системы, реализован в файлemain.cpp
графический движок, выполняющий
отрисовку изображения, класс Engine
прослойка, необходимая для
преобразования логических координат в оконные и обратно, класс Viewport
объект, отвечающий за реакцию на действия
пользователя, класс Action
В приведенном на сайте примере реализованы
эти функциональные блоки, с подробными комментариями.