七巧板 Chi7ren V0.1

来源:互联网 发布:淘宝会员名怎么取 编辑:程序博客网 时间:2024/04/29 04:09
Chi7ren
Version 0.1


    著名哲学家伯利克里:”一个有思想,但是不能表达的人,如同自己没有思想。“

前言
    前阵子看《COM技术内幕》,知道提供的最后一个程序是“七巧板”程序,当时看前几章的时候也很期待,那个Tangram程序会是怎么样的?看了介绍好像还有GDI版和OpenGL版,可以选择。甚是期待,但也是一直没去编译提供的源代码,只是按每一章的代码,去一章一章的去实现,想按“顺序”很自然的到最后一章,可以揭开它的“神秘面纱”。
     有一天,我觉得我应该看看到底是个怎么样的“七巧板”了,以为很酷很帅的。就开始去编译源代码,结果遇到了点小麻烦,还好网上搜了下,还是把一个个错误给解决了。直到编译通过,生成EXE了。打开一看,啊,太失望了。不管是GDI,还是OpenGL都是那么“难看”。但是想想,《COM技术内幕》当然是讲COM为主啦,至于最后的Tnagram也只是对所有COM知识的一次汇总,但是具体代码没去研究,觉得还是很神奇。毕竟看了《COM技术内幕》还是有很大的收获,不过忘了做点笔记,想想看书还是得做点笔记好,以后可以翻阅,有据可查。
     一直也想写个小游戏,可一直不知道该写什么,又想把看《Windows程序设计》的过程中能写点东西,加深对Windows SDK编程的理解,特别是消息机制的理解。还有就是家里可爱的小外甥,小外甥女,如果做的好的话,能给他们玩“舅舅做的游戏”,也是很酷的一件事。又觉得自己一直在看书,想问题,却很少动手写点东西,实践动手编程的能力实在有待提高。基于以上几点,“Chi7ren”(“七小孩”自己取的代号)就开始了...


    有的时候,想是一回事,做又是一回事,写代码又是另一回事!

开发:

 图形结构:

    先把“七小孩”给画出来吧。我把坐标都定死了,我的设想应该是按客户区的大小是可以伸缩的,这也是一个验证的过程,不管怎么样先把图呈现出来先。画图是比较容易的,建立颜色画刷(CreateSolidBrush),选进设备描述表(SelectObject),画多边形(Polygon),一切都那么容易...   

     既然是“七巧板”,当然要七种颜色,我想到了彩虹的七颜色,所以在网上搜了他们的“资料”:
      赤色 【RGB】255, 0, 0     【CMYK】 0, 100, 100, 0
     橙色 【RGB】255, 165, 0 【CMYK】0, 35, 100, 0
     黄色 【RGB】255, 255, 0 【CMYK】0, 0, 100, 0
     绿色 【RGB】0, 255, 0     【CMYK】100, 0, 100, 0
     青色 【RGB】0, 127, 255 【CMYK】100, 50, 0, 0
     蓝色 【RGB】0, 0, 255     【CMYK】100, 100, 0, 0
     紫色 【RGB】139, 0, 255 【CMYK】45, 100, 0, 0

    (说明:这里的显示颜色和具体的RGB并不配对,只是为了好看!!)
     "小孩"的结构:
    struct TangPolygon
    {
        POINT polyPoint[4];    //多边形点,因为最多四边形,为简单就设为“四个点”
        COLORREF rgb;        //图形颜色
        size_t polyType;        //图形类型,即 3:三角形;4:四边形
        float angle;        //保存“世界坐标”角度,绘图时用
    };


 图形移动:
    要实现图形的移动,首先得判断此时该移动哪个图形?通过鼠标左键单击,选择“激活”一个图形,然后鼠标拖动这个图形。然而问题是如何判断哪个图形被激活呢?难道还用判断鼠标坐标在哪个图形的“包围盒”内??这就麻烦了...后来想想,诶,我不是有每个图形的颜色吗?为什么不通过GetPixel得到当前鼠标位置的颜色,再和“七小孩”逐个比较不就知道哪个被激活了吗?这就大大简化了编码难度,也相当实用。

 图形旋转:
    这一点是我一开始就知道的可能难点所在!开始的时候,在想能有OpenGL里的坐标变换就好了,想当然的以为Win32 API应该没有这提供类似函数吧。然后试着通过改变顶点的坐标来实现图形的旋转,发现这实在有难度,大量的计算还不一定准确,难道没办法了吗??后来就去网上搜Win32 API有没有坐标变换的现成函数,啊哈、、还是被我找到了,说《Windows图形编程》里有提到这方面的内容,正好有电子书,也就去翻了下,还是找到了解决方法,以为之前学过OpengGL三维编程,理解这种坐标变换还是不难。关键还是这个函数ModifyWorldTransform,实现了左边转换。具体过程类似图1所示:

图1:坐标变换

SHOW:

当你有个锤子的时候,什么东西看起来都像是钉子。  
                                                                                          
-摘自《世界因你不同》

