基于Bresenham算法画圆

bresenham算法画圆思想与上篇 bresenham算法画线段 思想是一致的

画圆x^2+y^2=R^2

 

 

将他分为8个部分,如上图

 

1.

只要画出1中1/8圆的圆周,剩下的就可以通过对称关系画出这个圆

X变化从0->R

那为什么不采用从-R->R呢,

Y=+-sqrt(R^2-x^2);

dy/dx=-x/(sqrt(R^2-x^2)) =-x/y

所以采用从-R到R,每次横坐标增1,计算量大,而且在(x=+-R,y=0)处,x的很小变化就引起了y的很大变化。

 所以不是采用x从-R--->R变化。而是采用1/8画圆法。

2.

在2这1/8圆周上,x值单调增加,y值单调递减,且fabs(dx/dy)=fabs(-x/y)<=1;所以圆周上相应点的y值变化小于1.

假设当前点为(x1,y1)这下个点为(x1+1,y1)或(x1+1,y1-1)

d1=(x1+1)^2+y1^2-R^2;

d2=R^2-(x1+1)^2-(y1-1)^2;

p=d1-d2 若p>0 选点(x1+1,y1-1)否则选点(x1+1,y1)

p=2(x1+1)^2+2y1^2-2y1-2R^2+1

接下来就是求p了。

P1=3-2R(坐标为x=0,y=R)

然后仿照线段算法得出p(i+1)=p(i)+4(Xi-Yi)+10(pi>0)否则P(i+1)=p(i)+4Xi+6

这样就可以成功画出1/8圆弧了

3.接下来的问题,是剩下的部分怎么处理,

有两种方法:

方法一:存储器将前1/8的坐标存储起来,然后通过镜像求出其他圆弧坐标,调整顺序输出,即可得到。

方法二:分别求出对应的8个圆弧的算法式,一次画弧。

算法比较:前者算法比较简单,但是存储数据,需要较大的RAM,一旦在计算高精度,大半径的圆时,数据存储量就比较大,可能就需要扩充RAM。后者代码比较多,占用较多的程序存储空间。


然后从控制理论角度考虑:前者是先计算后执行,实时性比较差,后者运算和控制交叉进行。但不会减少总时间。

综上所述,当圆的精度低时,可以采用方法一。当圆精度高,半径大,或者对运动过程中实时性要求比较高时,采用方法二。

 在实际的实现过程中,发现按照方法二实现,圆无法画整。因为每次后面1/8圆弧都是与前面的1/8圆弧最好一个点相接的,这样累积下来,势必造成首尾无法相接,最后是采用方法一和方法二结合实现方法二的,即画一半圆,然后利用对称性实现的【即方法一的思想】。

方法一代码实现:

void Draw_Circle(u16 x0,u16 y0,u8 r)
{
int a,b;
a=0;b=r;	
p=3-(r<<1);             //判断下个点位置的标志

	while(a<=b)
	{
	    
		LCD_DrawPoint(x0-b,y0-a);             //3           
		LCD_DrawPoint(x0+b,y0-a);             //0           
		LCD_DrawPoint(x0-a,y0+b);             //1       
	    LCD_DrawPoint(x0-a,y0-b);             //2             
		LCD_DrawPoint(x0+b,y0+a);             //4               
		LCD_DrawPoint(x0+a,y0-b);             //5
		LCD_DrawPoint(x0+a,y0+b);             //6 
		LCD_DrawPoint(x0-b,y0+a);             
		a++;
		//使用Bresenham算法画圆
        //采用的是第一种算法,利用坐标的对称性
		if(p<0)p +=4*a+6;;	  
		else
		{
			p+=10+4*(a-b);   
			b--;
		} 
		
	}
}

方法二代码是实现:

void Draw_Circle2(u16 x0,u16 y0,u8 r)
{
u8 i=0;
 int a,b;   //不可定义为无符号
 int p;
 a=0;b=r;
switch(i)
{
case 0: 
        i++;
        p=3-2*r;
    	while(ar)
		{
		 LCD_DrawPoint(x0+a,y0-b); 	 
		 LCD_DrawPoint(x0-a,y0-b);
		 b--;
		 if(p<0)
		  {
		   p+=4*(a-b)+10;					   
		   a++;
		   
		   }
		  else
		    p+=6-4*b;
		//	delay_ms(500);
		//	printf("b=%da=%d\t",b,a);
		  }
		//break;
case 2:	i++;
       //此时a=r;b=0;
	   
       p=3-2*r;
      	while(a+b>0)
		{
		 LCD_DrawPoint(x0+a,y0-b);
		 LCD_DrawPoint(x0-a,y0-b);
		 b--;
		 if(p<0)
		   p+=6-4*b;
		  else
		 {
		  p+=10-4*(a+b);
		  a--;
		 }
		  
		}
case 3:	i++;
        p=3-sqrt(2)*r;
        while(a>0)
		{
		 LCD_DrawPoint(x0+a,y0-b); 	 
		 LCD_DrawPoint(x0-a,y0-b);
		 a--;
		 if(p<0)
		  {
		   p+=10-4*(a+b);					   
		   b--;
		   
		   }
		  else
		    p+=6-4*a;
			
		  }
}


说明:上述代码实现均是基于stm32处理器,tftLCD2.8寸屏上实现的

关于p的求法如果有疑问,可以参考上篇文章基于Bresenham和DDA算法画线段


对于屏上画点,对各坐标点赋值有疑问的可以参考这篇资料看看bresenham直线算法与画圆算法 

 下面两幅图,是方法二实现的两种比较,如果采用单一的 画8个圆弧,会出现累积误差,圆无法闭合。


图1


图 2
©️2020 CSDN 皮肤主题: Age of Ai 设计师: meimeiellie 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值