Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W Rysowaniu z Direct2D widzieliśmy, że metoda ID2D1RenderTarget::FillEllipse rysuje elipsę wyrównaną do osi poziomej i pionowej. Załóżmy jednak, że chcesz narysować wielokropek przechylony pod kątem?
Za pomocą przekształceń można zmienić kształt w następujący sposób.
- Obracanie wokół punktu.
- Skalowanie.
- Tłumaczenie (przemieszczenie w kierunku X lub Y).
- Niesymetryczność (znana również jako ścinanie).
Przekształcenie to operacja matematyczna, która przekształca zestaw punktów w nowy zbiór punktów. Na przykład na poniższym diagramie przedstawiono trójkąt obrócony wokół punktu P3. Po zastosowaniu rotacji punkt P1 jest mapowany na P1', punkt P2 jest mapowany na P2', a punkt P3 pozostaje niezmieniony.
Przekształcenia są implementowane przy użyciu macierzy. Jednak nie musisz rozumieć matematyki macierzy, aby ich używać. Jeśli chcesz dowiedzieć się więcej o matematyce, zobacz Dodatek: Przekształcenia macierzy.
Aby zastosować przekształcenie w trybie Direct2D, wywołaj metodę ID2D1RenderTarget::SetTransform . Ta metoda przyjmuje D2D1_MATRIX_3X2_F strukturę definiującą transformację. Tę strukturę można zainicjować, wywołując metody w klasie D2D1::Matrix3x2F . Ta klasa zawiera metody statyczne zwracające macierz dla każdego rodzaju transformacji:
Na przykład poniższy kod stosuje obrót 20 stopni wokół punktu (100, 100).
pRenderTarget->SetTransform(
D2D1::Matrix3x2F::Rotation(20, D2D1::Point2F(100,100)));
Przekształcenie jest stosowane do wszystkich późniejszych operacji rysowania do momentu ponownego wywołania metody SetTransform . Aby usunąć bieżącą transformację, wywołaj metodę SetTransform za pomocą macierzy tożsamości. Aby utworzyć macierz tożsamości, wywołaj funkcję Matrix3x2F::Identity .
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
Rysowanie rąk zegara
Wykorzystajmy przekształcenia, przekształcając nasz program do rysowania koła na zegar analogowy. Możemy to zrobić, dodając linie dla rąk.
Zamiast obliczać współrzędne linii, możemy obliczyć kąt, a następnie zastosować przekształcenie obrotu. Poniższy kod przedstawia funkcję, która rysuje jedną wskazówkę zegara. Parametr fAngle daje kąt dłoni w stopniach.
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);
}
Ten kod rysuje pionową linię, zaczynając od środka twarzy zegara i kończąc na punkcie końcowym. Linia jest obracana wokół środka elipsy przez zastosowanie przekształcenia obrotowego. Centralnym punktem obrotu jest środek wielokropka, który tworzy twarz zegara.
Poniższy kod pokazuje, jak jest rysowana cała twarz zegara.
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() );
}
Pełny projekt programu Visual Studio można pobrać z Direct2D Clock Sample. (Tylko dla zabawy, wersja do pobrania dodaje gradient promieniowy do tarczy zegara.)
Łączenie przekształceń
Cztery podstawowe przekształcenia można połączyć, mnożąc co najmniej dwie macierze. Na przykład poniższy kod łączy rotację z tłumaczeniem.
const D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(20);
const D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(40, 10);
pRenderTarget->SetTransform(rot * trans);
Klasa Matrix3x2F udostępnia operator*() dla mnożenia macierzy. Kolejność mnożenia macierzy jest ważna. Ustawienie przekształcenia (M × N) oznacza "Najpierw zastosuj M, a następnie N". Na przykład poniżej przedstawiono rotację, po której następuje tłumaczenie:
Oto kod dla tej transformacji:
const D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(45, center);
const D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(x, 0);
pRenderTarget->SetTransform(rot * trans);
Teraz porównaj tę transformację z przekształceniem w odwrotnej kolejności, tłumaczenie, a następnie rotacja.
Rotacja jest wykonywana wokół środka oryginalnego prostokąta. Oto kod dla tej transformacji.
D2D1::Matrix3x2F rot = D2D1::Matrix3x2F::Rotation(45, center);
D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(x, 0);
pRenderTarget->SetTransform(trans * rot);
Jak widać, macierze są takie same, ale kolejność operacji uległa zmianie. Dzieje się tak, ponieważ mnożenie macierzy nie jest przemienne: M × N ≠ N × M.
Dalej
dodatek : Przekształcenia macierzy