这个算法是纯原创,没有任何借鉴的元素
实现原理大概就是通过两个点算出直线方程然后描点
在这个函数中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来画出来!
于是就有了下面这个图
于是正常画法里就又细分出来两个根据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
运行结果
结果非常满意哈
至此,在OLED上画一条直线的思路和方法就说完了
源码公开,仅供学习