You may wonder about this use of starting and ending positions in the Arc, Chord, and Pie functions. Why not simply specify starting and ending points on the circumference of the ellipse? Well, you can, but you would have to figure out what those points are. Windows' method gets the job done without requiring such precision.
You can experiment with arcs, chords, and pie wedges using the ARCS program, shown in Figure 12-10 on the following page. The program draws the bounding box and the ellipse using a dotted pen. Your menu choice determines whether the program draws an arc, a chord, or a pie wedge. The line or figure is drawn with a pen that is 3 pixels wide. The starting and ending points are connected to the center of the ellipse with a normal black pen.
ARCS.MAK
#--------------------
# ARCS.MAK make file
#--------------------
arcs.exe : arcs.obj arcs.def arcs.res
link arcs, /align:16, NUL, /nod slibcew libw, arcs
rc arcs.res
arcs.obj : arcs.c arcs.h
cl -c -Gsw -Ow -W2 -Zp arcs.c
arcs.res : arcs.rc arcs.h
rc -r arcs.rc
ARCS.C
/*-------------------------------------------------------
ARCS.C -- Demonstrates Drawing Arcs, Chords, and Pies
(c) Charles Petzold, 1990
-------------------------------------------------------*/
#include <windows.h>
#include "arcs.h"
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[] = "Arcs" ;
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 = szAppName ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
hwnd = CreateWindow (szAppName, "Arcs, Chords, and Pies",
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 short cxClient, cyClient, x1, x2, x3, x4, y1, y2, y3, y4,
nFigure = IDM_ARC ;
HDC hdc ;
HMENU hMenu ;
HPEN hPen ;
PAINTSTRUCT ps ;
short x, y ;
switch (message)
{
case WM_SIZE :
x3 = y3 = 0 ;
x4 = cxClient = LOWORD (lParam) ;
y4 = cyClient = HIWORD (lParam) ;
x2 = 3 * (x1 = cxClient / 4) ;
y2 = 3 * (y1 = cyClient / 4) ;
return 0 ;
case WM_COMMAND :
switch (wParam)
{
case IDM_ARC :
case IDM_CHORD :
case IDM_PIE :
hMenu = GetMenu (hwnd) ;
CheckMenuItem (hMenu, nFigure, MF_UNCHECKED) ;
CheckMenuItem (hMenu, nFigure = wParam, MF_CHECKED) ;
InvalidateRect (hwnd, NULL, FALSE) ;
return 0 ;
}
break ;
case WM_LBUTTONDOWN :
if (!(wParam & MK_SHIFT))
{
x3 = LOWORD (lParam) ;
y3 = HIWORD (lParam) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
}
// fall through for MK_SHIFT
case WM_RBUTTONDOWN :
x4 = LOWORD (lParam) ;
y4 = HIWORD (lParam) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
hPen = SelectObject (hdc, CreatePen (PS_DOT, 1, 0L)) ;
Rectangle (hdc, x1, y1, x2, y2) ;
Ellipse (hdc, x1, y1, x2, y2) ;
DeleteObject (SelectObject (hdc, CreatePen (PS_SOLID, 3, 0L))) ;
switch (nFigure)
{
case IDM_ARC :
Arc (hdc, x1, y1, x2, y2, x3, y3, x4, y4) ;
break ;
case IDM_CHORD :
Chord (hdc, x1, y1, x2, y2, x3, y3, x4, y4) ;
break ;
case IDM_PIE :
Pie (hdc, x1, y1, x2, y2, x3, y3, x4, y4) ;
break ;
}
DeleteObject (SelectObject (hdc, hPen)) ;
MoveTo (hdc, x3, y3) ;
LineTo (hdc, cxClient / 2, cyClient / 2) ;
LineTo (hdc, x4, y4) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
ARCS.RC
/*-------------------------
ARCS.RC resource script
-------------------------*/
#include "arcs.h"
Arcs MENU
{
POPUP "&Options"
{
MENUITEM "&Arc", IDM_ARC, CHECKED
MENUITEM "&Chord", IDM_CHORD
MENUITEM "&Pie", IDM_PIE
}
}
ARCS.H
/*--------------------
ARCS.H header file
--------------------*/
#define IDM_ARC 1
#define IDM_CHORD 2
#define IDM_PIE 3
ARCS.DEF
;---------------------------------
; ARCS.DEF module definition file
;---------------------------------
NAME ARCS
DESCRIPTION 'Arc, Chord, and Pie Drawing Program (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOADMOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
When you click on the client area using the left mouse button, ARCS uses that point as the starting point, which is the point (x3, y3) in the program. Clicking on the client area with the right mouse button sets the ending point, the point (x4, y4). Users with a one-button mouse can hold down the Shift key and click the mouse to set the ending point.
ARCS also shows some typical pen-handle manipulation. After the program gets the device context by calling BeginPaint, it creates a dotted pen and selects it into the device context:
hPen = SelectObject (hdc, CreatePen (PS_DOT, 1, 0L)) ;
The hPen handle returned from SelectObject is a handle to the stock BLACK_PEN.
When ARCS needs to draw the arc, chord, or pie wedge, it creates a 3-pixel-wide pen and selects that into the device context:
DeleteObject (SelectObject (hdc, CreatePen (PS_SOLID, 3, 0L))) ;
The pen handle returned from SelectObject, which is the handle to the dotted pen, is then deleted using DeleteObject.
When ARCS needs to draw the lines connecting the starting and ending points with the center of the ellipse, it selects hPen—the handle to the stock BLACK_PEN—into the device context and deletes the 3-pixel-wide pen returned from SelectObject:
DeleteObject (SelectObject (hdc, hPen)) ;
Now the two pens that were created have been deleted. The pen currently selected in the device context is a stock pen, and the device context can be released.