《Windows32 SDK教程》08章 弧、饼、弦的绘制


弧、饼、弦的绘制是用Arc()、Pie()、Chord()这三个函数来完成的,这三个函数的参数和使用方法完全一致。

一、Arc()、Pie()、Chord()三个函数

BOOL Arc(                  BOOL Pie(                   BOOL Chord(
    HDC hdc,                   HDC hdc,                    HDC hdc,           //设备句柄
    int nLeftRect,             int nLeftRect,              int nLeftRect,     //包含整个椭圆的矩形的左上角x座标
    int nTopRect,              int nTopRect,               int nTopRect,      //包含整个椭圆的矩形的左上角y座标
    int nRightRect,            int nRightRect,             int nRightRect,    //包含整个椭圆的矩形的右下角x座标
    int nBottomRect,           int nBottomRect,            int nBottomRect,   //包含整个椭圆的矩形的右下角y座标
    int nXRadial1,             int nXRadial1,              int nXRadial1,     //起点半径的点的x座标
    int nYRadial1,             int nYRadial1,              int nYRadial1,     //起点半径的点的y座标
    int nXRadial2,             int nXRadial2,              int nXRadial2,     //终点半径的点的x座标
    int nYRadial2              int nYRadial2               int nYRadial2      //终点半径的点的y座标
);                         );                           );

上面是Arc()、Pie()、Chord()这三个函数的原型。小雅在5年前学习这三个函数时,怎么也不能理解MSDN中对第2到第5个参数的说明,譬如第二个参数的说明是“包含弧线的矩形的左上角x座标”,我想我任意画一段弧线,这矩形座标怎么确定?反复拍head之后豁然开朗,原来,弧线是椭圆的一部分,而椭圆有一个外切矩形,于是,我用这个外切矩形的左上右下座标一试,果然OK。

随后又对后4个参数发生了疑问,这起点半径和终点半径我可怎么确定?当然现在搞 清楚了,原来这起点和终点座标并否一个点,而是从矩形的中心开始的一条射线上的任一点,所画的结果都是一 样。例如,下例中32行,只要小于rc.right/2都是一样的结果,甚至超出窗口的点也可以,譬如将32行的10改成-100,结果一样。

起点和终点确定了之后,又有一个问题,即同样起点和终点应该有两条弧线,究竟算 是哪一条?经过实践终于知道是起点到终点按逆时针方向的弧线。后来发现这一点在MSDN上有说明。好了,了解 了这些内容就可以做出来了,下面看一下函数。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{;
    PAINTSTRUCT ps;
    HDC hdc;
    HPEN hPen, hOldPen;
    HBRUSH hBrush, hOldBrush;
    RECT rc;

    switch (message)
    {
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            GetClientRect(hWnd, &rc);

            hPen = CreatePen(PS_SOLID, 1, RGB(255, 100, 0));  //红色实线画笔
            hOldPen = (HPEN)SelectObject(hdc, hPen);
            hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 255, 0)); //绿色方格刷
            hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
            Rectangle(hdc,10, 10, rc.right-10, rc.bottom-10);  //画一个矩形作参照
            DeleteObject(hPen);
            DeleteObject(hBrush);

            hPen = CreatePen(PS_SOLID, 1, RGB(0, 100, 255));  //兰色实线画笔
            SelectObject(hdc, hPen);
            hBrush = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 0, 255)); //45度斜线刷
            SelectObject(hdc, hBrush);
            Pie(hdc,
                10,           //矩形左上角x座标
                10,           //矩形左上角y座标
                rc.right-10,  //矩形右下角x座标
                rc.bottom-10, //矩形右下角y座标
                10,           //起点x座标
                re.bottom/2,  //起点y座标
                rc.right/2,   //终点x座标
                10            //终点y座标
            );  //画饼图

            MoveToEx(hdc, 10, 10, NULL);   //移动到矩形左上角
            LineTo(hdc, rc.right-10, rc.bottom-10);   //画参照用的对角线
            MoveToEx(hdc, 10, rc.bottom-10,NULL);  //移动到矩形左下角
            LineTo(hdc, rc.right-10, 10);  //画参照用的对角线

            SelectObject(hdc, hOldPen);    //恢复旧笔
            SelectObject(hdc, hOldBrush);  //恢复旧刷
            DeleteObject(hPen);            //删除新笔
            DeleteObject(hBrush);          //删除新刷

            EndPaint(hWnd, &ps);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

