生成一段圆弧的方法主要有三种:
https://blog.csdn.net/BoyInC0de/article/details/90079870
利用隐函数方程: x 2 + y 2 = R 2 \ x^2+y^2=R^2 x2+y2=R2
( x i , y i ) = R 2 − x i 2 → 取 整 ( x i , y i , r ) \ (x_i,y_i)=\sqrt{R^2-x_i^2} \xrightarrow{取整} (x_i,y_{i,r}) (xi,yi)=R2−xi2取整(xi,yi,r)
离散角度:
利用参数方程:
{ x = R cos θ y = R sin θ \begin{cases} x=R\cos\theta \\ y=R\sin\theta \end{cases} { x=Rcosθy=Rsinθ
( r o u n d ( R cos θ i ) , r o u n d ( R sin θ i ) ) (round(R\cos\theta_i),round(R\sin\theta_i)) (round(Rcosθi),round(Rsinθi))
这种算法的缺点就是:
开平方, 三角函数计算量大
利用圆弧的正负划分性来绘制圆弧, 说到正负划分性, 可以参考上一篇 直线段的扫描算法中, 中点算法也利用了直线的正负划分性
传送门: https://blog.csdn.net/BoyInC0de/article/details/90549419
方程:
F ( x , y ) = x 2 + y 2 − R 2 = 0 F(x,y)=x^2+y_2-R^2=0 F(x,y)=x2+y2−R2=0
正负划分性:
圆弧外的点: F ( x , y ) > 0 \ F(x,y)\gt0 F(x,y)>0
圆弧内的点: F ( x , y ) < 0 \ F(x,y)\lt0 F(x,y)<0
圆弧上的点: F ( x , y ) = 0 \ F(x,y)=0 F(x,y)=0
圆具有八分对称性:
至于为什么八分, 就要从斜率的角度来考虑, 参考上一篇文章, 直线段在绘制时, 也分为了4个斜率
以第一象限切矢量斜率 − 1 < m < 0 \ -1\lt m\lt 0 −1<m<0为例说明:
构造判别式:
取第一个点 ( x 0 , y 0 ) \ (x_0,y_0) (x0,y0), 下一点(中点) M ( x 0 + 1 , y 0 − 0.5 ) \ M(x_0+1,y_0-0.5) M(x0+1,y0−0.5)
设 d = F ( M ) = F ( x 0 + 1 , y 0 − 0.5 ) = ( x 0 + 1 ) 2 + ( y 0 − 0.5 ) 2 − R 2 \begin{aligned} 设d=F(M) &=F(x_0+1,y_0 - 0.5)\\ &= (x_0+1)^2 + (y_0 - 0.5)^2 - R^2 \end{aligned} 设d=F(M)=F(x0+1,y0−0.5)=(x0+1)2+(y0−0.5)2−R2
取第三个点分两种情况:
第一种情况上一点的: d ≥ 0 \ d\geq0 d≥0, 取SE:
初始点: ( x 0 , y 0 ) \ (x_0,y_0) (x0,y0) , 第一个点: M ( x 0 + 1 , y 0 − 1 ) \ M(x_0+1,y_0-1) M(x0+1,y0−1), 那么第二个点 ( x 0 + 2 , y 0 − 1.5 ) \ (x_0+2,y_0-1.5) (x0+2,y0−1.5) 代入F(x,y)
F ( x 0 + 2 , y 0 − 1.5 ) = ( x 0 + 2 ) 2 + ( y 0 − 1.5 ) 2 − R 2 = ( x 0 2 + 2 x 0 + 1 ) + ( y 0 2 − y 0 + 0.25 ) − R 2 + 2 x 0 + 3 − 2 y 0 + 2 = d + ( 2 x 0 + 3 ) + ( − 2 y 0 + 2 ) \begin{aligned} F(x_0+2,y_0-1.5) &= (x_0+2)^2 + (y_0 - 1.5)^2 -R^2 \\ &= (x_0^2+2x_0+1)+(y_0^2-y_0+0.25)-R^2+2x_0+3-2y_0+2 \\ &= d +(2x_0+3)+(-2y_0+2) \end{aligned} F(x0+2,y0−1.5)=(x0+2)2+(y0−1.5)2−R2=(x02+2x0+1)+(y02−y0+0.25)−R2+2x0+3−2y0+2=d+(2x0+3)+(−2y0+2)
沿着右下方向, d的增量为 2 ( x 0 − y 0 ) + 5 \ 2(x_0-y_0)+5 2(x0−y0)+5
第二种情况上一点的: d < 0 \ d\lt0 d<0, 取E:
初始点: ( x 0 , y 0 ) \ (x_0,y_0) (x0,y0) , 第一个点: M ( x 0 + 1 , y 0 ) \ M(x_0+1,y_0) M(x0+1,y0), 那么第二个点 ( x 0 + 2 , y 0 − 0.5 ) \ (x_0+2,y_0-0.5) (x0+2,y0−0.5) 代入F(x,y)
F ( x 0 + 2 , y 0 − 0.5 ) = ( x 0 + 2 ) 2 + ( y 0 − 0.5 ) 2 − R 2 = ( x 0 2 + 2 x 0 + 1 ) + ( y 0 2 − y 0 + 0.25 ) − R 2 + 2 x 0 + 3 = d + ( 2 x 0 + 3 ) \begin{aligned} F(x_0+2,y_0-0.5) &= (x_0+2)^2 + (y_0 - 0.5)^2 -R^2 \\ &= (x_0^2+2x_0+1)+(y_0^2-y_0+0.25)-R^2+2x_0+3 \\ &= d +(2x_0+3) \end{aligned} F(x0+2,y0−0.5)=(x0+2)2+(y0−0.5)2−R2=(x02+2x0+1)+(y02−y0+0.25)−R2+2x0+3=d+(2x0+3)
沿着右下方向, d的增量为 2 x 0 + 3 \ 2x_0+3 2x0+3
d的初始值在第一个像素 ( 0 , R ) \ (0,R) (0,R)处
d 0 = F ( 1 , R − 0.5 ) = 1.25 − R d_0 = F(1,R-0.5) = 1.25-R d0=F(1,R−0.5)=1.25−R
因为算法中有浮点数, 和直线段中的做法类似, 我们用 h = 4 d \ h=4d h=4d, 得到:
H 0 = 5 − 4 R H_0=5-4R H0=5−4R
通过归纳法, 我们就得到递推公式:
H 0 = 5 − 4 R H i + 1 = { H i + 8 x i + 12 , H i ≤ 0 H i + 8 ( x i − y i ) + 20 , H i > 0 y i + 1 = { y i , H i ≤ 0 y i − 1 , H i > 0 x i + 1 = x i + 1 \begin{aligned} H_0 &= 5-4R \\ H_{i+1}&= \begin{cases} H_i + 8x_i + 12, & \text{$H_i\leq 0$} \\ H_i + 8(x_i-y_i)+20, & \text{$H_i\gt 0$} \\ \end{cases}\\ y_{i+1}&= \begin{cases} y_i, \quad\quad &\text{$H_i\leq 0$} \\ y_i-1, \quad\quad & \text{$H_i\gt 0$} \\ \end{cases}\quad\quad\quad x_{i+1} = x_i+1 \end{aligned} H0Hi+1yi+1=5−4R={ Hi+8xi+12,Hi+8(xi−yi)+20,Hi≤0Hi>0={ yi,yi−1,Hi≤0Hi>0xi+1=xi+1
为了便于程序实现:
令
E i = 8 x i + 12 E_i = 8x_i + 12 Ei=8xi+12
S E i = 8 ( x i − y i ) + 20 SE_i=8(x_i-y_i)+20 SEi=8(xi−yi)+20
递推公式:
E i + 1 = 8 ( x i + 1 ) + 12 = 8 x i + 12 + 8 = E i + 8 S E i + 1 = 8 ( x i + 1 − y i + 1 ) + 20 = { S E i + 8 , H i ≤ 0 S E i + 16 , H i > 0 \begin{aligned} E_{i+1} &= 8(x_i+1) +12 \\ &=8x_i+12+8 \\ &=E_i + 8 \\ SE_{i+1} &= 8(x_{i+1}-y_{i+1})+20 \\ &= \begin{cases} SE_i + 8, & \text{$H_i \leq 0$} \\ SE_i + 16, & \text{$H_i \gt 0$} \\ \end{cases} \end{aligned} Ei+1SEi+1=8(xi+1)+12=8xi+12+8=Ei+8=8(xi+1−yi+1)+20={ SEi+8,SEi+16,Hi≤0Hi>0
初始值:
E 0 = 8 x i + 12 = 12 S E 0 = 8 ( x i − y i ) + 20 = 20 − 8 R \begin{aligned} E_0 &= 8x_i+12=12 \\ SE_0 &= 8(x_i-y_i)+20=20-8R \end{aligned} E0SE0=8xi+12=12=8(xi−yi)+20=20−8R
为此, 可得到变量关系:
for (i = x0; i < x0 + (int)R * 0.707; i++) { //x0, y0 是圆心, x, y是缓冲区内写的点
if (h > 0) {
--y;
h += se;
e += 8;
se += 16;
}
else {
h += e;
e += 8;
se += 8;
}
x++;
}
则逼近边数 n \ n n, 每一边对应圆心角 α \alpha α
假设半径 R = 1 \ R=1 R=1, 初始点 ( x 0 , y 0 ) = ( cos α , sin α ) \ (x_0,y_0)=(\cos\alpha, \sin\alpha) (x0,y0)=(cosα,sinα)
( x 1 y 1 ) = ( s i n ( 2 α ) c o s ( 2 α ) ) = ( cos α − sin α sin α cos α ) ( c o s α s i n α ) = ( cos α − sin α sin α cos α ) ( x 0 y 0 ) \begin{aligned} \begin{pmatrix} x_1\\ y_1\\ \end{pmatrix} = \begin{pmatrix} sin(2\alpha)\\ cos(2\alpha)\\ \end{pmatrix} &= \begin{pmatrix} \cos\alpha&-\sin\alpha\\ \sin\alpha&\cos\alpha\\ \end{pmatrix} \begin{pmatrix} cos\alpha\\ sin\alpha\\ \end{pmatrix} \\ &=\begin{pmatrix} \cos\alpha &-\sin\alpha\\ \sin\alpha&\cos\alpha\\ \end{pmatrix} \begin{pmatrix} x_0\\ y_0\\ \end{pmatrix} \\ \end{aligned} (x1y1)=(sin(2α)cos(2α))=(cosαsinα−sinαcosα)(cosαsinα)=(cosαsinα−sinαcosα)(x0y0)
经过归纳法:
( x i + 1 y i + 1 ) = ( cos α − sin α sin α cos α ) ( x i y i ) \begin{aligned} \begin{pmatrix} x_{i+1}\\ y_{i+1}\\ \end{pmatrix} = \begin{pmatrix} \cos\alpha&-\sin\alpha\\ \sin\alpha&\cos\alpha\\ \end{pmatrix} \begin{pmatrix} x_i\\ y_i\\ \end{pmatrix} \\ \end{aligned} (xi+1yi+1)=(cosαsinα−sinαcosα)(xiyi)
特点:
α \alpha α是常数, sin α \sin\alpha sinα, cos α \cos\alpha cosα 只需要在开始时计算一次.
所以一个顶点只需4次乘法, 共 4 n \ 4n 4n次乘法, 外加直线段的中点算法的计算量.
(我上传的代码里是6n次乘法, 已经将改正写在后面)
for (int i = 0; i <= edgen; i++) {
glVertex2f(x0 + R * x, y_0 + R * y);
temp = x;
x = ca * x - sa * y;
y = sa * temp + ca * y;
}
该代码采用包含圆弧绘制的两种算法:
**中点算法, 和 内多边形逼近算法. **
分别采用函数来绘制, 可自选中点算法或内多边形逼近算法, 可调节内多边形边数, 可调节颜色
虽然OpenGL1.1库文件较老, 但不论是对于教学还是实践, 对理解直线段算法都具有重要意义.
下载地址: https://download.csdn.net/download/boyinc0de/11209226
更正:
下载后第78行改为:
x = R * ca; y = R * sa;
第80行改为:
glVertex2f(x0 + x, y_0 + y);
算法将少做乘法 2 n \ 2n 2n次 !!!
效果展示:
其中外侧图形为: 中点算法
内侧图形为: 逼近算法