文章目录
- 前言
- Bresenham 画圆算法原理
- 两个近似
- 构造判别式
- 圆与网格点的关系
- 关系由来
- 关系含义
- p i p_i pi 递推
- 画圆
- 程序伪码
- 圆与网格点的关系图示
前言
首先简要介绍一下生成圆的方法:
- 直接利用圆的方程生成圆
- 利用圆的对称性生成圆
方法一由于会涉及到浮点运算等因素,不采取该方案。
ps. 这部分想要知道为什么可以参考 计算机图形学 圆及椭圆的扫描转换_百度文库 前面一点。
方法二的原理如下图,利用圆的对称性,我们只需要对一个八分圆进行扫描转换。
在这里我们不妨选择第一象限上斜率绝对值小于1的那八分圆进行扫描转换,以及假设圆心为 (0, 0) 。也就是下图。
友情提示:圆在某点的斜率是该点处切线斜率。圆心不在 (0, 0) 处的点可以通过平移得到。
Bresenham 画圆算法原理
如下图,在 0≤x≤y 的 1/8 圆周上,像素坐标 x 值单调增加,y 值单调减少。
设第 i 步已确定 ( x i , y i ) (x_i, y_i) (xi,yi)是要画圆上的像素点,看第 i+1 步像素点 ( x i + 1 , y i + 1 ) (x_{i+1}, y_{i+1}) (xi+1,yi+1)应如何确定。下一个像素点只能是 H ( x i + 1 , y i ) H(x_i+1, y_i) H(xi+1,yi)或者 D ( x i + 1 , y i − 1 ) D(x_i+1,y_i-1) D(xi+1,yi−1)中的一个 。
具体选哪个就得看 d H 和 d D d_H 和 d_D dH和dD 二者的比较了:
- d H d_H dH 更小,下一个像素点就选 H ( x i + 1 , y i ) H(x_i+1, y_i) H(xi+1,yi)
- d D d_D dD 更小,下一个像素点就选 D ( x i + 1 , y i − 1 ) D(x_i+1,y_i-1) D(xi+1,yi−1)
- 一样大,选哪个都行。
比较方法就是做差: d H − d D d_H - d_D dH−dD,然后将结果与0进行比较。
那么问题是 d H 和 d D d_H 和 d_D dH和dD 二者的值是怎么求得呢?这里就涉及到一个近似:
两个近似
近似方法:
将 CH 近似为 d H d_H dH
将 DB 近似为 d D d_D dD
由大图显而易见可以知道 CH 和 DB 的值:
C H = O H − O C = ( x i + 1 ) 2 + y i 2 − R = d H CH = OH - OC = \sqrt{(x_i+1)^2 + y_i^2} - R = d_H CH=OH−OC=(xi+1)2+yi2−R=dH
D H = O B − O D = R − ( x i + 1 ) 2 + ( y i − 1 ) 2 = d D DH = OB - OD = R - \sqrt{(x_i+1)^2 + (y_i-1)^2} = d_D DH=OB−OD=R−(xi+1)2+(yi−1)2=dD
这里由于涉及到了根号运算,因此又做了一个近似:
d H = ( x i + 1 ) 2 + y i 2 − R 2 d_H = (x_i+1)^2 + y_i^2 - R^2 dH=(xi+1)2+yi2−R2
d D = R 2 − ( x i + 1 ) 2 − ( y i − 1 ) 2 d_D = R^2 - (x_i+1)^2 - (y_i-1)^2 dD=R2−(xi+1)2−(yi−1)2
构造判别式
我们不妨构造如下判别式
p i = d H − d D p_i = d_H - d_D pi=dH−dD
= ( x i + 1 ) 2 + y i 2 − R 2 − ( R 2 − ( x i + 1 ) 2 + ( y i − 1 ) 2 ) \space\space\space\space= (x_i+1)^2 + y_i^2 - R^2 - (R^2 - (x_i+1)^2 + (y_i-1)^2) =(xi+1)2+yi2−R2−(R2−(xi+1)2+(yi−1)2)
= 2 ( x i + 1 ) 2 + 2 y i 2 − 2 y i − 2 R 2 + 1 \space\space\space\space= 2(x_i+1)^2 + 2y_i^2-2y_i\textcolor{red}{-2R^2+1} =2(xi+1)2+2yi2−2yi−2R2+1
圆与网格点的关系
关系由来
圆与网格点有且仅有上述5种关系,情况分别如上:
其中的BCEF均为该网格上的中点。
首先,我们得再次强调的一点是,在 0≤x≤y 的 1/8 圆周上的每点的斜率绝对值都小于1。即它们与 x 轴负方向的倾角都不能超过图中的 FE 线段。
由于,我们现在点亮的点是 P ( x i , y i ) P(x_i, y_i) P(xi,yi) ,所以圆与 x=1 的交点应该在 BF 之间。由于此段圆弧的斜率均小于1,因此圆与 x=2 的交点应该在 CE 之间,由此上上图中的 5 条圆弧都有了解释:
圆弧1:圆与 x=1 的交点在 BP 间,且该圆斜率变化很小(半径很大)
圆弧4,5:我个人认为是在接近 y=x 这条直线处的网格可以产生这种情况
关系含义
构造判别式: p i = d H − d D p_i = d_H-d_D pi=dH−dD
- 精确圆弧是③,则 d H > 0 和 d D > 0 d_H >0和d_D>0 dH>0和dD>0
若 p i < 0 , 即 d H < d D 应 选 H 点 若p_i<0,即d_H <d_D应选H点 若pi<0,即dH<dD应选H点
若 p i ≥ 0 , 即 d H ≥ d D 应 选 D 点 若p_i≥0,即d_H ≥d_D应选D点 若pi≥0,即dH≥dD应选D点 - 若精确圆弧是①或②,显然H是应选择点,而此时 d H ≤ 0 , d D > 0 d_H ≤0,d_D>0 dH≤0,dD>0,必有 p i < 0 p_i<0 pi<0。
- 若精确圆弧是④或⑤,显然D是应选择点,而此时 d H > 0 , d D ≤ 0 d_H >0,d_D≤0 dH>0,dD≤0,必有 p i > 0 p_i>0 pi>0。
得出结论: p i p_i pi 做判别量, p i < 0 p_i<0 pi<0 时,选H点为下一个像素点, p i ≥ 0 p_i≥0 pi≥0 时,选D点为下一个像素点。
p i p_i pi 递推
因为 p i p_i pi 代表此次选择哪个点的判别式,如果我们知道下一次选点的判别式和其关系就可以节省很多思考。所以我们要从 p i p_i pi 计算 p i + 1 p_{i+1} pi+1。
ps. 对 p i + 1 p_{i+1} pi+1 数学意义的解释:此次点亮的点我们称做 ( x i , y i ) (x_i, y_i) (xi,yi),下一次点亮的点我们就称做 ( x i + 1 , y i + 1 ) (x_{i+1}, y_{i+1}) (xi+1,yi+1)。 x i + 1 x_{i+1} xi+1 的值和 x i x_i xi 是有递推关系的( x i + 1 = x i + 1 x_{i+1}=x_i+1 xi+1=xi+1 ,见关系由来
下的图)。而此次我们将判断点亮哪个点的判别式记作 p i p_i pi,类似的,下次判断点亮哪个点的判别式我们就记作 p i + 1 p_{i+1} pi+1
p i = d H − d D p_i = d_H - d_D pi=dH−dD
= ( x i + 1 ) 2 + y i 2 − R 2 − ( R 2 − ( x i + 1 ) 2 + ( y i − 1 ) 2 ) \space\space\space\space= (x_i+1)^2 + y_i^2 - R^2 - (R^2 - (x_i+1)^2 + (y_i-1)^2) =(xi+1)2+yi2−R2−(R2−(xi+1)2+(yi−1)2)
= 2 ( x i + 1 ) 2 + 2 y i 2 − 2 y i − 2 R 2 + 1 \space\space\space\space= 2(x_i+1)^2 + 2y_i^2-2y_i\textcolor{red}{-2R^2+1} =2(xi+1)2+2yi2−2yi−2R2+1
p i + 1 = 2 ( x i + 1 + 1 ) 2 + 2 y i + 1 2 − 2 y i + 1 − 2 R 2 + 1 p_{i+1}=2(x_{i+1}+1)^2 + 2y_{i+1}^2-2y_{i+1}\textcolor{red}{-2R^2+1} pi+1=2(xi+1+1)2+2yi+12−2yi+1−2R2+1
p i + 1 − p i = 4 x i + 6 + 2 ( y i + 1 2 − y i 2 − y i + 1 + y i ) p_{i+1}-p_i=4x_i+6+2(y_{i+1}^2-y_i^2-y_{i+1}+y_i) pi+1−pi=4xi+6+2(yi+12−yi2−yi+1+yi)
- 当 p i ≥ 0 p_i≥0 pi≥0 时,应选D点,此时 D 点的 y 坐标和 P 的 y 坐标的关系是 y i + 1 = y i − 1 y_{i+1} = y_i - 1 yi+1=yi−1,带入上式有:
p i + 1 = p i + 4 ( x i − y i ) + 10 p_{i+1}=p_i+4(x_i-y_i)+10 pi+1=pi+4(xi−yi)+10 - 当 p i < 0 p_i<0 pi<0 时,应选H点,此时 H 点的 y 坐标和 P 的 y 坐标的关系是 y i + 1 = y i y_{i+1} = y_i yi+1=yi,带入上式有:
p i + 1 = p i + 4 x i + 6 p_{i+1}=p_i+4x_i+6 pi+1=pi+4xi+6
画圆
画圆的起始点是(0, R),即 x 1 = 0 , y 1 = R x_1=0, y_1=R x1=0,y1=R,带入前式。即令 i=1 ,就得到:
p i = 3 − 2 R p_i=3-2R pi=3−2R
剩下的递推就行。
程序伪码
void BresenhamCircle(int R){ int x,y,p;x=0; y=R;p=3-2*R;for(;x<=y;x++){ SetPixel(x,y);if(p>=0){p+=4*(x-y)+10;y--;}else {p+=4*x+6;}}
}
根据对称性,只需修改语句 SetPixel(x,y),画八个对称的点,就可以画出全部圆周。若加一个平移,就可以画出圆心在任意位置的圆周
圆与网格点的关系图示
情况一: x 2 + y 2 = 10000 x^2+y^2=10000 x2+y2=10000
其中 1 4 2 + 9 9 2 = 9997 14^2+99^2=9997 142+992=9997
情况五: x 2 + y 2 = 10000 x^2+y^2=10000 x2+y2=10000,那条横线是 y=77.5
其中 6 5 2 + 7 6 2 = 10001 65^2+76^2=10001 652+762=10001
时间有限,姑且只画这俩。