前言
首先简要介绍一下生成圆的方法:
- 直接利用圆的方程生成圆
- 利用圆的对称性生成圆
方法一由于会涉及到浮点运算等因素,不采取该方案。
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
时间有限,姑且只画这俩。