02-13-2020 09:57 AM
Hi,
We are trying to visualize 3D data using PointPlotRenderer in a WPF graph . The data is stored in an ObservabaleCollection containing Point3D entries. Is there any way to use the last coordinate to set the color of each point? I guess you will need to create your own renderer? If this is possible do you have any examples of how this can be done?
Solved! Go to Solution.
02-13-2020 05:35 PM
I have attached an example of a custom plot renderer that uses the third dimension of data and a color scale to draw colored points in a WPF graph. Here is how you can add the renderer to your graph in XAML:
<ni:Graph>
<ni:Graph.Plots>
<ni:Plot>
<local:ColorPointRenderer>
<ni:ColorScaleDouble>
<ni:ColorScaleMarker Value="0.0" Color="White" />
<ni:ColorScaleMarker Value="50.0" Color="Blue" />
<ni:ColorScaleMarker Value="100.0" Color="Black" />
</ni:ColorScaleDouble>
</local:ColorPointRenderer>
</ni:Plot>
</ni:Graph.Plots>
</ni:Graph>
02-14-2020 08:31 AM
Thanks for a quick solution Paul, the code runs fine! However, I see that if I add 10k points the response time (zoom, window resize, etc.) gets very high compared to the PointPlotRenderer. Is this because the default renderer has a more efficient implementation?
02-14-2020 09:31 AM
I had a further look at the code, and I see that the RenderTarget provides a DrawShapes function and I guess this method is used in the default render? Since this method only allow for a single option and not a table, the only solution is to iterate trough each point using DrawShape and set individual options. I guess this is what makes the plot unresponsive when the number of points gets high?
02-14-2020 11:13 AM
Yes, making a lot of individual DrawShape
calls is much less efficient than a single DrawShapes
call. Depending on the granularity of your color scale and how IsInterpolated
is set, you might be able to group points by matching color, and make a smaller number of DrawShapes
calls with those groups, to improve performance.
02-14-2020 03:23 PM
Here is a quick example implementation of same-color grouping:
protected override void RenderGraphCore( PlotRenderArgs renderArgs ) {
var colorScale = this.ColorScale;
var target = renderArgs.RenderTarget;
var xValues = renderArgs.RelativeData[0];
var yValues = renderArgs.RelativeData[1];
var zValues = renderArgs.RelativeData[2];
var groups = new Dictionary<Color, (RenderTargetOptions, List<double>, List<double>)>( );
for( int i = 0; i < xValues.Size; ++i ) {
Color color = colorScale.RelativeValueToColor( zValues[i] );
if( groups.TryGetValue( color, out var group ) ) {
group.Item2.Add( xValues[i] );
group.Item3.Add( yValues[i] );
continue;
}
var options = new RenderTargetOptions( null,
RenderTargetOption.CreateValue( RenderTargetOptionsProperty.Fill, new SolidColorBrush( color ) ),
RenderTargetOption.CreateValue( RenderTargetOptionsProperty.Stroke, Brushes.Black ),
RenderTargetOption.CreateValue( RenderTargetOptionsProperty.StrokeThickness, 1.0 ) );
groups.Add( color, (options, new List<double> { xValues[i] }, new List<double> { yValues[i] }) );
}
Size shapeSize = new Size( 10, 10 );
foreach( var (options, xs, ys) in groups.Values ) {
using(var xBuffer = BufferPool.Default.GetBuffer(xs, RelativeValueUnit.Instance))
using(var yBuffer = BufferPool.Default.GetBuffer(ys, RelativeValueUnit.Instance))
target.DrawShapes( options, xBuffer, yBuffer, PointShape.Ellipse, shapeSize );
}
}
Testing on ten thousand random points, I saw a notable improvement. Hopefully this will work with your scenario!
(Note that it does not preserve ordering, so points will appear in whatever order they happen to be returned by the dictionary.)
02-14-2020 05:30 PM
Wow Paul, this really improved the code. Works perfect for my data! Thanks a lot for your quick and impressive help!