ZedGraph在NET.开发软件,感觉用起来还行,就一直在用。
一次开发汽车测量数据(MDF)的解析工具时,发现5.1.5版本在显示数据超过几千点后,图形缩放明显变慢,完全满足不了开发需要。
经过下载源代码,进行分析。
发现问题主要出现在绘制中,ZedGraph每次刷新都会讲所有点进行重绘,这就存在个问题,屏幕能够显示的分辨率有限的,重复绘制大大降低了显示效率。主要更改如下,具体算法还可以优化:
一、线段的绘制
修改 Lline.CS 中
public void DrawCurve(Graphics g, GraphPane pane,
CurveItem curve, float scaleFactor)
……
using (Pen pen = source.GetPen(pane, scaleFactor))
{
// 数据过滤缓冲区
PointPairList filteList = new PointPairList();
PointPairList filtePoint = new PointPairList();
int filteCount = 0; // 过滤缓冲最后一个值
int filteXLast = int.MaxValue;
double filtePower = 0;
……
if (!lastBad)
try
{
// GDI+ plots the data wrong and/or throws an exception for
// outrageous coordinates, so we do a sanity check here
if (lastX > 5000000 || lastX < -5000000 ||
lastY > 5000000 || lastY < -5000000 ||
tmpX > 5000000 || tmpX < -5000000 ||
tmpY > 5000000 || tmpY < -5000000)
{
// InterpolatePoint(g, pane, curve, lastPt, scaleFactor, pen,
// lastX, lastY, tmpX, tmpY);
filteCount = 0;
filteXLast = int.MaxValue;
filtePower = 0;
}
else if (!isOut)
{
if (filteXLast != tmpX)
{
if (filteCount == 0)
{
filtePower = tmpY;
filteCount = 1;
filteList.Add(tmpX, filtePower / filteCount);
}
else
{
filteList.Add(filteXLast, filtePower / filteCount);
}
filtePoint.Add(curPt);
filteCount = 0;
filteXLast = tmpX;
filtePower = 0;
}
else
{
filtePower += tmpY;
filteCount++;
}
}
catch
{
// InterpolatePoint(g, pane, curve, lastPt, scaleFactor, pen,
// lastX, lastY, tmpX, tmpY);
}
}
lastBad = false;
//lastOut = isOut;
}
}
}
lastX = (int)(filteList[0].X);
lastY = (int)(filteList[0].Y);
lastPt = filtePoint[0];
for (int i = 0; i < filteList.Count; i++)
{
tmpX = (int)(filteList[i].X);
tmpY = (int)(filteList[i].Y);
curPt = filtePoint[i];
if (!curve.IsSelected && this._gradientFill.IsGradientValueType)
{
二、点的绘制(同理)
在 Symbol.CS的
public void Draw( Graphics g, GraphPane pane, LineItem curve, float scaleFactor,
bool isSelected )
{
Symbol source = this;
if ( isSelected )
source = Selection.Symbol;
int tmpX, tmpY;
int minX = (int)pane.Chart.Rect.Left;
int maxX = (int)pane.Chart.Rect.Right;
int minY = (int)pane.Chart.Rect.Top;
int maxY = (int)pane.Chart.Rect.Bottom;
// (Dale-a-b) we'll set an element to true when it has been drawn
bool[,] isPixelDrawn = new bool[maxX + 1, maxY + 1];
double curX, curY, lowVal;
IPointList points = curve.Points;
if ( points != null && ( _border.IsVisible || _fill.IsVisible ) )
{
SmoothingMode sModeSave = g.SmoothingMode;
if ( _isAntiAlias )
g.SmoothingMode = SmoothingMode.HighQuality;
// For the sake of speed, go ahead and create a solid brush and a pen
// If it's a gradient fill, it will be created on the fly for each symbol
//SolidBrush brush = new SolidBrush( this.fill.Color );
using ( Pen pen = source._border.GetPen( pane, scaleFactor ) )
using ( GraphicsPath path = MakePath( g, scaleFactor ) )
{
RectangleF rect = path.GetBounds();
using ( Brush brush = source.Fill.MakeBrush( rect ) )
{
// 数据过滤
int filteCount = 0; // 过滤缓冲最后一个值
int filteXLast = int.MaxValue;
double filtePower = 0;
ValueHandler valueHandler = new ValueHandler( pane, false );
Scale xScale = curve.GetXAxis( pane ).Scale;
Scale yScale = curve.GetYAxis( pane ).Scale;
bool xIsLog = xScale.IsLog;
bool yIsLog = yScale.IsLog;
bool xIsOrdinal = xScale.IsAnyOrdinal;
double xMin = xScale.Min;
double xMax = xScale.Max;
// Loop over each defined point
int start = (int)xScale.Min;
start = start < 0 ? 0 : start;
int end = (int)xScale.Max;
int Nnum = (end - start) / (maxX - minX);
Nnum = Nnum > 0 ? Nnum : 1;
///Test
start = 0; end = points.Count; Nnum = 1;
for (int i = start; i < end && i < points.Count; i += Nnum)
//for ( int i = 0; i < points.Count; i++ )
{
// Get the user scale values for the current point
// use the valueHandler only for stacked types
if ( pane.LineType == LineType.Stack )
{
valueHandler.GetValues( curve, i, out curX, out lowVal, out curY );
}
// otherwise, just access the values directly. Avoiding the valueHandler for
// non-stacked types is an optimization to minimize overhead in case there are
// a large number of points.
else
{
curX = points[i].X;
if ( curve is StickItem )
curY = points[i].Z;
else
curY = points[i].Y;
}
// Any value set to double max is invalid and should be skipped
// This is used for calculated values that are out of range, divide
// by zero, etc.
// Also, any value <= zero on a log scale is invalid
if ( curX != PointPair.Missing &&
curY != PointPair.Missing &&
!System.Double.IsNaN( curX ) &&
!System.Double.IsNaN( curY ) &&
!System.Double.IsInfinity( curX ) &&
!System.Double.IsInfinity( curY ) &&
( curX > 0 || !xIsLog ) &&
( !yIsLog || curY > 0.0 ) &&
( xIsOrdinal || ( curX >= xMin && curX <= xMax ) ) )
{
// Transform the user scale values to pixel locations
tmpX = (int) xScale.Transform( curve.IsOverrideOrdinal, i, curX );
tmpY = (int) yScale.Transform( curve.IsOverrideOrdinal, i, curY );
// Maintain an array of "used" pixel locations to avoid duplicate drawing operations
if ( tmpX >= minX && tmpX <= maxX && tmpY >= minY && tmpY <= maxY ) // guard against the zoom-in case
{
if ( isPixelDrawn[tmpX, tmpY] )
continue;
isPixelDrawn[tmpX, tmpY] = true;
}
if (filteXLast != tmpX)
{
if (filteCount == 0)
{
}
else
{
tmpY = (int)(filtePower / filteCount);
}
filteCount = 0;
filteXLast = tmpX;
filtePower = 0;
}
else
{
filtePower += tmpY;
filteCount++;
}
// If the fill type for this symbol is a Gradient by value type,
// the make a brush corresponding to the appropriate current value
if ( _fill.IsGradientValueType || _border._gradientFill.IsGradientValueType )
{
using ( Brush tBrush = _fill.MakeBrush( rect, points[i] ) )
using ( Pen tPen = _border.GetPen( pane, scaleFactor, points[i] ) )
this.DrawSymbol( g, tmpX, tmpY, path, tPen, tBrush );
}
else
{
// Otherwise, the brush is already defined
// Draw the symbol at the specified pixel location
this.DrawSymbol( g, tmpX, tmpY, path, pen, brush );
}
}
}
}
}
g.SmoothingMode = sModeSave;
}
}