PDA

View Full Version : Lots of data in an IMSL C# chart



ed
09-18-2008, 08:24 AM
Some customers have a requirement to plot a very large number of points in a chart. Specifically for the IMSL C# .NET product, this can be challenge sometimes. For example, if you try to draw a simple scatter plot with 50,000 data points using the PanelChart class the .NET rendering engine takes a long time to draw the chart. In fact, even though the axis might appear quickly, the application appears to hang while the data are drawn. And while you're waiting, it's likely that some other system event will cause the window to be flagged 'dirty' and then the process starts again. Even on a relatively fast desktop it's quite a challenge to get the full image to show. Even if you're patient enough, as soon as you do anything else with the application, it's going to have to redraw again. I've tried many settings available in a Windows Form application, but haven't been able to get things much better.

However, there is a workaround. You can use a PictureBox object instead of a PanelChart in the Form, render the picture in memory, and then simply grab the result as an Image. Here are some code snippets to show how this is done.

First, let's create a pretty large array:


int n = 50000;
double[] x = new double[n];
Imsl.Stat.Random r = new Imsl.Stat.Random(123457);
for (int i = 0; i < n; i++)
{
x[i] = r.NextNormal();
}


I'm using 50,000 points here, but it scales up pretty well to higher values with linear time.

Next we'll create a chart object in the standard way:


Imsl.Chart2D.Chart chart = new Imsl.Chart2D.Chart();
Imsl.Chart2D.AxisXY axis = new Imsl.Chart2D.AxisXY(chart);

Imsl.Chart2D.Data data1 = new Imsl.Chart2D.Data(axis, x);
data1.DataType = Imsl.Chart2D.Data.DATA_TYPE_MARKER;


Note that we're just creating a new Chart object and not using a PanelChart.

The next steps are key: set the proper size (obtained from the PictureBox instance, pictureBox1), create a new Bitmap object, and then use the Chart.PaintChart() method to draw into the Bitmap. Finally, we set the PictureBox's Image property to the rendered image and call Refresh():


chart.ScreenSize = pictureBox1.Size;
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(pictureBox1.Size.Width, pictureBox1.Size.Height, (System.Drawing.Imaging.PixelFormat)System.Drawing .Imaging.PixelFormat.Format32bppArgb);
chart.PaintChart(System.Drawing.Graphics.FromImage (bmp));
pictureBox1.Image = bmp;
Refresh();


The best part is that the final product is just an image object, so the GUI can be moved, resized, or anything else and the redraw is instantaneous. While some traditional chart interactions (rubberband zooming or selecting points) would be harder to program now (need to map coordinates from the Bitmap instead of the Chart/Axis), they would still be possible.

As for the linear time scaling, on my laptop it takes about 1 second for 50,000 points with the + data marker. For 500,000 points, it took almost 10 seconds. A million points took 20 seconds (very linear).

Attached is a snapshot from a test GUI that implements the code above. The only difference is that 'n' is pulled from the TextBox and I've added some timing code using calls to the GetTime method below.


private static long GetTime()
{
return (System.DateTime.Now.Ticks - 621355968000000000) / 10000;
}