Click here to Skip to main content
15,917,454 members
Articles / Programming Languages / C#
Article

FPlot, a .NET library for plotting functions and measurement data

Rate me:
Please Sign up or sign in to vote.
3.93/5 (11 votes)
9 Oct 20052 min read 60.4K   1.6K   56   5
FPlot is a program together with a library for plotting and fitting mathematical functions and measurement data.

Sample Image - FPlot_Mandelbrot.png

Introduction

FPlot is a program and a library for plotting and fitting mathematical functions and measurement data. What is the advantage of FPlot over similar plotting tools? The functions are entered as C# source code and compiled on the fly. You can also edit and compile a C# library your functions can refer to. Therefore your functions can be as complicated as you like and still be evaluated at the full speed of .NET. You can of course also refer to external .NET libraries from your functions. Unfortunately I don't know of any freeware numerical libraries for .NET. There are some good commercial packages though, for example, check out CenterSpace Software or Visual Numerics.

At the moment, the program uses some Numerical Recipes routines and therefore not the full source code can be published. If you download the source, the file Fit.cs in the FPlot directory has been captured, so FPlot won't compile. There is another VS.NET project in the source, FPlotDemo that shows how to build an application that uses the library FPlotLibrary. All source is Open Source, you can freely modify and include it in your own applications.

The library implements a System.Windows.Forms control that displays functions and data. Three dimensional plots and logarithmic plots are not supported. Here is an image of some data fitted with three gauss curves:

Sample image

In Visual Studio 2003, you can add this control to your toolbox, by right-clicking on the toolbox and clicking on the button Add/Remove items.... In the dialog box that follows, you click on Browse and then you choose the path to the FPlotLibrary.dll.

The program and the library come with complete although somewhat brief documentation. Here is the homepage of FPlot.

Here is an example that creates an application that shows the usage of the FPlot library:

C#
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using FPlotLibrary;

namespace FPlotDemo {

  public class MainForm : System.Windows.Forms.Form {

    private System.Windows.Forms.Button sin;
    private System.Windows.Forms.Button cos;
    private System.Windows.Forms.Button data;
    private FPlotLibrary.GraphControl graph;
    private System.Windows.Forms.Button quit;
    private System.Windows.Forms.Button gaussian;
    private System.Windows.Forms.Button mandelbrot;
    private System.Windows.Forms.ProgressBar progressBar;

    private System.ComponentModel.Container components = null;

    public MainForm() {
      InitializeComponent();
      graph.Bar = progressBar;
    }

