【问题标题】:WPF DynamicDataDisplay - Slow plotting with MarkersWPF DynamicDataDisplay - 使用标记缓慢绘图
【发布时间】:2012-11-28 22:21:47
【问题描述】:

在使用标记时,我很难等待 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<double>(xData[seriesCounter]);
            xDataSource.SetXMapping(x => x);

            var yDataSource = new EnumerableDataSource<double>(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. 我最小化和最大化绘图窗口,否则绘图不会更新,或者它更新但没有保存到文件中。如果我移动窗口(我猜是某种重绘事件),这也有效,但由于该过程是自动的,因此用户没有任何交互。我尝试无效,无济于事。想法?

谢谢!

【问题讨论】:

  • 另外,我刚刚注意到.. 您的颜色列表中有两次黄绿色!
  • 与您的问题无关,我的 D3 库版本没有 ChartPlotter.MainHorizo​​ntalAxis 属性(也没有 AddChild)。我从 codeplex 下载了库,我有 0.3 版。有更新的版本吗?谢谢

标签: c# wpf performance marker dynamic-data-display


【解决方案1】:

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

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. 实际上,重绘情节非常容易。

我发现做到这一点的最佳方法是在您的代码中使用一个属性来表示数据源并将图表的数据源绑定到该属性。每次更新或重新分配数据源时,让您的代码实现 INotifyPropertyChanged 并调用 OnPropertyChanged。这将强制绘图仪观察绑定并重新绘制图表。

例子:

EnumerableDataSource<Point> m_d3DataSource;
public EnumerableDataSource<Point> 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 应该为您处理其余的事情。

【讨论】:

  • 我刚刚看到您对 Felice 帖子的评论。如果您无法操作图表,不确定这是否是您正在寻找的答案。这只会虚拟化屏幕外的点。或许您可以多解释一下您的项目,然后可能会有更好的解决方案?
  • 杰森:谢谢(再次...)。我正在为电子流程编写一个自动报告生成器。我不想让用户打开一个新的 word 文档,从头开始编写,在 matlab 中制作自己的图表,然后分析结果,我想为用户提供一个包含所有图表的表单 - 只需添加解释即可。大多数数据是电子状态文件,包含数十万条记录。希望这能解释它。再次感谢。
  • 您是否同时制作多个图表绘图仪?我发现绘制标记非常快,只有操纵它们才是导致我速度变慢的原因。您如何将源应用于图表?我已经通过 EnumerableDataSource 上的 XAML 绑定完成了我的工作,并且映射工作就像一个魅力。也许您可以使用不同的数据源(例如,我知道 EnumerableDataSource 比 ObservableDataSource 快得多!),或者问题可能在于您在绘制数据之前如何实际解析数据!也许你可以准确地指出你减速的地方。
  • Jason,我编辑了我的问题,希望它能回答你的问题。任何建议都会有所帮助。谢谢
  • Jason - 我已经阅读了您所写的所有内容,这又一次很有帮助。 “奇异物体”是什么意思?以及如何“使用不同的数据源”?谢谢一百万。
【解决方案2】:

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

【讨论】:

  • 嗨菲丽丝。用户不操纵 UI。我只是制作情节并将其粘贴到word中。我可以很好地看到标记,这对我很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
相关资源
最近更新 更多