WPF DynamicDataDisplay – 使用标记缓慢绘图

在使用标记时,我很难等待D3中的ChartPlotter显示自己。 当然,我正试图绘制Gazillion记录(好的,700,000条记录)。 当只使用一条线时,一切都很好(大约20秒)。 使用标记时,我们说的是5分钟。 这是不可接受的。

有任何想法吗?

这就是我所做的,并在其下进行解释。

public static string MakeSimplePlot(double[][] xData, double[][] yData, string[] legend, string xAxisTitle, string yAxisTitle, bool[] showLines, bool[] showMarkers) { ChartPlotter plotter = new ChartPlotter(); plotter.MainHorizontalAxis = new HorizontalAxis(); plotter.MainVerticalAxis = new VerticalAxis(); HorizontalAxisTitle horizontalAxisTitle = new HorizontalAxisTitle(); horizontalAxisTitle.Content = xAxisTitle; plotter.AddChild(horizontalAxisTitle); VerticalAxisTitle verticalAxisTitle = new VerticalAxisTitle(); verticalAxisTitle.Content = yAxisTitle; plotter.AddChild(verticalAxisTitle); Color[] plotColors = new Color[13] { Colors.Blue, Colors.Red, Colors.Green, Colors.Chartreuse, Colors.Yellow, Colors.Violet, Colors.Tan, Colors.Silver, Colors.Salmon, Colors.Lime, Colors.Brown, Colors.Chartreuse, Colors.DarkGray }; for (int seriesCounter = 0; seriesCounter < legend.Count(); seriesCounter++) { DataFile clearedInputs = ClearExcess(new DataFile(xData[seriesCounter], yData[seriesCounter])); xData[seriesCounter] = clearedInputs.time; yData[seriesCounter] = clearedInputs.data; var xDataSource = new EnumerableDataSource(xData[seriesCounter]); xDataSource.SetXMapping(x => x); var yDataSource = new EnumerableDataSource(yData[seriesCounter]); yDataSource.SetYMapping(x => x); CompositeDataSource plotSeries = new CompositeDataSource(xDataSource, yDataSource); CirclePointMarker circlePointMarker = new CirclePointMarker(); circlePointMarker.Fill = new SolidColorBrush(plotColors[seriesCounter]); circlePointMarker.Pen = new Pen(circlePointMarker.Fill, 0); circlePointMarker.Size = (showMarkers[seriesCounter] == false) ? 0 : 8; int lineWidth = (showLines[seriesCounter] == false) ? 0 : 2; if (showMarkers[seriesCounter] == false) { plotter.AddLineGraph(plotSeries, new Pen(circlePointMarker.Fill, lineWidth), new PenDescription("Dummy")); } else { plotter.AddLineGraph(plotSeries, new Pen(circlePointMarker.Fill, lineWidth), circlePointMarker, new PenDescription("Dummy")); } } UIParameters.plotWindow.mainGrid.Children.Clear(); UIParameters.plotWindow.mainGrid.RowDefinitions.Clear(); UIParameters.plotWindow.mainGrid.Children.Add(plotter); plotter.Viewport.FitToView(); plotter.LegendVisible = false; plotter.NewLegendVisible = false; if (legend.Count() > 1) { ShowLegend(legend, plotColors); } UIParameters.plotWindow.WindowState = WindowState.Minimized; UIParameters.plotWindow.WindowState = WindowState.Normal; string filename = Path.ChangeExtension(Path.GetTempFileName(), "png"); RenderTargetBitmap targetBitmap = new RenderTargetBitmap((int)UIParameters.plotWindow.mainGrid.ActualWidth, (int)UIParameters.plotWindow.mainGrid.ActualHeight, 96d, 96d, PixelFormats.Default); targetBitmap.Render(UIParameters.plotWindow.mainGrid); PngBitmapEncoder encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(targetBitmap)); using (var fileStream = File.Open(filename, FileMode.OpenOrCreate)) { encoder.Save(fileStream); UIParameters.plotWindow.mainGrid.Clip = null; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); targetBitmap.Freeze(); if (targetBitmap != null) targetBitmap.Clear(); targetBitmap = null; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); } return filename; } 

