Bresenham直线算法

基本原理

以0<斜率k<1的斜线为例,红点为判定为依据点,绿点是应绘制的像素点,直线在红点下方则y坐标不增,反之y坐标自增1。
Bresenham直线算法_第1张图片

一、算法1思路

  1. 根据两点坐标算出斜率k=dy/dx(通常为浮点数)
  2. 斜线与x=x1的交点y坐标y=x1+k ->往后自增k
  3. y0、y1中点y坐标 middle=x1+0.5 ->往后根据两者位置增1或不变
  4. 判别式:y>=middle
    参考代码:
void OLED_ShowLine(u8 x1,u8 y1,u8 x2,u8 y2)
{	
	u8 dx = x2 - x1;
	u8 dy = y2 - y1;
	float k = (float)(dy) / dx;
	u8 x = x1 + 1;		    //直线与x=x1的交点的x坐标
	float y = k;	        //直线与x=x1的交点的y坐标,由于在判别式中y1相消,可以去掉y1
	float middle = 0.5;     //第一个中点,由于在判别式中y1相消,可以去掉y1
	
	OLED_DrawPoint(x1,y1,1);//画起点
	OLED_DrawPoint(x2,y2,1);//画终点
	
	while( x < x2 )			//中间线段
	{
		if(y >= middle)		//若直线点在中点或之上
		{
			y1++;
			middle++;
		}
		OLED_DrawPoint(x,y1,1);//画点
		x++;
		y += k;
	}
}

缺点:存在浮点运算,应用方面较局限,对起止点及其他们的斜率有要求

二、算法2思路

  1. 针对算法1解决浮点数问题,改用整数运算,方法:同比例放大法
  2. 针对判别式修改:k>0.5,即dy/dx>0.5,两边乘以2倍dx,得2dy>dx
  3. 此时斜线与x=x1的交点y坐标y=2dy ->往后自增2dy
  4. y0、y1中点y坐标 middle=dx ->往后根据两者位置增2dx或不变
    参考代码:
void OLED_ShowLine(u8 x1,u8 y1,u8 x2,u8 y2)
{	
	u8 dx = x2 - x1;
	u8 dy = y2 - y1;
	u8 x = x1 + 1;		    //直线与x=x1的交点的x坐标
	u16 y = (dy<<1);	        //直线与x=x1的交点的y坐标,由于在判别式中y1相消,可以去掉y1
	u16 middle = dx;     //第一个中点,由于在判别式中y1相消,可以去掉y1
	
	OLED_DrawPoint(x1,y1,1);//画起点
	OLED_DrawPoint(x2,y2,1);//画终点
	
	//y=dy/dx + dy/dx  >=  middle=0.5 + 1
	
	while( x < x2 )			//中间线段
	{
		if(y >= middle)		//若直线点在中点或之上
		{
			y1++;
			middle+=(dx<<1);
		}
		OLED_DrawPoint(x,y1,1);//画点
		x++;
		y += (dy<<1);
	}
}

三、算法3思路

  1. 针对算法2解决斜率和其他象限不可用问题
  2. 引入坐标轴变换,保证dx为最长轴,交换后标记
  3. 引入sign_x及sign_y标记符,当起始点大于终点则signe_x或sign_y为-1
    参考代码:
void OLED_ShowLine(u8 x1,u8 y1,u8 x2,u8 y2)
{	
	u8 i=0,temp=0;
	u8 interChange = 0;
	u8 dx=0,dy=0;
	int sign_x=0,sign_y=0;	
	u8 x = 0;		   
	u16 y = 0;	       
	u16 middle = 0;    
	
	//取绝对值,并标记
	x2-x1>=0?(dx=x2-x1,sign_x=1):(dx=x1-x2,sign_x=-1);
	y2-y1>=0?(dy=y2-y1,sign_y=1):(dy=y1-y2,sign_y=-1);
	
	if(dx<dy)				//交换坐标轴,保证dx为长轴
	{
		temp = dx;
		dx = dy;
		dy = temp;
		interChange = 1;	//标记
	}
	
	x = x1;            		//直线与x=x1的交点的x坐标
	y = (dy<<1);       		//直线与x=x1的交点的y坐标,由于在判别式中y1相消,可以去掉y1
	middle = dx;       		//第一个中点,由于在判别式中y1相消,可以去掉y1
	
	OLED_DrawPoint(x1,y1,1);//画起点
	OLED_DrawPoint(x2,y2,1);//画终点
	
	
	for(i=0;i<(dx-1);i++)	//中间线段
	{
		
		if(interChange==0)
		{
			x+=sign_x;
		}else{
			y1+=sign_y;
		}

		if(y > middle)		//若直线点在中点或之上
		{
			if(interChange==0)
				y1+=sign_y;
			else
				x+=sign_x;
			middle+=(dx<<1);
		}
		
		OLED_DrawPoint(x,y1,1);//画点
		
		y += (dy<<1);
	}
}

参考视频链接https://www.bilibili.com/video/BV1eE411p7tn?t=3233

你可能感兴趣的:(算法)