Click here to Skip to main content
Licence CPOL
First Posted 25 Jan 2009
Views 153,303
Downloads 13,429
Bookmarked 342 times

A simple C# library for graph plotting

By | 8 Jul 2009 | Article
An easy to use C# library for quick and simple graph plotting.

Introduction

In our application, we had to display the output of a multichannel ECG (Electro Cardiograph) device. I had a look at some commercial libraries, but none of them met by demands. So, I decided to design a simple solution by myself.

This is my attempt to design a flexible, easy to use library for drawing graphs.

The library is capable of displaying multiple graphs in different layouts. Right now, five modes of display are possible:

  • Normal: means that all data sources are displayed in one graph window, with separate ordinates.
  • Stacked: means that all data sources are displayed in one graph window, stacked vertically, with shared ordinate and shared abscissa.
  • Vertical aligned: means that the graph windows are displayed vertically aligned, with separate ordinates and shared abscissa.
  • Tiled horizontal: means that the data sources are displayed in tiled windows (preferred alignment direction is horizontal).
  • Tiled vertically: means that the data sources are displayed in tiled windows (preferred alignment direction is vertical).

Graphs can be displayed unscaled or auto-scaled. In the auto-scale mode, the visible graph is automatically fit to the visible area.

The following images show examples for the different display modes:

Normal:

graph_normal.png

Stacked:

graph_stacked.png

Tiled horizontal:

graph_tiled_horizontal.png

Tiled vertical:

graph_tiled_vertically.png

Vertical aligned:

graph_vertically_aligned.png

Autoscaled X-Axis

graph_fixed_x_range.png

The following images show a sample of an ECG application, where eight data sources are displayed vertically tiled and auto-scaled.

sample_2.jpg

Using the Code

The control is very simple to use. Just have a look at the sample application. The following code shows how the part in the demo application where the graphs for the different examples are generated:

        protected void CalcDataGraphs()
        {

            this.SuspendLayout();
           
            display.DataSources.Clear();
            display.SetDisplayRangeX(0, 400);

            for (int j = 0; j < NumGraphs; j++)
            {
                display.DataSources.Add(new DataSource());
                display.DataSources[j].Name = "Graph " + (j + 1);                
                display.DataSources[j].OnRenderXAxisLabel += RenderXLabel;
              
                switch (CurExample)
                {
                    case  "NORMAL":
                        this.Text = "Normal Graph";
                        display.DataSources[j].Length = 5800;
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.NORMAL;
                        display.DataSources[j].AutoScaleY = false;
                        display.DataSources[j].SetDisplayRangeY(-300, 300);
                        display.DataSources[j].SetGridDistanceY(100);
                        display.DataSources[j].OnRenderYAxisLabel = RenderYLabel;
                        CalcSinusFunction_0(display.DataSources[j], j);      
                        break;

                    case "NORMAL_AUTO":
                        this.Text = "Normal Graph Autoscaled";
                        display.DataSources[j].Length = 5800;
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.NORMAL;
                        display.DataSources[j].AutoScaleY = true;
                        display.DataSources[j].SetDisplayRangeY(-300, 300);
                        display.DataSources[j].SetGridDistanceY(100);
                        display.DataSources[j].OnRenderYAxisLabel = RenderYLabel;
                        CalcSinusFunction_0(display.DataSources[j], j);
                        break;

                    case "STACKED":
                        this.Text = "Stacked Graph";
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.STACKED;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = false;
                        display.DataSources[j].SetDisplayRangeY(-250, 250);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_1(display.DataSources[j], j);
                        break;

                    case "VERTICAL_ALIGNED":
                        this.Text = "Vertical aligned Graph";
                        display.PanelLayout = 
                            PlotterGraphPaneEx.LayoutMode.VERTICAL_ARRANGED;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = false;
                        display.DataSources[j].SetDisplayRangeY(-300, 300);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_2(display.DataSources[j], j);    
                        break;

                    case "VERTICAL_ALIGNED_AUTO":
                        this.Text = "Vertical aligned Graph autoscaled";
                        display.PanelLayout = 
                            PlotterGraphPaneEx.LayoutMode.VERTICAL_ARRANGED;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = true;
                        display.DataSources[j].SetDisplayRangeY(-300, 300);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_2(display.DataSources[j], j);
                        break;

                    case "TILED_VERTICAL":
                        this.Text = "Tiled Graphs (vertical prefered)";
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_VER;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = false;
                        display.DataSources[j].SetDisplayRangeY(-300, 600);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_2(display.DataSources[j], j);    
                        break;

                    case "TILED_VERTICAL_AUTO":
                        this.Text = "Tiled Graphs (vertical prefered) autoscaled";
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_VER;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = true;
                        display.DataSources[j].SetDisplayRangeY(-300, 600);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_2(display.DataSources[j], j);
                        break;

                    case "TILED_HORIZONTAL":
                        this.Text = "Tiled Graphs (horizontal prefered)";
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_HOR;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = false;
                        display.DataSources[j].SetDisplayRangeY(-300, 600);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_2(display.DataSources[j], j);    
                        break;

                    case "TILED_HORIZONTAL_AUTO":
                        this.Text = "Tiled Graphs (horizontal prefered) autoscaled";
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_HOR;
                        display.DataSources[j].Length = 5800;
                        display.DataSources[j].AutoScaleY = true;
                        display.DataSources[j].SetDisplayRangeY(-300, 600);
                        display.DataSources[j].SetGridDistanceY(100);
                        CalcSinusFunction_2(display.DataSources[j], j);
                        break;

                    case "ANIMATED_AUTO":
                       
                        this.Text = "Animated graphs fixed x range";
                        display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_HOR;
                        display.DataSources[j].Length = 402;
                        display.DataSources[j].AutoScaleY = false;
                        display.DataSources[j].AutoScaleX = true;
                        display.DataSources[j].SetDisplayRangeY(-300, 500);
                        display.DataSources[j].SetGridDistanceY(100);
                        display.DataSources[j].XAutoScaleOffset = 50;
                        CalcSinusFunction_3(display.DataSources[j], j, 0);
                        display.DataSources[j].OnRenderYAxisLabel = RenderYLabel;
                        break;
                }             
            }
            
            ApplyColorSchema();

            this.ResumeLayout();
            display.Refresh();
           
        }

