首页 > 编程语言 >Bresenham算法画圆

Bresenham算法画圆

时间:2023-09-04 10:55:05浏览次数:37  
标签:xc yc Bresenham getx 算法 画圆 圆周 circPt

目录

问题背景

如何在屏幕上绘制一个圆?
可以先看看圆的特性,根据其特性决定如何绘制。。

  • 圆的特性

圆定义:所有距离中心位置(xc, yc)为给定值r的点集。

圆的方程:

\[(x-x_c)^2+(y-y_c)^2=r^2 \tag{1} \]

  • 根据圆的方程绘制圆

若沿着x轴从\([x_c-r, x_c+r]\)以1为单位步长,计算y值,就能得到圆周每个点位置:

\[y=y_c±\sqrt{r^2-(x_c-x)^2} \tag{2} \]

这种方法优点:简单,直观。
缺点:
1)计算量大,每一步都要进行平方、开方运算。
2)所画像素位置的间距不一致,因为x虽然逐像素递增,但y经过平方、开方运算后,并不会随x逐次变化。

  • 利用对称性优化圆方程绘制圆

不论用何种算法计算圆周的坐标值(x,y),只需要考虑[0, 45°]的圆周,其他部分圆周可以利用圆的对称性求出。

假设P(x, y)为第一象限圆心角为[0, 45°]的圆周上任意一点,那么可以根据对称性求出另外7点坐标。当P遍历圆弧上每一点时,也就能得到另外7段圆弧。

Bresenham算法画圆

算法推演

如何解决根据圆方程计算像素点位置,而导致的运算量大、像素间距不一致的问题?
可以用Bresenham算法。Bresenham画圆算法,也叫中心点画圆算法。

类似于光栅画线的算法,在每一步以1像素间隔为单位进行取样,确定距离圆周最近的像素点位置。
对于半径r、圆心坐标(xc,yc)点圆,可以先讨论圆心在原点(0,0)的圆,然后将其移动到(xc, yc)。
由圆的对称性可知,只用求第一象限中圆心角为45°的圆弧(1/8圆),此时,圆心与圆上一点连线斜率为0~1。

定义圆函数:

\[f_{circ}(x, y) = x^2+y^2-r^2 \tag{3} \]

对于任意一点(x,y):

\[\tag{4} f_{circ}(x,y)=\begin{cases} < 0, & (x,y)在圆内 \\\\ = 0, & (x,y)在圆上 \\\\ > 0, & (x,y)在圆外 \end{cases} \]

假设当前绘制了像素点\((x_k, y_k)\),那么下一个点\((x_{k+1}, y_{k+1})\)在哪?
有2个选择:\((x_k+1, y_k) or (x_k+1, y_k-1)\)
tips:在第一象限内,圆周点随着x增大y减小(非递增)。

Bresenham的基本思想,是判断哪个点到曲线的距离更近,就选择那个点。然而,直接求点到圆周的距离会非常复杂,可以通过判断2个备选点的中点\((x_k+1, y_k-1/2)\)与圆的位置关系:

  • 当中点位于圆内时,则\(y_k\)更接近圆;
  • 当中点位于圆外时,则\(y_k-1\)更接近圆;

tips:一个最简单的理解方式,就是考虑极端情况,如\(y_k\)在圆上(最接近),则中点肯定在圆内;\(y_k-1\)在圆上(最接近),则中点肯定在圆外。

于是,决策参数:

\[\tag{5} \begin{aligned} p_k &= f_{circ}(x_k+1, y_k-1/2) \\\\ & = (x_k+1)^2+(y_k-1/2)^2-r^2 \end{aligned} \]

当k取值k+1时,有

\[\begin{aligned} p_{k+1} &= f_{circ}(x_{k+1}+1, y_{k+1}-1/2) \\\\ & = [(x_k+1)+1]^2+(y_{k+1}-1/2)^2-r^2 \end{aligned} \]

当\(p_k < 0\)时,中点位于圆内,\(y_k\)更接近圆;
当\(p_k > 0\)时,中点位于圆外,\(y_k-1\)更接近圆;

做差值,有

