A jedeme dál. Minule jsme se začali trochu zajímat o GDI a kreslení čar, dnes si probereme křivky.
Tak se opět vracím k našemu seriálu o WinApi.
Minule jsme začali kapitolu o kreslení čar, dnes v ní chci pokračovat a naučit vás, jak kreslit i jiné čáry.
Když jsme minule chtěli nakreslit přímou čáru, použili jsme na to dvě funkce. Funkci MoveToEx , která nám vyznačila počáteční bod, od kterého se čára kreslí, a funkci LineTo, která zase označuje konečný bod, kde kreslení čáry končí.
Když bysme chtěli například nakreslit obdélník, museli bychom použít několik volání LineTo a upravit souřadnice tak, aby výsledný efekt splňoval naše požadavky. Jednodušším způsobem je použití funkce Polyline. Máme-li pole bodů, které chceme propojit, je ideálním řešením použití Polyline a pospojovat tak body čarami. Takto velice jednoduše nakreslíme zmíněný obdélník. Je samozřejmostí, že pomocí této funkce lze kreslit i jiné obrazce skládající se z čar a to daleko složitější. Tato funkce nepoužívá a ani nemění aktuální polohu. Funkce PolylineTo je trochu jiná. Používá aktuální pozici pro výchozí bod a nastavuje aktuální polohu na polohu koncového bodu poslední vykreslené čáry. Polyline i PolylineTo lze použít pro vykreslování několika čar, jejich opravdová síla tkví v kreslení opravdu složitých křivek. Mám na mysli stovky či tisíce velmi malých čárek. Jsou-li tyto čárky dostatečně krátké a je jich dost, budou dohromady vypadat jako křivka.
Polyline(hdc, apt, count);
PolylineTo(hdc, apt, count);
První parametr již dobře známe, nebudu ho popisovat. Druhý parametr je pole struktur na strukturu POINT. Třetí parametr je počet bodů. Tahle hodnota se dá i vypočítat podle jednoduchého vzorce:
sizeof(apt)/sizeof(POINT).Příklad: Jedná se o známý příklad pro kreslení sinusoidy:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClient, cyClient;
HDC hDC;
int i;
PAINTSTRUCT ps;
POINT apt[NUM];
switch(message)
{
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
case WM_PAINT:
hDC=BeginPaint(hWnd, &ps);
MoveToEx(hDC, 0, cyClient/2, NULL);
LineTo(hDC, cxClient, cyClient/2);
for(i=0;i<1000;i++)
{
apt[i].x = i*cxClient/NUM;
apt[i].y = (int) (cyClient/2*(1-sin(TWOPI * i/NUM)));
}
Polyline(hDC, apt, NUM);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Za include dejte do define tyto definice konstant:
#define NUM 1000
#define TWOPI 2*3.14159)
Zde vyvstává skutečná výhoda Polyline oproti LineTo. Polyline je implementována na úrovni ovladače zařízení, tudíž je rychlejší, což má při tak značném volání v cyklu svůj význam. Muselo by se 1000x volat LineTo!
Rectangle
Rectangle, Ellipse, RoundRect, Chord a Pie, všechny tyto funkce spojuje to, že jsou postaveny na obdélníkovém „ohraničujícím“ rámu. Definujeme souřadnice rámu, který objekt obklopuje a Windows objekt vykreslí uvnitř rámu. Také vyplní vzniklou uzavřenou oblast barvou.
Nejjednodušší z těchto funkcí je obdélník:Rectangle(hdc, xLeft, yTop, xRight, yBottom);
xLeft, yTop je levý horní roh obdélníku a xRight, yBottom je pravý dolní roh obdélníku. Strany obdélníku sou vždy rovnoběžné s vertikální a horizontální stranou displeje. Abyste se dobře vyznali v souřadnicích a v tom, jak Windows kreslí tyto a jiné složitější tvary, je dobré na display pohlížet jako na mřížku, kde každý bod je v buňce mřížky. Do mřížky se nakreslí pomyslný ohraničující rám a do něj se potom nakreslí obdélník. Oblast oddělující obdélník je od horní a levé strany klientské oblasti 1 bod široká. Takto kreslí Windows.
Rectangle vyplňuje také uzavřenou oblast vybranou barvou. Jelikož je standardní štětec bílé barvy, není výplň vidět a ani není zřejmé, že se vůbec oblast vyplňuje.
Ellipse
Funkce Ellipse má shodné parametry jako funkce Rectangle.
Ellipse(hdc, xLeft, yTop, xRight, yBottom);
Platí zde to samé co pro obdélník.
RoundRect
Jedná se o funkci pro kreslení obdélníků se zaoblenými rohy. Využívá stejný ohraničující rám jako Rectangle nebo Ellipse, ale očekává další dva parametry:
RoundRect(hdc, xLeft, yTop, xRight, yBottom, xCornerEllipse, yCornerEllipse);
Pro kreslení malých zaoblených rohů se používá malé elipsy. Šířka elipsy je xCornerEllipse a výška elipsy je yCornerEllipse. Zaoblení rohů je výraznější pro větší hodnoty xCornerEllipse a yCornerEllipse. Pokud se xCornerEllipse rovná rozdílu mezi xLeft a xRight a yCornerEllipse se rovná rozdílu mezi yTop a yBottom, pak funkce RoundRect nakreslí elipsu.
Funkce Arc, Chord a Pie mají shodné parametry:Arc(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Chord(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Pie(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Windows používají pomyslnou čáru pro spojení bodu (xStart, yStart) se středem elipsy. V místě, kde čára protíná elipsu, se začne kreslit oblouk po obvodu elipsy ve směru hodinových ručiček. Windows také používají pomyslnou čáru pro spojení bodu (xEnd, yEnd) se středem elipsy. V bodě, ve kterém čára protíná elipsu, Windows kreslení oblouku ukončí. U funkce Arc je oblouk eliptická čára a ne vyplněná oblast. U funkce Chord Windows spojí krajní body oblouku. U funkce Pie spojí každý krajní bod oblouku se středem elipsy. Vnitřní plochy tětivy a koláčového klínu budou vyplněny aktuálním štětcem.
Beziérovy křivky
Jednoduché beziérovy křivky jsou definovány 4mi body – 2 body koncové a 2 body řídící. Konce křivky jsou určeny 2mi koncovými body. Řídící body vystupují jako magnety pro přetahování křivky od přímé spojnice mezi dvěma koncovými body. V podstatě vezmete přímku, zatáhnete za ní a zkřivíte její tvar určitým směrem. Jedná se tedy o dnes již téměř povinnou výbavu každého modelovacího software. Pro nakreslení jedné nebo více spojených Beziérových křivek používáme funkce:
PolyBezier(hdc, apt, iCount);
PolyBezierTo(hdc, apt, iCount);
V obou případech je apt pole struktur POINT, ale to už známe. První 4 body určují(v tomto pořadí) počáteční bod, první řídící bod, druhý řídící bod a koncový bod Beziérovy křivky. Každá další Beziérova křivka vyžaduje pouze 3 body, protože počáteční bod druhé Beziérovy křivky je současně koncovým bodem předcházející Beziérovy křivky. Parametr iCount má vždy hodnotu 1+trojnásobek spojených křivek, které chceme kreslit.
Funkce PolyBezierTo používá aktuální polohu prvního počátečního bodu. A tedy první a každá další křivka potřebuje pouze 3 body. Když se funkce dokončí, bude aktuální poloha nastavena na poslední koncový bod.
Pozn.: Když kreslíte posloupnost Beziérových křivek, bude spojovací bod hladký pouze v případě, že druhý řídící bod první Beziérovy křivky, koncový bod první Beziérovy křivky(což je také počáteční bod následující Beziérovy křivky) a první řídící bod následující Beziérovy křivky budou kolineární; to znamená, že leží v jedné přímé čáře.
Příště: Pera a štětce.