Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

How to make graph draw over old data when using a PagedChart Adjuster on axis

Solved!
Go to solution

Hello friends,

 

I have created a WPF graph and defined its horizontal axis to use a PagedChart Adjuster as follows:

 

<ni:AxisDouble x:Name="XAxisRange" Label="Time (ms)" Orientation="Horizontal" Adjuster="PagedChart" Range="{Binding XAxisRange, Mode=OneWay}"/>

When using the PagedChart adjuster, once the plot showing my data reaches the end of the graph (right most side), the horizontal view/range is shifted and my plot begins drawing data again from the beginning (left most side) of the graph. When the horizontal view/range is shifted, my graph is effectively cleared. Put differently, when the data begins drawing again from from the beginning of the graph (after reaching the end), there is no data visible on my graph. This situation is pictured below...

 

CurrentPagingBehavior.png

 

What I am trying to achieve is the PagedChart behavior but when the it begins drawing again from the beginning of the graph, I do not want my graph to be cleared, I want to draw over data from the previous page/view/range. To make what I am trying to achieve more clear, I have created the image below...

 

DesiredPagingBehavior.png

 

The plotted data to the right of the red line would be the data from the previous page/range/view that has not been drawn over yet. The plotted data to the left of the red line would be the data would be the data from the new page/range/view. You can see that the new data has a higher frequency than the old data in the image. I made the image this way help make it clear that I want to the new data to draw over the old data so to speak.

 

How can I accomplish this?

0 Kudos
Message 1 of 13
(3,169 Views)
Solution
Accepted by topic author b!tmaster

This can be accomplished in a variety of ways. I have attached a simple project demonstrating two approaches: using a phosphor effect to fade the plot over time, and using a custom renderer to draw the historic data in range (along with the vertical separator line).

~ Paul H
0 Kudos
Message 2 of 13
(3,134 Views)

Paul,

 

Thank your for providing a demonstration project. I have a question about finding the transition point from off-screen to on-screen data. Here is the relevant codeblock:

// Find the transition point from off-screen to on-screen data.
int firstVisibleIndex = 0;
while( firstVisibleIndex < xData.Size && xData[firstVisibleIndex] < 0.0 )
++firstVisibleIndex;
int visibleTransitionIndex = Math.Max( 0, firstVisibleIndex - 1 );

