netlib.narod.ru< Назад | Оглавление | Далее >

Линейные преобразования

Рассмотрим действие различных методов преобразования с математической точки зрения. По-видимому, простейшим из этих методов является

  grfx.TranslateTransform(dx, dy);

где символы dx и dy обозначают координаты (d — это дельта, или приращение в математической символике). Мировое преобразование, осуществляемое при вызове этого метода, можно представить так:


xстр. = xмир. + dx

yстр. = yмир. + dy


Все довольно просто. Как видите, вызов TranslateTransform приводит к смещению всех координат.

А вот похожий вызов ScaleTransform:

  grfx.ScaleTransform(sx, sy);

Здесь s означает масштаб (scale), а мировое преобразование осуществляется путем умножения, а не сложения:


xстр. = xмир. × sx

yстр. = yмир. × sy


Действие масштабирования очень похоже на преобразование страницы.

Не буду заставлять вас гадать, что происходит при вызове:

  grfx.Rotate!ransfоrm(α);

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


xстр. =   xмир. · cos(α) + yмир. · sin(α)

yстр. = –xмир. · sin(α) + yмир. · cos(α)


Эта небольшая таблица синусов и косинусов поможет убедиться, что эти формулы действительно работают:


Угол α Синус Косинус

0 0 1
45 1/2 1/2
90 1 0
135 1/2 –√1/2
180 0 –1
225 –√1/2 –√1/2
270 –1 0
315 –√1/2 1/2
360 0 1


Кстати, если вы уже встречались с вращением во время работы в других средах программирования графики: эти две формулы вращения могут отличаться от виденных вами раньше. Дело в том, что GDI+ осуществляет вращение по часовой стрелке. В более математически ориентированных средах вращение выполняется против часовой стрелки. В этом случае член первой формулы, содержащий синус, является отрицательным, а аналогичный член второй формулы — положительным.

Все три вышеперечисленных преобразования можно обобщить двумя формулами:


xстр. = sx · xмир. + rx · yмир. + dx

yстр. = ry · xмир. + sy · yмир. + dy


где sx, sy, rx, ry, dx и dy — константы, определяющие некоторое преобразование. С коэффициентами масштабирования sx и sy, как и со значениями смещения dx и dy вы уже знакомы. Вы могли заметить, что некоторые комбинации sx, sy, rx и ry — комбинации, определяемые тригонометрическими функциями определенных углов, — задают вращение. У коэффициентов rx и ry есть собственный смысл, их действие на графику мы скоро выясним.

Вместе эти две формулы называются «общее линейное преобразование плоскости 1». Хотя xстр. и yстр. одновременно являются функциями xмир. и yмир., в формулах линейного преобразования они не подвергаются возведению в степень или чему-то подобному. Линейность глобального преобразования предполагает существование у него некоторых ограничений:

Преобразование, действующее на входе в событие Paint или PrintPage нового, еще не измененного класса Graphics, называется единичным: значения коэффициентов sx и sy этого преобразования равны 1, а остальные параметры — 0. Метод ResetTransform возвращает объект Graphics к использованию единичного преобразования.

Как вы уже видели, действие последовательных вызовов TranslateTransform, ScaleTransform и RotateTransform суммируется. Однако результирующее мировое преобразование может быть различным в зависимости от порядка вызова этих методов. Причину этого продемонстрировать несложно, но я немного боюсь, поэтому не обижусь, если вы закроете глаза и пропустите самое страшное.

Во-первых, предположим, что имеется глобальное преобразование, назовем его T1:


x' = sx1 · x + rx1 · y + dx1

y' = ry1 · x + sy1 · y + dy1


Чтобы не использовать индексы для обозначения мировых координат и координат на странице, я обозначу первые просто x и y, а вторые — x' и y'. Введем второе преобразование T2 с другими коэффициентами:


x' = sx2 · x + rx2 · y + dx2

y' = ry2 · x + sy2 · y + dy2


Применив к мировым координатам преобразование T1, а к его результату — преобразование T2, получаем следующее преобразование:


x' = sx2 · sx1 · x + sx2 · rx1 · y + sx2 · dx1 + rx2 · ry1 · x + rx2 · sy1 · y + rx2 · dy1 + dx2

y' = ry2 · sx1 · x + ry2 · rx1 · y + ry2 · dx1 + sy2 · ry1 · x + sy2 · sy1 · y + sy2 · dy1 + dy2


После приведения подобных получаем:


x' = (sx2 · sx1 + rx2 · ry1) · x + (sx2 · rx1 + rx2 · sy1) · y + (sx2 · dx1 + rx2 · dy1 + dx2)

y' = (ry2 · sx1 + sy2 · ry1) · x + (ry2 · rx1 + sy2 · sy1) · y + (ry2 · dx1 + sy2 · dy1 + dy2)


Если применить сначала T2, а потом T1, результат будет другим:


x' = sx1 · sx2 · x + sx1 · rx2 · y + sx1 · dx2 + rx1 · ry2 · x + rx1 · sy2 · y + rx1 · dy2 + dx1

y' = ry1 · sx2 · x + ry1 · rx2 · y + ry1 · dx2 + sy1 · ry2 · x + sy1 · sy2 · y + sy1 · dy2 + dy1


После приведения подобных получаем:


x' = (sx1 · sx2 + rx1 · ry2) · x + (sx1 · rx2 + rx1 · sy2) · y + (sx1 · dx2 + rx1 · dy2 + dx1)

y' = (ry1 · sx2 + sy1 · ry2) · x + (ry1 · rx2 + sy1 · sy2) · y + (ry1 · dx2 + sy1 · dy2 + dy1)


В этом-то, друзья мои, и причина разных результатов в зависимости о того, какой из методов — ScaleTransform или TranslateTransform — будет вызван первым.



1 Строгое изложение математического подхода см. в разделе 3-7 главы 3 книги Anthony J. Pettofrezzo, Matrices and Transformations (New Yoik: Dover, 1978).


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz