Применение преобразований в Direct2D
В рисовании с помощью Direct2Dмы увидели, что метод ID2D1RenderTarget::FillEllipse рисует эллипс, ориентированный по осям x и y. Но предположим, что вы хотите нарисовать эллипс, наклоненный под углом?
С помощью преобразований можно изменить фигуру следующими способами.
- Поворот вокруг точки.
- Масштабирование.
- Перемещение (смещение в направлении X или Y).
- Скос (также известный как сдвиг).
Преобразование — это математическая операция, которая сопоставляет набор точек с новым набором точек. Например, на следующей схеме показан треугольник, повернутый вокруг точки P3. После применения поворота точка P1 сопоставляется с P1', точка P2 сопоставляется с P2', а точка P3 сопоставляется с самой собой.
Преобразования реализуются с помощью матриц. Тем не менее, вам не нужно понимать математику матриц, чтобы использовать их. Если вы хотите узнать больше о математике, см. в приложении : Преобразования матрицы.
Чтобы применить преобразование в Direct2D, вызовите метод ID2D1RenderTarget::SetTransform. Этот метод принимает D2D1_MATRIX_3X2_F структуру, которая определяет преобразование. Эту структуру можно инициализировать, вызвав методы в классе D2D1::Matrix3x2F. Этот класс содержит статические методы, возвращающие матрицу для каждого типа преобразования:
Например, следующий код применяет поворот на 20 градусов вокруг точки (100, 100).
pRenderTarget->SetTransform(
D2D1::Matrix3x2F::Rotation(20, D2D1::Point2F(100,100)));
Преобразование применяется ко всем последующим операциям рисования, пока не вызовете SetTransform. Чтобы удалить текущее преобразование, вызовите SetTransform с матрицей удостоверений. Чтобы создать матрицу удостоверений, вызовите функцию Matrix3x2F::Identity.
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
Рисование стрелок часов
Давайте воспользуемся преобразованиями, преобразовав нашу программу круг в аналоговые часы. Это можно сделать, добавив линии для рук.
Вместо вычисления координат для линий можно вычислить угол, а затем применить преобразование поворота. В следующем коде показана функция, которая рисует одну часовую руку. Параметр fAngle дает угол руки в градусах.
void Scene::DrawClockHand(float fHandLength, float fAngle, float fStrokeWidth)
{
m_pRenderTarget->SetTransform(
D2D1::Matrix3x2F::Rotation(fAngle, m_ellipse.point)
);
// endPoint defines one end of the hand.
D2D_POINT_2F endPoint = D2D1::Point2F(
m_ellipse.point.x,
m_ellipse.point.y - (m_ellipse.radiusY * fHandLength)
);
// Draw a line from the center of the ellipse to endPoint.
m_pRenderTarget->DrawLine(
m_ellipse.point, endPoint, m_pStroke, fStrokeWidth);
}
Этот код рисует вертикальную линию, начиная с центра циферблата и заканчивая точкой конечной точки. Линия поворачивается вокруг центра эллипса с помощью преобразования вращения. Центральная точка поворота — это центр эллипса, который формирует циферблат часов.
В следующем коде показано, как рисуется весь циферблат часов.
void Scene::RenderScene()
{
m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::SkyBlue));
m_pRenderTarget->FillEllipse(m_ellipse, m_pFill);
m_pRenderTarget->DrawEllipse(m_ellipse, m_pStroke);
// Draw hands
SYSTEMTIME time;
GetLocalTime(&time);
// 60 minutes = 30 degrees, 1 minute = 0.5 degree
const float fHourAngle = (360.0f / 12) * (time.wHour) + (time.wMinute * 0.5f);
const float fMinuteAngle =(360.0f / 60) * (time.wMinute);
DrawClockHand(0.6f, fHourAngle, 6);
DrawClockHand(0.85f, fMinuteAngle, 4);
// Restore the identity transformation.
m_pRenderTarget->SetTransform( D2D1::Matrix3x2F::Identity() );
}
Вы можете скачать полный проект Visual Studio из примера Пример часов Direct2D. (Просто для удовольствия, загружаемая версия добавляет радиальный градиент к циферблату часов.)
Объединение преобразований
Четыре основных преобразования можно объединить путем умножения двух или более матриц. Например, следующий код объединяет поворот с переводом.
const D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(20);
const D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(40, 10);
pRenderTarget->SetTransform(rot * trans);
Класс Matrix3x2F предоставляетоператора*() для умножения матрицы. Порядок умножения матриц важен. Установка преобразования (M × N) означает "Применить M первым, а затем N". Например, ниже приводится вращение, за которым следует перевод:
Ниже приведен код для этого преобразования:
const D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(45, center);
const D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(x, 0);
pRenderTarget->SetTransform(rot * trans);
Теперь сравните это преобразование с преобразованием в обратном порядке: сначала перемещение, затем вращение.
Поворот выполняется вокруг центра исходного прямоугольника. Ниже приведен код для этого преобразования.
D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(45, center);
D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(x, 0);
pRenderTarget->SetTransform(trans * rot);
Как видно, матрицы одинаковы, но порядок операций изменился. Это происходит потому, что умножение матрицы не является коммутативным: M × N ≠ N × M.
Далее
Приложение . Матрицные преобразования