Delen via


Transformaties toepassen in Direct2D

In Tekenen met Direct2Dhebben we gezien dat de methode ID2D1RenderTarget::FillEllipse een ellips tekent die is uitgelijnd op de x- en y-assen. Maar stel dat u een ellips wilt tekenen die met een hoek is gekanteld?

een afbeelding met een gekantelde ellips.

Door transformaties te gebruiken, kunt u een vorm op de volgende manieren wijzigen.

  • Draaien rond een punt.
  • Schaalvergroting.
  • Vertaling (verplaatsing in de X- of Y-richting).
  • Scheefheid (ook wel sheargenoemd).

Een transformatie is een wiskundige bewerking waarmee een set punten wordt toegewezen aan een nieuwe set punten. In het volgende diagram ziet u bijvoorbeeld een driehoek die rond het punt P3 is gedraaid. Nadat de draaiing is toegepast, wordt het punt P1 toegewezen aan P1,wordt het punt P2 toegewezen aan P2 en wordt het punt P3 toegewezen aan zichzelf.

een diagram met draaiing rond een punt.

Transformaties worden geïmplementeerd met behulp van matrices. U hoeft echter niet de wiskunde van matrices te begrijpen om ze te kunnen gebruiken. Zie Bijlage: Matrixtransformatiesals u meer wilt weten over de wiskunde.

Als u een transformatie wilt toepassen in Direct2D, roept u de methode ID2D1RenderTarget::SetTransform aan. Deze methode gebruikt een D2D1_MATRIX_3X2_F structuur die de transformatie definieert. U kunt deze structuur initialiseren door methoden aan te roepen in de klasse D2D1::Matrix3x2F. Deze klasse bevat statische methoden die een matrix retourneren voor elk type transformatie:

Met de volgende code wordt bijvoorbeeld een draaiing van 20 graden rond het punt (100, 100) toegepast.

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

De transformatie wordt toegepast op alle latere tekenbewerkingen totdat u SetTransform- opnieuw aanroept. Als u de huidige transformatie wilt verwijderen, roept u SetTransform- aan met de identiteitsmatrix. Als u de identiteitsmatrix wilt maken, roept u de functie Matrix3x2F::Identity aan.

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

Klokhanden tekenen

We zetten transformaties in gebruik door ons Circle-programma om te zetten in een analoge klok. We kunnen dit doen door lijnen voor de handen toe te voegen.

een schermafbeelding van het analoge klokprogramma.

In plaats van de coördinaten voor de lijnen te berekenen, kunnen we de hoek berekenen en vervolgens een rotatietransformatie toepassen. De volgende code toont een functie die één klokhand tekent. De parameter fAngle geeft de hoek van de hand, in graden.

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

Met deze code wordt een verticale lijn getekend, beginnend vanaf het midden van het klokvlak en eindigend op het punt eindpunt. De lijn wordt rond het midden van de ellips geroteerd door een rotatietransformatie toe te passen. Het middelpunt voor de draaien is het midden van de ellips die de wijzerplaat vormt.

een diagram met de draaiing van de klokhand.

De volgende code laat zien hoe het hele klokgezicht wordt getekend.

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

U kunt het volledige Visual Studio-project downloaden van Direct2D Clock Sample. (Alleen voor de lol voegt de downloadversie een radiale kleurovergang toe aan het klokvlak.)

Transformaties combineren

De vier basistransformaties kunnen worden gecombineerd door twee of meer matrices te vermenigvuldigen. De volgende code combineert bijvoorbeeld een rotatie met een vertaling.

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

pRenderTarget->SetTransform(rot * trans);

De klasse Matrix3x2F biedt operator*() voor matrixvermeniging. De volgorde waarin u de matrices vermenigvuldigt, is belangrijk. Het instellen van een transformatie (M × N) betekent 'M eerst toepassen, gevolgd door N'. Hier ziet u bijvoorbeeld rotatie gevolgd door vertaling:

een diagram met draaiing gevolgd door vertaling.

Dit is de code voor deze transformatie:

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

Vergelijk die transformatie nu met een transformatie in de omgekeerde volgorde, eerst translatie gevolgd door rotatie.

een diagram waarin de vertaling wordt weergegeven, gevolgd door rotatie.

De draaiing wordt uitgevoerd rond het midden van de oorspronkelijke rechthoek. Hier volgt de code voor deze transformatie.

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

Zoals u ziet, zijn de matrices hetzelfde, maar de volgorde van bewerkingen is gewijzigd. Dit komt doordat matrixvermeniging niet commutatief is: M × N ≠ N × M.

Volgende

Bijlage Matrixtransformaties