图形学入门(2)——圆弧生成算法(中点画圆法)
时间:2019-10-22
本文章向大家介绍图形学入门(2)——圆弧生成算法(中点画圆法),主要包括图形学入门(2)——圆弧生成算法(中点画圆法)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一个图形学萌新的学习记录。
学习了直线生成算法之后,继续来学习绘制圆弧的方法,如果要生成一个整圆,可以利用坐标系的八对称性,在其中一个象限绘制之后再在其他象限的对称点绘制即可。
我们首先考虑圆心在原点,半径为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是不会影响符号的。
代码实现如下:
void CirclePoints(HDC hdc, int x, int y,int offx,int offy)//利用对称性画整圆 { SetPixel(hdc, x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, -x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, -x + offy, RGB(0, 0, 0)); } void MidPointCircle(HDC hdc, int x1, int y1, int r)//中点画圆 { int x, y, e; x = 0; y = r; e = 1 - r; CirclePoints(hdc, x, y, x1, y1); while (x <= y) { if (e < 0) e += 2 * x + 3; else { e += 2 * (x - y) + 5; y--; } x++; CirclePoints(hdc, x, y, x1, y1); } }
完整可运行Windows代码:
#include<Windows.h> #include<iostream> #include<cmath> using namespace std; const int ScreenWidth = 500; const int ScreenHeight = 500; LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; } void CirclePoints(HDC hdc, int x, int y,int offx,int offy)//利用对称性画整圆 { SetPixel(hdc, x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, -x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, -x + offy, RGB(0, 0, 0)); } void MidPointCircle(HDC hdc, int x1, int y1, int r) { int x, y, e; x = 0; y = r; e = 1 - r; CirclePoints(hdc, x, y, x1, y1); while (x <= y) { if (e < 0) e += 2 * x + 3; else { e += 2 * (x - y) + 5; y--; } x++; CirclePoints(hdc, x, y, x1, y1); } } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nShowCmd) { WNDCLASS wcs; wcs.cbClsExtra = 0; // 窗口类附加参数 wcs.cbWndExtra = 0; // 窗口附加参数 wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 窗口DC背景 wcs.hCursor = LoadCursor(hInstance, IDC_CROSS); // 鼠标样式 wcs.hIcon = LoadIcon(NULL, IDI_WINLOGO); // 窗口icon wcs.hInstance = hInstance; // 应用程序实例 wcs.lpfnWndProc = (WNDPROC)WinProc; wcs.lpszClassName = "CG"; wcs.lpszMenuName = NULL; wcs.style = CS_VREDRAW | CS_HREDRAW; RegisterClass(&wcs); HWND hWnd; hWnd = CreateWindow("CG", "DrawCircle", WS_OVERLAPPEDWINDOW, 200, 200, ScreenWidth, ScreenHeight, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nShowCmd); UpdateWindow(hWnd); MSG msg; // hdc init HDC hdc = GetDC(hWnd); MidPointCircle(hdc, 200, 200, 150); // 消息循环 while (GetMessage(&msg, 0, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } // release ReleaseDC(hWnd, hdc); return 0; }
运行结果:
接下来是区域填充算法,加油~
原文地址:https://www.cnblogs.com/LiveForGame/p/11718526.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【翻译】C++14的新特性简介
- Python 为什么要有 pass 语句?
- 【翻译】C++17的新特性简介
- 各类SQL日期时间处理方法
- 这可能是最全最实用的Vim操作集合
- 个人珍藏的80道多线程并发面试题(1-10答案解析)
- 理解DOM Diff算法
- 腾讯云ES集群通过COS实现跨地域备份与恢复
- Thymeleaf一篇就够了
- python scipy.stats实现各种常见的统计分布
- 操作系统基础 - 文件系统
- Linux升级OpenSSH修复高危漏洞
- Pytest之收集用例及命令行参数
- CVE-2020-14645:Weblogic远程代码执行复现
- 从0到1开发测试平台(七)后端服务添加swagger第三方类库