The CLOVER Program

The CLOVER program forms a region out of four ellipses, selects this region into the device context, and then draws a series of lines emanating from the center of the window's client area. The lines appear only in the area defined by the region. The resulting display is shown in Figure 12-17.

To draw this graphic by conventional methods, you would have to calculate the end point of each line based on formulas involving the circumference of an ellipse. By using a complex clipping region, you can draw the lines and let Windows determine the end points. The CLOVER program is shown in Figure 12-18, beginning on the following page.

CLOVER.MAK

#----------------------

# CLOVER.MAK make file

#----------------------

clover.exe : clover.obj clover.def

link clover, /align:16, NUL, /nod slibcew win87em libw, clover

rc clover.exe

clover.obj : clover.c

cl -c -Gsw -Ow -W2 -Zp clover.c

CLOVER.C

/*--------------------------------------------------

CLOVER.C -- Clover Drawing Program Using Regions

(c) Charles Petzold, 1990

--------------------------------------------------*/

#include <windows.h>

#include <math.h>

#define TWO_PI (2.0 * 3.14159)

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

static char szAppName[] = "Clover" ;

HWND hwnd ;

MSG msg ;

WNDCLASS wndclass ;

if (!hPrevInstance)

{

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = WndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = NULL ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hwnd = CreateWindow (szAppName, "Draw a Clover",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, nCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

}

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static HRGN hRgnClip ;

static short cxClient, cyClient ;

double fAngle, fRadius ;

HCURSOR hCursor ;

HDC hdc ;

HRGN hRgnTemp [6] ;

PAINTSTRUCT ps ;

short i ;

switch (message)

{

case WM_SIZE :

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

hCursor = SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

ShowCursor (TRUE) ;

if (hRgnClip)

DeleteObject (hRgnClip) ;

hRgnTemp [0] = CreateEllipticRgn (0, cyClient / 3,

cxClient / 2, 2 * cyClient / 3) ;

hRgnTemp [1] = CreateEllipticRgn (cxClient / 2, cyClient / 3,

cxClient, 2 * cyClient / 3) ;

hRgnTemp [2] = CreateEllipticRgn (cxClient / 3, 0,

2 * cxClient / 3, cyClient / 2) ;

hRgnTemp [3] = CreateEllipticRgn (cxClient / 3, cyClient / 2,

2 * cxClient / 3, cyClient) ;

hRgnTemp [4] = CreateRectRgn (0, 0, 1, 1) ;

hRgnTemp [5] = CreateRectRgn (0, 0, 1, 1) ;

hRgnClip = CreateRectRgn (0, 0, 1, 1) ;

CombineRgn (hRgnTemp [4], hRgnTemp [0], hRgnTemp [1], RGN_OR) ;

CombineRgn (hRgnTemp [5], hRgnTemp [2], hRgnTemp [3], RGN_OR) ;

CombineRgn (hRgnClip, hRgnTemp [4], hRgnTemp [5], RGN_XOR) ;

for (i = 0 ; i < 6 ; i++)

DeleteObject (hRgnTemp [i]) ;

SetCursor (hCursor) ;

ShowCursor (FALSE) ;

return 0 ;

case WM_PAINT :

hdc = BeginPaint (hwnd, &ps) ;

SetViewportOrg (hdc, cxClient / 2, cyClient / 2) ;

SelectClipRgn (hdc, hRgnClip) ;

fRadius = hypot (cxClient / 2.0, cyClient / 2.0) ;

for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)

{

MoveTo (hdc, 0, 0) ;

LineTo (hdc, (short) ( fRadius * cos (fAngle) + 0.5),

(short) (-fRadius * sin (fAngle) + 0.5)) ;

}

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY :

DeleteObject (hRgnClip) ;

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

CLOVER.DEF

;-----------------------------------

; CLOVER.DEF module definition file

;-----------------------------------

NAME CLOVER

DESCRIPTION 'Clover Drawing Using Regions (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

Because regions always use device coordinates, the CLOVER program has to re-create the region every time it receives a WM_SIZE message. This takes several seconds. CLOVER begins by creating four elliptical regions that are stored as the first four elements of the hRgnTemp array. Then the program creates three ”dummy“ regions:

hRgnTemp [4] = CreateRectRgn (0, 0, 1, 1) ;

hRgnTemp [5] = CreateRectRgn (0, 0, 1, 1) ;

hRgnClip = CreateRectRgn (0, 0, 1, 1) ;

The two elliptical regions at the left and right of the client area are combined:

CombineRgn (hRgnTemp [4], hRgnTemp [0], hRgnTemp [1], RGN_OR) ;

Similarly, the two elliptical regions at the top and bottom of the client area are combined:

CombineRgn (hRgnTemp [5], hRgnTemp [2], hRgnTemp [3], RGN_OR) ;

Finally, these two combined regions are in turn combined into hRgnClip:

CombineRgn (hRgnClip, hRgnTemp [4], hRgnTemp [5], RGN_XOR) ;

The RGN_XOR identifier is used to exclude overlapping areas from the resultant region. Finally, the six temporary regions are deleted:

for (i = 0 ; i < 6 ; i++)

DeleteObject (hRgnTemp [i]) ;

The WM_PAINT processing is simple, considering the results. The viewport origin is set to the center of the client area (to make the line drawing easier), and the region created during the WM_SIZE message is selected as the device context's clipping region:

SetViewportOrg (hdc, xClient / 2, yClient / 2) ;

SelectClipRgn (hdc, hRgnClip) ;

Now all that's left is drawing the lines—360 of them, spaced 1 degree apart. The length of each line is the variable fRadius, which is the distance from the center to the corner of the client area:

fRadius = hypot (xClient / 2.0, yClient / 2.0) ;

for (fAngle = 0.0 ; fAngle < TWO_PI ; fAngle += TWO_PI / 360)

{

MoveTo (hdc, 0, 0) ;

LineTo (hdc, (short) ( fRadius * cos (fAngle) + 0.5),

(short) (-fRadius * sin (fAngle) + 0.5)) ;

}

During processing of WM_DESTROY, the region is deleted:

DeleteObject (hRgnClip) ;