\[\tag{6} \begin{aligned} p_{k+1} - p_k &= 2(x_k+1)+(y_{k+1}^2 - y_k^2) - (y_{k+1}-y_k) + 1 \\\\ & = \begin{cases} 2x_{k+1}+1, & y_k更近<=>p_k<0 \\\\ 2x_{k+1}+1-2y_{k+1}, & y_k-1更近<=>p_k>0 \end{cases} \end{aligned} \]

因为,\(x_{k+1}=x_k+1\)且,

\[y_{k+1}=\begin{cases} y_k, & y_k更近 <=> p_k < 0 \\\\ y_k-1, & y_k-1更近<=>p_k > 0 \end{cases} \]

对于初值\(p_0\),在起始点\((x_0,y_0)=(0, r)\)处求值即可:

\[\tag{7} \begin{aligned} p_0 &= f_{circ}(1, r-1/2) \\\\ &= 1^2+(r-1/2)^2-r^2 \\\\ &= 5/4-r \end{aligned} \]

如果半径r为整数,则可以对\(p_0\)取整:\(p_0=1-r\)

算法步骤

Bresenham算法画圆步骤,可以归纳如下:

  1. 输入圆半径r, 圆心\((x_c, y_c)\),得到圆周(圆心移动到原点)上第一个点:\((x_0,y_0)=(0,r)\);
  2. 计算决策参数的初值:\(p_0=5/4-r\);
  3. 从k=0开始,对每个\(x_k\)进行以下计算及判断:
    如果\(p_k<0\),则圆的下一个点为\((x_{k+1}, y_k)\),且\(p_{k+1}=p_k+2x_{k+1}+1\)
    如果\(p_k>0\),则圆的下一个点为\((x_{k+1}, y_k-1))\),且\(p_{k+1}=p_k+2x_{k+1}+1-2y_{k+1}\)
  4. 计算得到1/8后,可用对称性得到其他7个部分;
  5. 将计算出的像素位置(x,y)移回圆心\((x_c,y_c)\)的圆周上,并绘制出像素点:

\[x=x+x_c, y=y+y_c \]

  1. 重复3~5,直至x≥y。

算法程序

Bresenham算法画圆程序如下。
注意:算法求出1/8圆弧的像素点,然后用对称性得到另外7/8的像素点。

// 在指定坐标(xCoord,yCoord)绘制像素点
void set_pixel(GLint xCoord, GLint yCoord)
{
       glBegin(GL_POINTS);
       glVertex2i(xCoord, yCoord);
       glEnd();
}

// 绘制圆心(xc,yc), 半径radius的圆
void circleMidpoint(GLint xc, GLint yc, GLint radius)
{
       screenPt circPt; // 圆周上的点(像素点)

       GLint p = 1 - radius; // Initial value for midpoint parameter
       circPt.setCoords(0, radius); // Set coordinates for top point of circle.

       circlePlotPoints(xc, yc, circPt);

       while (circPt.getx() < circPt.gety()) {
              circPt.incrementx();
              if (p < 0) {
                     p += 2 * circPt.getx() + 1;
              }
              else {
                     circPt.decrementy();
                     p += 2 * (circPt.getx() - circPt.gety()) + 1;
              }
              circlePlotPoints(xc, yc, circPt);
       }
}

// 用对称性由1/8圆弧的点得到另外7/8圆弧的点, 并绘制出圆周上的点
// (xc, yc) 圆心位置
// circPt 圆心为(0,0)的圆心角0~45°的圆周上点的位置
void circlePlotPoints(GLint xc, GLint yc, screenPt circPt)
{
       set_pixel(xc + circPt.getx(), yc + circPt.gety());
       set_pixel(xc - circPt.getx(), yc + circPt.gety());
       set_pixel(xc + circPt.getx(), yc - circPt.gety());
       set_pixel(xc - circPt.getx(), yc - circPt.gety());
       
       set_pixel(xc + circPt.gety(), yc + circPt.getx());
       set_pixel(xc - circPt.gety(), yc + circPt.getx());
       set_pixel(xc + circPt.gety(), yc - circPt.getx());
       set_pixel(xc - circPt.gety(), yc - circPt.getx());
}

参考

[1]DonaldHearn,M.PaulineBaker,赫恩,等.计算机图形学(第四版)[M].电子工业出版社,2014.

标签:xc,yc,Bresenham,getx,算法,画圆,圆周,circPt
From: https://www.cnblogs.com/fortunely/p/17676341.html