More specifically I have a question about the xData[firstVisibleIndex < 0.0 condition. At the beginning of the RenderGraphCore override function, xData gets set to renderArgs.RelativeData[0].

 

What do negative elements of xData indicate?

0 Kudos
Message 3 of 13
(3,129 Views)

Alright, I suspect that the negative values for xData indicate that the element is part of the on-screen data.

 

I put the WraparoundRenderer class in my application and changed my xaml to use it for my graph's plot. I start plotting on the graph with a start button and end plotting on the graph with a stop button.

 

I am experiencing an issue where the vertical red transition line & off-screen data (plotted in grey) do not appear on my graph until I select the stop button. They should show up after my graph fills up a page for the first time while data is being actively plotted but, that is not the case.

 

Any idea what might be causing such an issue?

0 Kudos
Message 4 of 13
(3,125 Views)

To answer your first question, “relative values” use the inclusive range [0.0,1.0] to represent visible values (they are produced by the data mapper associated with a scale). This means any value less than zero or greater than one will be outside the visible range. On a default (non-inverted) scale using the PagedChart range adjuster, all new values should stay below the range maximum (i.e. all relative values will be less than or equal to 1.0), so I took a shortcut and just checked for values less than zero. Other configurations can result in different relative values (for example, if you allow panning through the data), so feel free to adapt the logic in the example to fit the needs of your scenario.

 

For your second question, I am not sure what the issue is based on just your initial description. Are you also using a chart collection (like in the example project)? If not, how are you plotting on the graph? Do you send different data to the graph after the stop button is pressed vs. when the start button is pressed?

~ Paul H
0 Kudos
Message 5 of 13
(3,119 Views)

Thanks for the explanation.

 

To answer your first question, my graph is displaying 2 plots so In my XAML I have a binding declared for my graph's DataSource property thats binds to a ChartCollection<double>[] property on my view model which returns data from my model.

<ni:Graph x:Name="Graph" Margin="0" HorizontalAlignment="Stretch" Height="600" DataSource="{Binding ChartCollections, Mode=OneWay}" PlotAreaBackground="Beige" Interactions="">
public ChartCollection<double>[] ChartCollections
{
    get { return _classFromModel.ChartCollections; }
}

TL;DR: More or less, I am binding to an array of ChartCollection objects, not a single ChartCollection object itself.

 

For you last question, I don't believe I am sending different data to the graph after the stop button is pressed vs. when the start button is pressed. Before the start button gets pressed, the ChartCollection instances in my ChartCollection<double>[] array have no data in them so nothing is displayed on the graph. Once the start button is pressed, the mentioned ChartCollection instances will begin to be populated with data. They stop getting values when the stop button is pressed. Therefore, the view model does not trigger anymore notifications to the view and the graph stops until the start button is pressed again. Does that answer it?

 

Edit 1

To be clear, I am hitting the stop button once my graph has begun plotting data on it's second page. And then I see the vertical red line & old data (gray plot) shown on the graph as expected.

 

If I put a breakpoint in the WraparoundRenderer class on the line that increments the firstVisibleIndex, I will not hit the breakpoint until after I have pressed the startbutton. If I do this same thing in your provided project, the breakpoint is hit once plotting begins on the second page.

 

Edit 2

<ni:Graph.Plots>
            <ni:Plot Name="channelPlot">
                <local:WraparoundRenderer Stroke="Green"/>
            </ni:Plot>
            <ni:Plot Name="triggerPlot">
                <ni:LinePlotRenderer Stroke="Purple" StrokeThickness="2"/>
            </ni:Plot>
</ni:Graph.Plots>

How my plots are defined in XAML.

 

0 Kudos
Message 6 of 13
(3,113 Views)

Thanks for the update!

 

Based on your description, my main suspicion is on the data source binding. Will "_classFromModel.ChartCollections" return an array with non-null chart collections when the binding first reads the property? Or do you have a PropertyChanged event that will fire when the value on "_classFromModel" changes, so that the WPF binding knows to update?

 

[Edit: looks like you answered this in your second edit] Another possibility is the plot renderer initialization on the plots. Are you initializing the plots with the WraparoundRenderer in XAML with your graph, or are you doing that later in code?

 

[Edit: this would still be interesting to know] Also, when you hit the start button, do you see any data on the graph? Or is it completely blank until you press the stop button?

 


I changed the example code to better match your data, but could not reproduce the issue (the example still uses a direct assignment, instead of a binding). Here are the relevant changes:

// MainWindow.xaml.cs
private readonly ChartCollection<double>[] _data;

public MainWindow( ) {
    InitializeComponent( );

    int capacity = 1 + 2 * (int)(_xAxis.Range.Maximum - _xAxis.Range.Minimum);
    _data = Enumerable.Repeat( capacity, 2 ).Select( c => new ChartCollection<double>( c ) ).ToArray( );
    _data[0].Append( pattern );
    _data[1].Append( pattern.Select( v => v + 1 ).ToArray( ) );
    _graph.DataSource = _data;

    _timer = new DispatcherTimer( TimeSpan.FromSeconds( 0.5 ), DispatcherPriority.Normal, OnTimerTick, Dispatcher );
}

private void OnTimerTick( object sender, EventArgs e ) {
    int valueIndex = (int)_data[0].AppendCount % pattern.Length;
    double value = pattern[valueIndex] + (_xAxis.Range.Maximum / 100.0) % 1.0;
    for( int i = 0; i < _data.Length; ++i )
        _data[i].Append( value + i );
}

(I also added one more plot using a WraparoundRenderer in the XAML.)

~ Paul H
0 Kudos
Message 7 of 13
(3,105 Views)

I fire a PropertyChanged event when the value of _classFromModel.ChartCollections is changed so the view does know to update.

 

I see data in my graph starting immediately after I press the start button. New data will continue to get displayed until I press the stop button. After I press the stop button is when the "off-screen data" & red vertical transition line get displayed.

0 Kudos
Message 8 of 13
(3,101 Views)

From what you describe, it sounds like everything is setup correctly. If you could post a small application reproducing the issue (either an updated version of the example, or a simplified version of your application), that would be very helpful for finding the cause of the problem.

~ Paul H
0 Kudos
Message 9 of 13
(3,098 Views)

I will work on reproducing the issue in a sample application and report back.

0 Kudos
Message 10 of 13
(3,095 Views)