The functions CalcSinusFunction_0 CalcSinusFunction_1 and CalcSinusFunction_2 are used to calculate the different sinus functions for the graphs:

protected void CalcSinusFunction_0(DataSource src, int idx)
{
    for (int i = 0; i  < src.Length; i++)
    {
        src.Samples[i].x = i;
        src.Samples[i].y = (float)(((float)200 * Math.Sin((idx + 1) *(
            i + 1.0) * 48 / src.Length)));
    }    
}

The functions RenderYLabel and RenderXLabel are used to render the X and Y legends of the graph.

private String RenderXLabel(DataSource s, int idx)
{
    if (s.AutoScaleX)
    {         
        int Value = (int)(s.Samples[idx].x );
        return "" + Value;     
    }
    else
    {
        int Value = (int)(s.Samples[idx].x / 200);
        String Label = "" + Value + "\"";
        return Label;
    }    
}

private String RenderYLabel(DataSource s, float value)
{ 
    return String.Format("{0:0.0}", value);
}

Summary

There are lots of parameters that can be twisted which are not explained here - just look at the code. This library is far from being finished, but it is a good point to start from. The code is simple and self-explaining. From here, it will be simple to adopt the code for your needs.

I got so much inputs from here, and I wanted to give something back. So, here is my first article on The Code Project. I hope you like it.

To Do

  • code clean ups.

Version History

  • 12.07.2009 - Updated article.
  • 12.02.2009 - Implemented x autoscaling.
  • 28.01.2009 - Some more cleanups. Implemented Print Form.
  • 27.01.2009 - New display mode, some cleanups.
  • 25.01.2009 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Zimmermann Stephan

Software Developer (Senior)

Austria Austria

Member

I have started programming at the age of 13 on the commodore 64.
 
Ever since then I have been programming on many systems in many languages.
 
During the last 12 years I have been working as professional programmer in different companies and different areas.
 
Now I am working as freelancer programmer / consultant

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionAsking for a some kind of favor..??? PinmemberMember 87494277:06 25 Apr '12  
QuestionECG graph plotting PinmemberMember 77724736:33 23 Apr '12  
QuestionEasy way to remove control panel? Pinmembercclaunch5:12 13 Apr '12  
QuestionThanks, it worked for me with modifications PinmemberBinary622:01 20 Mar '12  
GeneralThank You!! This library is awesome!!! Pinmembermasterdima13:51 11 Mar '12  
Questionhi Pinmemberaimzol20:53 25 Jan '12  
Questionhelp!! PinmemberMember 852456518:35 21 Jan '12  
QuestionHow Can I add zoom in / out capability to this library Pinmemberrah.you4:17 10 Jan '12  
GeneralMy vote of 3 PinmemberStein Gregory2:28 31 Dec '11  
GeneralRe: My vote of 3 [modified] PinmemberStein Gregory18:27 31 Dec '11  
QuestionNice library, but has several bugs PinmemberMediaThrall25:32 21 Dec '11  
AnswerRe: Nice library, but has several bugs PinmemberMediaThrall28:25 21 Dec '11  
GeneralMy vote of 4 Pinmemberrobindegen22:17 22 Nov '11  
GeneralMy vote of 5 Pinmembersupermachine9:53 2 Nov '11  
QuestionThank you for such a great lib PinmemberSadahar8:06 28 Sep '11  
QuestionThanks PinmemberBigPilot967:00 17 Sep '11  
GeneralMy vote of 5 Pinmemberuday14893:31 11 Sep '11  
QuestionCannot implement with real data Pinmemberm_46600:21 27 Jul '11  
BugFirst and last Samples are not being plotted PinmemberMember 795576722:13 24 Jun '11  
GeneralA fisheye.... Pinmemberboblogan11:55 16 Jun '11  
GeneralMy vote of 5. PinmemberFilip D'haene1:52 23 May '11  
GeneralMaking a FIFO buffer to use is it as a scope Pinmembersdecorme3:53 8 Feb '11  
GeneralRe: Making a FIFO buffer to use is it as a scope Pinmemberboblogan12:35 16 Jun '11  
GeneralMy vote of 5 PinmemberKanasz Robert4:15 25 Jan '11  
GeneralAbout the labels for axis Y Pinmemberisalsfy0:00 8 Jan '11  

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

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 8 Jul 2009
Article Copyright 2009 by Zimmermann Stephan
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid