0.96寸OLED用两点式画直线算法思路分享—代码开源—简单易懂超详细

这个算法是纯原创,没有任何借鉴的元素

实现原理大概就是通过两个点算出直线方程然后描点

在这个函数中OLED_DrawDot函数是描点函数,如果和自己代码里不匹配可以换成自己代码里的描点函数。

先看整个源码,后面在讲细节

/*========================================================
*功能描述:	在0.96Oled中画线
*参数说明:	x1 -> 绝对x1坐标		y1 -> 绝对y1坐标
*			x2 -> 绝对x1坐标		y2 -> 绝对y1坐标
*附加说明:	无
*========================================================*/
void OLED_DrawLine(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2)
{
	unsigned char i = 0;
	//先计算增量Δy和Δx
	char DeltaY = 0,DeltaX = 0;
	float k = 0,b = 0;									//考虑到斜率有小数的情况,所以b也写成浮点型
	if(x1>x2)														//保持Δx为正,方便后面使用
	{
		i = x2;x2 = x1;x1 = i;
		i = y2;y2 = y1;y1 = i;
		i = 0;
	}
	DeltaY = y2 - y1;
	DeltaX = x2 - x1;
	if(DeltaX == 0)											//斜率k不存在时的画法
	{
		if(y1<=y2)
			{
				for(y1;y1<=y2;y1++)
				{
					OLED_DrawDot(x1,y1,1);
				}
			}else if(y1>y2)
			{
				for(y2;y2<=y1;y2++)
				{
					OLED_DrawDot(x1,y2,1);
				}
			}
	}else if(DeltaY == 0)								//斜率k为0时的画法
	{
		for(x1;x1<=x2;x1++)
		{
			OLED_DrawDot(x1,y1,1);
		}	
	}else																//斜率正常存在时的画法
	{
		k = ((float)DeltaY)/((float)DeltaX);		//计算斜率
		b = y2 - k * x2;												//计算截距
		if((k>-1&k<1))
		{
			for(x1;x1<=x2;x1++)
			{
				OLED_DrawDot(x1,(int)(k * x1 + b),1);
			}
		}else if((k>=1)|(k<=-1))
		{
			if(y1<=y2)
			{
				for(y1;y1<=y2;y1++)
				{
					OLED_DrawDot((int)((y1 - b) / k),y1,1);
				}
			}else if(y1>y2)
			{
				for(y2;y2<=y1;y2++)
				{
					OLED_DrawDot((int)((y2 - b) / k),y2,1);
				}
			}
		}
	}
}

思路部分:

先声明四个变量:

x变化量DeltaX和y变化量DeltaY,这两个变量在代码里是随着两个x和y变化的,而且有正负,所以声明为char类型。

还有斜率k和截距b,因为斜率和截距肯定有正负和小数,所以声明为double类型。

随后判断一下x1和x2的大小,保证x2一直都是大于x1的,这样就能保证DeltaX一直都是正的,方便后面使用(人的惯性思维都是先看X的)。

根据高中数学的知识,知道直线方程在宏观方面有三种情况:

1.斜率k = 0的时候,直线为平行x轴的直线,此时的直线方程为y=b。

2.斜率k不存在(或者说k趋向于无穷)的时候,直线为平行y轴的直线,此时的直线方程为x=b。

3.斜率存在的时候,直线方程正常存在,此时的直线方程为y=kx+b。

所以上面的代大概就被if分成了三段

if(DeltaX == 0)            //斜率不存在
{
}
else if(DeltaY == 0)       //斜率等于0
{
}
else                       //排除这两个情况
{
}

前两个没什么好说的,就是用for来描点

但是到目前为止看着好像没什么问题,但在实际运行时就会发现问题

(注意,接下来就比较复杂了)

我把画图的模式分为了两个:

1.通过x算y描点         2.通过y算x描点

为什么要这么麻烦的分为两个来画?就不能全部都用第一种来画吗?

举两个例子就能理解了:

一、假如我要程序画从A(0,0)到B(1,10)这个直线,那么通过直线方程求出来的直线就只能是两个点(0,0)和(1,10),因为像素点只能是整数,不存在半个像素这种说法,所以这个直线只通过第一种想法来画的话就会只画出来三个点。

二、同样是花画点,这次我让程序画A(0,0)到B(3.4)这个直线,同样是上面的原理,最后程序只会画出来三个点(0,0)、(1,2)和(3,4)这三个点,也是不连贯的

Q:那怎么办?

A:通过这两个例子是不是能找到什么规律?仔细看会发现,这两个直线例子的斜率都是k>1时出现的!通过推理我们还能知道,当k<-1时也会出现同样的问题!

Q:那怎么解决呢?

A:上面的问题到什么时候能解决呢?当-1<=k<=1时候就会解决!而这个区间用的画法也是通过x计算y画出来的。所以如法炮制,当k<-1|k>-1时,可以选择通过y计算x来画出来!

于是就有了下面这个图

0.96寸OLED用两点式画直线算法思路分享—代码开源—简单易懂超详细_第1张图片

于是正常画法里就又细分出来两个根据k来决定的画法

if((k>-1&k<1))                //绿色部分
{
}
else if((k>=1)|(k<=-1))       //红色部分
{
}

还记得之前的那个DeltaX吗?代码开头我就已经让它强制x2>x1了,所以DeltaX必定是>0的,所以对于通过x计算y画法里,只需要:

for(x1;x1<=x2;x1++)
{
    OLED_DrawDot(x1,(int)(k * x1 + b),1);
}

但是我能控制完x并不一定能控制的住y,所以我没法控制DeltaY一定是正的,所以对于通过y计算x的画法就有了两个分支:

if(y1<=y2)
{
    for(y1;y1<=y2;y1++)
    {
	    OLED_DrawDot((int)((y1 - b) / k),y1,1);
    }
}else if(y1>y2)
{
	for(y2;y2<=y1;y2++)
	{
		OLED_DrawDot((int)((y2 - b) / k),y2,1);
	}
}

先画6条线看看效果

OLED_DrawLine(0,0,30,15);        //01
OLED_DrawLine(0,63,30,15);       //-1

运行结果

0.96寸OLED用两点式画直线算法思路分享—代码开源—简单易懂超详细_第2张图片

结果非常满意哈

至此,在OLED上画一条直线的思路和方法就说完了

源码公开,仅供学习

你可能感兴趣的:(单片机,C语言,c语言,算法,单片机)