说明:

  1. 我隐藏了绘图仪的图例并使用ShowLegend自己创建,因为图例中没有显示它是否只有标记(我错了吗?)
  2. 我最小化并最大化绘图窗口,因为否则绘图不会更新,或者它会更新但不会保存到文件中。 这也适用于我移动窗口(我想某种重绘事件),但由于该过程是自动的,用户没有任何交互。 我尝试无效,无济于事。 想法?

谢谢!

我写了自己的类来隐藏屏幕上的标记。 这是一种虚拟化技术,当您在屏幕上没有大量标记时,可以将性能提高十倍。 它看起来像这样:

 using System; using System.Windows; using System.Windows.Media; using Microsoft.Research.DynamicDataDisplay.DataSources; using Microsoft.Research.DynamicDataDisplay.PointMarkers; using Microsoft.Research.DynamicDataDisplay.Common; namespace Microsoft.Research.DynamicDataDisplay.Charts { public class FilteredMarkerPointsGraph : MarkerPointsGraph { public FilteredMarkerPointsGraph() : base() { ; } public FilteredMarkerPointsGraph(IPointDataSource dataSource) : base(dataSource) { ; } protected override void OnRenderCore(DrawingContext dc, RenderState state) { // base.OnRenderCore if (DataSource == null) return; if (Marker == null) return; var left = Viewport.Visible.Location.X; var right = Viewport.Visible.Location.X + Viewport.Visible.Size.Width; var top = Viewport.Visible.Location.Y; var bottom = Viewport.Visible.Location.Y + Viewport.Visible.Size.Height; var transform = Plotter2D.Viewport.Transform; DataRect bounds = DataRect.Empty; using (IPointEnumerator enumerator = DataSource.GetEnumerator(GetContext())) { Point point = new Point(); while (enumerator.MoveNext()) { enumerator.GetCurrent(ref point); if (point.X >= left && point.X <= right && point.Y >= top && point.Y <= bottom) { enumerator.ApplyMappings(Marker); Point screenPoint = point.DataToScreen(transform); bounds = DataRect.Union(bounds, point); Marker.Render(dc, screenPoint); } } } Viewport2D.SetContentBounds(this, bounds); } } 

确保在XAML中调用FilteredMarkerPointsGraph而不是MarkerPointsGraph!

编辑

  1. 我不确定你需要带标记的图例,我实际上并没有在我的任何图表中使用过图例,但你的解决方案似乎很好。

  2. 实际上重绘绘图很容易。

我发现这样做的最好方法是在代码后面有一个表示DataSource的Property,并将图表的DataSource绑定到该属性。 让代码隐藏实现INotifyPropertyChanged并在每次更新或重新分配数据源时调用OnPropertyChanged。 这将强制绘图仪观察绑定并重绘图形。

例:

 EnumerableDataSource m_d3DataSource; public EnumerableDataSource D3DataSource { get { return m_d3DataSource; } set { //you can set your mapping inside the set block as well m_d3DataSource = value; OnPropertyChanged("D3DataSource"); } } protected void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, e); } } protected void OnPropertyChanged(string propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } 

关于你使用标记的性能..很难确切地指出导致性能问题的原因,但我的建议是尝试使用不同的数据源。 我一直在使用EnumerableDataSource,它总是像魅力一样。 尝试在单个对象中引入数据并在设置块中设置映射,如上所示:

 value.SetYMapping(k => Convert.ToDouble(k.yData)); value.SetXMapping(k => Convert.ToDouble(k.xData)); 

您唯一需要担心的是Enumerable数据源中的映射,D3应该为您处理剩下的事情。

当你显示“Gazillion”点时,用户无论如何都不可能看到标记:当缩放级别更合理时,你不能将模式从线切换到标记吗?