c# .net wpf performance 2d

c# - ¿La forma más eficaz de graficar miles de puntos de datos con WPF?



.net performance (9)

He escrito una tabla que muestra datos financieros. El rendimiento fue bueno mientras dibujaba menos de 10,000 puntos mostrados como una línea conectada usando PathGeometry junto con PathFigure y LineSegment s. Pero ahora necesito mostrar hasta 100.000 puntos al mismo tiempo (sin desplazamiento) y ya es muy lento con 50.000 puntos. Estaba pensando en StreamGeometry , pero no estoy seguro, ya que básicamente es lo mismo que PathGeometry marca la información como flujo de bytes. ¿Alguien tiene una idea para hacer que esto sea mucho más eficaz o tal vez alguien ya haya hecho algo similar?

EDITAR: Estos puntos de datos no cambian una vez que se dibujan, por lo que si existe un potencial de optimización, hágamelo saber (los segmentos de línea están congelados ahora).

EDIT: he intentado StreamGeometry. La creación del gráfico llevó incluso más tiempo por alguna razón, pero este no es el problema. Dibujar en la tabla después de dibujar todos los puntos sigue siendo tan lento como el método anterior. Creo que son demasiados puntos de datos para tratar con WPF.

EDITAR: Experimenté un poco y noté que el rendimiento mejoró un poco al convertir las coordenadas que antes eran dobles a int para evitar las líneas de subpíxeles anti-aliasing de WPF.

EDIT: Gracias por todas las respuestas que sugieren reducir el número de segmentos de línea. Los he reducido a, como máximo, el doble de la resolución horizontal para líneas escalonadas y, a lo sumo, la resolución horizontal para líneas simples y el rendimiento es bastante bueno ahora.


Otra idea sería utilizar el control de imagen con la propiedad Fuente establecida en una DrawingImage que haya creado dinámicamente.

Según Pavan Podila en WPF Control Development Unleashed, este enfoque puede ser muy útil cuando tiene miles y miles de imágenes que no necesitan ninguna interactividad. Echa un vistazo a la página 25 de su libro para más información.

Este es un hilo antiguo, pero pensé que valía la pena mencionar que se podría lograr la interactividad con el método anterior utilizando el evento MouseUp (). Sabe el tamaño de la ventana gráfica de la imagen, la resolución de la imagen y la posición del mouse. Por ejemplo, podría mantener la colección actualScreenPoints a través de un temporizador adjunto a su evento UserControl_SizeChanged:

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); }

Y luego, cuando se dispare el evento MouseUp (), compruebe si alguno de los puntos de la colección está dentro de + -2px. Ahí está tu MouseUp en un punto dado.


Consideraría reducir la cantidad de puntos que está intentando representar. Es posible que tenga 50,000 puntos de datos pero es poco probable que pueda incluirlos todos en la pantalla; ¡incluso si graficara cada punto en una pantalla, necesitaría 100,000 píxeles de resolución horizontal para dibujarlos todos! Incluso en D3D eso es mucho para dibujar.

Como es más probable que tenga algo así como 2,048 píxeles, también puede reducir los puntos que está graficando y dibujar una curva aproximada que se ajuste a la pantalla y tenga solo un par de miles de verts. Si, por ejemplo, el usuario grafica un marco de tiempo que incluye 10000 puntos, entonces disminuya esos 10000 puntos a 1000 antes de graficar. Hay varias técnicas que puede probar, desde promedios simples hasta media-vecina, convolución gaussiana y (mi sugerencia) interpolación bicúbica . 100,000 .

A medida que el usuario se acerca a una parte de un gráfico, puede volver a muestrear para obtener resoluciones más altas y un ajuste de curvas más preciso.


Creo que el único método que podría ser más rápido mientras se mantiene en el marco de trabajo de WPF sería anular OnRender en un control personalizado. Luego puedes renderizar tu geometría directamente a la escena persistente, eliminando cualquier cosa que no esté a la vista. Si el usuario solo puede ver una pequeña parte del conjunto de datos a la vez, la selección podría ser suficiente por sí sola.

