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






3.93/5 (11 votes)
Sep 12, 2005
2 min read

60974

1640
FPlot is a program together with a library for plotting and fitting mathematical functions and measurement data.
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:
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:
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();
}
}
}