赞
踩
这个算法是纯原创,没有任何借鉴的元素
实现原理大概就是运用高中知识利用圆形的标准方程计算坐标然后描点
在这个函数中OLED_DrawDot函数是描点函数,如果和自己代码里不匹配可以换成自己代码里的描点函数。
先看整个源码,后面在讲细节
- /*========================================================
- *功能描述: 在0.96Oled中画圆形
- *参数说明: m -> 绝对x坐标 n -> 绝对y坐标
- * r -> 半径
- *附加说明: 无
- *========================================================*/
- void OLED_Round(double m,double n,unsigned char r)
- {
- char i,y1,y2;
- char x00 = sqrt(2)*r/2; //切换画法的临界值
- double c;
- for(i=m-x00;i<=m+x00;i++) //这是通过x画y画法
- {
- c = (n * n - r * r + i * i - 2 * m * i + m * m);
- y1 = (QEOOU_Answer(1,-(2 * n),c,1));
- y2 = (QEOOU_Answer(1,-(2 * n),c,0));
- OLED_DrawDot(i,y1,1);
- OLED_DrawDot(i,y2,1);
- }
- for(i=n-x00;i<=n+x00;i++) //这是通过y画x画法
- {
- c = (m * m - r * r + i * i - 2 * n * i + n * n);
- y1 = (QEOOU_Answer(1,-(2 * m),c,1));
- y2 = (QEOOU_Answer(1,-(2 * m),c,0)); //此y非彼y,其实是x
- OLED_DrawDot(y1,i,1);
- OLED_DrawDot(y2,i,1);
- }
- }
这里面有个QEOOU_Answer函数,是解一元二次方程的小程序,参数就是QEOOU_Answer(a,b,c,第几个解),含返回值
- /*附属运算函数,解一元二次方程*/
- /*re是返回两个解中的一个*/
- char QEOOU_Answer(double a,double b,double c,unsigned char re)
- {
- //ax^2+bx+c=0
- double w;
- w=b*b-4*a*c;
- if (w<0) //这个方程没有解
- {
- }else if(w==0) //这个方程有两个一样的解
- {
- return -b/(2*a); //返回一个解
- }else //这个方程有两个不一样的解
- {
- if(re == 0)
- {
- return (-b+sqrt(w))/(2*a); //根据参数re返回一个值
- }else if(re == 1)
- {
- return (-b-sqrt(w))/(2*a); //根据参数re返回一个值
- }
- }
- }
这个函数没什么好说的,简单的解一元二次方程算法,在单片机里用不到小数,所以只考虑到了负数存在,返回值定为char足矣
高中我们学习过圆的标准方程(x-m)^2 + (y-n)^2 = r^2,这其中A(m,n)为所画圆的圆心,r为所画圆的半径。
把这个方程展开后,会得到这样的两个方程
1. y^2 - 2ny + n^2 - r^2 + x^2 - 2mx + m^2= 0
2. x^2 - 2mx + m^2 - r^2 + y^2 - 2ny + n^2= 0
哎有细心的人看到就会发现问题
Q :这两个方程不是一样的吗?就顺序调换了一下啊!
A:这两个方程确实是一样的,但我刻意这样调换位置肯定是有原因的,可以去我上一个博客里看那里的重要说明,画圆和画直线一样会出现线不连贯的问题,所以也分为两个,通过x计算y画图和通过y计算x画图。
Q:那么这两个方程有什么不一样呢?
A:还记得上面说的那个解一元二次方程的函数吗?就是给这个东西准备的,就拿第一个方程来说,对于一元二次函数的标准方程ax^2+bx+c=0来说,这里的值可以一一对应起来
a = 1
b = -(2 * n)
c = - r^2 + x^2 - 2mx + m^2
哦吼,看到了,c里面夹杂了x这个变量,所以想计算出来y值还得把c给求出来,
先不着急,我们先把把用x求y画法中的x的取值范围给确定好
这个画圆的方法和画直线的方法一样的,只是在画圆形里确定范围用的不是斜率k了,用的就是x的范围了
借用上一个博客里的图来说话
看这个图是不是就一目了然了呢!
以防万一在解释一下
看这个圆,那个直线的长度就是r而它的斜率就是1
我们知道,斜率为1的直线与x轴的夹角就是45°,所以a=45°
那我们用三角函数就能求出来那个线与圆的交点在x轴投影的x值
就是r * cos45°
所以对于用x计算y画法的取值范围就是[m - sqrt(2) * r / 2,m + sqrt(2) * r / 2]
但到现在只是求出来了取值范围而已
还记得哪个含参的c吗?这里的取值范围就是为这个参数服务的
带入解一元二次方程函数里,得到的两个值就是这个x对应的两个y值
所以就有了代码里的那个看起来很难懂的for
- char x00 = sqrt(2)*r/2; //切换画法的临界值
- double c;
- for(i=m-x00;i<=m+x00;i++) //这是通过x画y画法
- {
- c = (n * n - r * r + i * i - 2 * m * i + m * m);
- y1 = (QEOOU_Answer(1,-(2 * n),c,1));
- y2 = (QEOOU_Answer(1,-(2 * n),c,0));
- OLED_DrawDot(i,y1,1);
- OLED_DrawDot(i,y2,1);
- }
但仅仅如此,也只是画了一个圆的上下两半而已,另一半如法炮制
- for(i=n-x00;i<=n+x00;i++) //这是通过y画x画法
- {
- c = (m * m - r * r + i * i - 2 * n * i + n * n);
- y1 = (QEOOU_Answer(1,-(2 * m),c,1));
- y2 = (QEOOU_Answer(1,-(2 * m),c,0)); //此y非彼y,其实是x
- OLED_DrawDot(y1,i,1);
- OLED_DrawDot(y2,i,1);
- }
主要是不想在定义新变量了,就y凑合用
来,画俩圆试试效果
- OLED_Round(30,30,20);
- OLED_Round(60,40,10);
运行结果
效果不错,很满意
至此,画圆的算法思路已经说完了
源码公开,仅供学习
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。