Con esta cantidad de puntos de datos, es poco probable que el usuario pueda ver todos los detalles cuando todo el conjunto de datos está a la vista. Por lo tanto, también podría valer la pena considerar la posibilidad de simplificar el conjunto de datos para una vista completa y luego mostrar una vista más detallada cuando se acercan.

Edición: También, dale un tiro a StreamGeometry. Todo el motivo de la existencia es el rendimiento, y nunca se sabe hasta que lo intentas.


Cuando empiece a tratar con cientos de miles de vértices y vectores distintos en su geometría, probablemente debería considerar migrar su código de gráficos para usar un marco de gráficos en lugar de depender de WPF (que, aunque está construido sobre Direct3D y por lo tanto es capaz de ser sorprendentemente eficiente) la representación de gráficos vectoriales, tiene una gran cantidad de sobrecarga adicional que obstaculiza su eficiencia). Es posible alojar ventanas de representación de gráficos tanto Direct3D como OpenGL dentro de WPF; sugeriría mover esa dirección en lugar de continuar trabajando únicamente dentro de WPF.

(EDITAR: se cambió "DirectX" en la respuesta original a "Direct3D")


Esta es una muy buena pregunta, y en el fondo está la pregunta "¿Puede un usuario hacer uso práctico de una pantalla que contenga 100,000 puntos discretos?".

Siguiendo las mejores prácticas en la filosofía de diseño de GUI, la respuesta debería ser No , lo que me llevaría a preguntarme si no hay una manera diferente de cumplir con el requisito de la aplicación.

Si realmente hay un caso de buena fe para mostrar 100,000 puntos en la pantalla, sin desplazamiento, entonces usar un búfer fuera de la pantalla es el camino a seguir. Componga su imagen en un mapa de bits, luego coloque ese mapa de bits en su Ventana / Página según sea necesario. De esta manera, el trabajo pesado se realiza solo una vez, después de lo cual la aceleración de hardware se puede utilizar cada vez que se necesita dibujar la ventana.

Espero que esto ayude.


No he trabajado con WPF (descargo de responsabilidad), pero sospecho que su problema de rendimiento se debe a que su código está tratando de ajustar una línea curva suave a través de todos sus datos, y el tiempo requerido aumenta geométricamente (o peor) con el número de puntos de datos.

No sé si esto sería aceptable en cuanto a la apariencia, pero intente graficar sus datos conectando cada punto al último con una línea recta. Esto debería hacer que el tiempo de graficación sea proporcional al número de puntos de datos, y con tantos puntos como tenga, la gráfica puede terminar luciendo exactamente igual de todos modos.


No sé qué tan bien se escala, pero he tenido cierto éxito al usar ZedGraph en WPF (control de WinForms dentro de un WindowsFormsPresenter). Me sorprende que nadie lo haya mencionado aún. Vale la pena echarle un vistazo, incluso si no planea usarlo para su proyecto actual.

ZedGraph

¡Buena suerte!


Simplemente me encontré con esta pregunta, pero como mencioné en this hilo, el enfoque más eficaz podría ser programar contra la capa Visual de WPF .

Todo lo visual en WPF eventualmente va contra esta capa ... y por eso es el enfoque más liviano de todos.

Ver this y this para más información. El capítulo 14 del libro Pro de WPF de Matthew MacDonald en C # 2008 también tiene una buena sección.

Como otra referencia ... vea el Capítulo 2 del libro de Pavan Podila, WPF Control Development Unleashed . En la página 13, explica cómo DrawingVisuals sería una excelente opción para un componente de gráficos.

Finalmente, me di cuenta de que Charles Petzold escribió un article MSDN Magazine en el que la mejor solución global (con un rendimiento de todos modos) (para un diagrama de dispersión) era un enfoque DrawingVisual.


Otra idea sería utilizar el control de imagen con la propiedad Fuente establecida en una DrawingImage que haya creado dinámicamente.

Según Pavan Podila en WPF Control Development Unleashed , este enfoque puede ser muy útil cuando tiene miles y miles de imágenes que no necesitan ninguna interactividad. Echa un vistazo a la página 25 de su libro para más información.