相关文章

  • 机器学习算法编程小技巧——numpy用法之np.c_
     importnumpyasnp#创建两个一维数组a=np.array([1,2,3])b=np.array([4,5,6])#使用numpy.c_将它们连接在一起"""numpy.c_是一个方便的工具,用于沿第二轴连接数组。它将数组转换为至少2-D,并将它们堆叠在一起。这在需要将多个数组组合成一个更大数组的情况......
  • Lnton羚通智能分析算法车辆拥堵智能识别检测
    车辆拥堵智能识别是基于图像处理和计算机视觉技术,通过分析道路交通图像或视频信息,实时检测和识别交通拥堵状况的一种方法。车辆拥堵智能识别算法包括以下几种常见的方法和技术:1.基于图像处理的拥堵识别:通过分析交通监控摄像头拍摄到的道路图像,利用图像处理和计算机视觉技术来检测和......
  • 2023“钉耙编程”中国大学生算法设计超级联赛(3)
    1005.OutofControl题意:有n个数\(x_1,x_2,...,x_n\),在其中选k个数依次放入栈中。如果当前放入栈中的数\(x_i\)小于栈顶的数,则向栈中放入与先前的栈顶相同的数而不是\(x_i\)。求对于每个k对应的方案数。分析:先排序离散化,然后考虑dp。状态定义:f[i][j]表示长度为i且最后一个......
  • 代码随想录算法训练营第二十九天| 491.递增子序列 46.全排列 47.全排列 II
     491.递增子序列   卡哥建议:本题和大家刚做过的 90.子集II 非常像,但又很不一样,很容易掉坑里。 https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html  视频讲解:https://www.bilibili.com/video/BV1EG4y1h78v  做题思路......
  • 代码随想录算法训练营第三十天| 51. N皇后 37. 解数独 总结
        卡哥建议:今天这三道题都非常难,那么这么难的题,为啥一天做三道? 因为 一刷 也不求大家能把这么难的问题解决,所以 大家一刷的时候,就了解一下题目的要求,了解一下解题思路,不求能直接写出代码,先大概熟悉一下这些题,二刷的时候,随着对回溯算法的深入理解,再去解决如下三题。 ......
  • Dubbo(六)_时间轮算法
    时间轮算法介绍HashedWheelTimer定时轮算法在netty、dubbo等框架中运用广泛。比如在Dubbo中为了增强系统的容错能力,会有相应的监听判断机制比如RPC调用超时机制,消费者判断请求是否超时,如果超时就将结果返回给应用层。初期Dubbo实现时采取将所有返回的结果DefaultFutu......
  • MD5算法原理(未完成)
    MD5简介MD5不是一种加密算法,而是一种哈希算法,用于生成固定长度的哈希值。哈希算法通常不涉及加密或解密,它们是单向操作,将输入数据转换为固定长度的哈希值,而无法从哈希值还原原始数据。MD5算法核心步骤:填充数据:首先,将输入数据填充到长度为512位的多重数(multipleof512bits),......
  • Lnton羚通算法算力云平台【PyTorch】教程:torch.nn.Hardtanh
    torch.nn.Hardtanh原型CLASStorch.nn.Hardtanh(min_val=-1.0,max_val=1.0,inplace=False,min_value=None,max_value=None)参数min_val ([float])–线性区域的最小值,默认为-1max_val ([float])–线性区域的最大值,默认为1inplace ([bool])–默认为Falsetorch.nn.Ha......
  • 五、调度算法
    1、进程调度算法也称CPU调度算法,因为进程由CPU调度。当CPU空闲时选择某个就绪状态的进程并给其分配CPU发生CPU调度的常见情况:进程从运行状态转到等待状态进程从运行状态转到就绪状态进程从等待状态转到就绪状态进程从运行状态到终止状态1和4两种情况下的调度称为非抢占式......
  • Lnton羚通智能分析算法道路病害识别监测系统,使用CNN网络深度学习算法
    道路病害识别监测系统通过CNN网络深度学习算法,道路病害识别监测对巡检车上实时监控道路影像数据进行分析,输出道路病害裂缝巡检报告并落图展示。卷积神经网络(ConvolutionalNeuralNetwork,CNN)在图像处理和图像识别任务中取得了很大的成功。它通过卷积层、池化层和全连接层的组......