二、饼图的另一半

将最后4个参数的起点和终点换一下,便得到另外一部分图形。下面例子请参考。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{;
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc;

    switch (message)
    {
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            GetClientRect(hWnd, &rc1);
            rc2 = rc1;

            rc1.bottom = rc1.bottom / 2 + 5;
            Draw(hdc, rc1, FALSE);
            rc2.top = rc2.bottom / 2 - 5;
            Draw(hdc, rc2, TRUE);

            EndPaint(hWnd, &ps);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

void Draw(HDC hdc, RECT rc, BOOL flg) {
    HPEN hPen, hOldPen;
    HBRUSH hBrush, hOldBrush;

    //画一个矩形作参照
    hPen = CreatePen(PS_SOLID, 1, RGB(255, 100, 0));
    hOldPen = (HPEN)SelectObject(hdc, hPen);
    hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 255, 0));
    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
    Rectangle(hdc,rc.left+10, rc.top+10, rc.right-10, rc.bottom-10);    
    DeleteObject(hPen);
    DeleteObject(hBrush);

    hPen = CreatePen(PS_SOLID, 1, RGB(0, 100, 255));  //兰色实线画笔
    SelectObject(hdc, hPen);
    hBrush = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 0, 255)); //45度斜线刷
    SelectObject(hdc, hBrush);
    if (flg) {
        Pie(hdc, rc.left+10, rc.top+10, rc.right-10, rc.bottom-10,
        rc.left+10, (rc.top+rc.bottom)/2, (rc.left+rc.right)/2, rc.top+10);  //画饼图(下半个)
    } else {
        Pie(hdc, rc.left+10, rc.top+10, rc.right-10, rc.bottom-10,
        (rc.left+rc.right)/2, rc.top+10, rc.left+10, (rc.top+rc.bottom)/2);  //画饼图(上半个)
    }

    //画参照用的对角线
    MoveToEx(hdc, rc.left+10, rc.top+10, NULL);
    LineTo(hdc, rc.right-10, rc.bottom-10);
    MoveToEx(hdc, rc.left+10, rc.bottom-10,NULL);
    LineTo(hdc, rc.right-10, rc.top+10);  

    //恢复旧笔刷,删除新笔刷
    SelectObject(hdc, hOldPen);
    SelectObject(hdc, hOldBrush);
    DeleteObject(hPen);
    DeleteObject(hBrush);
}

三、弧和弦

将pie()函数换成arc()函数和chord()函数便可画出弧和弦的图形。


    //......(省略)
    if (flg) {
        Chord(hdc,
              rc.left+10,
              rc.top+10,
              rc.right-10,
              rc.bottom-10,
              rc.left+10,
              (rc.top+rc.bottom)/2,
              (rc.left+rc.right)/2, rc.top+10
        );  //画弦图
    } else {
        Arc(hdc,
            rc.left+10,
            rc.top+10,
            rc.right-10,
            rc.bottom-10,
            (rc.left+rc.right)/2,
            rc.top+10,
            rc.left+10,
            (rc.top+rc.bottom)/2
        );  //画弧线
    }
    //......(省略)

四、弧、饼、弦与椭圆的关系

当上例中的起点和终点相同时,便画成了椭圆。当然,画椭圆还有另外一个函数Ellipse()。


    //......(省略)
    if (flg) {
        Chord(hdc,
              rc.left+10,
              rc.top+10,
              rc.right-10,
              10, 20,
              10, 20,
        );  //画弦图
    } else {
        Arc(hdc,
            rc.left+10,
            rc.top+10,
            rc.right-10,
            rc.bottom-10,
            30, 25,
            30, 25,
        );  //画弧线
    }
    //......(省略)