Compartilhar via


Aplicar transformações em Direct2D

Em Desenho com Direct2D, vimos que o método ID2D1RenderTarget::FillEllipse desenha uma elipse alinhada aos eixos x e y. Mas suponha que você queira desenhar uma elipse inclinada em um ângulo?

uma imagem que mostra uma elipse inclinada.

Usando transformações, você pode alterar uma forma das seguintes maneiras.

  • Rotação em torno de um ponto.
  • Dimensionamento.
  • Tradução (deslocamento na direção X ou Y).
  • Distorção (também conhecida como tesoura).

Uma transformação é uma operação matemática que mapeia um conjunto de pontos para um novo conjunto de pontos. Por exemplo, o diagrama a seguir mostra um triângulo girado ao redor do ponto P3. Depois que a rotação é aplicada, o ponto P1 é mapeado para P1', o ponto P2 é mapeado para P2' e o ponto P3 é mapeado para si mesmo.

um diagrama que mostra a rotação em torno de um ponto.

As transformações são implementadas usando matrizes. No entanto, você não precisa entender a matemática das matrizes para usá-las. Se você quiser saber mais sobre a matemática, confira Apêndice: Transformações de Matriz.

Para aplicar uma transformação em Direct2D, chame o método ID2D1RenderTarget::SetTransform. Esse método usa uma estrutura D2D1_MATRIX_3X2_F que define a transformação. Você pode inicializar essa estrutura chamando métodos na classe D2D1::Matrix3x2F . Essa classe contém métodos estáticos que retornam uma matriz para cada tipo de transformação:

Por exemplo, o código a seguir aplica uma rotação de 20 graus ao redor do ponto (100, 100).

pRenderTarget->SetTransform(
    D2D1::Matrix3x2F::Rotation(20, D2D1::Point2F(100,100)));

A transformação é aplicada a todas as operações de desenho posteriores até que você chame SetTransform novamente. Para remover a transformação atual, chame SetTransform com a matriz de identidade. Para criar a matriz de identidade, chame a função Matrix3x2F::Identity .

pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

Desenhando mãos do relógio

Vamos colocar transformações a serem usadas convertendo nosso programa Circle em um relógio analógico. Podemos fazer isso adicionando linhas para as mãos.

uma captura de tela do programa de relógio analógico.

Em vez de calcular as coordenadas das linhas, podemos calcular o ângulo e aplicar uma transformação de rotação. O código a seguir mostra uma função que desenha uma mão de relógio. O parâmetro fAngle fornece o ângulo da mão, em graus.

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);
}

Esse código desenha uma linha vertical, começando do centro da face do relógio e terminando no ponto de extremidadePoint. A linha é girada ao redor do centro da elipse aplicando uma transformação de rotação. O ponto central da rotação é o centro da elipse que forma a face do relógio.

um diagrama que mostra a rotação da mão do relógio.

O código a seguir mostra como toda a face do relógio é desenhada.

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() );
}

Você pode baixar o projeto completo do Visual Studio Direct2D Exemplo de Relógio. (Só por diversão, a versão de download adiciona um gradiente radial à face do relógio.)

Combinando transformações

As quatro transformações básicas podem ser combinadas multiplicando duas ou mais matrizes. Por exemplo, o código a seguir combina uma rotação com uma tradução.

const D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(20);
const D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(40, 10);

pRenderTarget->SetTransform(rot * trans);

A classe Matrix3x2F fornece operator*() para multiplicação de matriz. A ordem na qual você multiplica as matrizes é importante. Definir uma transformação (M × N) significa "Aplicar M primeiro, seguido por N". Por exemplo, aqui está a rotação seguida pela tradução:

um diagrama que mostra a rotação seguida pela tradução.

Este é o código para esta transformação:

const D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(45, center);
const D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(x, 0);
pRenderTarget->SetTransform(rot * trans);

Agora compare essa transformação com uma transformação na ordem inversa, tradução seguida de rotação.

um diagrama que mostra a tradução seguida de rotação.

A rotação é executada ao redor do centro do retângulo original. Este é o código para essa transformação.

D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(45, center);
D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(x, 0);
pRenderTarget->SetTransform(trans * rot);

Como você pode ver, as matrizes são as mesmas, mas a ordem das operações foi alterada. Isso acontece porque a multiplicação de matriz não é comutativa: M × N ≠ N × M.

Avançar

Apêndice: Transformações de Matriz