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 | < Назад | Оглавление | Далее > |