经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
图形学入门(2)——圆弧生成算法(中点画圆法)
来源:cnblogs  作者:青空哲也  时间:2019/10/22 16:08:36  对本文有异议

一个图形学萌新的学习记录。

学习了直线生成算法之后,继续来学习绘制圆弧的方法,如果要生成一个整圆,可以利用坐标系的八对称性,在其中一个象限绘制之后再在其他象限的对称点绘制即可。

我们首先考虑圆心在原点,半径为r的圆,计算出像素之后只需加上一个偏移量即可绘制圆心在任意一点的圆。

要画圆最暴力的方法当然是利用圆的参数方程来计算:

 

 

 但这样计算使用了三角函数和浮点运算,效率低下,所以通常我们使用中点画圆法来进行圆的绘制。

中点画圆法

中点画圆法利用的也是类似于Bresenham直线算法的思想,利用判别式选择像素,只需做简单的整数运算。

我们八象限中第二象限的1/8圆为例,若确定了一个像素点为($x_{p},y_{p}$),那么下一个点要么是右方的P1,要么是右下方的P2。

 

构造函数一个函数F(x,y)=$x^{2}$+$y^{2}$-$R^{2}$,当F大于0时,点在圆外,反之则在圆内。

图中的M是P1和P2的中点,所以M=($x_{p}$+1,$y_{p}-0.5$),当F(M)<0时,M在圆内,说明P1离圆弧更近,反之M在圆外,P2更近。

根据以上原理,可构造判别式:

 

 当$d_{p}$<0时,取P1为下一像素,下一像素判别式为:

 

 当$d_{p}$>0时,取P2为下一像素,下一像素判别式为:

 

 我们按顺时针方式生成八分圆,所以第一个像素为(0,R),初始判别式为:

 1.25-R可以简化成1-R,去除浮点数运算,因为运算过程中增量都为整数,所以减去0.25是不会影响符号的。

代码实现如下:

  1. void CirclePoints(HDC hdc, int x, int y,int offx,int offy)//利用对称性画整圆
  2. {
  3. SetPixel(hdc, x + offx, y + offy, RGB(0, 0, 0));
  4. SetPixel(hdc, y + offx, x + offy, RGB(0, 0, 0));
  5. SetPixel(hdc, x + offx, -y + offy, RGB(0, 0, 0));
  6. SetPixel(hdc, -y + offx, x + offy, RGB(0, 0, 0));
  7. SetPixel(hdc, -x + offx, y + offy, RGB(0, 0, 0));
  8. SetPixel(hdc, y + offx, -x + offy, RGB(0, 0, 0));
  9. SetPixel(hdc, -x + offx, -y + offy, RGB(0, 0, 0));
  10. SetPixel(hdc, -y + offx, -x + offy, RGB(0, 0, 0));
  11. }
  12. void MidPointCircle(HDC hdc, int x1, int y1, int r)//中点画圆
  13. {
  14. int x, y, e;
  15. x = 0; y = r; e = 1 - r;
  16. CirclePoints(hdc, x, y, x1, y1);
  17. while (x <= y)
  18. {
  19. if (e < 0)
  20. e += 2 * x + 3;
  21. else
  22. {
  23. e += 2 * (x - y) + 5;
  24. y--;
  25. }
  26. x++;
  27. CirclePoints(hdc, x, y, x1, y1);
  28. }
  29. }

 

完整可运行Windows代码:

  1. #include<Windows.h>
  2. #include<iostream>
  3. #include<cmath>
  4. using namespace std;
  5. const int ScreenWidth = 500;
  6. const int ScreenHeight = 500;
  7. LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  8. {
  9. switch (message) {
  10. case WM_CLOSE:
  11. DestroyWindow(hWnd);
  12. break;
  13. case WM_DESTROY:
  14. PostQuitMessage(0);
  15. break;
  16. default:
  17. return DefWindowProc(hWnd, message, wParam, lParam);
  18. break;
  19. }
  20. return 0;
  21. }
  22. void CirclePoints(HDC hdc, int x, int y,int offx,int offy)//利用对称性画整圆
  23. {
  24. SetPixel(hdc, x + offx, y + offy, RGB(0, 0, 0));
  25. SetPixel(hdc, y + offx, x + offy, RGB(0, 0, 0));
  26. SetPixel(hdc, x + offx, -y + offy, RGB(0, 0, 0));
  27. SetPixel(hdc, -y + offx, x + offy, RGB(0, 0, 0));
  28. SetPixel(hdc, -x + offx, y + offy, RGB(0, 0, 0));
  29. SetPixel(hdc, y + offx, -x + offy, RGB(0, 0, 0));
  30. SetPixel(hdc, -x + offx, -y + offy, RGB(0, 0, 0));
  31. SetPixel(hdc, -y + offx, -x + offy, RGB(0, 0, 0));
  32. }
  33. void MidPointCircle(HDC hdc, int x1, int y1, int r)
  34. {
  35. int x, y, e;
  36. x = 0; y = r; e = 1 - r;
  37. CirclePoints(hdc, x, y, x1, y1);
  38. while (x <= y)
  39. {
  40. if (e < 0)
  41. e += 2 * x + 3;
  42. else
  43. {
  44. e += 2 * (x - y) + 5;
  45. y--;
  46. }
  47. x++;
  48. CirclePoints(hdc, x, y, x1, y1);
  49. }
  50. }
  51. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nShowCmd)
  52. {
  53. WNDCLASS wcs;
  54. wcs.cbClsExtra = 0; // 窗口类附加参数
  55. wcs.cbWndExtra = 0; // 窗口附加参数
  56. wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 窗口DC背景
  57. wcs.hCursor = LoadCursor(hInstance, IDC_CROSS); // 鼠标样式
  58. wcs.hIcon = LoadIcon(NULL, IDI_WINLOGO); // 窗口icon
  59. wcs.hInstance = hInstance; // 应用程序实例
  60. wcs.lpfnWndProc = (WNDPROC)WinProc;
  61. wcs.lpszClassName = "CG";
  62. wcs.lpszMenuName = NULL;
  63. wcs.style = CS_VREDRAW | CS_HREDRAW;
  64. RegisterClass(&wcs);
  65. HWND hWnd;
  66. hWnd = CreateWindow("CG", "DrawCircle", WS_OVERLAPPEDWINDOW, 200, 200, ScreenWidth, ScreenHeight, NULL, NULL, hInstance, NULL);
  67. ShowWindow(hWnd, nShowCmd);
  68. UpdateWindow(hWnd);
  69. MSG msg;
  70. // hdc init
  71. HDC hdc = GetDC(hWnd);
  72. MidPointCircle(hdc, 200, 200, 150);
  73. // 消息循环
  74. while (GetMessage(&msg, 0, NULL, NULL)) {
  75. TranslateMessage(&msg);
  76. DispatchMessage(&msg);
  77. }
  78. // release
  79. ReleaseDC(hWnd, hdc);
  80. return 0;
  81. }
View Code

运行结果:

 

 

 接下来是区域填充算法,加油~

原文链接:http://www.cnblogs.com/LiveForGame/p/11718526.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号