C# 使用 WPF 绘制数千个数据点的最高效方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/952657/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Most performant way to graph thousands of data points with WPF?
提问by
I have written a chart that displays financial data. Performance was good while I was drawing less than 10.000 points displayed as a connected line using PathGeometry
together with PathFigure
and LineSegment
s. But now I need to display up to 100.000 points at the same time (without scrolling) and it's already very slow with 50.000 points. I was thinking of StreamGeometry
, but I am not sure since it's basically the same as a PathGeometry
stroring the information as byte stream. Does any one have an idea to make this much more performant or maybe someone has even done something similar already?
我写了一个图表来显示财务数据。当我绘制少于 10.000 个点显示为PathGeometry
与PathFigure
和LineSegment
s一起使用的连接线时,性能很好。但是现在我需要同时显示多达 100.000 个点(不滚动)并且它已经非常慢了 50.000 个点。我在想StreamGeometry
,但我不确定,因为它与PathGeometry
将信息存储为字节流基本相同。有没有人有一个想法来提高性能,或者也许有人已经做了类似的事情?
EDIT: These data points do not change once drawn so if there is potential optimizing it, please let me know (line segments are frozen right now).
编辑:这些数据点一旦绘制就不会改变,所以如果有潜在的优化,请告诉我(线段现在被冻结)。
EDIT: I tried StreamGeometry. Creating the graphic took even longer for some reason, but this is not the issue. Drawing on the chart after drawing all the points is still as slow as the previous method. I think it's just too many data points for WPF to deal with.
编辑:我尝试了 StreamGeometry。由于某种原因,创建图形需要更长的时间,但这不是问题。绘制完所有点后在图表上绘制仍然和之前的方法一样慢。我认为 WPF 处理的数据点太多了。
EDIT: I've experimented a bit and I noticed that performance improved a bit by converting the coordinates which were previously in double to int to prevent WPF anti-aliasing sub-pixel lines.
编辑:我进行了一些实验,我注意到通过将以前的 double 坐标转换为 int 以防止 WPF 抗锯齿子像素线,性能有所提高。
EDIT: Thanks for all the responses suggesting to reduce the number of line segments. I have reduced them to at most twice the horizontal resolution for stepped lines and at most the horizontal resolution for simple lines and the performance is pretty good now.
编辑:感谢所有建议减少线段数量的回应。我已经将它们减少到最多两倍于阶梯线的水平分辨率和最多是简单线的水平分辨率,现在性能相当不错。
采纳答案by Crashworks
I'd consider downsampling the number of points you are trying to render. You may have 50,000 points of data but you're unlikely to be able to fit them all on the screen; even if you charted every single point in one display you'd need 100,000pixels of horizontal resolution to draw them all! Even in D3D that's a lot to draw.
我会考虑对您尝试渲染的点数进行下采样。您可能有 50,000 个数据点,但不太可能将它们全部显示在屏幕上;即使您在一个显示器中绘制了每一个点,您也需要100,000像素的水平分辨率来绘制它们!即使在 D3D 中,也需要大量绘制。
Since you are more likely to have something like 2,048 pixels, you may as well reduce the points you are graphing and draw an approximate curve that fits onto the screen and has only a couple thousand verts. If for example the user graphs a time frame including 10000 points, then downsample those 10000 points to 1000 before graphing. There are numerous techniques you could try, from simple averaging to median-neighbor to Gaussian convolution to (my suggestion) bicubic interpolation. Drawing any number of points greater than 1/2 the screen resolution will simply be a waste.
由于您更有可能拥有 2,048 像素之类的东西,因此您不妨减少正在绘制的点并绘制一条适合屏幕且只有几千个顶点的近似曲线。例如,如果用户绘制了一个包含 10000 个点的时间范围,那么在绘制之前将这 10000 个点降采样到 1000。您可以尝试多种技术,从简单平均到中值邻域到高斯卷积再到(我的建议)双三次插值。绘制大于屏幕分辨率 1/2 的任意点数将是一种浪费。
As the user zooms in on a part of a graph, you can resample to get higher resolutions and more accurate curve fitting.
当用户放大图形的一部分时,您可以重新采样以获得更高的分辨率和更准确的曲线拟合。
回答by Dathan
When you start dealing with hundreds of thousands of distinct vertices and vectors in your geometry, you should probably consider migrating your graphics code to use a graphics framework instead of depending on WPF (which, while built on top of Direct3D and therefore capable of remarkably efficient vector graphics rendering, has a lot of extra overhead going on that hampers its efficiency). It's possible to host both Direct3D and OpenGL graphics rendering windows within WPF -- I'd suggest moving that direction instead of continuing to work solely within WPF.
当您开始处理几何体中成千上万个不同的顶点和向量时,您可能应该考虑迁移图形代码以使用图形框架而不是依赖 WPF(虽然它构建在 Direct3D 之上,因此能够非常高效地矢量图形渲染,有很多额外的开销会阻碍其效率)。可以在 WPF 中同时托管 Direct3D 和 OpenGL 图形渲染窗口——我建议朝着这个方向发展,而不是继续单独在 WPF 中工作。
(EDIT: changed "DirectX" in original answer to "Direct3D")
(编辑:将原始答案中的“DirectX”更改为“Direct3D”)
回答by PeterAllenWebb
I believe the only method that might be faster while remaining in the WPF framework would be to override OnRender in a custom control. You can then render your geometry directly to the persisted scene, culling anything out of view. If the user can only see a small part of the data set at a time, culling could be enough on its own.
我相信保留在 WPF 框架中的唯一可能更快的方法是在自定义控件中覆盖 OnRender。然后,您可以将几何图形直接渲染到持久化场景中,剔除视野之外的任何东西。如果用户一次只能看到数据集的一小部分,单独剔除就足够了。
With this many data points, it's unlikely that the user can see full detail when the entire dataset is in view. So it might also be worthwhile to consider simplifying the dataset for full view and then showing a more detailed view if and when they zoom in.
有了这么多数据点,当整个数据集都在视野中时,用户不太可能看到完整的细节。因此,考虑简化数据集以获得完整视图,然后在放大时显示更详细的视图也可能是值得的。
Edit: Also, give StreamGeometry a shot. Its whole reason for existing is performance, and you never know until you try.
编辑:另外,给 StreamGeometry 一个机会。它存在的全部原因是性能,只有尝试才能知道。
回答by MusiGenesis
I haven't worked with WPF (disclaimer), but I suspect that your performance problem is because your code is trying to fit a smooth curved line through all of your data, and the time required increases geometrically (or worse) with the number of data points.
我没有使用过 WPF(免责声明),但我怀疑您的性能问题是因为您的代码试图在所有数据中拟合一条平滑的曲线,并且所需的时间随着数量的增加呈几何级数(或更糟)增加数据点。
I don't know if this would be acceptable appearance-wise, but try graphing your data by connecting each point to the last with a straight line. This should make the time-to-graph proportional to the number of data points, and with as many points as you have the graph may end up looking exactly the same anyway.
我不知道这在外观上是否可以接受,但是尝试通过用直线将每个点连接到最后一个点来绘制您的数据。这应该使绘图时间与数据点的数量成正比,并且无论您拥有多少点,图表最终看起来都可能完全相同。
回答by Mark
This is a verygood question, and at it's heart begs the question "Can any user make practical use of, or business descisions from, a screen containing 100,000 discrete points?".
这是一个非常好的问题,并在它的心脏引出了一个问题:“可以在任何用户做出实际用途,或含100000个离散点的屏幕业务descisions?”。
Following best practice in GUI design philosphy, the answer should be No, which would lead me to question whether there isn't a different way to meet the requirement for the application.
遵循 GUI 设计哲学中的最佳实践,答案应该是No,这会让我怀疑是否没有不同的方法来满足应用程序的要求。
If there really is a bona-fide case for displaying 100,000 points on screen, with no scrolling, then using an off-screen buffer is the way to go. Composite your image to a bitmap, than whack that bitmap onto your Window / Page as needed. This way the heavy lifting is only done once, after which the hardware acceleration can be used every time the window needs to be drawn.
如果确实有在屏幕上显示 100,000 个点而不滚动的真实案例,那么使用屏幕外缓冲区是可行的方法。将您的图像合成为位图,然后根据需要将该位图重击到您的窗口/页面上。这样繁重的工作只做一次,之后每次需要绘制窗口时都可以使用硬件加速。
Hope this helps.
希望这可以帮助。
回答by Mark Carpenter
I don't know how well it scales, but I've had some success using ZedGraph in WPF (WinForms control inside a WindowsFormsPresenter). I'm surprised no one mentioned it yet. It's worth taking a look at, even if you're not planning on using it for your current project.
我不知道它的扩展性如何,但我在 WPF(WindowsFormsPresenter 中的 WinForms 控件)中使用 ZedGraph 取得了一些成功。我很惊讶还没有人提到它。值得一看,即使您不打算在当前项目中使用它。
Good luck!
祝你好运!
回答by cplotts
Just ran into this question, but as I mentioned in thisthread, the most performant approach might be to program against WPF's Visual layer.
刚刚遇到这个问题,但正如我在此线程中提到的,最高效的方法可能是针对 WPF 的 Visual layer 进行编程。
Everything Visual in WPF eventually goes against this layer ... and so it is the most lightweight approach of them all.
WPF 中的所有 Visual 最终都与这一层背道而驰……因此它是所有这些方法中最轻量级的方法。
See thisand thisfor more info. Chapter 14 of Matthew MacDonald's Pro WPF in C# 2008book also has a good section on it.
有关更多信息,请参阅此和此。Matthew MacDonald's Pro WPF in C# 2008book 的第 14 章也有一个很好的部分。
As another reference ... see Chapter 2 of Pavan Podila's book WPF Control Development Unleashed. On page 13, he discusses how DrawingVisuals would be an excellent choice for a charting component.
作为另一个参考...请参阅 Pavan Podila 的书WPF Control Development Unleashed 的第 2 章。在第 13 页,他讨论了 DrawingVisuals 如何成为图表组件的绝佳选择。
Finally, I just noticed that Charles Petzoldwrote an MSDN Magazine articlewhere the best overall (performant anyway) solution (to a scatter plot) was a DrawingVisual approach.
最后,我刚刚注意到Charles Petzold写了一篇 MSDN 杂志文章,其中最好的整体(无论如何都是高性能的)解决方案(对于散点图)是 DrawingVisual 方法。
回答by cplotts
Another idea would be to use the Image control with the Source property set to a DrawingImage that you've dynamically created.
另一个想法是使用 Image 控件,并将 Source 属性设置为您动态创建的 DrawingImage。
According to Pavan Podilain WPF Control Development Unleashed, this approach can be very helpful when you have thousands and thousands of visuals that don't need any interactivity. Check out page 25 of his book for more info.
根据帕文Podila在WPF控件开发偷跑,当你不需要任何交互视觉效果的成千上万的这种做法是非常有益的。查看他的书的第 25 页了解更多信息。
回答by Peter O'Brien
Another idea would be to use the Image control with the Source property set to a DrawingImage that you've dynamically created.
According to Pavan Podila in WPF Control Development Unleashed, this approach can be very helpful when you have thousands and thousands of visuals that don't need any interactivity. Check out page 25 of his book for more info.
另一个想法是使用 Image 控件,并将 Source 属性设置为您动态创建的 DrawingImage。
根据 WPF Control Development Unleashed 中的 Pavan Podila 的说法,当您拥有成千上万个不需要任何交互的视觉效果时,这种方法非常有用。查看他的书的第 25 页了解更多信息。
This is an old thread, but I thought it was worth mentioning that you could attain interactivity with the above method by using the MouseUp() event. You know the size of the image's viewport, the resolution of the image, and the mouse's position. For example, you could maintain the collection actualScreenPoints through a timer attached to your UserControl_SizeChanged event:
这是一个旧线程,但我认为值得一提的是,您可以通过使用 MouseUp() 事件与上述方法进行交互。您知道图像视口的大小、图像的分辨率和鼠标的位置。例如,您可以通过附加到 UserControl_SizeChanged 事件的计时器来维护集合 actualScreenPoints:
double xworth = viewport.ActualWidth / (XEnd - XStart);
double xworth = viewport.ActualHeight / (YEnd - YStart);
List<Point> actualScreenPoints = new List<Point>();
for (var i = 0; i < points.Count; i++)
{
double posX = points[i].X * xworth;
double posY = points[i].Y * yworth;
actualScreenPoints.Add(posX, posY);
}
And then when your MouseUp() event fires, check if any of the points in the collection are within +-2px. There's your MouseUp on a given point.
然后当您的 MouseUp() 事件触发时,检查集合中的任何点是否在 +-2px 之内。在给定的点上有你的 MouseUp。