P.S.:
    程序就像版本号一样(Version0.1),只是个简单的雏形,对于程序的改进可能会继续,我想后续版本的改进可能会出于以下几个方向:
  1. 将Win32 API封装,这样有利于后续开发,也有利于对“面向对象思想”的加深理解;
  2. 将《Windows程序设计》中每章的内容都融入在程序中,这样有利于对Win32编程的进一步掌握和理解;
  3. 使用3D图形库如OpenGL进行开发,用户可选择;
  4. 结合招聘各种能力,锻炼各种能力;
  5. ...   
    希望:多指点,多提意见,感激不尽!

致谢:
  • 感谢我的室友,Zhu,感谢他一直的鼓励,也感谢他提的一些宝贵意见。
  • 还有最近看的几本书对我的影响《把时间当做朋友-运用心智获得解放》,特别是其中提到《奇特的一生》中的事件-时间日记;以及李开复自传《世界因你不同》,让我不停的寻找发自内心的声音,里面的很多方法如“新闻头条”,“墓志铭”,“从心选择”,也让我受益匪浅。  

成功并没有绝对的意义,成功,就是做最好的自己,并把最好的你呈现出来。 

源代码:
///////////////////////////////////////////////////////////////////////////
//Author:    shenzi
//Date:       2009.12.15
//Version:    0.1
///////////////////////////////////////////////////////////////////////////

#include
#include
#include
#include "resource.h"

//右键点击“七巧板”,旋转的角度,5度
const float acc = static_cast(5.0/180.0*3.1415926);

//“七巧板”图形的多边形结构
struct TangPolygon
{
    POINT polyPoint[4];    //多边形点,因为最多四边形,为简单就设为“四个点”
    COLORREF rgb;          //图形颜色
    size_t polyType;       //图形类型,即 3:三角形;4:四边形
    float angle;           //保存“世界坐标”角度,绘图时用
};

//预设的“七巧板”
TangPolygon g_Tangram[7] = {
    {{0, 0, 0, 320, 160, 160, 0, 0},RGB(255, 0, 0)/*赤色*/,3, 0},    
    {{0, 0, 160, 160, 320, 0, 0, 0},RGB(255, 165, 0)/*橙色*/,3, 0},
    {{0, 320, 160, 320, 80, 240, 0, 0},RGB(255, 255, 0)/*黄色*/,3, 0},
    {{160, 160, 240, 240, 240, 80, 0, 0},RGB(0, 255, 0)/*绿色*/,3, 0},
    {{160, 320, 320, 320, 320, 160, 0, 0},RGB(0, 127, 255)/*青色*/,3, 0},    
    {{80, 240, 160, 320, 240, 240, 160, 160},RGB(0, 0, 255)/*蓝色*/,4, 0},
    {{240, 80, 240, 240, 320, 160, 320, 0},RGB(139, 0, 255)/*紫色*/,4, 0}
                         };

///////////////////////////////////////////////////////////////////////////
//Author:    shenzi
//Date:        2009.12.15
//Version:    0.1
///////////////////////////////////////////////////////////////////////////

#include "Tangram.h"

//窗口过程函数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//辅助函数,得到图形的中心坐标
POINT  GetCenter(POINT *pPoint, size_t size);

//主函数
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PTSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Tangram");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

    wndclass.cbClsExtra        = 0;
    wndclass.cbWndExtra        = 0;
    wndclass.hbrBackground    = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
    //画了个“七巧板”ICON
    wndclass.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TANGRAM));
    wndclass.hInstance        = hInstance;
    wndclass.lpfnWndProc    = WndProc;
    wndclass.lpszClassName    = szAppName;
    wndclass.lpszMenuName    = NULL;
    wndclass.style            = CS_HREDRAW | CS_VREDRAW ;

    if (!::RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("Erroe:..."), TEXT("Error"), MB_ICONERROR);
        return 0;
    }
    
    hwnd = ::CreateWindow(szAppName,
                        //取了个名字“七小孩”,版本就是0.1了,以后可能慢慢完善。
                        TEXT("Chi7ren! Version 0.1"),
                        WS_OVERLAPPEDWINDOW | WS_VSCROLL,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        NULL,
                        hInstance,
                        NULL);

    ::ShowWindow(hwnd, iCmdShow);
    ::UpdateWindow(hwnd);

    while(::GetMessage(&msg, NULL, 0, 0))
    {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }

    return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //客户区大小
    static int cxClient, cyClient; 
 

    HDC hdc, hdcMem ;    
    HBITMAP  hBmp;
    HBRUSH hbrush;
   
    static int priX, priY;
    int moveLenthX, moveLenthY;

    //指定当前可移动的“小孩”。
    static int movePolygon;        
    PAINTSTRUCT ps;

    COLORREF color;
    POINT center;
    unsigned int i;

    //“世界坐标”旋转用到的两个XFORM结构
    static XFORM Tansform = {1, 0, 0, 1, 0, 0};
    static XFORM Tanslate = {1, 0, 0, 1, 0, 0};

    switch (message)
    {
        //窗口大小改变时,得到新的客户区大小;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);    
        return 0;
        //鼠标左键,确定当前可移动的图形
    case WM_LBUTTONDOWN:    
        priX = LOWORD(lParam);
        priY = HIWORD(lParam);

        hdc = ::GetDC(hwnd);
        color = ::GetPixel(hdc, priX, priY);
        for (i = 0; i < 7; i++)
        {
            if (color == g_Tangram[i].rgb)
            {
                movePolygon = i;            
                break;
            }
        }
        ::ReleaseDC(hwnd, hdc);
        return 0;
        //鼠标右键,实现图形的旋转功能,"SHIFT"改变旋转方向;
    case WM_RBUTTONDOWN:        
        priX = LOWORD(lParam);
        priY = HIWORD(lParam);
        
        hdc = ::GetDC(hwnd);
        color = ::GetPixel(hdc, priX, priY);
        for (i = 0; i < 7; i++)
        {
            if (color == g_Tangram[i].rgb)
            {
                movePolygon = i;
                if (wParam & MK_SHIFT)
                {
                    g_Tangram[i].angle += acc;
                }
                else
                {
                    g_Tangram[i].angle -= acc;
                }
                break;
            }
        }
        ::ReleaseDC(hwnd, hdc);
        ::InvalidateRect(hwnd, NULL, FALSE);
        return 0;
        //鼠标移动并且左键按下,得到移动距离,并重绘
    case WM_MOUSEMOVE:
        if (wParam & MK_LBUTTON)
        {
            moveLenthX = LOWORD(lParam) - priX;
            moveLenthY = HIWORD(lParam) - priY;
            priX = LOWORD(lParam);
            priY = HIWORD(lParam);

            for (i = 0; i < g_Tangram[movePolygon].polyType; i++)
            {
                g_Tangram[movePolygon].polyPoint[i].x += moveLenthX;
                g_Tangram[movePolygon].polyPoint[i].y += moveLenthY;
            }        
        }
        ::InvalidateRect(hwnd, NULL, FALSE);
        return 0;
        //状态改变时,绘图
    case WM_PAINT:
        hdc = ::BeginPaint(hwnd, &ps);
 
        //兼容DC,并选进相应的位图,解决“屏幕闪烁”问题;在兼容DC上画图,
        //画完后再BitBlt到客户区,实现双缓存。

        hdcMem = ::CreateCompatibleDC(hdc);
        hBmp = ::CreateCompatibleBitmap(hdc, cxClient, cyClient);
        ::SelectObject(hdcMem, hBmp);

        //为选进位图设定背景:白色
        ::PatBlt(hdcMem, 0, 0, cxClient, cyClient, WHITENESS);

        //设定设备描述表高级图形模式,允许全局转换,即实现图形世界坐标转换;
        ::SetGraphicsMode(hdcMem, GM_ADVANCED);

        //绘制“七巧板”
        for (i = 0; i < 7; i++)
        {
            //得到当前处理图形的中心点
            center = GetCenter(g_Tangram[i].polyPoint, g_Tangram[i].polyType);

            //全局转换:
            //1.重置当前全局转换

            ::ModifyWorldTransform(hdcMem, NULL, MWT_IDENTITY);
            
            //2.先把世界坐标移到“中心”点
            Tansform.eDx = static_cast(-center.x);
            Tansform.eDy = static_cast(-center.y);
            ::ModifyWorldTransform(hdcMem, &Tansform, MWT_RIGHTMULTIPLY);

            //3.再做旋转
            Tanslate.eM11 = cos(g_Tangram[i].angle);
            Tanslate.eM12 = -sin(g_Tangram[i].angle);
            Tanslate.eM21 = sin(g_Tangram[i].angle);
            Tanslate.eM22 = cos(g_Tangram[i].angle);
            ::ModifyWorldTransform(hdcMem, &Tanslate, MWT_RIGHTMULTIPLY);

            //4.最后移回原处
            Tansform.eDx = static_cast(center.x);
            Tansform.eDy = static_cast(center.y);
            ::ModifyWorldTransform(hdcMem, &Tansform, MWT_RIGHTMULTIPLY);

            //选定图形颜色,绘制该图形
            hbrush = ::CreateSolidBrush(g_Tangram[i].rgb);
            ::SelectObject(hdcMem, hbrush);         
            ::Polygon(hdcMem, g_Tangram[i].polyPoint, g_Tangram[i].polyType);
            ::DeleteObject(hbrush);        
        }

        //****重置全局转换,不知道为什么需要加这条语句,不加这条语句最后个图形的
        //旋转就会处问题。

        ::ModifyWorldTransform(hdcMem, NULL, MWT_IDENTITY);

        //把结果“贴到”客户区
        ::BitBlt(hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY);

        //删除创建的对象
        ::DeleteObject(hBmp);
        ::DeleteDC(hdcMem);

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

POINT  GetCenter(POINT *pPoint, size_t size)
{
    unsigned int i = 0;
    POINT center = {0, 0};
    for (i = 0; i < size; i++)
    {
        center.x += pPoint[i].x;
        center.y += pPoint[i].y;
    }
    center.x /= size;
    center.y /= size;
    return center;
}









 

原创粉丝点击