本文主要是介绍七巧板 Chi7ren V0.1,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Chi7ren
Version 0.1
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:坐标变换
赤色 【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),只是个简单的雏形,对于程序的改进可能会继续,我想后续版本的改进可能会出于以下几个方向:
程序就像版本号一样(Version0.1),只是个简单的雏形,对于程序的改进可能会继续,我想后续版本的改进可能会出于以下几个方向:
- 将Win32 API封装,这样有利于后续开发,也有利于对“面向对象思想”的加深理解;
- 将《Windows程序设计》中每章的内容都融入在程序中,这样有利于对Win32编程的进一步掌握和理解;
- 使用3D图形库如OpenGL进行开发,用户可选择;
- 结合招聘各种能力,锻炼各种能力;
- ...
希望:多指点,多提意见,感激不尽!
致谢:
- 感谢我的室友,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; } |
这篇关于七巧板 Chi7ren V0.1的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!