通过学习《Hearn&Backer.Computer Graphics with OpenGL》 实现中点画圆
圆的定义
圆定义为距中心位置(x,y)为给定值r的点集
圆的点坐标方程:
(x−xc)2+(y−yc)2=r2
由圆的点坐标方程可以沿x轴对圆取样计算出对应圆上的y坐标:
y=yc±r2−(xc−x)2‾‾‾‾‾‾‾‾‾‾‾‾‾‾√
圆的极坐标方程组:
x=xc+rcosθ
y=yc+rsinθ
通过极坐标方程计算圆周上的各个坐标点需要进行乘法和三角函数的运算,同样的用点坐标方程计算也是需要乘法和平方根运算,都很耗费资源,写成算法不合适。
圆的对称特性
一个八分圆上的点可以映射为其余七个八分圆的点
利用圆的对称性可以减少计算量,只需要计算出一个八分之一圆上需要画的点,就可以映射出整个圆
中点画圆算法
以单位间隔取样,并通过最接近圆周的两个像素的中点测定离圆最近的像素。
对于给定的半径为r,原点为(xc,yc)的圆:
1. 假定圆心在坐标原点(0,0)的像素位置,把计算出每个圆上像素点(x,y)加到屏幕位置上,即(xc+x,yc+y)
2. 以x的正方向,以x的单位距离对八分圆取样,得到每一x单位长度对应的两个可能的y位置
3. 取两个可能的y位置的中点,判断中点相对于圆的位置
对于取样位置为xk+1,刚在(xk,yk)绘制了一个点,下一个点需要判断是在(xk+1,yk)还是(xk+1,yk-1)
定义一个决策参数p:
f=x2+y2−r2
pk=f(xk+1,yk−12)=(xk+1)2+(yk−12)2−r2
- 如果中点位于圆内,即Pk<0,则下一个点是(xk+1,yk)
- 如果位于圆上或者圆外,即Pk+1>=0,则下一个点是(xk+1,yk-1)
决策参数p的循环表达为:
pk+1=f(xk+1+1,yk+1−12)
pk+1−pk=2(xk+1)+(y2k+1−y2k)−(yk+1−yk)+1
- 当pk<0时:yk+1=yk
pk+1=pk+2∗xk+1+1
- 当pk>0时:yk+1=yk-1
pk+1=pk+2xk+1−2yk+1+1
起始位置(0,r)处初始决策参数p0为:
p0=f(0+1,r−12)=54−r
转化为int型则p0=1-r
算法步骤为
- 输入半径r,和圆心(xc,yc),得到第一个点(0+xc,r+xc)
- 计算初始决策参数值 p0 = 1 - r
从原点开始以单位长度取样,计算相应决策参数的值pk
如果pk<0,则下一个点是(xk+1+xc,yk+xc),且pk+1=pk+2xk+1+1
如果pk>=0,则下一个点是(xk+1+xc,yk-1+xc),且pk+1=pk+2xk+1-2yk+1+1
根据确定的一个点,映射出其他七个八分圆上的点
- 重复3,4步骤直到x>=y
算法代码
void drawEightPoints(int xc,int yc,int addx,int addy)
{
drawPixel(xc+addx, yc+addy);
drawPixel(xc-addx, yc+addy);
drawPixel(xc+addx, yc-addy);
drawPixel(xc-addx, yc-addy);
drawPixel(xc+addy, yc+addx);
drawPixel(xc-addy, yc+addx);
drawPixel(xc+addy, yc-addx);
drawPixel(xc-addy, yc-addx);
}
void drawCircle(int xc,int yc,int r)
{
int p,addx,addy;
p=1-r;
addx=0;
addy=r;
drawEightPoints(xc, yc, addx, addy);
while(addx<addy){
addx++;
if(p<0){
p+=2*addx+1;
}
else{
addy--;
p+=2*(addx-addy)+1;
}
drawEightPoints(xc, yc, addx, addy);
}
}
验证:
drawCircle(250,200,100);