Search code examples
c#zedgraph

Zedgraph plotting real time graph with fast input rate


I am plotting real time graph using RollingPointPairList of Zedgraph. I am receiving 100 point per second and trying to plot 100 point per second. But zedgraph shows lag and is able to plot only maximum 20 point per second. I am using following code for plotting

    RollingPointPairList samp = new RollingPointPairList(105);

    private void Form1_Load(object sender, EventArgs e)
    {
        CreateGraph(zg1);
        SetSize();
    }
    int x1 = 0;
    private void TimerEventProcessor(int[] diff)
    {
        zg1.GraphPane.XAxis.Scale.MaxAuto = true;
        x1 += 1;
        samp.Add(diff[0], x1);
        if (samp.Count >= 100)
        {
            zg1.AxisChange();
            zg1.Invalidate();
            zg1.Refresh();
            Thread.Sleep(50);
        }
    }

    private void Form1_Resize(object sender, EventArgs e)
    {
        SetSize();
    }

    private void SetSize()
    {
        zg1.Location = new Point(10, 10);
        zg1.Size = new Size(this.ClientRectangle.Width - 10, this.ClientRectangle.Height - 10);
    }

    private void CreateGraph(ZedGraphControl zgc)
    {

        GraphPane myPane = zgc.GraphPane;
        myPane.Title.Text = "Test";
        myPane.XAxis.Title.Text = "X Value";
        myPane.YAxis.Title.Text = "Y Axis";

        LineItem myCurve;
        myCurve= myPane.AddCurve("Curve 1", samp, Color.Blue, SymbolType.Star);

        myCurve.Symbol.Fill = new Fill(Color.White);
        myPane.Chart.Fill = new Fill(Color.White, Color.LightGoldenrodYellow, 45F);
        myPane.Fill = new Fill(Color.White, Color.FromArgb(220, 220, 255), 45F);

        zgc.AxisChange();
        zgc.Refresh();
    }

Thus is their any way of improving my code so that I can plot 100 points or more per second.


Solution

  • Drawing 100 dots per seconds is far from ZedGraph limits. I suspect the bottleneck is not ZedGraph. To be able to reproduce your use case the best I can, I quickly did this piece of code that throws the maximum dots per seconds it can through a ZedGraph control I initialized the same way as you:

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using ZedGraph;
    
    namespace WindowsFormsApplication1 {
    
        static class Program {
            /// <summary>
            /// Point d'entrée principal de l'application.
            /// </summary>
            [STAThread]
            static void Main() {
                Application.Run(new Form1());
            }
    
            class Form1 : Form {
                ZedGraphControl zgc;
                System.Windows.Forms.Label lbl;
    
                public Form1() {
                    this.WindowState = FormWindowState.Maximized;
    
    
                    lbl = new System.Windows.Forms.Label { Parent = this, Dock = DockStyle.Bottom, AutoSize = false, TextAlign = ContentAlignment.MiddleCenter };
                    zgc = new ZedGraphControl {
                        Parent = this,
                        Dock = DockStyle.Fill,
                        Margin = new Padding(10)
                    };
    
                    var myPane = zgc.GraphPane;
                    myPane.Title.Text = "Test";
                    myPane.XAxis.Title.Text = "X Value";
                    myPane.YAxis.Title.Text = "Y Axis";
                    myPane.XAxis.Scale.MaxAuto = true;
    
                    var myCurve = myPane.AddCurve("Curve 1", samp, Color.Blue, SymbolType.Star);
                    myCurve.Symbol.Fill = new Fill(Color.White);
                    myPane.Chart.Fill = new Fill(Color.White, Color.LightGoldenrodYellow, 45F);
                    myPane.Fill = new Fill(Color.White, Color.FromArgb(220, 220, 255), 45F);
                }
    
                RollingPointPairList samp = new RollingPointPairList(105);
                int c;
    
                CancellationTokenSource cts = new CancellationTokenSource();
    
                protected override void OnLoad(EventArgs e) {
                    base.OnLoad(e);
    
                    var t = new System.Timers.Timer { Interval = 1000 };
                    t.Elapsed += (sender, eventargs) => { this.BeginInvoke(new Action(() => { lbl.Text = "Dot per seconds: " + c.ToString(); c = 0; })); };
                    t.Start();
    
                    Task.Run(() => {
                        var r = new Random();
                        while(!cts.IsCancellationRequested) {
                            TimerEventProcessor(r.Next(-10, 10));
                        };
                    });
    
                }
    
                protected override void OnClosing(CancelEventArgs e) {
                    cts.Cancel();
                    cts.Token.WaitHandle.WaitOne();
                    base.OnClosing(e);
                }
    
                int x1 = 0;
    
                void TimerEventProcessor(int d) {
                    x1++; c++;
                    samp.Add(d, x1);
                    zgc.AxisChange();
                    zgc.Invalidate();
                }
            }
        }
    }
    

    If I wrongly assumed how your code is supposed to run, please let me know. However, it runs fine on my machine.

    IMHO, the advices I use to give when someone uses ZedGraph in my team are:

    • Do not invoke the Invalidate method more than necessary. (Your code is using Invalidate, then Refresh. As Refresh performs an invalidation, your code do it twice)
    • AxisChange method is only useful if Auto scaling is used (Your code is ok with this)
    • Do not perform CPU intensive processing on the UI thread.

    I suspect this last point to be the reason of your bottleneck: receiving the packet could be better achieved on a separate thread that invokes Invalidate on the ZedGraph control using the windows Forms SynchronizationContext.