ScottPlot.NET
GitHub Repo stars

How to Plot Realtime Data

Use the DataLogger to show growing datasets

The DataLogger plot type is a like a scatter plot but it has features for recording and displaying growing datasets.

See the demo and source code on the ScottPlot Demo page

Use the DataStreamer to show the latest N values

The DataStreamer plot type is a like a signal plot but it has features for displaying the latest N values of a streaming dataset.

See the demo and source code on the ScottPlot Demo page

Change Values in Fixed-Length Arrays

After plotting an array you can change its values and re-render at any time. This is the most performant option for displaying changing fixed-length data. Although this same method can be used for most plot types, Signal plots are almost always the most performant option.

In this example a fixed-length readonly array is created and added to the plot, then a Timer calls a method to change the values inside that array and re-render the plot.

readonly double[] Values = new double[25];
readonly Stopwatch Stopwatch = Stopwatch.StartNew();

public Form1()
{
    InitializeComponent();
    
    // add the data to the plot only once
    formsPlot1.Plot.Add.Signal(Values);
}

private void timer1_Tick(object sender, EventArgs e)
{
    // change the values inside the array any time
    double phase = Stopwatch.Elapsed.TotalSeconds;
    double multiplier = 2 * Math.PI / Values.Length;
    for (int i = 0; i < Values.Length; i++)
        Values[i] = Math.Sin(i * multiplier + phase);

    // force a redraw after changing the data values
    formsPlot1.Render();
}

Signal Plot: Partial Array Rendering

You can create a large array and only display the first N values, increasing N as new data is added. This gives the illusion of a growing plot, even though its source is a fixed-length array. The range of visible values is controlled by the MinRenderIndex and MaxRenderIndex fields of Signal plots and Scatter plots.

readonly double[] Values = new double[100_000];
readonly ScottPlot.Plottable.Signal SignalPlot;
int NextPointIndex = 0;

public Form1()
{
    InitializeComponent();
    SignalPlot = formsPlot1.Plot.Add.Signal(Values);
    formsPlot1.Plot.SetAxisLimits(0, 100, -2, 2);
}

// This timer adds data frequently (1000 times / second)
private void timer1_Tick(object sender, EventArgs e)
{
    Values[NextPointIndex] = Math.Sin(NextPointIndex * .05);
    SignalPlot.MaxRenderIndex = NextPointIndex;
    NextPointIndex += 1;
}

// This timer renders infrequently (10 times per second)
private void timer2_Tick(object sender, EventArgs e)
{
    // expand axis limits to ensure new data is always visible
    double currentRightEdge = formsPlot1.Plot.GetAxisLimits().XMax;
    if (NextPointIndex > currentRightEdge)
        formsPlot1.Plot.SetAxisLimits(xMax: currentRightEdge + 100);

    formsPlot1.Refresh();
}

Note that this example will crash when the array is full. To prevent this, detect when NextPointIndex equals or exceeds the size of the array, and create a new larger array to hold the data. When you do this you will have to copy all the existing values from the old array to the new one.