最近把计算器完善了一下,添加了变量的支持,添加了更多的函数,把逻辑短路操作也实现了,并修正了一些小错误。想起来以前在一本书里看到过一个示例,输入函数表达式,就可以绘制函数的波形。最开始学VB的时候,就喜欢用函数来画图。再加上对电子技术有点兴趣,很多波形都可以用函数来表示,很自然就想到用程序来模拟示波器显示波形。但是因为函数都需要在代码里面写死,如果需要新增函数或者进行修改,需要修改程序代码再编译运行。既然现在可以做到对表达式进行计算,也可以支持变量,那么让变量的值变化就可以计算得到不同的值,再把这些值组合成坐标点,连接起来就成了波形。于是乎,咱也试试做一个显示函数波形的小程序玩玩,效果如下:
先说说新添加的变量支持功能。这里的变量并不需要声明,只要不是保留的关键字,程序就把它作为变量。在以前的版本中遇到不认识的字符串会报错,现在是在分析关键字的时候做了特殊处理,遇到非关键字字符串则添加到一个静态的变量字典中。变量字典的Key是该变量的字符串表示,Value是一个TokenValue对象。在添加到字典之后,如果再遇到相同的字符串,则返回变量字典中对应的TokenValue对象。下面给个例子:
从例子可以看出,在未赋值之前,n的值为空,和其他值运算不会发生错误。下面是语法树分析的图:
从图上可以看出变量n是引用的,在第一句中n的值是空,类型为未初始化类型,但是在PropertyGrid中显示的信息是最后一次赋值的结果。而且这里把赋值操作符"="作为赋值操作的根节点,并没有像左括号"("一样处理。比如最后一个表达式sin(n+20)的语法树中,TokenSin的下级是TokenPlus,而不是TokenLeftBracket。对于赋值操作符"="之所以这保留了原始结构,是因为这样可以在修改下级节点的值之后继续调用Execute方法进行计算,否则如果把值直接指定给变量,下次调用Execute的时候就没法执行了。左括号只是分割表达式,但赋值操作符是有真正的运算过程,所以必须用不一样的分析方法。这一点对于下面要实现的函数波形非常重要。在绘制波形的时候需要改变变量,如果采用变量字符串替换的方法,每次都需要分析表达式,而变量的值域可能很大,这样会把大量时间消耗在分析上。但是如果能保留完整的语法树,只需要将变量对应的TokenRecord的值改变,再次调用顶级节点的Execute方法,这时候只需要逐级向下调用计算方法即可,不需要重新分析表达式了。
接下来就介绍怎么实现函数波形绘制的吧。首先这里引入了一个变量n,在进行计算之前在程序里面进行初始化,然后根据设置的范围用for递增。定义两个表达式X和Y,分别对应坐标点的X和Y,这两个表达式中包含n,在对n进行递增之前调用语法分析类进行分析,得到顶级节点,这时候语法树已经分析完成了。在对n进行递增的时候,计算X和Y,形成一系列坐标点。调用Graphics类的DrawLines方法,把计算得到的一系列坐标点作为参数传递给该方法,这样就可以看到特定的波形。
比如阿基米德螺旋线用伪代码表示如下:
{
X = n*sin(n);
Y = n*cos(n);
PointCollection.Add(new Point(X, Y));
}
myGraphics.DrawLines(myPen, PointCollection);
strX = "n*sin(n)";
strY = "n*cos(n)";
TokenN = mySyntaxAnalyse.Analyse(strN);
TokenX = mySyntaxAnalyse.Analyse(strX);
TokenY = mySyntaxAnalyse.Analyse(strY);
for(int index = 1; n < 360; n++)
{
TokenN.TokenValue = index;
TokenX.Execute();
TokenY.Execute();
PointCollection.Add(new Point(TokenX.TokenValue, TokenY.TokenValue));
}
myGraphics.DrawLines(myPen, PointCollection);
为了同时支持多个波形图,这里用一个类来记录一个函数对,以及线条颜色、线条宽度等信息。该类的代码如下:
在界面上添加相关控件,用来操作绘图信息。点击绘图按钮之后,按照界面上的PictureBox的尺寸创建一个Bitmap对象,然后把它作为参数调用绘图代码,代码如下:
}
}
这里的代码还有不少可以改进的地方,比如可以设置图片尺寸、图片背景、坐标原点、背景网格,甚至可以让波形一段一段慢慢的显示出来,更好的了解波形的绘制过程。如果有需要可以自行完善。
本文开头给出的示例的各个设置如下表:
|
名称 |
表达式X |
表达式Y |
|
相位1 |
n |
a=100*sin(n) |
|
相位2 |
n |
b=100*sin(n+120) |
|
相位3 |
n |
c=100*sin(n-120) |
|
三相整流波形 |
n |
abs(a)+abs(b)+abs(c) |
|
李沙育图 |
100*sin(n*2) |
100*cos(n*3+90)-200 |
|
阿基米德螺旋线 |
n*sin(abs(n))/20-240 |
n*cos(abs(n))/20-180 |
相位1、相位2、相位3是模拟三相电的波形,都是标准正弦波,只是相位差120度。这里用一个赋值操作声明了三个变量a, b, c,这样在三相整流波形中就可以直接操作这三个变量了,所以三相整流波形的表达式Y的值是abs(a)+abs(b)+abs(c)。通过声明变量的方法可以很容易让波形之间关联起来,也可以减少计算量。
有时候胡乱输入一些函数,会有一些很好玩的波形出来,下面给一些例子。
折线图
柱状图
横道图
饼图
下图是统计图中需要绘制的区域注释,实际绘图时根据数据分析,然后计算出相关的坐标就可以进行绘图了。
本文就到此结束了,下面是源代码下载,做的还不是很完善,有需要的朋友可以自行修改一下。
源代码下载:https://files.cnblogs.com/conexpress/ConExpress_MyCalculator_Wave.rar