第5章,[Win32 章节] :绘制平面直角坐标系 专栏导航上一篇编程数学三角函数基础05部分三角函数公式回到目录下一篇第5章[Win32 章节] 在平面直角坐标系中描点本专栏课件关于本专栏课件的获取方法请参考下述课节。参考课节MFC笔记本专栏课件本节前言对于本节所讲解的知识有可能你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点请你自行查阅。我呢也会提到一部分的参考课节。但是呢你不应该依赖于我的主动提及。最好呢你自己能够多去了解和查看本专栏目录。编程中需要用到的三角函数知识我们大体上算是复习完了。为了到时候能够顺利地讲解 SineWave 程序我们还需要去讲解一下平面直角坐标系的绘制与描点方法。从平面直角坐标系的描点方法中我们来掌握一个编程技巧。本节我们要去做的是绘制一个平面直角坐标系。本节我们只绘制坐标系而不去描点。下一节我们来描点。一. 运行结果展示与绘制思路讲解图1程序运行结果坐标系图1 所示就是我们本节程序的运行结果。图1 的绘制我全是用 Windows 编程方式来绘制的没有用 PhotoShop 来修饰。我们来分析一下绘制思路。除了绘制原点 O 以外其余的部分基本上都是画线函数 MoveToEx 和 LineTo 的使用。在构思的时候我首先想到的是原点的定位。原点在客户区的横纵坐标我将其取名为 xCenter 和 yCenter 。一X 轴与 Y 轴的绘制思路接下来是 X 轴与 Y 轴的线段部分。X 轴的左右两端点与原点的距离是一样的Y 轴也是。并且呢OX 与 OY 的长度也是一样的。我定义了一个变量nRadiusn 是数据类型 int 的标识Radius 是英语 “半径” 的意思。原点的横坐标减去这个半径值就是 X 轴左端点的横坐标原点的横坐标加上这个半径值就是 X 轴右端点的横坐标。至于两个端点的纵坐标它们与 yCenter 是相等的。X 轴的左右端点的横坐标由变量 xLeft 和 xRight 来表示。原点的纵坐标减去这个半径值就是 Y 轴上端点的纵坐标原点的纵坐标加上这个半径值就是 Y 轴下端点的纵坐标。至于两个端点的横坐标它们与 xCenter 是相等的。Y 轴的上下端点的纵坐标由变量 yTop 和 yBottom 来表示。有了这些个构思与计算代码以后接下来用 MoveToEx 和 LineTo 函数对来绘制直线也就可以了。二坐标轴的指示箭头的绘制首先来说一说 X 轴的指示箭头。箭头有三个端点。我们可以将其称为上端点中端点和下端点。中端点就是xRight, yCenter就是 X 轴的右端点。在箭头之中中端点上面的那个端点也就是 X 轴箭头的上端点我将其表示为 xXArrow1, yXArrow1中端点下面的那个端点也就是 X 轴箭头的下端点我将其表示为xXArrow2, yXArrow2。X 轴指示箭头的上端点和下端点它们的横坐标的值是一样的它们的纵坐标与 X 轴右端点的纵坐标的距离是一样的。我设置了一个变量nLen1 。在 WM_CREATE 消息的初始化代码中我将其赋值为 10 。X 轴的右端点的横坐标减去 nLen1作为上下端点的横坐标。X 轴右端点的纵坐标减去 nLen1作为箭头上端点的纵坐标X 轴右端点的纵坐标加上 nLen1作为箭头下端点的纵坐标。然后呢我们来说 Y 轴的指示箭头。箭头也有三个端点。我们可以将其称为左端点中端点和右端点。箭头的中端点就是xCenter, yTop就是 Y 轴的上端点。在箭头之中中端点左面的那个端点也就是 Y 轴箭头的左端点我将其表示为 xYArrow1, yYArrow1中端点右面的那个端点也就是 Y 轴箭头的右端点我将其表示为xYArrow2, yYArrow2。Y 轴指示箭头的左端点和右端点它们的纵坐标的值是一样的它们的横坐标与 Y 轴上端点的横坐标的距离是一样的。我设置了一个变量nLen1 。在 WM_CREATE 消息的初始化代码中我将其赋值为 10 。Y 轴的上端点的纵坐标加上 nLen1作为箭头左右端点的纵坐标。Y 轴上端点的横坐标减去 nLen1作为箭头左端点的横坐标Y 轴上端点的横坐标加上 nLen1作为箭头右端点的横坐标。有了这些个构思与计算代码以后接下来用 MoveToEx 和 LineTo 函数对来绘制直线也就可以了。三原点的绘制这部分我们用椭圆函数 Ellipse 来绘制。绘制的时候我们还需要获取备用画笔 NULL_PEN 和新建一个黑色的画刷 hBrush 。我没有单独设置变量来表示原点的左右上下四个坐标。我设置了一个变量nLen2 。原点的横坐标减去 nLen2作为原点所在椭圆的左侧横坐标原点的横坐标加上 nLen2作为原点所在椭圆的右侧横坐标原点的纵坐标减去 nLen2作为原点所在椭圆的上侧纵坐标原点的纵坐标加上 nLen2作为原点所在椭圆的下侧纵坐标。四文字标识的绘制在图1 中还有 “X 轴”、“Y 轴” 和 “O” 这样的文字标识。对于其字符串我分别用三个字符数组来标识如下面的代码块所示。static TCHAR strXAxisText[10], strYAxisText[10]; static TCHAR strOrigin[10];既然是用字符数组来存储字符串那么对于字符串的赋值就得是采用特殊的方法。在本节程序中我们所用的赋值函数是 lstrcpy 函数。对于这个函数如果你忘了或者不熟悉可以参考下述课节。参考课节第2章 Windows 的字符串函数上述参考课节中的 一.二里面就有讲解 lstrcpy 函数的使用方法。赋值代码如下。lstrcpy(strXAxisText, _T(X 轴)); lstrcpy(strYAxisText, _T(Y 轴)); lstrcpy(strOrigin, _T(O));在 Windows API 程序设计中想要使用【_T】这个宏我们还需要在代码开头包含着 tchar.h 头文件。所以呢本节程序开头部分的头文件如下图所示。图2绘制文字时我们使用了 TextOut 函数TextOut 函数中有一个字符数的参数。我们专门地给三个字符数组的字符串的最后一个有效字符的下一个位置设置为 0并且还调用 lstrlen 函数来获取了三个字符串的长度。代码如下。图3去进行这种查询字符数与设置结尾的 NULL 字符的事情其实是不必要的。因为lstrcpy 函数本身就会复制字符串结尾的 NULL 字符。不过呢我当时写程序的时候忘了说lstrcpy是否会复制字符串结尾的 NULL 字符了所以呢就写了图3 所示的代码。这属于是一种偷懒不愿意去查信息。实际上在下述的两个课节中是可以了解到 lstrcpy 的复制字符串结尾的 NULL 字符的行为的。参考课节第2章 Windows 的字符串函数参考课节编程技能字符串函数02strcpy然后呢在绘制文字的时候文字的位置也需要去设置一下的。X 轴文字的坐标是xXAxisText, yXAxisTextY 轴坐标的文字是xYAxisText, yYAxisText原点 O 的文字坐标是xOriginText, yOriginText。将 X 轴箭头的下端点的横坐标设置为 xXAxisText 。将 X 轴箭头的下端点的纵坐标加上 nLen1设置为 yXAxisText 。将 Y 轴箭头的右端点的纵坐标设置为 yYAxisText 。将 Y 轴箭头的右端点的横坐标加上 nLen1设置为 xYAxisText 。将原点的横坐标加上 nLen1设置为 xOriginText 。将原点的纵坐标加上 nLen1设置为 yOriginText 。三个文字的位置设置代码如下图所示。图4到了这里本节程序代码的思路基本上我也就说完了。剩下的你自己研究吧你应该能看懂。在下一分节我们来展示完整代码。二. 本节代码代码如下。/*------------------------------------------------------------ 绘制平面直角坐标系不绘制刻度线 水饺编程2026 ------------------------------------------------------------*/ #include windows.h #include tchar.h LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] TEXT(CoordinateSystem); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc WndProc; wndclass.cbClsExtra 0; wndclass.cbWndExtra 0; wndclass.hInstance hInstance; wndclass.hIcon LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName NULL; wndclass.lpszClassName szAppName; if (!RegisterClass(wndclass)) { MessageBox(NULL, TEXT(注册窗口类失败), szAppName, MB_ICONERROR); return 0; } hwnd CreateWindow(szAppName, // window class name TEXT(绘制平面直角坐标系), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters 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) { HDC hdc; PAINTSTRUCT ps; HBRUSH hBrush, hOldBrush; COLORREF color; TEXTMETRIC tm; static int xLeft, xRight, yTop, yBottom; static int xXArrow1, yXArrow1, xXArrow2, yXArrow2; static int xYArrow1, yYArrow1, xYArrow2, yYArrow2; static int nRadius, xCenter, yCenter; static int nLen1, nLen2; static int xXAxisText, yXAxisText; static int xYAxisText, yYAxisText; static int xOriginText, yOriginText; static int nLenTextX, nLenTextY, nLenTextO; static TCHAR strXAxisText[10], strYAxisText[10]; static TCHAR strOrigin[10]; switch (message) { case WM_CREATE: nRadius 150; xCenter 300; yCenter 300; nLen1 10; nLen2 4; xLeft xCenter - nRadius; xRight xCenter nRadius;; yTop yCenter - nRadius; yBottom yCenter nRadius; xXArrow1 xRight - nLen1; yXArrow1 yCenter - nLen1; xXArrow2 xRight - nLen1; yXArrow2 yCenter nLen1; xYArrow1 xCenter - nLen1; yYArrow1 yTop nLen1; xYArrow2 xCenter nLen1; yYArrow2 yTop nLen1; xXAxisText xXArrow2; yXAxisText yXArrow2 nLen1; xYAxisText xYArrow2 nLen1; yYAxisText yYArrow2; xOriginText xCenter nLen1; yOriginText yCenter nLen1; lstrcpy(strXAxisText, _T(X 轴)); lstrcpy(strYAxisText, _T(Y 轴)); lstrcpy(strOrigin, _T(O)); nLenTextX lstrlen(strXAxisText); nLenTextY lstrlen(strYAxisText); nLenTextO lstrlen(strOrigin); strXAxisText[nLenTextX] 0; strYAxisText[nLenTextY] 0; strOrigin[nLenTextO] 0; return 0; case WM_PAINT: hdc BeginPaint(hwnd, ps); // 绘制 x 轴和 y 轴的长线。 MoveToEx(hdc, xLeft, yCenter, NULL); LineTo(hdc, xRight, yCenter); MoveToEx(hdc, xCenter, yBottom, NULL); LineTo(hdc, xCenter, yTop); // 绘制 x 轴的箭头 MoveToEx(hdc, xXArrow1, yXArrow1, NULL); LineTo(hdc, xRight, yCenter); MoveToEx(hdc, xRight, yCenter, NULL); LineTo(hdc, xXArrow2, yXArrow2); // 绘制 y 轴的箭头 MoveToEx(hdc, xYArrow1, yYArrow1, NULL); LineTo(hdc, xCenter, yTop); MoveToEx(hdc, xCenter, yTop, NULL); LineTo(hdc, xYArrow2, yYArrow2); // 描原点 color RGB(0, 0, 0); hBrush CreateSolidBrush(color); hOldBrush (HBRUSH)SelectObject(hdc, hBrush); Ellipse(hdc, xCenter - nLen2, yCenter - nLen2, xCenter nLen2, yCenter nLen2); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); // 写 x 轴、y 轴和原点的标识文字 TextOut(hdc, xXAxisText, yXAxisText, strXAxisText, nLenTextX); TextOut(hdc, xYAxisText, yYAxisText, strYAxisText, nLenTextY); TextOut(hdc, xOriginText, yOriginText, strOrigin, nLenTextO); EndPaint(hwnd, ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }程序的编译方法如果你忘了可以参考下面的课节。参考课节使用 VS2010 编写 C语言程序参考课节用 VS2019 编写C语言程序图1 就是运行结果。结束语本节程序本身不难。不过我讲解编程思路的文字还是多了些。专栏导航上一篇编程数学三角函数基础05部分三角函数公式回到目录下一篇第5章[Win32 章节] 在平面直角坐标系中描点