画函数图形的C#程序
来源:http://www.cnblogs.com/skyivben/archive/2005/11/01/266649.html
我在10月份发表过一篇随笔“画函数图形的C#程序,兼论一个病态函数”,在那篇随笔中写道:
后来,根据“空间/IV”的评论,我写了个动态生成用户输入的函数表达式的类,用以改进这个画函数图形的C#程序。下面是该程序的运行效果:  
 
可以看到,不但要画的函数的表达式可以由用户动态地输入,而且函数自变量的范围也可以是常量表达式。 下面就是源程序: 
 // plot.cs: 画函数图形, 编译方法: csc /t:winexe plot.cs Expression.cs
// plot.cs: 画函数图形, 编译方法: csc /t:winexe plot.cs Expression.cs  using System;
using System;  using System.Drawing;
using System.Drawing;  using System.Windows.Forms;
using System.Windows.Forms;  using Skyiv.Util;
using Skyiv.Util;  
  namespace Skyiv.Ben.Plot
namespace Skyiv.Ben.Plot  {
{  sealed class PlotForm : Form
  sealed class PlotForm : Form  {
  {  const int yBase = 24; // 屏幕保留区域的高度
    const int yBase = 24; // 屏幕保留区域的高度  
  TextBox tbxX0, tbxX1;  // 函数自变量的取值范围
    TextBox tbxX0, tbxX1;  // 函数自变量的取值范围  TextBox tbxExpression; // 函数的表达式
    TextBox tbxExpression; // 函数的表达式  
      PlotForm()
    PlotForm()  {
    {  SuspendLayout();
      SuspendLayout();  
        Button btnSubmit = new Button();
      Button btnSubmit = new Button();  btnSubmit.Text = "刷新";
      btnSubmit.Text = "刷新";  btnSubmit.Location = new Point(0, 0);
      btnSubmit.Location = new Point(0, 0);  btnSubmit.Size = new Size(48, 24);
      btnSubmit.Size = new Size(48, 24);  btnSubmit.Click += new EventHandler(BtnSubmit_Click);
      btnSubmit.Click += new EventHandler(BtnSubmit_Click);  
  tbxX0 = new TextBox();
      tbxX0 = new TextBox();  tbxX0.Text = "-Math.PI";
      tbxX0.Text = "-Math.PI";  tbxX0.Location = new Point(55, 3);
      tbxX0.Location = new Point(55, 3);  tbxX0.Size = new Size(100, 20);
      tbxX0.Size = new Size(100, 20);  
  tbxX1 = new TextBox();
      tbxX1 = new TextBox();  tbxX1.Text = "Math.PI";
      tbxX1.Text = "Math.PI";  tbxX1.Location = new Point(160, 3);
      tbxX1.Location = new Point(160, 3);  tbxX1.Size = new Size(100, 20);
      tbxX1.Size = new Size(100, 20);  
  tbxExpression = new TextBox();
      tbxExpression = new TextBox();  tbxExpression.Text = "Math.Sin(x)";
      tbxExpression.Text = "Math.Sin(x)";  tbxExpression.Location = new Point(265, 3);
      tbxExpression.Location = new Point(265, 3);  tbxExpression.Size = new Size(335, 20);
      tbxExpression.Size = new Size(335, 20);  tbxExpression.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);
      tbxExpression.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);  
  Controls.AddRange(new Control[]{btnSubmit, tbxX0, tbxX1, tbxExpression});
      Controls.AddRange(new Control[]{btnSubmit, tbxX0, tbxX1, tbxExpression});  Text = "Plot";
      Text = "Plot";  BackColor = Color.White;
      BackColor = Color.White;  ClientSize = new Size(600, 600 + yBase);
      ClientSize = new Size(600, 600 + yBase);  // WindowState = FormWindowState.Maximized;
      // WindowState = FormWindowState.Maximized;  
  ResumeLayout(false);
      ResumeLayout(false);  }
    }  
  // 点击“刷新”按钮时重绘程序主窗口
    // 点击“刷新”按钮时重绘程序主窗口  void BtnSubmit_Click(object sender, EventArgs e)
    void BtnSubmit_Click(object sender, EventArgs e)  {
    {  Invalidate();
      Invalidate();  }
    }  
      /*
    /*  // 因为本程序使用 C# 的反射功能动态生成数学表达式并计算其值
    // 因为本程序使用 C# 的反射功能动态生成数学表达式并计算其值  // 所以重画时有点慢,如果你的计算机的速度不是非常快的,
    // 所以重画时有点慢,如果你的计算机的速度不是非常快的,  // 就不要在窗口改变大小时强制重绘,而是通过点击发“刷新”按钮重绘。
    // 就不要在窗口改变大小时强制重绘,而是通过点击发“刷新”按钮重绘。  protected override void OnSizeChanged(EventArgs e)
    protected override void OnSizeChanged(EventArgs e)  {
    {  Invalidate();
      Invalidate();  base.OnSizeChanged(e);
      base.OnSizeChanged(e);  }
    }  */
    */  
      protected override void OnPaint(PaintEventArgs e)
    protected override void OnPaint(PaintEventArgs e)  {
    {  Graphics gc = e.Graphics;
      Graphics gc = e.Graphics;  try
      try  {
      {  double x0 = new Expression(tbxX0.Text).Compute(0);
        double x0 = new Expression(tbxX0.Text).Compute(0);  double x1 = new Expression(tbxX1.Text).Compute(0);
        double x1 = new Expression(tbxX1.Text).Compute(0);  Size size = ClientSize;
        Size size = ClientSize;  int i0 = 0;
        int i0 = 0;  int i1 = size.Width - 1;
        int i1 = size.Width - 1;  int j0 = yBase;
        int j0 = yBase;  int j1 = size.Height - 1;
        int j1 = size.Height - 1;  Pen pen = new Pen(Color.Black, 1);
        Pen pen = new Pen(Color.Black, 1);  gc.DrawLine(pen, i0, j0, i1, j0); // 画图区和保留区的分界线
        gc.DrawLine(pen, i0, j0, i1, j0); // 画图区和保留区的分界线  double rx = (x1 - x0) / (i1 - i0);
        double rx = (x1 - x0) / (i1 - i0);  double y0, y1;
        double y0, y1;  Expression fx = new Expression(tbxExpression.Text);
        Expression fx = new Expression(tbxExpression.Text);  GetFunctionValueRange(fx, x0, rx, i0, i1, out y0, out y1);
        GetFunctionValueRange(fx, x0, rx, i0, i1, out y0, out y1);  double ry = (y1 - y0) / (j1 - j0);
        double ry = (y1 - y0) / (j1 - j0);  Out(gc, 0, "ClientSize: {0}x{1}", i1 - i0 + 1, j1 - j0 + 1);
        Out(gc, 0, "ClientSize: {0}x{1}", i1 - i0 + 1, j1 - j0 + 1);  Out(gc, 1, "f(x): " + tbxExpression.Text);
        Out(gc, 1, "f(x): " + tbxExpression.Text);  Out(gc, 2, "x:[{0}, {1}] range:{2}", x0, x1, x1 - x0);
        Out(gc, 2, "x:[{0}, {1}] range:{2}", x0, x1, x1 - x0);  Out(gc, 3, "y:[{0}, {1}] range:{2}", y0, y1, y1 - y0);
        Out(gc, 3, "y:[{0}, {1}] range:{2}", y0, y1, y1 - y0);  Out(gc, 4, "rx:{0}", 1 / rx);  // 函数自变量每单位值用多少个象素表示
        Out(gc, 4, "rx:{0}", 1 / rx);  // 函数自变量每单位值用多少个象素表示  Out(gc, 5, "ry:{0}", 1 / ry);  // 函数的值每单位值用多少个象素表示
        Out(gc, 5, "ry:{0}", 1 / ry);  // 函数的值每单位值用多少个象素表示  Out(gc, 6, "r :{0}", rx / ry); // 该值如果小于1表示图形纵向被压扁,反之则被拉伸
        Out(gc, 6, "r :{0}", rx / ry); // 该值如果小于1表示图形纵向被压扁,反之则被拉伸  pen.Color = Color.Green;
        pen.Color = Color.Green;  int j = j1 + (int)(y0 / ry);
        int j = j1 + (int)(y0 / ry);  if (j >= j0 && j <= j1) gc.DrawLine(pen, i0, j, i1, j); // x坐标轴
        if (j >= j0 && j <= j1) gc.DrawLine(pen, i0, j, i1, j); // x坐标轴  int i = i0 - (int)(x0 / rx);
        int i = i0 - (int)(x0 / rx);  if (i >= i0 && i <= i1) gc.DrawLine(pen, i, j0, i, j1); // y坐标轴
        if (i >= i0 && i <= i1) gc.DrawLine(pen, i, j0, i, j1); // y坐标轴  pen.Color = Color.Red;
        pen.Color = Color.Red;  for (i = i0; i <= i1; i++)
        for (i = i0; i <= i1; i++)  {
        {  double x = x0 + (i - i0) * rx;
          double x = x0 + (i - i0) * rx;  double y = fx.Compute(x);
          double y = fx.Compute(x);  if (double.IsInfinity(y) || double.IsNaN(y)) continue;
          if (double.IsInfinity(y) || double.IsNaN(y)) continue;  j = j1 - (int)((y - y0) / ry);
          j = j1 - (int)((y - y0) / ry);  if (j > j1 || j < j0) continue;
          if (j > j1 || j < j0) continue;  gc.DrawLine(pen, i, j, i + 1, j); // 画函数的图形
          gc.DrawLine(pen, i, j, i + 1, j); // 画函数的图形  }
        }  }
      }  catch (Exception ex)
      catch (Exception ex)  {
      {  Out(gc, 0, ex.Message);
        Out(gc, 0, ex.Message);  }
      }  base.OnPaint(e);
      base.OnPaint(e);  }
    }  
      // 函数值的取值范围
    // 函数值的取值范围  void GetFunctionValueRange(Expression fx, double x0, double rx, int i0, int i1, out double y0, out double y1)
    void GetFunctionValueRange(Expression fx, double x0, double rx, int i0, int i1, out double y0, out double y1)  {
    {  y0 = double.MaxValue;
      y0 = double.MaxValue;  y1 = double.MinValue;
      y1 = double.MinValue;  for (int i = i0; i <= i1; i++)
      for (int i = i0; i <= i1; i++)  {
      {  double x = x0 + (i - i0) * rx;
        double x = x0 + (i - i0) * rx;  double y = fx.Compute(x);
        double y = fx.Compute(x);  if (double.IsInfinity(y) || double.IsNaN(y)) continue;
        if (double.IsInfinity(y) || double.IsNaN(y)) continue;  if (y0 > y) y0 = y;
        if (y0 > y) y0 = y;  if (y1 < y) y1 = y;
        if (y1 < y) y1 = y;  }
      }  }
    }  
      // 在指定的位置写字符串
    // 在指定的位置写字符串  void Out(Graphics gc, int line, string fmt, params object [] args)
    void Out(Graphics gc, int line, string fmt, params object [] args)  {
    {  gc.DrawString(string.Format(fmt, args), new Font("Courier New", 10), Brushes.Blue, new PointF(5, yBase + 15 * line));
      gc.DrawString(string.Format(fmt, args), new Font("Courier New", 10), Brushes.Blue, new PointF(5, yBase + 15 * line));  }
    }  
  static void Main()
    static void Main()  {
    {  Application.Run(new PlotForm());
      Application.Run(new PlotForm());  }
    }  }
  }  }
} 
其中的“Expression.cs”程序请参看我的另一篇随笔:“动态地生成用户输入的函数表达式(C#)”。 
这里的表达式是使用C#语法。如需要使用 VisualBasic 语法,请参阅该随笔的评论。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号