    protected override void Dispose( bool disposing ) {
      if( disposing ) {
        if (components != null) {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

    #region Windows Form Designer generated code

    private void InitializeComponent() {
      this.graph = new FPlotLibrary.GraphControl();
      this.quit = new System.Windows.Forms.Button();
      this.sin = new System.Windows.Forms.Button();
      this.cos = new System.Windows.Forms.Button();
      this.data = new System.Windows.Forms.Button();
      this.gaussian = new System.Windows.Forms.Button();
      this.mandelbrot = new System.Windows.Forms.Button();
      this.progressBar = new
        System.Windows.Forms.ProgressBar();
      this.SuspendLayout();
      // 
      // graph
      // 
      this.graph.Anchor = ((System.Windows.Forms.AnchorStyles)
        ((((System.Windows.Forms.AnchorStyles.Top 
        | System.Windows.Forms.AnchorStyles.Bottom) 
        | System.Windows.Forms.AnchorStyles.Left) 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.graph.BackColor = System.Drawing.Color.White;
      this.graph.Border = true;
      this.graph.Cursor = System.Windows.Forms.Cursors.Cross;
      this.graph.Location = new System.Drawing.Point(0, 0);
      this.graph.Name = "graph";
      this.graph.Size = new System.Drawing.Size(312, 328);
      this.graph.TabIndex = 0;
      this.graph.x0 = -4;
      this.graph.x1 = 4;
      this.graph.y0 = -4;
      this.graph.y1 = 4;
      this.graph.z0 = 0;
      this.graph.z1 = 20;
      this.graph.FixYtoX = true;
      // 
      // quit
      // 
      this.quit.Anchor = ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Bottom 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.quit.Location = new System.Drawing.Point(328, 272);
      this.quit.Name = "quit";
      this.quit.TabIndex = 1;
      this.quit.Text = "Quit";
      this.quit.Click += new
        System.EventHandler(this.quitClick);
      // 
      // sin
      // 
      this.sin.Anchor = ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Top 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.sin.Location = new System.Drawing.Point(328, 8);
      this.sin.Name = "sin";
      this.sin.Size = new System.Drawing.Size(104, 23);
      this.sin.TabIndex = 3;
      this.sin.Text = "Add sin(x)...";
      this.sin.Click += new
        System.EventHandler(this.sinClick);
      // 
      // cos
      // 
      this.cos.Anchor = ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Top 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.cos.Location = new System.Drawing.Point(328, 40);
      this.cos.Name = "cos";
      this.cos.Size = new System.Drawing.Size(104, 24);
      this.cos.TabIndex = 4;
      this.cos.Text = "Add cos(x)...";
      this.cos.Click += new
        System.EventHandler(this.cosClick);
      // 
      // data
      // 
      this.data.Anchor = ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Top 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.data.Location = new System.Drawing.Point(328, 136);
      this.data.Name = "data";
      this.data.Size = new System.Drawing.Size(104, 23);
      this.data.TabIndex = 5;
      this.data.Text = "Load ASCII data...";
      this.data.Click += new
        System.EventHandler(this.asciiClick);
      // 
      // gaussian
      // 
      this.gaussian.Anchor = 
        ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Top
        | System.Windows.Forms.AnchorStyles.Right)));
      this.gaussian.Location = new
        System.Drawing.Point(328, 72);
      this.gaussian.Name = "gaussian";
      this.gaussian.Size = new System.Drawing.Size(104, 24);
      this.gaussian.TabIndex = 7;
      this.gaussian.Text = "Add gaussian...";
      this.gaussian.Click += new
        System.EventHandler(this.gaussClick);
      // 
      // mandelbrot
      // 
      this.mandelbrot.Anchor =
        ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Top 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.mandelbrot.Location = new
        System.Drawing.Point(328, 104);
      this.mandelbrot.Name = "mandelbrot";
      this.mandelbrot.Size = new System.Drawing.Size(104, 24);
      this.mandelbrot.TabIndex = 8;
      this.mandelbrot.Text = "Add Mandelbrot...";
      this.mandelbrot.Click += new
        System.EventHandler(this.mandelbrotClick);
      // 
      // progressBar
      // 
      this.progressBar.Anchor = 
        ((System.Windows.Forms.AnchorStyles)
        ((System.Windows.Forms.AnchorStyles.Bottom 
        | System.Windows.Forms.AnchorStyles.Right)));
      this.progressBar.Location = new
        System.Drawing.Point(328, 304);
      this.progressBar.Name = "progressBar";
      this.progressBar.Size = new
        System.Drawing.Size(104, 16);
      this.progressBar.TabIndex = 9;
      // 
      // MainForm
      // 
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(440, 325);
      this.Controls.Add(this.progressBar);
      this.Controls.Add(this.mandelbrot);
      this.Controls.Add(this.gaussian);
      this.Controls.Add(this.data);
      this.Controls.Add(this.cos);
      this.Controls.Add(this.sin);
      this.Controls.Add(this.quit);
      this.Controls.Add(this.graph);
      this.Name = "MainForm";
      this.Text = "FPlot Demo";
      this.ResumeLayout(false);

    }
    #endregion

    [MTAThread]
    // The MTAThread flag is needed, because the C#-compiler
    // requires it. Unfortunately the OpenFileDialogBox does
    // not work correctly if this flag is set.
    static void Main() {
      Application.Run(new MainForm());
    }

    private void quitClick(object sender, System.EventArgs e)
    {
      Application.Exit();
    }

    private void sinClick(object sender, System.EventArgs e) {
      Function1D sin = new Function1D();
      // The source represents the body of the following
      // function:
      // double[] p, dfdp;
      // double f(double x) {
      //   ...
      // }
      sin.source = "return sin(x);";
      sin.Compile(true);
      sin.Color = Color.Blue;
      sin.lineWidth = 2;
      graph.Model.Items.Add(sin);
      graph.Invalidate();
    }

    private void cosClick(object sender, System.EventArgs e) {
      Function1D cos = new Function1D();
      cos.source = "return cos(x);";
      cos.Compile(true);
      cos.Color = Color.Red;
      cos.lineWidth = 2;
      cos.lineStyle = DashStyle.Dash; 
      graph.Model.Items.Add(cos);
      graph.Invalidate();
    }

    private void gaussClick(object sender,
      System.EventArgs e)
    {
      Function1D gauss = new Function1D();
     //Here the source code refers to the array p, an array of
     //function parameters. When you compile the item, the
     //size of p is automatically set to the highest element
     //referred to in the source.
     gauss.source = "double arg = (x-p[0])/p[1];" +
        "return p[2]*exp(-arg*arg);";
      gauss.Compile(true);
      gauss.p[0] = 1;
      gauss.p[1] = 1;
      gauss.p[2] = 4;
      graph.Model.Items.Add(gauss);
      graph.Invalidate();
    }

    private void mandelbrotClick(object sender,
      System.EventArgs e)
    {
      Function2D m = new Function2D();
      // The source represents the body of the following
      // function:
      // double[] p, dfdp;
      // double f(double x, double y) {
      //   ...
      // }
      m.source = "double xn = 0, yn = 0, x2 = 0, y2 = 0;" +
        "for (int n = 0; n < 500; n++) {" +
        "  yn = 2*xn*yn + y;" +
        "  xn = x2 - y2 + x;" +
        "  x2 = xn*xn; y2 = yn*yn;" + 
        "  if (x2 + y2 > 4) return n;" +
        "} return 0;";
      m.Compile(true);
      graph.SetRange(graph.x0, graph.x1, graph.y0, graph.y1,
        0, 20);  
      graph.Model.Items.Add(m);
      graph.Invalidate();
    }

    private void asciiClick(object sender,
      System.EventArgs e)
    {
      DataItem data = new DataItem();
      //The loadsource represents the body of the following
      // function:
      //void Load(System.IO.FileStream stream) {
      //  ...
      //}
      //This function loads data into the arrays x, y, dx,
      // and dy. Note that the Length of the arrays is
      // adapted automatically.
      data.loadsource = 
        "using (StreamReader r = new StreamReader(stream)){" +
        "  int n = 0; string line; string[] tokens;" +
        "  char[] separator = \";,|\".ToCharArray();" +
        "  while ((line = r.ReadLine()) != null) {" +
        "    tokens = line.Split(separator);" +
        "    try {x[n] = double.Parse(tokens[0]);}" +
        "    catch {x[n] = 0;}" +
        "    try {y[n] = double.Parse(tokens[1]);}" +
        "    catch {y[n] = 0;}" +
        "    try {dx[n] = double.Parse(tokens[2]);}" +
        "    catch {dx[n] = 0;}" +
        "    try {dy[n] = double.Parse(tokens[3]);}" +
        "    catch {dy[n] = 0;}" +
        "    n++;" +
        "  }" +
        "}";
      data.Compile(true);
      if (!data.compiled) MessageBox.Show("Error in " +
        "sourcecode:\n" + data.errors[0]);
      try {
        data.LoadFromFile("data.csv");
      } catch (Exception ex) {
        MessageBox.Show("Could not open the file data.csv\n"
          + ex.Message);
      }
      graph.Model.Items.Add(data);
      Console.WriteLine("data Length: {0}", data.Length);
      Console.WriteLine("x     y     dx     dy");
      for (int n = 0; n < data.Length; n++) {
        Console.WriteLine("{0}, {1}, {2}, {3}", data.x[n],
          data.y[n], data.dx[n], data.dy[n]);
      }
      graph.Invalidate();
    }

  }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionvery good Pin
Southmountain3-Mar-18 14:45
Southmountain3-Mar-18 14:45 
GeneralAbout 2 try. Pin
SPanicker*27-Jan-07 20:56
SPanicker*27-Jan-07 20:56 
GeneralFree numerical library Pin
Blair01119-Oct-05 11:47
Blair01119-Oct-05 11:47 
GeneralIncomplete, article very short Pin
Herbert Sauro18-Sep-05 16:37
Herbert Sauro18-Sep-05 16:37 
The article in incomplete and very short, codeproject is known for its quality, try to update your article to reflect expectations. Your software looks interesting, it would be a shame to leave it as it is.
GeneralDownloads incomplete/broken Pin
mav.northwind12-Sep-05 20:21
mav.northwind12-Sep-05 20:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.