Who Ate All The Pie(...)s?
Hello,
I thought I'd have a go over the weekend at using GDI's built-in function for drawing pie segments to display some data but, as always, it's never that straightforward... :rolleyes:
The thing is, the Pie function expects the coordinates of points between which an arc is drawn and the segment closed by drawing lines back to the centre of the pie, defined by its bounding box. As this means the pie may be elliptical in shape, seperate radii have to be found for both axes. I've pretty much go the hang of that, but for some reason, beyond a certain point, the sign of the x-coordinate calculations have to be reversed in order to ensure that the acute angle between the coordinates is used instead of the obtuse.
The problem is, I can't determine the condition(s) for this. It would have been much easier if this thing could just take angles! :mad:
The following C code illustrates the dilemma. Note that I add (pi / 2) to the calculations so that the segments are drawn from north instead of east. A couple Ellipse calls are used to illustrate where each coordinate ends up as a debugging measure.
Code:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
#define TWOPI 2 * 3.14159265
#define HALFPI 3.14159265 / 2
#define NUMVALUES 5
double g_dValues[NUMVALUES] = { 20, 20, 20, 20, 20 };
HBITMAP g_hbmp = NULL, g_hbmpOld;
HBRUSH g_hbrBlue, g_hbrRed;
HDC g_hdc;
BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
{
DeleteObject(g_hbrRed);
DeleteObject(g_hbrBlue);
SelectObject(g_hdc, g_hbmpOld);
DeleteObject(g_hbmp);
DeleteDC(g_hdc);
EndDialog(hwndDlg, 0);
break;
}
case WM_INITDIALOG:
{
g_hdc = CreateCompatibleDC(NULL);
g_hbrBlue = CreateSolidBrush(0xFF0000);
g_hbrRed = CreateSolidBrush(0xFF);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwndDlg, &ps);
BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, g_hdc, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
EndPaint(hwndDlg, &ps);
break;
}
case WM_SIZE:
{
double dRadiusX, dRadiusY, dSums[NUMVALUES + 1];
int i;
POINT pt1, pt2;
if (wParam != SIZE_MINIMIZED)
{
if (g_hbmp)
{
SelectObject(g_hdc, g_hbmpOld);
DeleteObject(g_hbmp);
}
g_hbmp = CreateCompatibleBitmap(GetDC(NULL), LOWORD(lParam), HIWORD(lParam));
g_hbmpOld = SelectObject(g_hdc, g_hbmp);
SelectObject(g_hdc, GetSysColorBrush(COLOR_BTNFACE));
PatBlt(g_hdc, 0, 0, LOWORD(lParam), HIWORD(lParam), PATCOPY);
dSums[0] = 0;
for (i=0;i<NUMVALUES;i++)
dSums[i + 1] = dSums[i] + g_dValues[i];
dRadiusX = LOWORD(lParam) / 2.0;
dRadiusY = HIWORD(lParam) / 2.0;
for (i=0;i<NUMVALUES;i++)
{
SelectObject(g_hdc, (i & 1) ? g_hbrRed : g_hbrBlue);
if (dSums[i] <= dSums[NUMVALUES] / 2 || i == NUMVALUES - 1)
pt1.x = (int)(dRadiusX - dRadiusX * cos((TWOPI * dSums[i + 1]) / dSums[NUMVALUES] + HALFPI));
else
pt1.x = (int)(dRadiusX + dRadiusX * cos((TWOPI * dSums[i + 1]) / dSums[NUMVALUES] + HALFPI));
pt1.y = (int)(dRadiusY - dRadiusY * sin((TWOPI * dSums[i + 1]) / dSums[NUMVALUES] + HALFPI));
if (dSums[i] <= dSums[NUMVALUES] / 2 || i == NUMVALUES - 1)
pt2.x = (int)(dRadiusX - dRadiusX * cos((TWOPI * dSums[i]) / dSums[NUMVALUES] + HALFPI));
else
pt2.x = (int)(dRadiusX + dRadiusX * cos((TWOPI * dSums[i]) / dSums[NUMVALUES] + HALFPI));
pt2.y = (int)(dRadiusY - dRadiusY * sin((TWOPI * dSums[i]) / dSums[NUMVALUES] + HALFPI));
Pie(g_hdc, 0, 0, LOWORD(lParam), HIWORD(lParam), pt1.x, pt1.y, pt2.x, pt2.y);
SelectObject(g_hdc, GetStockObject(WHITE_BRUSH));
Ellipse(g_hdc, pt1.x - 4, pt1.y - 4, pt1.x + 4, pt1.y + 4);
SelectObject(g_hdc, GetStockObject(GRAY_BRUSH));
Ellipse(g_hdc, pt2.x - 4, pt2.y - 4, pt2.x + 4, pt2.y + 4);
}
InvalidateRect(hwndDlg, NULL, TRUE);
}
break;
}
default:
return FALSE;
}
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
DWORD dwData[6];
LPDLGTEMPLATE lpTemplate;
ZeroMemory(dwData, sizeof(dwData));
lpTemplate = (LPDLGTEMPLATE)dwData;
lpTemplate->style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
lpTemplate->x = (short)0x8000;
lpTemplate->y = (short)0x8000;
lpTemplate->cx = (short)0x8000;
lpTemplate->cy = (short)0x8000;
return DialogBoxIndirect(hInstance, lpTemplate, NULL, DialogProc);
}