/// <summary> /// Determine the coords for the rectangle associated with a specified point for /// this <see cref="CurveItem" /> /// </summary> /// <param name="pane">The <see cref="GraphPane" /> to which this curve belongs</param> /// <param name="i">The index of the point of interest</param> /// <param name="coords">A list of coordinates that represents the "rect" for /// this point (used in an html AREA tag)</param> /// <returns>true if it's a valid point, false otherwise</returns> public override bool GetCoords(GraphPane pane, int i, out string coords) { coords = string.Empty; if (i < 0 || i >= _points.Count) return false; Axis valueAxis = ValueAxis(pane); Axis baseAxis = BaseAxis(pane); // pixBase = pixel value for the bar center on the base axis // pixHiVal = pixel value for the bar top on the value axis // pixLowVal = pixel value for the bar bottom on the value axis float pixBase, pixHiVal, pixLowVal; float clusterWidth = pane.BarSettings.GetClusterWidth(); float barWidth = GetBarWidth(pane); float clusterGap = pane._barSettings.MinClusterGap*barWidth; float barGap = barWidth*pane._barSettings.MinBarGap; // curBase = the scale value on the base axis of the current bar // curHiVal = the scale value on the value axis of the current bar // curLowVal = the scale value of the bottom of the bar double curBase, curLowVal, curHiVal; ValueHandler valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(this, i, out curBase, out curLowVal, out curHiVal); // 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 (!_points[i].IsInvalid3D) { // calculate a pixel value for the top of the bar on value axis pixLowVal = valueAxis.Scale.Transform(_isOverrideOrdinal, i, curLowVal); pixHiVal = valueAxis.Scale.Transform(_isOverrideOrdinal, i, curHiVal); // calculate a pixel value for the center of the bar on the base axis pixBase = baseAxis.Scale.Transform(_isOverrideOrdinal, i, curBase); // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - clusterWidth/2.0F + clusterGap/2.0F + pane.CurveList.GetBarItemPos(pane, this)*(barWidth + barGap); // Draw the bar if (baseAxis is XAxis || baseAxis is X2Axis) coords = string.Format("{0:f0},{1:f0},{2:f0},{3:f0}", pixSide, pixLowVal, pixSide + barWidth, pixHiVal); else coords = string.Format("{0:f0},{1:f0},{2:f0},{3:f0}", pixLowVal, pixSide, pixHiVal, pixSide + barWidth); return true; } return false; }
/// <summary> /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device as a symbol at each defined point. The routine /// only draws the symbols; the lines are draw by the /// <see cref="Line.DrawCurve"/> method. This method /// is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> public void Draw(Graphics g, GraphPane pane, LineItem curve, float scaleFactor) { float tmpX, tmpY; 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 = _border.MakePen(pane.IsPenWidthScaled, scaleFactor)) using (GraphicsPath path = MakePath(g, scaleFactor)) { RectangleF rect = path.GetBounds(); using (Brush brush = Fill.MakeBrush(rect)) { var valueHandler = new ValueHandler(pane, false); Scale xScale = pane.XAxis.Scale; Scale yScale = curve.GetYAxis(pane).Scale; bool xIsLog = xScale.IsLog; bool yIsLog = yScale.IsLog; // Loop over each defined point 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 && !Double.IsNaN(curX) && !Double.IsNaN(curY) && !Double.IsInfinity(curX) && !Double.IsInfinity(curY) && (curX > 0 || !xIsLog) && (!yIsLog || curY > 0.0)) { // Transform the user scale values to pixel locations tmpX = xScale.Transform(curve.IsOverrideOrdinal, i, curX); tmpY = yScale.Transform(curve.IsOverrideOrdinal, i, curY); // 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) { using (Brush tBrush = _fill.MakeBrush(rect, points[i])) DrawSymbol(g, tmpX, tmpY, path, pen, tBrush); } else { // Otherwise, the brush is already defined // Draw the symbol at the specified pixel location DrawSymbol(g, tmpX, tmpY, path, pen, brush); } } } } } g.SmoothingMode = sModeSave; } }
/// <summary> /// Determine the coords for the rectangle associated with a specified point for /// this <see cref="CurveItem" /> /// </summary> /// <param name="pane">The <see cref="GraphPane" /> to which this curve belongs</param> /// <param name="i">The index of the point of interest</param> /// <param name="coords">A list of coordinates that represents the "rect" for /// this point (used in an html AREA tag)</param> /// <returns>true if it's a valid point, false otherwise</returns> public override bool GetCoords(GraphPane pane, int i, out string coords) { coords = string.Empty; if (i < 0 || i >= _points.Count) return false; PointPair pt = _points[i]; if (pt.IsInvalid) return false; double x, y, z; ValueHandler valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(this, i, out x, out z, out y); Axis yAxis = GetYAxis(pane); Axis xAxis = GetXAxis(pane); PointF pixPt = new PointF(xAxis.Scale.Transform(_isOverrideOrdinal, i, x), yAxis.Scale.Transform(_isOverrideOrdinal, i, y)); if (!pane.Chart.Rect.Contains(pixPt)) return false; float halfSize = _symbol.Size*pane.CalcScaleFactor(); coords = string.Format("{0:f0},{1:f0},{2:f0},{3:f0}", pixPt.X - halfSize, pixPt.Y - halfSize, pixPt.X + halfSize, pixPt.Y + halfSize); return true; }
/// <summary> /// Draw all the <see cref="ErrorBar"/>'s to the specified <see cref="Graphics"/> /// device as a an error bar at each defined point. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn.</param> /// <param name="baseAxis">The <see cref="Axis"/> class instance that defines the base (independent) /// axis for the <see cref="Bar"/></param> /// <param name="valueAxis">The <see cref="Axis"/> class instance that defines the value (dependent) /// axis for the <see cref="Bar"/></param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> public void Draw( Graphics g, GraphPane pane, ErrorBarItem curve, Axis baseAxis, Axis valueAxis, float scaleFactor ) { ValueHandler valueHandler = new ValueHandler( pane, false ); float pixBase, pixValue, pixLowValue; double scaleBase, scaleValue, scaleLowValue; if ( curve.Points != null && this.IsVisible ) { using ( Pen pen = !curve.IsSelected ? new Pen( _color, _penWidth ) : new Pen( Selection.Border.Color, Selection.Border.Width ) ) { // Loop over each defined point for ( int i = 0; i < curve.Points.Count; i++ ) { valueHandler.GetValues( curve, i, out scaleBase, out scaleLowValue, out scaleValue ); // 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 ( !curve.Points[i].IsInvalid3D && ( scaleBase > 0 || !baseAxis._scale.IsLog ) && ( ( scaleValue > 0 && scaleLowValue > 0 ) || !valueAxis._scale.IsLog ) ) { pixBase = baseAxis.Scale.Transform( curve.IsOverrideOrdinal, i, scaleBase ); pixValue = valueAxis.Scale.Transform( curve.IsOverrideOrdinal, i, scaleValue ); pixLowValue = valueAxis.Scale.Transform( curve.IsOverrideOrdinal, i, scaleLowValue ); //if ( this.fill.IsGradientValueType ) // brush = fill.MakeBrush( _rect, _points[i] ); this.Draw( g, pane, baseAxis is XAxis || baseAxis is X2Axis, pixBase, pixValue, pixLowValue, scaleFactor, pen, curve.IsSelected, curve.Points[i] ); } } } } }
/// <summary> /// Build an array of <see cref="PointF"/> values (pixel coordinates) that represents /// the low values for the current curve. /// </summary> /// <remarks>Note that this drawing routine ignores <see cref="PointPairBase.Missing"/> /// values, but it does not "break" the line to indicate values are missing. /// </remarks> /// <param name="pane">A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object.</param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> /// <param name="arrPoints">An array of <see cref="PointF"/> values in pixel /// coordinates representing the current curve.</param> /// <param name="count">The number of points contained in the "arrPoints" /// parameter.</param> /// <returns>true for a successful points array build, false for data problems</returns> public bool BuildLowPointsArray( GraphPane pane, CurveItem curve, out PointF[] arrPoints, out int count ) { arrPoints = null; count = 0; IPointList points = curve.Points; if ( this.IsVisible && !this.Color.IsEmpty && points != null ) { int index = 0; float curX, curY, lastX = 0, lastY = 0; double x, y, hiVal; ValueHandler valueHandler = new ValueHandler( pane, false ); // Step type plots get twice as many points. Always add three points so there is // room to close out the curve for area fills. arrPoints = new PointF[( _stepType == ZedGraph.StepType.NonStep ? 1 : 2 ) * ( pane.LineType == LineType.Stack ? 2 : 1 ) * points.Count + 1]; // Loop backwards over all points in the curve // In this case an array of points was already built forward by BuildPointsArray(). // This time we build backwards to complete a loop around the area between two curves. for ( int i = points.Count - 1; i >= 0; i-- ) { // Make sure the current point is valid if ( !points[i].IsInvalid ) { // Get the user scale values for the current point valueHandler.GetValues( curve, i, out x, out y, out hiVal ); if ( x == PointPair.Missing || y == PointPair.Missing ) continue; // Transform the user scale values to pixel locations Axis xAxis = curve.GetXAxis( pane ); curX = xAxis.Scale.Transform( curve.IsOverrideOrdinal, i, x ); Axis yAxis = curve.GetYAxis( pane ); curY = yAxis.Scale.Transform( curve.IsOverrideOrdinal, i, y ); // Add the pixel value pair into the points array // Two points are added for step type curves // ignore step-type setting for smooth curves if ( _isSmooth || index == 0 || this.StepType == StepType.NonStep ) { arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if ( this.StepType == StepType.ForwardStep ) { arrPoints[index].X = curX; arrPoints[index].Y = lastY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if ( this.StepType == StepType.RearwardStep ) { arrPoints[index].X = lastX; arrPoints[index].Y = curY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } lastX = curX; lastY = curY; index++; } } // Make sure there is at least one valid point if ( index == 0 ) return false; // Add an extra point at the end, since the smoothing algorithm requires it arrPoints[index] = arrPoints[index - 1]; index++; count = index; return true; } else { return false; } }
/// <summary> /// Draw the this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device. The format (stair-step or line) of the curve is /// defined by the <see cref="StepType"/> property. The routine /// only draws the line segments; the symbols are drawn by the /// <see cref="Symbol.Draw"/> method. This method /// is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> public void DrawCurve( Graphics g, GraphPane pane, CurveItem curve, float scaleFactor ) { Line source = this; if ( curve.IsSelected ) source = Selection.Line; float tmpX, tmpY, lastX = float.MaxValue, lastY = float.MaxValue; double curX, curY, lowVal; PointPair curPt, lastPt = new PointPair(); bool lastBad = true; IPointList points = curve.Points; ValueHandler valueHandler = new ValueHandler( pane, false ); Axis yAxis = curve.GetYAxis( pane ); Axis xAxis = curve.GetXAxis( pane ); bool xIsLog = xAxis._scale.IsLog; bool yIsLog = yAxis._scale.IsLog; float minX = pane.Chart.Rect.Left; float maxX = pane.Chart.Rect.Right; using ( Pen pen = source.GetPen( pane, scaleFactor ) ) { if ( points != null && !_color.IsEmpty && this.IsVisible ) { bool lastOut = false; bool isOut; // Loop over each point in the curve for ( int i = 0; i < points.Count; i++ ) { curPt = points[i]; if ( pane.LineType == LineType.Stack ) { if ( !valueHandler.GetValues( curve, i, out curX, out lowVal, out curY ) ) { curX = PointPair.Missing; curY = PointPair.Missing; } } else { curX = curPt.X; curY = curPt.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 ) || ( xIsLog && curX <= 0.0 ) || ( yIsLog && curY <= 0.0 ) ) { // If the point is invalid, then make a linebreak only if IsIgnoreMissing is false // LastX and LastY are always the last valid point, so this works out lastBad = lastBad || !pane.IsIgnoreMissing; isOut = true; } else { // Transform the current point from user scale units to // screen coordinates tmpX = xAxis.Scale.Transform( curve.IsOverrideOrdinal, i, curX ); tmpY = yAxis.Scale.Transform( curve.IsOverrideOrdinal, i, curY ); isOut = (tmpX < minX || tmpX > maxX); 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 ); else if ( !lastOut || !isOut ) { if ( !curve.IsSelected && this._gradientFill.IsGradientValueType ) { using ( Pen tPen = GetPen( pane, scaleFactor, lastPt ) ) { if ( this.StepType == StepType.ForwardStep ) { g.DrawLine( tPen, lastX, lastY, tmpX, lastY ); g.DrawLine( tPen, tmpX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.RearwardStep ) { g.DrawLine( tPen, lastX, lastY, lastX, tmpY ); g.DrawLine( tPen, lastX, tmpY, tmpX, tmpY ); } else // non-step g.DrawLine( tPen, lastX, lastY, tmpX, tmpY ); } } else { if ( this.StepType == StepType.ForwardStep ) { g.DrawLine( pen, lastX, lastY, tmpX, lastY ); g.DrawLine( pen, tmpX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.RearwardStep ) { g.DrawLine( pen, lastX, lastY, lastX, tmpY ); g.DrawLine( pen, lastX, tmpY, tmpX, tmpY ); } else // non-step g.DrawLine( pen, lastX, lastY, tmpX, tmpY ); } } } catch { InterpolatePoint( g, pane, curve, lastPt, scaleFactor, pen, lastX, lastY, tmpX, tmpY ); } } lastPt = curPt; lastX = tmpX; lastY = tmpY; lastBad = false; lastOut = isOut; } } } } }
// Call this method after calling AxisChange() private void CreateBarLabels( GraphPane pane ) { // Make the gap between the bars and the labels = 2% of the axis range float labelOffset = (float)( pane.YAxis.Scale.Max - pane.YAxis.Scale.Min ) * 0.02f; foreach ( CurveItem curve in pane.CurveList ) { BarItem bar = curve as BarItem; if ( bar != null ) { IPointList points = curve.Points; for ( int i = 0; i < points.Count; i++ ) { ValueHandler valueHandler = new ValueHandler( pane, true ); int curveIndex = pane.CurveList.IndexOf( curve ); double labelXCoordintate = valueHandler.BarCenterValue( bar, bar.GetBarWidth( pane ), i, points[ i ].X, curveIndex ); float labelYCoordinate = ( float ) points[ i ].Y + labelOffset; string barLabelText = ( points[ i ].Y / 1000 ).ToString( "N2" ); TextObj label = new TextObj( barLabelText, ( float ) labelXCoordintate, labelYCoordinate ); label.Location.CoordinateFrame = CoordType.AxisXYScale; label.FontSpec.Size = 10; label.FontSpec.FontColor = Color.Black; label.FontSpec.Angle = 90; label.Location.AlignH = AlignH.Left; label.Location.AlignV = AlignV.Center; label.FontSpec.Border.IsVisible = false; label.FontSpec.Fill.IsVisible = false; pane.GraphObjList.Add( label ); } } } }
/// <summary> /// Calculate the range for stacked bars and lines. /// </summary> /// <remarks>This method is required for the stacked /// types because (for bars), the negative values are a separate stack than the positive /// values. If you just sum up the bars, you will get the sum of the positive plus negative, /// which is less than the maximum positive value and greater than the maximum negative value. /// </remarks> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">The <see cref="CurveItem"/> for which to calculate the range</param> /// <param name="tXMinVal">The minimum X value so far</param> /// <param name="tYMinVal">The minimum Y value so far</param> /// <param name="tXMaxVal">The maximum X value so far</param> /// <param name="tYMaxVal">The maximum Y value so far</param> /// <seealso cref="GraphPane.IsBoundedRanges"/> private void GetStackRange( GraphPane pane, CurveItem curve, out double tXMinVal, out double tYMinVal, out double tXMaxVal, out double tYMaxVal ) { // initialize the values to outrageous ones to start tXMinVal = tYMinVal = Double.MaxValue; tXMaxVal = tYMaxVal = Double.MinValue; ValueHandler valueHandler = new ValueHandler( pane, false ); Axis baseAxis = curve.BaseAxis( pane ); bool isXBase = baseAxis is XAxis || baseAxis is X2Axis; double lowVal, baseVal, hiVal; for ( int i=0; i<curve.Points.Count; i++ ) { valueHandler.GetValues( curve, i, out baseVal, out lowVal, out hiVal ); double x = isXBase ? baseVal : hiVal; double y = isXBase ? hiVal : baseVal; if ( x != PointPair.Missing && y != PointPair.Missing && lowVal != PointPair.Missing ) { if ( x < tXMinVal ) tXMinVal = x; if ( x > tXMaxVal ) tXMaxVal = x; if ( y < tYMinVal ) tYMinVal = y; if ( y > tYMaxVal ) tYMaxVal = y; if ( !isXBase ) { if ( lowVal < tXMinVal ) tXMinVal = lowVal; if ( lowVal > tXMaxVal ) tXMaxVal = lowVal; } else { if ( lowVal < tYMinVal ) tYMinVal = lowVal; if ( lowVal > tYMaxVal ) tYMaxVal = lowVal; } } } }
/// <summary> /// Calculate the range for stacked bars and lines. /// </summary> /// <remarks>This method is required for the stacked /// types because (for bars), the negative values are a separate stack than the positive /// values. If you just sum up the bars, you will get the sum of the positive plus negative, /// which is less than the maximum positive value and greater than the maximum negative value. /// </remarks> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">The <see cref="CurveItem"/> for which to calculate the range</param> /// <param name="tXMinVal">The minimum X value so far</param> /// <param name="tYMinVal">The minimum Y value so far</param> /// <param name="tXMaxVal">The maximum X value so far</param> /// <param name="tYMaxVal">The maximum Y value so far</param> /// <seealso cref="GraphPane.IsBoundedRanges"/> private void GetStackRange(GraphPane pane, CurveItem curve, out double tXMinVal, out double tYMinVal, out double tXMaxVal, out double tYMaxVal) { // initialize the values to outrageous ones to start tXMinVal = tYMinVal = Double.MaxValue; tXMaxVal = tYMaxVal = Double.MinValue; ValueHandler valueHandler = new ValueHandler(pane, false); Axis baseAxis = curve.BaseAxis(pane); bool isXBase = baseAxis is XAxis || baseAxis is X2Axis; double lowVal, baseVal, hiVal; for (int i = 0; i < curve.Points.Count; i++) { valueHandler.GetValues(curve, i, out baseVal, out lowVal, out hiVal); double x = isXBase ? baseVal : hiVal; double y = isXBase ? hiVal : baseVal; if (x != PointPair.Missing && y != PointPair.Missing && lowVal != PointPair.Missing) { if (x < tXMinVal) { tXMinVal = x; } if (x > tXMaxVal) { tXMaxVal = x; } if (y < tYMinVal) { tYMinVal = y; } if (y > tYMaxVal) { tYMaxVal = y; } if (!isXBase) { if (lowVal < tXMinVal) { tXMinVal = lowVal; } if (lowVal > tXMaxVal) { tXMaxVal = lowVal; } } else { if (lowVal < tYMinVal) { tYMinVal = lowVal; } if (lowVal > tYMaxVal) { tYMaxVal = lowVal; } } } } }
/// <summary> /// Protected internal routine that draws the specified single bar (an individual "point") /// of this series to the specified <see cref="Graphics"/> device. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn.</param> /// <param name="index"> /// The zero-based index number for the single bar to be drawn. /// </param> /// <param name="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) /// in the cluster of bars. /// </param> /// <param name="baseAxis">The <see cref="Axis"/> class instance that defines the base (independent) /// axis for the <see cref="Bar"/></param> /// <param name="valueAxis">The <see cref="Axis"/> class instance that defines the value (dependent) /// axis for the <see cref="Bar"/></param> /// <param name="barWidth"> /// The width of each bar, in pixels. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> override protected void DrawSingleBar(Graphics g, GraphPane pane, CurveItem curve, int index, int pos, Axis baseAxis, Axis valueAxis, float barWidth, float scaleFactor) { //float scaledSize = GetBarWidth( pane, baseAxis, scaleFactor ); // pixBase = pixel value for the bar center on the base axis // pixValue = pixel value for the bar top on the value axis // pixLow = pixel value for the bar bottom on the value axis float pixBase, pixHiVal, pixLowVal; // curBase = the scale value on the base axis of the current bar // curValue = the scale value on the value axis of the current bar double curBase, curLowVal, curHiVal; ValueHandler valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(curve, index, out curBase, out curLowVal, out curHiVal); barWidth = GetBarWidth(pane, baseAxis, scaleFactor); // curLow = the scale value on the value axis for the bottom of the current bar // Get a "low" value for the bottom of the bar and verify validity if (curLowVal == PointPair.Missing || System.Double.IsNaN(curLowVal) || System.Double.IsInfinity(curLowVal)) { curLowVal = 0; } // 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 (!curve.Points[index].IsInvalid) { // calculate a pixel value for the top of the bar on value axis pixHiVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curHiVal); // calculate a pixel value for the center of the bar on the base axis pixBase = baseAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curBase); pixLowVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curLowVal); // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - barWidth / 2.0F; // Draw the bar if (baseAxis is XAxis || baseAxis is X2Axis) { this.Draw(g, pane, pixSide, pixSide + barWidth, pixLowVal, pixHiVal, scaleFactor, true, curve.IsSelected, curve.Points[index]); } else { this.Draw(g, pane, pixLowVal, pixHiVal, pixSide, pixSide + barWidth, scaleFactor, true, curve.IsSelected, curve.Points[index]); } } }
/// <summary> /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device as a symbol at each defined point. The routine /// only draws the symbols; the lines are draw by the /// <see cref="Line.DrawCurve"/> method. This method /// is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <param name="isSelected">Indicates that the <see cref="Symbol" /> should be drawn /// with attributes from the <see cref="Selection" /> class. /// </param> 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)) { 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 for (int i = 0; i < points.Count; i++) { if (points[i].Symbol == null && curve.Symbol != this) { continue; } if (points[i].Symbol != null && (points[i].Symbol != this)) { continue; } // 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 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; } }
/// <summary> /// Determine the coords for the rectangle associated with a specified point for /// this <see cref="CurveItem" /> /// </summary> /// <param name="pane">The <see cref="GraphPane" /> to which this curve belongs</param> /// <param name="i">The index of the point of interest</param> /// <param name="coords">A list of coordinates that represents the "rect" for /// this point (used in an html AREA tag)</param> /// <returns>true if it's a valid point, false otherwise</returns> override public bool GetCoords(GraphPane pane, int i, out string coords) { coords = string.Empty; if (i < 0 || i >= _points.Count) { return(false); } Axis valueAxis = ValueAxis(pane); Axis baseAxis = BaseAxis(pane); // pixBase = pixel value for the bar center on the base axis // pixHiVal = pixel value for the bar top on the value axis // pixLowVal = pixel value for the bar bottom on the value axis float pixBase, pixHiVal, pixLowVal; float clusterWidth = pane.BarSettings.GetClusterWidth(); float barWidth = GetBarWidth(pane); float clusterGap = pane._barSettings.MinClusterGap * barWidth; float barGap = barWidth * pane._barSettings.MinBarGap; // curBase = the scale value on the base axis of the current bar // curHiVal = the scale value on the value axis of the current bar // curLowVal = the scale value of the bottom of the bar double curBase, curLowVal, curHiVal; ValueHandler valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(this, i, out curBase, out curLowVal, out curHiVal); // 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 (!_points[i].IsInvalid3D) { // calculate a pixel value for the top of the bar on value axis pixLowVal = valueAxis.Scale.Transform(_isOverrideOrdinal, i, curLowVal); pixHiVal = valueAxis.Scale.Transform(_isOverrideOrdinal, i, curHiVal); // calculate a pixel value for the center of the bar on the base axis pixBase = baseAxis.Scale.Transform(_isOverrideOrdinal, i, curBase); // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - clusterWidth / 2.0F + clusterGap / 2.0F + pane.CurveList.GetBarItemPos(pane, this) * (barWidth + barGap); // Draw the bar if (baseAxis is XAxis || baseAxis is X2Axis) { coords = String.Format("{0:f0},{1:f0},{2:f0},{3:f0}", pixSide, pixLowVal, pixSide + barWidth, pixHiVal); } else { coords = String.Format("{0:f0},{1:f0},{2:f0},{3:f0}", pixLowVal, pixSide, pixHiVal, pixSide + barWidth); } return(true); } return(false); }
/// <summary> /// Create a <see cref="TextObj" /> for each bar in the <see cref="GraphPane" />. /// </summary> /// <remarks> /// This method will go through the bars, create a label that corresponds to the bar value, /// and place it on the graph depending on user preferences. This works for horizontal or /// vertical bars in clusters or stacks, but only for <see cref="BarItem" /> types. This method /// does not apply to <see cref="ErrorBarItem" /> or <see cref="HiLowBarItem" /> objects. /// Call this method only after calling <see cref="GraphPane.AxisChange()" />. /// </remarks> /// <param name="pane">The GraphPane in which to place the text labels.</param> /// <param name="isBarCenter">true to center the labels inside the bars, false to /// place the labels just above the top of the bar.</param> /// <param name="valueFormat">The double.ToString string format to use for creating /// the labels. /// </param> /// <param name="fontColor">The color in which to draw the labels</param> /// <param name="fontFamily">The string name of the font family to use for the labels</param> /// <param name="fontSize">The floating point size of the font, in scaled points</param> /// <param name="isBold">true for a bold font type, false otherwise</param> /// <param name="isItalic">true for an italic font type, false otherwise</param> /// <param name="isUnderline">true for an underline font type, false otherwise</param> public static void CreateBarLabels(GraphPane pane, bool isBarCenter, string valueFormat, string fontFamily, float fontSize, Color fontColor, bool isBold, bool isItalic, bool isUnderline) { bool isVertical = pane.BarSettings.Base == BarBase.X; // keep a count of the number of BarItems int curveIndex = 0; // Get a valuehandler to do some calculations for us ValueHandler valueHandler = new ValueHandler(pane, true); // Loop through each curve in the list foreach (CurveItem curve in pane.CurveList) { // work with BarItems only BarItem bar = curve as BarItem; if (bar != null) { IPointList points = curve.Points; // ADD JKB 9/21/07 // The labelOffset should depend on whether the curve is YAxis or Y2Axis. // JHC - Generalize to any value axis // Make the gap between the bars and the labels = 1.5% of the axis range float labelOffset; Scale scale = curve.ValueAxis(pane).Scale; labelOffset = (float)(scale._max - scale._min) * 0.015f; // Loop through each point in the BarItem for (int i = 0; i < points.Count; i++) { // Get the high, low and base values for the current bar // note that this method will automatically calculate the "effective" // values if the bar is stacked double baseVal, lowVal, hiVal; valueHandler.GetValues(curve, i, out baseVal, out lowVal, out hiVal); // Get the value that corresponds to the center of the bar base // This method figures out how the bars are positioned within a cluster float centerVal = (float)valueHandler.BarCenterValue(bar, bar.GetBarWidth(pane), i, baseVal, curveIndex); // Create a text label -- note that we have to go back to the original point // data for this, since hiVal and lowVal could be "effective" values from a bar stack string barLabelText = (isVertical ? points[i].Y : points[i].X).ToString(valueFormat); // Calculate the position of the label -- this is either the X or the Y coordinate // depending on whether they are horizontal or vertical bars, respectively float position; if (isBarCenter) { position = (float)(hiVal + lowVal) / 2.0f; } else if (hiVal >= 0) { position = (float)hiVal + labelOffset; } else { position = (float)hiVal - labelOffset; } // Create the new TextObj TextObj label; if (isVertical) { label = new TextObj(barLabelText, centerVal, position); } else { label = new TextObj(barLabelText, position, centerVal); } label.FontSpec.Family = fontFamily; // Configure the TextObj // CHANGE JKB 9/21/07 // CoordinateFrame should depend on whether curve is YAxis or Y2Axis. label.Location.CoordinateFrame = (isVertical && curve.IsY2Axis) ? CoordType.AxisXY2Scale : CoordType.AxisXYScale; label.FontSpec.Size = fontSize; label.FontSpec.FontColor = fontColor; label.FontSpec.IsItalic = isItalic; label.FontSpec.IsBold = isBold; label.FontSpec.IsUnderline = isUnderline; label.FontSpec.Angle = isVertical ? 90 : 0; label.Location.AlignH = isBarCenter ? AlignH.Center : (hiVal >= 0 ? AlignH.Left : AlignH.Right); label.Location.AlignV = AlignV.Center; label.FontSpec.Border.IsVisible = false; label.FontSpec.Fill.IsVisible = false; // Add the TextObj to the GraphPane pane.GraphObjList.Add(label); } curveIndex++; } } }
/// <summary> /// Draw the this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device. The format (stair-step or line) of the curve is /// defined by the <see cref="StepType"/> property. The routine /// only draws the line segments; the symbols are drawn by the /// <see cref="Symbol.Draw"/> method. This method /// is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> public void DrawCurve( Graphics g, GraphPane pane, CurveItem curve, float scaleFactor) { Line source = this; if ( curve.IsSelected ) source = Selection.Line; // switch to int to optimize drawing speed (per Dale-a-b) int tmpX, tmpY, lastX = int.MaxValue, lastY = int.MaxValue; double curX, curY, lowVal; PointPair curPt, lastPt = new PointPair(); bool lastBad = true; IPointList points = curve.Points; ValueHandler valueHandler = new ValueHandler( pane, false ); Axis yAxis = curve.GetYAxis( pane ); Axis xAxis = curve.GetXAxis( pane ); bool xIsLog = xAxis._scale.IsLog; bool yIsLog = yAxis._scale.IsLog; // switch to int to optimize drawing speed (per Dale-a-b) 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; using ( Pen pen = source.GetPen( pane, scaleFactor ) ) { if ( points != null && !_color.IsEmpty && this.IsVisible ) { //bool lastOut = false; bool isOut; bool isOptDraw = _isOptimizedDraw && points.Count > 1000; // (Dale-a-b) we'll set an element to true when it has been drawn bool[,] isPixelDrawn = null; if ( isOptDraw ) isPixelDrawn = new bool[maxX + 1, maxY + 1]; // Loop over each point in the curve for ( int i = 0; i < points.Count; i++ ) { curPt = points[i]; if ( pane.LineType == LineType.Stack ) { if ( !valueHandler.GetValues( curve, i, out curX, out lowVal, out curY ) ) { curX = PointPair.Missing; curY = PointPair.Missing; } } else { curX = curPt.X; curY = curPt.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 ) || ( xIsLog && curX <= 0.0 ) || ( yIsLog && curY <= 0.0 ) ) { // If the point is invalid, then make a linebreak only if IsIgnoreMissing is false // LastX and LastY are always the last valid point, so this works out lastBad = lastBad || !pane.IsIgnoreMissing; isOut = true; } else { // Transform the current point from user scale units to // screen coordinates tmpX = (int) xAxis.Scale.Transform( curve.IsOverrideOrdinal, i, curX ); tmpY = (int) yAxis.Scale.Transform( curve.IsOverrideOrdinal, i, curY ); // Maintain an array of "used" pixel locations to avoid duplicate drawing operations // contributed by Dale-a-b if ( isOptDraw && tmpX >= minX && tmpX <= maxX && tmpY >= minY && tmpY <= maxY ) // guard against the zoom-in case { if ( isPixelDrawn[tmpX, tmpY] ) continue; isPixelDrawn[tmpX, tmpY] = true; } isOut = ( tmpX < minX && lastX < minX ) || ( tmpX > maxX && lastX > maxX ) || ( tmpY < minY && lastY < minY ) || ( tmpY > maxY && lastY > maxY ); 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 ); else if ( !isOut ) { if ( !curve.IsSelected && this._gradientFill.IsGradientValueType ) { using ( Pen tPen = GetPen( pane, scaleFactor, lastPt ) ) { if ( this.StepType == StepType.NonStep ) { g.DrawLine( tPen, lastX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.ForwardStep ) { g.DrawLine( tPen, lastX, lastY, tmpX, lastY ); g.DrawLine( tPen, tmpX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.RearwardStep ) { g.DrawLine( tPen, lastX, lastY, lastX, tmpY ); g.DrawLine( tPen, lastX, tmpY, tmpX, tmpY ); } else if ( this.StepType == StepType.ForwardSegment ) { g.DrawLine( tPen, lastX, lastY, tmpX, lastY ); } else { g.DrawLine( tPen, lastX, tmpY, tmpX, tmpY ); } } } else { if ( this.StepType == StepType.NonStep ) { g.DrawLine( pen, lastX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.ForwardStep ) { g.DrawLine( pen, lastX, lastY, tmpX, lastY ); g.DrawLine( pen, tmpX, lastY, tmpX, tmpY ); } else if ( this.StepType == StepType.RearwardStep ) { g.DrawLine( pen, lastX, lastY, lastX, tmpY ); g.DrawLine( pen, lastX, tmpY, tmpX, tmpY ); } else if ( this.StepType == StepType.ForwardSegment ) { g.DrawLine( pen, lastX, lastY, tmpX, lastY ); } else if ( this.StepType == StepType.RearwardSegment ) { g.DrawLine( pen, lastX, tmpY, tmpX, tmpY ); } } } } catch { InterpolatePoint( g, pane, curve, lastPt, scaleFactor, pen, lastX, lastY, tmpX, tmpY ); } } lastPt = curPt; lastX = tmpX; lastY = tmpY; lastBad = false; //lastOut = isOut; } } } } }
// Call this method only after calling AxisChange() private void CreateStackBarLabels( GraphPane graphPane ) { float labelOffset = (float)( 0.02 * ( graphPane.XAxis.Scale.Max - graphPane.XAxis.Scale.Min ) ); ValueHandler valueHandler = new ValueHandler( graphPane, true ); int barIndex = -1; foreach ( CurveItem curve in graphPane.CurveList ) { if ( curve is BarItem ) { BarItem bar = curve as BarItem; barIndex++; float barWidth = bar.GetBarWidth( graphPane ); IPointList points = bar.Points; for ( int i = 0; i < points.Count; i++ ) { double labelYCoordinate = valueHandler.BarCenterValue( bar, barWidth, i, points[i].Y, barIndex ); double baseVal, lowVal, hiVal; valueHandler.GetValues( bar, i, out baseVal, out lowVal, out hiVal ); float labelXCoordinate = (float)( lowVal + hiVal ) / 2.0f; string barLabelText = ( points[i].X ).ToString( "N2" ); TextObj label = new TextObj( barLabelText, (float)labelXCoordinate, (float)labelYCoordinate ); label.Location.CoordinateFrame = CoordType.AxisXYScale; label.FontSpec.Size = 10; label.FontSpec.FontColor = Color.Black; label.Location.AlignH = AlignH.Left; label.Location.AlignV = AlignV.Center; label.FontSpec.Border.IsVisible = false; label.FontSpec.Fill.IsVisible = false; graphPane.GraphObjList.Add( label ); } } } }
private Point HandlePointValues( Point mousePt ) { int iPt; GraphPane pane; object nearestObj; using ( Graphics g = this.CreateGraphics() ) { if ( _masterPane.FindNearestPaneObject( mousePt, g, out pane, out nearestObj, out iPt ) ) { if ( nearestObj is CurveItem && iPt >= 0 ) { CurveItem curve = (CurveItem)nearestObj; // Provide Callback for User to customize the tooltips if ( this.PointValueEvent != null ) { string label = this.PointValueEvent( this, pane, curve, iPt ); if ( label != null && label.Length > 0 ) { this.SetToolTip(label, mousePt); this.EnableToolTip(); } else this.DisableToolTip(); } else { if ( curve is PieItem ) { this.SetToolTip(((PieItem)curve).Value.ToString(this._pointValueFormat), mousePt); } else { PointPair pt = curve.Points[iPt]; if ( pt.Tag is string ) this.SetToolTip((string)pt.Tag, mousePt); else { double xVal, yVal, lowVal; ValueHandler valueHandler = new ValueHandler( pane, false ); if ( ( curve is BarItem || curve is ErrorBarItem || curve is HiLowBarItem ) && pane.BarSettings.Base != BarBase.X ) valueHandler.GetValues( curve, iPt, out yVal, out lowVal, out xVal ); else valueHandler.GetValues( curve, iPt, out xVal, out lowVal, out yVal ); string xStr = MakeValueLabel( curve.GetXAxis( pane ), xVal, iPt, curve.IsOverrideOrdinal ); string yStr = MakeValueLabel( curve.GetYAxis( pane ), yVal, iPt, curve.IsOverrideOrdinal ); this.SetToolTip("( " + xStr + ", " + yStr + " )", mousePt); } } this.EnableToolTip(); } } else this.DisableToolTip(); } else this.DisableToolTip(); } return mousePt; }
private void Form1_Load(object sender, EventArgs e) { CreateGraph_BasicDate(); Trace.Listeners.Add(new TextWriterTraceListener( @"myTrace.txt" ) ); Trace.AutoFlush = true; memGraphics.CreateDoubleBuffer(this.CreateGraphics(), this.ClientRectangle.Width, this.ClientRectangle.Height); #if false // Multi Y Axis demo myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "Demonstration of Multi Y Graph", "Time, s", "Velocity, m/s" ); // Set the titles and axis labels myPane.Y2Axis.Title.Text = "Acceleration, m/s2"; // Make up some data _points based on the Sine function PointPairList vList = new PointPairList(); PointPairList aList = new PointPairList(); PointPairList dList = new PointPairList(); PointPairList eList = new PointPairList(); for ( int i=0; i<30; i++ ) { double time = (double) i; double acceleration = 2.0; double velocity = acceleration * time; double distance = acceleration * time * time / 2.0; double energy = 100.0 * velocity * velocity / 2.0; aList.Add( time, acceleration ); vList.Add( time, velocity ); eList.Add( time, energy ); dList.Add( time, distance ); } // Generate a red curve with diamond symbols, and "Velocity" in the _legend LineItem myCurve = myPane.AddCurve( "Velocity", vList, Color.Red, SymbolType.Diamond ); // Fill the symbols with white myCurve.Symbol.Fill = new Fill( Color.White ); // Generate a blue curve with circle symbols, and "Acceleration" in the _legend myCurve = myPane.AddCurve( "Acceleration", aList, Color.Blue, SymbolType.Circle ); // Fill the symbols with white myCurve.Symbol.Fill = new Fill( Color.White ); // Associate this curve with the Y2 axis myCurve.IsY2Axis = true; // Generate a green curve with square symbols, and "Distance" in the _legend myCurve = myPane.AddCurve( "Distance", dList, Color.Green, SymbolType.Square ); // Fill the symbols with white myCurve.Symbol.Fill = new Fill( Color.White ); // Associate this curve with the second Y axis myCurve.YAxisIndex = 1; // Generate a Black curve with triangle symbols, and "Energy" in the _legend myCurve = myPane.AddCurve( "Energy", eList, Color.Black, SymbolType.Triangle ); // Fill the symbols with white myCurve.Symbol.Fill = new Fill( Color.White ); // Associate this curve with the Y2 axis myCurve.IsY2Axis = true; // Associate this curve with the second Y2 axis myCurve.YAxisIndex = 1; // Show the x axis grid myPane.XAxis.MajorGrid.IsVisible = true; // Make the Y axis scale red myPane.YAxis.Scale.FontSpec.FontColor = Color.Red; myPane.YAxis.Title.FontSpec.FontColor = Color.Red; // turn off the opposite tics so the Y tics don't show up on the Y2 axis myPane.YAxis.MajorTic.IsOpposite = false; myPane.YAxis.MinorTic.IsOpposite = false; // Don't display the Y zero line myPane.YAxis.MajorGrid.IsZeroLine = false; // Align the Y axis labels so they are flush to the axis myPane.YAxis.Scale.Align = AlignP.Inside; myPane.YAxis.Scale.Max = 100; // Enable the Y2 axis display myPane.Y2Axis.IsVisible = true; // Make the Y2 axis scale blue myPane.Y2Axis.Scale.FontSpec.FontColor = Color.Blue; myPane.Y2Axis.Title.FontSpec.FontColor = Color.Blue; // turn off the opposite tics so the Y2 tics don't show up on the Y axis myPane.Y2Axis.MajorTic.IsOpposite = false; myPane.Y2Axis.MinorTic.IsOpposite = false; // Display the Y2 axis grid lines myPane.Y2Axis.MajorGrid.IsVisible = true; // Align the Y2 axis labels so they are flush to the axis myPane.Y2Axis.Scale.Align = AlignP.Inside; myPane.Y2Axis.Scale.Min = 1.5; myPane.Y2Axis.Scale.Max = 3; myPane.YAxis.IsVisible = true; //myPane.YAxis.IsTic = false; myPane.YAxis.MinorTic.IsOutside = false; myPane.YAxis.MajorTic.IsCrossOutside = false; myPane.YAxis.MinorTic.IsCrossOutside = false; myPane.YAxis.MajorTic.IsInside = false; myPane.YAxis.MinorTic.IsInside = false; myPane.YAxis.MajorTic.IsOpposite = false; myPane.YAxis.MinorTic.IsOpposite = false; // Create a second Y Axis, green YAxis yAxis3b = new YAxis( "Test Axis" ); myPane.YAxisList.Add( yAxis3b ); yAxis3b.Scale.FontSpec.FontColor = Color.Brown; yAxis3b.Title.FontSpec.FontColor = Color.Brown; yAxis3b.Color = Color.Brown; yAxis3b.MajorTic.IsOutside = false; yAxis3b.MinorTic.IsOutside = false; yAxis3b.MajorTic.IsOpposite = false; yAxis3b.MinorTic.IsOpposite = false; //yAxis3b.IsScaleLabelsInside = true; yAxis3b.Title.IsTitleAtCross = false; yAxis3b.MajorTic.IsInside = false; yAxis3b.MinorTic.IsInside = false; yAxis3b.MajorTic.IsOpposite = false; yAxis3b.MinorTic.IsOpposite = false; // Create a second Y Axis, green YAxis yAxis3c = new YAxis( "Test 2 Axis" ); myPane.YAxisList.Add( yAxis3c ); yAxis3c.Scale.FontSpec.FontColor = Color.Brown; yAxis3c.Title.FontSpec.FontColor = Color.Brown; yAxis3c.Color = Color.Brown; yAxis3c.MajorTic.IsOutside = false; yAxis3c.MinorTic.IsOutside = false; yAxis3c.MajorTic.IsOpposite = false; yAxis3c.MinorTic.IsOpposite = false; //yAxis3c.IsScaleLabelsInside = true; yAxis3c.Title.IsTitleAtCross = false; yAxis3c.MajorTic.IsInside = false; yAxis3c.MinorTic.IsInside = false; yAxis3c.MajorTic.IsOpposite = false; yAxis3c.MinorTic.IsOpposite = false; // Create a second Y Axis, green YAxis yAxis3 = new YAxis( "Distance, m" ); myPane.YAxisList.Add( yAxis3 ); yAxis3.Scale.FontSpec.FontColor = Color.Green; yAxis3.Title.FontSpec.FontColor = Color.Green; yAxis3.Color = Color.Green; // turn off the opposite tics so the Y2 tics don't show up on the Y axis yAxis3.MajorTic.IsInside = false; yAxis3.MinorTic.IsInside = false; yAxis3.MajorTic.IsOpposite = false; yAxis3.MinorTic.IsOpposite = false; // Align the Y2 axis labels so they are flush to the axis yAxis3.Scale.Align = AlignP.Inside; //yAxis3.AxisGap = 0; Y2Axis yAxis4 = new Y2Axis( "Energy" ); yAxis4.IsVisible = true; myPane.Y2AxisList.Add( yAxis4 ); // turn off the opposite tics so the Y2 tics don't show up on the Y axis yAxis4.MajorTic.IsInside = false; yAxis4.MinorTic.IsInside = false; yAxis4.MajorTic.IsOpposite = false; yAxis4.MinorTic.IsOpposite = false; // Align the Y2 axis labels so they are flush to the axis yAxis4.Scale.Align = AlignP.Inside; yAxis4.Type = AxisType.Log; yAxis4.Scale.Min = 100; // Fill the axis background with a gradient myPane.Chart.Fill = new Fill( Color.White, Color.LightGoldenrodYellow, 45.0f ); #endif #if false // SampleMultiPointList Demo myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "Demo for SampleMultiPointList", "Time", "Distance Traveled" ); SetSize(); SampleMultiPointList myList = new SampleMultiPointList(); myList.YData = PerfDataType.Distance; // note how it does not matter that we created the second list before actually // adding the data -- this is because the cloned list shares data with the // original SampleMultiPointList myList2 = new SampleMultiPointList( myList ); myList2.YData = PerfDataType.Velocity; for ( int i=0; i<20; i++ ) { double time = (double) i; double acceleration = 1.0; double velocity = acceleration * time; double distance = acceleration * time * time / 2.0; PerformanceData perfData = new PerformanceData( time, distance, velocity, acceleration ); myList.Add( perfData ); } myPane.AddCurve( "Distance", myList, Color.Blue ); myPane.AddCurve( "Velocity", myList2, Color.Red ); #endif #if false // GradientByZ myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "Wacky Widget Company\nProduction Report", "Time, Days\n(Since Plant Construction Startup)", "Widget Production\n(units/hour)" ); SetSize(); string[] ystr = { "one", "two", "three", "four", "five" }; double[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; double[] y = { .1, .2, .3, .4, .5, .4, .3, .2, .1, .2 }; //double[] y = { 20, 10, 50, 25, 35, 75, 90, 40, 33, 50 }; double[] z = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 }; PointPairList list = new PointPairList( x, y, z ); Color[] colors = { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange }; Fill fill = new Fill( colors ); fill.Type = FillType.GradientByZ; fill.RangeMin = 1; fill.RangeMax = 5; BarItem myBar = myPane.AddBar( "My Bar", list, Color.Tomato ); myBar.Bar.Fill = fill; myPane.XAxis.Type = AxisType.Ordinal; //myPane.YAxis.Type = AxisType.Text; //myPane.YAxis.TextLabels = ystr; //myPane.ClusterScaleWidth = 1; //myPane.AxisChange( this.CreateGraphics() ); #endif #if false // GradientByZ dual bars myPane = new GraphPane( new RectangleF(0,0,300,400), "Title", "X Label", "Y Label" ); double[] xx = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; double[] yy = { 1, 2, 3, 4, 5, 4, 3, 2, 1, 2 }; double[] yy2 = { 4, 5, 7, 8, 1, 3, 5, 2, 4, 9 }; double[] zz = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 }; double[] zz2 = { 5, 1, 4, 2, 3, 4, 2, 1, 5, 5 }; PointPairList list = new PointPairList( xx, yy, zz ); PointPairList list2 = new PointPairList( xx, yy2, zz2 ); Color[] colors = { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange }; Fill fill = new Fill( colors ); fill.Type = FillType.GradientByZ; fill.RangeMin = 1; fill.RangeMax = 5; BarItem myBar = myPane.AddBar( "My Bar", list, Color.Tomato ); myBar.Bar.Fill = fill; BarItem myBar2 = myPane.AddBar( "My Bar 2", list2, Color.Tomato ); myBar2.Bar.Fill = fill; myPane.XAxis.Type = AxisType.Ordinal; myPane.MinBarGap = 0.1f; //myPane.MinClusterGap = 0; myPane.AxisChange( this.CreateGraphics() ); #endif #if false // stacked bars Random rand = new Random(); myPane = new GraphPane(); // myPane.Title.Text = "My Title"; // myPane.XAxis.Title.Text = "X Axis"; // myPane.YAxis.Title.Text = "Y Axis"; PointPairList list1 = new PointPairList(); PointPairList list2 = new PointPairList(); PointPairList list3 = new PointPairList(); PointPairList list4 = new PointPairList(); for ( int i=1; i<5; i++ ) { double y = (double) i; double x1 = 100.0 + rand.NextDouble() * 100.0; double x2 = 100.0 + rand.NextDouble() * 100.0; double x3 = 100.0 + rand.NextDouble() * 100.0; double x4 = 100.0 + rand.NextDouble() * 100.0; list1.Add( x1, y ); list2.Add( x2, y ); list3.Add( x3, y ); list4.Add( x4, y ); } BarItem bar1 = myPane.AddBar( "Bar 1", list1, Color.Red ); BarItem bar2 = myPane.AddBar( "Bar 2", list2, Color.Blue ); BarItem bar3 = myPane.AddBar( "Bar 3", list3, Color.Green ); BarItem bar4 = myPane.AddBar( "Bar 4", list4, Color.Beige ); myPane.BarBase = BarBase.Y; myPane.BarType = BarType.Stack; myPane.AxisChange( this.CreateGraphics() ); this.CreateStackBarLabels( myPane ); #endif #if false // Bars and Dates // Color color = Color.FromArgb( 123, 45, 67, 89 ); // HSBColor hsbColor = new HSBColor( color ); // Color color2 = hsbColor; Random rand = new Random(); myPane = new GraphPane(); myPane.Title.Text = "My Title"; myPane.XAxis.Title.Text = "X Axis"; myPane.YAxis.Title.Text = "Y Axis"; //myPane.XAxis.Type = AxisType.Ordinal; //myPane.XAxis.Type = AxisType.Date; //myPane.ClusterScaleWidth = 0.75 / 1440.0; //myPane.XAxis.MinorStep = 1; //myPane.XAxis.MinorUnit = DateUnit.Minute; PointPairList list1 = new PointPairList(); PointPairList list2 = new PointPairList(); for ( int i=1; i<10; i++ ) { //double x = new XDate( 1995, 5, 10, 12, i+1, 0 ); double x = (double) i; double y1 = rand.NextDouble() * 100.0; double y2 = rand.NextDouble() * 100.0; list1.Add( x-0.25, y1, 0 ); list2.Add( x+0.17, y2, 0 ); } //myPane.AddCurve( "junk", list1, Color.Green ); HiLowBarItem bar1 = myPane.AddHiLowBar( "Bar 1", list1, Color.Red ); //bar1.Bar.Border.IsVisible = false; bar1.Bar.Size = 15; //bar1.Bar.Fill = new Fill( Color.Red ); HiLowBarItem bar2 = myPane.AddHiLowBar( "Bar 2", list2, Color.Blue ); //bar2.Bar.Border.IsVisible = false; //bar2.Bar.Fill = new Fill( Color.Blue ); bar2.Bar.Size = 10; MasterPane mPane = new MasterPane(); mPane.Add( myPane ); myPane.AxisChange( this.CreateGraphics() ); //this.CreateBarLabels(mPane); #endif #if false // bar test with no gap myPane = new GraphPane( new Rectangle( 40, 40, 600, 300 ), "Score Report", "", "" ); // Make up some random data points string[] labels = { "" }; double[] y = { 800, 900 }; double[] y2 = { 500 }; // Generate a red bar with "Curve 1" in the legend BarItem myBar = myPane.AddBar( null, y, null, Color.RoyalBlue ); // Generate a blue bar with "Curve 2" in the legend myBar = myPane.AddBar( null, y2, null, Color.Red ); // Draw the X tics between the labels instead of at the labels myPane.YAxis.IsTicsBetweenLabels = true; // Set the XAxis labels myPane.YAxis.TextLabels = labels; // Set the XAxis to Text type myPane.YAxis.Type = AxisType.Text; // Fill the Axis and Pane backgrounds myPane.Chart.Fill = new Fill( Color.White, Color.FromArgb( 255, 255, 166), 90F ); myPane.PaneFill = new Fill( Color.FromArgb( 250, 250, 255) ); myPane.BarBase = BarBase.Y; myPane.MinBarGap = 0; myPane.MinClusterGap = 1; // Tell ZedGraph to refigure the // axes since the data have changed myPane.AxisChange( CreateGraphics() ); #endif #if false // Standard Sample Graph myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "Wacky Widget Company\nProduction Report", "Time, Days\n(Since Plant Construction Startup)", "Widget Production\n(units/hour)" ); SetSize(); // Set the titles and axis labels myPane.Title.Text = "Wacky Widget Company\nProduction Report"; myPane.XAxis.Title.Text = "Time, Days\n(Since Plant Construction Startup)"; myPane.YAxis.Title.Text = "Widget Production\n(units/hour)"; LineItem curve; // Set up curve "Larry" double[] x = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; double[] y = { 20, 10, 50, 25, 35, 75, 90, 40, 33, 50 }; // Use green, with circle symbols curve = myPane.AddCurve( "Larry", x, y, Color.Green, SymbolType.Circle ); curve.Line.Width = 1.5F; // Fill the area under the curve with a white-green gradient curve.Line.Fill = new Fill( Color.White, Color.FromArgb( 60, 190, 50), 90F ); // Make it a smooth line curve.Line.IsSmooth = true; curve.Line.SmoothTension = 0.6F; // Fill the symbols with white curve.Symbol.Fill = new Fill( Color.White ); curve.Symbol.Size = 10; // Second curve is "moe" double[] x3 = { 150, 250, 400, 520, 780, 940 }; double[] y3 = { 5.2, 49.0, 33.8, 88.57, 99.9, 36.8 }; // Use a red color with triangle symbols curve = myPane.AddCurve( "Moe", x3, y3, Color.FromArgb( 200, 55, 135), SymbolType.Triangle ); curve.Line.Width = 1.5F; // Fill the area under the curve with semi-transparent pink using the alpha value curve.Line.Fill = new Fill( Color.White, Color.FromArgb( 160, 230, 145, 205), 90F ); // Fill the symbols with white curve.Symbol.Fill = new Fill( Color.White ); curve.Symbol.Size = 10; // Third Curve is a bar, called "Wheezy" double[] x4 = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; double[] y4 = { 30, 45, 53, 60, 75, 83, 84, 79, 71, 57 }; BarItem bar = myPane.AddBar( "Wheezy", x4, y4, Color.SteelBlue ); // Fill the bars with a RosyBrown-White-RosyBrown gradient bar.Bar.Fill = new Fill( Color.RosyBrown, Color.White, Color.RosyBrown ); // Fourth curve is a bar double[] x2 = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; double[] y2 = { 10, 15, 17, 20, 25, 27, 29, 26, 24, 18 }; bar = myPane.AddBar( "Curly", x2, y2, Color.RoyalBlue ); // Fill the bars with a RoyalBlue-White-RoyalBlue gradient bar.Bar.Fill = new Fill( Color.RoyalBlue, Color.White, Color.RoyalBlue ); // Fill the pane background with a gradient myPane.PaneFill = new Fill( Color.WhiteSmoke, Color.Lavender, 0F ); // Fill the axis background with a gradient myPane.Chart.Fill = new Fill( Color.FromArgb( 255, 255, 245), Color.FromArgb( 255, 255, 190), 90F ); // Make each cluster 100 user scale units wide. This is needed because the X Axis // type is Linear rather than Text or Ordinal myPane.ClusterScaleWidth = 100; // Bars are stacked myPane.BarType = BarType.Stack; // Enable the X and Y axis grids myPane.XAxis.IsShowGrid = true; myPane.YAxis.IsShowGrid = true; // Manually set the scale maximums according to user preference myPane.XAxis.Max = 1200; myPane.YAxis.Max = 120; // Add a text item to decorate the graph TextItem text = new TextItem("First Prod\n21-Oct-93", 175F, 80.0F ); // Align the text such that the Bottom-Center is at (175, 80) in user scale coordinates text.Location.AlignH = AlignH.Center; text.Location.AlignV = AlignV.Bottom; text.FontSpec.Fill = new Fill( Color.White, Color.PowderBlue, 45F ); text.FontSpec.StringAlignment = StringAlignment.Near; myPane.GraphItemList.Add( text ); // Add an arrow pointer for the above text item ArrowItem arrow = new ArrowItem( Color.Black, 12F, 175F, 77F, 100F, 45F ); arrow.Location.CoordinateFrame = CoordType.AxisXYScale; myPane.GraphItemList.Add( arrow ); // Add a another text item to to point out a graph feature text = new TextItem("Upgrade", 700F, 50.0F ); // rotate the text 90 degrees text.FontSpec.Angle = 90; // Align the text such that the Right-Center is at (700, 50) in user scale coordinates text.Location.AlignH = AlignH.Right; text.Location.AlignV = AlignV.Center; // Disable the border and background fill options for the text text.FontSpec.Fill.IsVisible = false; text.FontSpec.Border.IsVisible = false; myPane.GraphItemList.Add( text ); // Add an arrow pointer for the above text item arrow = new ArrowItem( Color.Black, 15, 700, 53, 700, 80 ); arrow.Location.CoordinateFrame = CoordType.AxisXYScale; arrow.PenWidth = 2.0F; myPane.GraphItemList.Add( arrow ); // Add a text "Confidential" stamp to the graph text = new TextItem("Confidential", 0.85F, -0.03F ); // use AxisFraction coordinates so the text is placed relative to the ChartRect text.Location.CoordinateFrame = CoordType.AxisFraction; // rotate the text 15 degrees text.FontSpec.Angle = 15.0F; // Text will be red, bold, and 16 point text.FontSpec.FontColor = Color.Red; text.FontSpec.IsBold = true; text.FontSpec.Size = 16; // Disable the border and background fill options for the text text.FontSpec.Border.IsVisible = false; text.FontSpec.Fill.IsVisible = false; // Align the text such the the Left-Bottom corner is at the specified coordinates text.Location.AlignH = AlignH.Left; text.Location.AlignV = AlignV.Bottom; myPane.GraphItemList.Add( text ); // Add a BoxItem to show a colored band behind the graph data BoxItem box = new BoxItem( new RectangleF( 0, 110, 1200, 10 ), Color.Empty, Color.FromArgb( 225, 245, 225) ); box.Location.CoordinateFrame = CoordType.AxisXYScale; // Align the left-top of the box to (0, 110) box.Location.AlignH = AlignH.Left; box.Location.AlignV = AlignV.Top; // place the box behind the axis items, so the grid is drawn on top of it box.ZOrder = ZOrder.E_BehindAxis; myPane.GraphItemList.Add( box ); // Add some text inside the above box to indicate "Peak Range" TextItem myText = new TextItem( "Peak Range", 1170, 105 ); myText.Location.CoordinateFrame = CoordType.AxisXYScale; myText.Location.AlignH = AlignH.Right; myText.Location.AlignV = AlignV.Center; myText.FontSpec.IsItalic = true; myText.FontSpec.IsBold = false; myText.FontSpec.Fill.IsVisible = false; myText.FontSpec.Border.IsVisible = false; myPane.GraphItemList.Add( myText ); // Calculate the Axis Scale Ranges Graphics g = this.CreateGraphics(); myPane.AxisChange( g ); g.Dispose(); #endif #if false // MasterPane master = new MasterPane( "ZedGraph MasterPane Example", new Rectangle( 10, 10, 10, 10 ) ); master.PaneFill = new Fill( Color.White, Color.MediumSlateBlue, 45.0F ); //master.IsShowTitle = true; //master.MarginAll = 10; //master.InnerPaneGap = 10; //master.Legend.IsVisible = true; //master.Legend.Position = LegendPos.TopCenter; /* TextItem text = new TextItem( "Priority", 0.88F, 0.12F ); text.Location.CoordinateFrame = CoordType.PaneFraction; text.FontSpec.Angle = 15.0F; text.FontSpec.FontColor = Color.Red; text.FontSpec.IsBold = true; text.FontSpec.Size = 16; text.FontSpec.Border.IsVisible = false; text.FontSpec.Border.Color = Color.Red; text.FontSpec.Fill.IsVisible = false; text.Location.AlignH = AlignH.Left; text.Location.AlignV = AlignV.Bottom; master.GraphItemList.Add( text ); text = new TextItem("DRAFT", 0.5F, 0.5F ); text.Location.CoordinateFrame = CoordType.PaneFraction; text.FontSpec.Angle = 30.0F; text.FontSpec.FontColor = Color.FromArgb( 70, 255, 100, 100 ); text.FontSpec.IsBold = true; text.FontSpec.Size = 100; text.FontSpec.Border.IsVisible = false; text.FontSpec.Fill.IsVisible = false; text.Location.AlignH = AlignH.Center; text.Location.AlignV = AlignV.Center; text.ZOrder = ZOrder.A_InFront; master.GraphItemList.Add( text ); */ ColorSymbolRotator rotator = new ColorSymbolRotator(); for ( int j=0; j<6; j++ ) { // Create a new graph with topLeft at (40,40) and size 600x400 GraphPane myPaneT = new GraphPane( new Rectangle( 40, 40, 600, 400 ), "Case #" + (j+1).ToString(), "Time, Days", "Rate, m/s" ); myPaneT.PaneFill = new Fill( Color.White, Color.LightYellow, 45.0F ); myPaneT.BaseDimension = 6.0F; // Make up some data arrays based on the Sine function double x, y; PointPairList list = new PointPairList(); for ( int i=0; i<36; i++ ) { x = (double) i + 5; y = 3.0 * ( 1.5 + Math.Sin( (double) i * 0.2 + (double) j ) ); list.Add( x, y ); } LineItem myCurve = myPaneT.AddCurve( "Type " + j.ToString(), list, rotator.NextColor, rotator.NextSymbol ); myCurve.Symbol.Fill = new Fill( Color.White ); master.Add( myPaneT ); } myPane = master[0]; Graphics g = this.CreateGraphics(); //master.AutoPaneLayout( g, PaneLayout.ExplicitRow32 ); //master.AutoPaneLayout( g, 2, 4 ); master.AutoPaneLayout( g, false, new int[] { 1, 3, 2 }, new float[] { 2, 1, 3 } ); master.AxisChange( g ); g.Dispose(); #endif #if false // MasterPane - Single Pane master = new MasterPane( "ZedGraph MasterPane Single Pane Example", new Rectangle( 10, 10, 10, 10 ) ); master.Fill = new Fill( Color.White, Color.MediumSlateBlue, 45.0F ); // Create a new graph with topLeft at (40,40) and size 600x400 GraphPane myPaneT = new GraphPane( new Rectangle( 40, 40, 600, 400 ), "Case 1", "Time, Days", "Rate, m/s" ); myPaneT.Fill = new Fill( Color.White, Color.LightYellow, 45.0F ); myPaneT.BaseDimension = 6.0F; // Make up some data arrays based on the Sine function double x, y; PointPairList list = new PointPairList(); for ( int i=0; i<36; i++ ) { x = (double) i + 5; y = 3.0 * ( 1.5 + Math.Sin( (double) i * 0.2 ) ); list.Add( x, y ); } LineItem myCurve = myPaneT.AddCurve( "Type 1", list, Color.Blue, SymbolType.Circle ); myCurve.Symbol.Fill = new Fill( Color.White ); master.Add( myPaneT ); Graphics g = this.CreateGraphics(); master.Title.IsVisible = false; master.Margin.All = 0; //master.AutoPaneLayout( g, PaneLayout.ExplicitRow32 ); //master.AutoPaneLayout( g, 2, 4 ); master.AutoPaneLayout( g ); //master.AutoPaneLayout( g, false, new int[] { 1, 3, 2 }, new float[] { 2, 1, 3 } ); master.AxisChange( g ); g.Dispose(); #endif #if false // Pie myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "2004 ZedGraph Sales by Region\n($M)", "", "" ); myPane.FontSpec.IsItalic = true; myPane.FontSpec.Size = 24f; myPane.FontSpec.Family = "Times"; myPane.PaneFill = new Fill( Color.White, Color.Goldenrod, 45.0f ); myPane.Chart.Fill.Type = FillType.None; myPane.Legend.Position = LegendPos.Float ; myPane.Legend.Location = new Location( 0.95f, 0.15f, CoordType.PaneFraction, AlignH.Right, AlignV.Top ); myPane.Legend.FontSpec.Size = 10f; myPane.Legend.IsHStack = false; PieItem segment1 = myPane.AddPieSlice( 20, Color.Navy, Color.White, 45f, 0, "North" ); PieItem segment3 = myPane.AddPieSlice( 30, Color.Purple, Color.White, 45f, .0, "East" ); PieItem segment4 = myPane.AddPieSlice( 10.21, Color.LimeGreen, Color.White, 45f, 0, "West" ); PieItem segment2 = myPane.AddPieSlice( 40, Color.SandyBrown, Color.White, 45f, 0.2, "South" ); PieItem segment6 = myPane.AddPieSlice( 250, Color.Red, Color.White, 45f, 0, "Europe" ); PieItem segment7 = myPane.AddPieSlice( 50, Color.Blue, Color.White, 45f, 0.2, "Pac Rim" ); PieItem segment8 = myPane.AddPieSlice( 400, Color.Green, Color.White, 45f, 0, "South America" ); PieItem segment9 = myPane.AddPieSlice( 50, Color.Yellow, Color.White, 45f, 0.2, "Africa" ); segment2.LabelDetail.FontSpec.FontColor = Color.Red ; CurveList curves = myPane.CurveList ; double total = 0 ; for ( int x = 0 ; x < curves.Count ; x++ ) total += ((PieItem)curves[x]).Value ; TextItem text = new TextItem( "Total 2004 Sales\n" + "$" + total.ToString () + "M", 0.18F, 0.40F, CoordType.PaneFraction ); text.Location.AlignH = AlignH.Center; text.Location.AlignV = AlignV.Bottom; text.FontSpec.Border.IsVisible = false ; text.FontSpec.Fill = new Fill( Color.White, Color.FromArgb( 255, 100, 100 ), 45F ); text.FontSpec.StringAlignment = StringAlignment.Center ; myPane.GraphItemList.Add( text ); TextItem text2 = new TextItem( text ); text2.FontSpec.Fill = new Fill( Color.Black ); text2.Location.X += 0.008f; text2.Location.Y += 0.01f; myPane.GraphItemList.Add( text2 ); myPane.AxisChange( this.CreateGraphics() ); #endif #if false // simple pie myPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "People Signed Up", "", "" ); // Create some pie slices PieItem segment1 = myPane.AddPieSlice(8, Color.Green, .3, "Signed Up"); PieItem segment2 = myPane.AddPieSlice(5,Color.Red, 0.0, "Still Needed"); //segment1.FontSpec = new FontSpec("GenericSansSerif", 35, Color.Black, true, false, false); // Sum up the values CurveList curves = myPane.CurveList; double total = 0; for (int x = 0; x < curves.Count; x++) total += ((PieItem)curves[x]).Value; myPane.PaneBorder.IsVisible = false; myPane.Legend.Border.IsVisible = false; myPane.Legend.Position = LegendPos.TopCenter; // ArrowItem arrow = new ArrowItem( (float) new XDate(2007,1,1), 0, (float) new XDate(2007,1,1), 50 ); #endif #if false // Commerical Sales Graph myPane = new GraphPane( new RectangleF(0,0,10,10), "Sales Growth Compared to\nActual Sales by Store Size - Rank Order Low to High", "", "" ); myPane.MarginAll = 20; myPane.FontSpec.Size = 10; myPane.Legend.Position = LegendPos.BottomCenter; myPane.Legend.FontSpec.Size = 7; Random rand = new Random(); double y1 = 184; PointPairList list1 = new PointPairList(); PointPairList list2 = new PointPairList(); PointPairList trendList = new PointPairList(); PointPairList projList = new PointPairList(); string[] labels = new string[26]; for ( int i=0; i<26; i++ ) { double h1 = rand.NextDouble() * 10 - 2; double h2 = rand.NextDouble() * 10 - 2; double h3 = rand.NextDouble() * 8; double y2 = y1 + ( rand.NextDouble() * 2 - 1 ); trendList.Add( i + 1.0 - 0.2, y1 ); trendList.Add( i + 1.0 + 0.2, y2 ); list1.Add( (double) i, y1+h1, y1 ); list2.Add( (double) i, y2+h2, y2 ); projList.Add( (double) i + 1.0 - 0.35, y1 + h3 ); projList.Add( (double) i + 1.0 + 0.35, y1 + h3 ); projList.Add( PointPair.Missing, PointPair.Missing ); labels[i] = "Store " + (i+1).ToString(); y1 += rand.NextDouble() * 4.0; } PointPairList minTargList = new PointPairList(); minTargList.Add( 0.7, 218 ); minTargList.Add( 26.3, 218 ); PointPairList prefTargList = new PointPairList(); prefTargList.Add( 0.7, 228 ); prefTargList.Add( 26.3, 228 ); LineItem minCurve = myPane.AddCurve("Minimum Target", minTargList, Color.Green, SymbolType.None ); minCurve.Line.Width = 3.0f; minCurve.IsOverrideOrdinal = true; LineItem prefCurve = myPane.AddCurve("Preferred Target", prefTargList, Color.Blue, SymbolType.None ); prefCurve.Line.Width = 3.0f; prefCurve.IsOverrideOrdinal = true; LineItem projCurve = myPane.AddCurve( "Projected Sales", projList, Color.Orange, SymbolType.None ); projCurve.IsOverrideOrdinal = true; projCurve.Line.Width = 3.0f; LineItem myCurve = myPane.AddCurve( "Trendline", trendList, Color.FromArgb( 50, 50, 50 ), SymbolType.None ); myCurve.Line.Width = 2.5f; myCurve.Line.IsSmooth = true; myCurve.Line.SmoothTension = 0.3f; myCurve.IsOverrideOrdinal = true; BarItem myBar = myPane.AddBar( "Store Growth", list1, Color.Black ); //myBar.Bar.Fill = new Fill( Color.Black ); BarItem myBar2 = myPane.AddBar( "Average Growth", list2, Color.LightGray ); //myBar2.Bar.Fill = new Fill( Color.LightGray ); myPane.XAxis.Type = AxisType.Text; myPane.XAxis.TextLabels = labels; myPane.XAxis.Scale.FontSpec.Angle = -90; myPane.XAxis.Scale.FontSpec.Size = 8; myPane.XAxis.Scale.FontSpec.IsBold = true; myPane.XAxis.IsTicsBetweenLabels = true; myPane.XAxis.IsInsideTic = false; myPane.XAxis.IsOppositeTic = false; myPane.XAxis.IsMinorInsideTic = false; myPane.XAxis.IsMinorOppositeTic = false; myPane.YAxis.Scale.FontSpec.Size = 8; myPane.YAxis.Scale.FontSpec.IsBold = true; myPane.YAxis.IsShowGrid = true; myPane.YAxis.GridDashOn = 1.0f; myPane.YAxis.GridDashOff = 0.0f; myPane.BarType = BarType.ClusterHiLow; myPane.AxisChange( this.CreateGraphics() ); myPane.YAxis.MinorStep = myPane.YAxis.Step; #endif #if false // Basic curve test - Images as symbols myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<10; i++ ) { double x = (double) i; double y = Math.Sin( x / 8.0 ); list.Add( x, y ); } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); Bitmap bm = new Bitmap( @"c:\windows\winnt256.bmp" ); Image image = Image.FromHbitmap( bm.GetHbitmap() ); myCurve.Line.IsVisible = false; myCurve.Symbol.Type = SymbolType.Square; myCurve.Symbol.Size = 16; myCurve.Symbol.Border.IsVisible = false; myCurve.Symbol.Fill = new Fill( image, WrapMode.Clamp ); myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 359; trackBar1.Value = 0; #endif #if false // Stick Item Test myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<100; i++ ) { double x = (double) i; double y = Math.Sin( i / 8.0 ); double z = Math.Abs(Math.Cos( i / 8.0 )) * y; list.Add( x, y, z ); } StickItem myCurve = myPane.AddStick( "curve", list, Color.Blue ); myCurve.Line.Width = 2.0f; myPane.XAxis.IsShowGrid = true; myPane.XAxis.Max = 100; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 359; trackBar1.Value = 0; #endif #if false // Basic curve test - Dual Y axes myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); myPane.Y2Axis.Title.Text = "My Y2 Axis"; PointPairList list = new PointPairList(); PointPairList list2 = new PointPairList(); for ( int i=0; i<100; i++ ) { double x = (double) i; double y = Math.Sin( i / 8.0 ) * 100000 + 150000; double y2 = Math.Sin( i / 3.0 ) * 300 - 400; list.Add( x, y ); list2.Add( x, y2 ); //double z = Math.Abs( Math.Cos( i / 8.0 ) ) * y; } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); LineItem myCurve2 = myPane.AddCurve( "curve2", list2, Color.Red, SymbolType.Diamond ); myCurve2.IsY2Axis = true; myPane.Y2Axis.IsVisible = true; AlignYZeroLines( myPane, 12 ); myPane.YAxis.IsMinorOppositeTic = false; myPane.Y2Axis.IsMinorOppositeTic = false; trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic curve test - Multi-Y axes myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); myPane.AddYAxis( "Another Y Axis" ); myPane.AddY2Axis( "Another Y2 Axis" ); myPane.Y2Axis.Title.Text = "My Y2 Axis"; myPane.Y2AxisList[0].IsVisible = true; myPane.Y2AxisList[1].IsVisible = true; PointPairList list = new PointPairList(); for ( int i=0; i<100; i++ ) { //double x = (double) i; double x = new XDate( 2001, 1, i*3 ); double y = Math.Sin( i / 8.0 ) * 100000 + 100001; list.Add( x, y ); double z = Math.Abs( Math.Cos( i / 8.0 ) ) * y; } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); myCurve.YAxisIndex = 1; myPane.XAxis.IsSkipLastLabel = false; myPane.XAxis.Type = AxisType.DateAsOrdinal; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic curve test - Date Axis w/ Time Span myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<100; i++ ) { double x = (double) i/123.0; //double x = new XDate( 0, 0, i, i*3, i*2, i ); double y = Math.Sin( i / 8.0 ) * 1 + 1; list.Add( x, y ); double z = Math.Abs( Math.Cos( i / 8.0 ) ) * y; } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); myPane.XAxis.IsSkipLastLabel = false; //myPane.XAxis.IsPreventLabelOverlap = false; myPane.XAxis.ScaleFormat = "[d].[h]:[m]:[s]"; myPane.XAxis.Type = AxisType.Date; myPane.AxisChange( this.CreateGraphics() ); myPane.YAxis.ScaleFormat = "0.0'%'"; trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; myPane.PaneFill = new Fill( Color.FromArgb( 100, Color.Blue ), Color.FromArgb( 100, Color.White ), 45.0f ); #endif #if false // Basic curve test - DateAsOrdinal myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<100; i++ ) { //double x = (double) i; double x = new XDate( 2001, 1, i*3 ); double y = Math.Sin( i / 8.0 ) * 100000 + 100001; list.Add( x, y ); double z = Math.Abs( Math.Cos( i / 8.0 ) ) * y; } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); myPane.XAxis.IsSkipLastLabel = false; myPane.XAxis.IsPreventLabelOverlap = false; myPane.XAxis.ScaleFormat = ZedGraph.Axis.Default.FormatDayDay; myPane.XAxis.Type = AxisType.DateAsOrdinal; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic curve test - Linear Axis myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<20; i++ ) { double x = (double) i; double y = Math.Sin( x / 8.0 ); double z = Math.Abs(Math.Cos( i / 8.0 )) * y; list.Add( x, y, z ); } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); //myPane.XAxis.Min = 1; //myPane.XAxis.Max = 100; //myPane.XAxis.IsReverse = true; //myPane.XAxis.Type = AxisType.Log; //RectangleF rect = new RectangleF( 3, 0.7, 8, 0.2 ); myPane.AxisChange( this.CreateGraphics() ); BoxItem m_selectionBox = new BoxItem(); // rect ); m_selectionBox.Border.Color = Color.Orange; m_selectionBox.Border.IsVisible = true; m_selectionBox.Fill.Color = Color.LightYellow; m_selectionBox.Fill.Type = FillType.Solid; m_selectionBox.Fill.RangeMin = 1.0; m_selectionBox.Fill.RangeMax = 1.0; m_selectionBox.Fill.IsVisible = true; m_selectionBox.Location = new Location( (float)3, (float)myPane.YAxis.Max, (float)(8 - 3), (float)myPane.YAxis.Max - (float)myPane.YAxis.Min, CoordType.AxisXYScale, AlignH.Left, AlignV.Top); m_selectionBox.IsClippedToChartRect = true; m_selectionBox.ZOrder = ZOrder.E_BehindAxis; m_selectionBox.IsVisible = true; myPane.GraphItemList.Add( m_selectionBox ); #endif #if false // Box and Whisker diagram myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); // Throw some data points on the chart for good looks PointPairList list = new PointPairList(); for ( int i=0; i<20; i++ ) { double x = (double) i * 5; double y = Math.Sin( x / 8.0 ); list.Add( x, y ); } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); myCurve.Line.IsVisible = false; // Horizontal box and whisker chart // yval is the vertical position of the box & whisker double yval = 0.3; // pct5 = 5th percentile value double pct5 = 5; // pct25 = 25th percentile value double pct25 = 40; // median = median value double median = 55; // pct75 = 75th percentile value double pct75 = 80; // pct95 = 95th percentile value double pct95 = 95; // Draw the box PointPairList list2 = new PointPairList(); list2.Add( pct25, yval, median ); list2.Add( median, yval, pct75 ); HiLowBarItem myBar = myPane.AddHiLowBar( "box", list2, Color.Black ); // set the size of the box (in points, scaled to graph size) myBar.Bar.Size = 20; myBar.Bar.Fill.IsVisible = false; myPane.BarBase = BarBase.Y; // Draw the whiskers double[] xwhisk = { pct5, pct25, PointPair.Missing, pct75, pct95 }; double[] ywhisk = { yval, yval, yval, yval, yval }; PointPairList list3 = new PointPairList(); list3.Add( xwhisk, ywhisk ); LineItem mywhisk = myPane.AddCurve( "whisker", list3, Color.Black, SymbolType.None ); myPane.AxisChange( this.CreateGraphics() ); #endif #if false // Basic curve test - Linear Axis with Many Points myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<100000; i++ ) { double x = (double) i; double y = Math.Sin( x / 8.0 ); double z = Math.Abs(Math.Cos( i / 8.0 )) * y; list.Add( x, y, z ); } LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.HDash ); myCurve.Symbol.IsVisible = false; //myPane.XAxis.Min = 1; //myPane.XAxis.Max = 100; //myPane.XAxis.IsReverse = true; //myPane.XAxis.Type = AxisType.Log; //RectangleF rect = new RectangleF( 3, 0.7, 8, 0.2 ); Graphics g = this.CreateGraphics(); myPane.AxisChange( g ); SetSize(); int startTick = Environment.TickCount; myPane.Draw( g ); int endTick = Environment.TickCount; MessageBox.Show( "ticks = " + ( endTick - startTick ).ToString() ); #endif #if false // Gantt Chart myPane = new GraphPane(); myPane.Title.Text = "Gantt Chart"; myPane.XAxis.Title.Text = "Date"; myPane.YAxis.Title.Text = "Project"; myPane.XAxis.Type = AxisType.Date; myPane.YAxis.Type = AxisType.Text; myPane.BarBase = BarBase.Y; string[] labels = { "Project 1", "Project 2" }; myPane.YAxis.TextLabels = labels; myPane.YAxis.IsTicsBetweenLabels = true; // First, define all the bars that you want to be red PointPairList ppl = new PointPairList(); XDate start = new XDate( 2005, 10, 31 ); XDate end = new XDate( 2005, 11, 15 ); // x is start of bar, y is project number, z is end of bar // Define this first one using start/end variables for illustration ppl.Add( start, 1.0, end ); // add another red bar, assigned to project 2 // Didn't use start/end variables here, but it's the same concept ppl.Add( new XDate( 2005, 12, 16 ), 2.0, new XDate( 2005, 12, 31 ) ); HiLowBarItem myBar = myPane.AddHiLowBar( "job 1", ppl, Color.Red ); // This tells the bar that we want to manually define the Y position // Y is AxisType.Text, which is ordinal, so a Y value of 1.0 goes with the first label, // 2.0 with the second, etc. myBar.IsOverrideOrdinal = true; myBar.Bar.Fill = new Fill( Color.Red, Color.White, Color.Red, 90.0f ); // This size is the width of the bar myBar.Bar.Size = 20f; // Now, define all the bars that you want to be Green ppl = new PointPairList(); ppl.Add( new XDate( 2005, 11,16 ), 2.0, new XDate( 2005, 11, 26 ) ); myBar = myPane.AddHiLowBar( "job 2", ppl, Color.Green ); myBar.IsOverrideOrdinal = true; myBar.Bar.Fill = new Fill( Color.Green, Color.White, Color.Green, 90.0f ); myBar.Bar.Size = 20f; // Define all the bars that you want to be blue ppl = new PointPairList(); ppl.Add( new XDate( 2005, 11, 27 ), 1.0, new XDate( 2005, 12, 15 ) ); myBar = myPane.AddHiLowBar( "job 3", ppl, Color.Blue ); myBar.IsOverrideOrdinal = true; myBar.Bar.Fill = new Fill( Color.Blue, Color.White, Color.Blue, 90.0f ); myBar.Bar.Size = 20f; #endif #if false // DeSerialize DeSerialize( out myPane, @"c:\temp\myZedGraphFile" ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; myPane.AxisChange( this.CreateGraphics() ); #endif #if false // spline test myPane = new GraphPane(); PointPairList ppl = new PointPairList(); ppl.Add( 0, 713 ); ppl.Add( 7360, 333 ); ppl.Add( 10333.333, 45.333336 ); ppl.Add( 11666.667, 5 ); ppl.Add( 12483.333, 45.333336 ); ppl.Add( 13600, 110 ); ppl.Add( 15800, 184.66667 ); ppl.Add( 18644.998, 186.33368 ); ppl.Add( 18770.002, 186.66664 ); ppl.Add( 18896.666, 187.08336 ); ppl.Add( 18993.334, 187.50002 ); ppl.Add( 19098.332, 188.08334 ); ppl.Add( 19285.002, 189.41634 ); ppl.Add( 19443.332, 190.83334 ); ppl.Add( 19633.334, 193.16634 ); ppl.Add( 19823.336, 196.49983 ); ppl.Add( 19940.002, 199.16669 ); ppl.Add( 20143.303, 204.66566 ); ppl.Add( 20350, 210.91667 ); ppl.Add( 36000, 713 ); LineItem curve = myPane.AddCurve( "test", ppl, Color.Green, SymbolType.Default ); curve.Line.IsSmooth = true; curve.Line.SmoothTension = 0.4F; trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // hilowbar test myPane = new GraphPane(); myPane.Title.Text = "Bar Type Sample"; myPane.XAxis.Title.Text = "Text Axis"; myPane.YAxis.Title.Text = "Some Data Value"; myPane.XAxis.Type = AxisType.Text; myPane.ClusterScaleWidth = 1.0; //myPane.BarType = BarType.Overlay; myPane.FontSpec.Size = 18; myPane.YAxis.TitleFontSpec.Size = 16; myPane.XAxis.TitleFontSpec.Size = 16; string[] labels = { "North", "South", "East", "West", "Up", "Down" }; myPane.XAxis.TextLabels = labels; //Random rand = new Random(); double[] xArray = { 3, 5, 9, 11, 16, 18 }; double[] xArray2 = { 10, 12, 13, 15, 17, 19 }; double[] yArray = { 10, 45, 78, 34, 15, 26 }; double[] yArray2 = { 54, 34, 64, 24, 44, 74 }; PointPairList list1 = new PointPairList( xArray, yArray ); PointPairList list2 = new PointPairList( xArray2, yArray2 ); /* for ( int i = 0; i < 6; i++ ) { double x = xArray[i]; double y1 = rand.NextDouble() * 1.0 + .00001; double y2 = rand.NextDouble() * 1.0 + .00001; list1.Add( x, y1 ); list2.Add( x, y2 ); } */ HiLowBarItem bar1 = myPane.AddHiLowBar( "First", list1, Color.Blue ); HiLowBarItem bar2 = myPane.AddHiLowBar( "Second", list2, Color.Red ); //myPane.YAxis.Type = AxisType.Log; //myPane.BarType = BarType.ClusterHiLow; //myPane.XAxis.Scale.ScaleFormatEvent += new Scale.ScaleFormatHandler( CustomFormatter ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; myPane.AxisChange( this.CreateGraphics() ); #endif #if false // Basic bar test - Linear myPane = new GraphPane(); myPane.Title.Text = "BarItem Sample (BarType.ClusterHiLow)"; myPane.XAxis.Title.Text = "Text Axis"; myPane.YAxis.Title.Text = "Some Data Value"; myPane.XAxis.Type = AxisType.Text; myPane.ClusterScaleWidth = 2.0; myPane.BarType = BarType.ClusterHiLow; myPane.FontSpec.Size = 18; myPane.YAxis.TitleFontSpec.Size = 16; myPane.XAxis.TitleFontSpec.Size = 16; string[] labels = { "North", "South", "East", "West", "Up", "Down" }; myPane.XAxis.TextLabels = labels; //Random rand = new Random(); //double[] xArray = { 3, 5, 9, 11, 16, 18 }; double[] xArray = { 1, 1.8, 3.2, 4, 5, 6 }; double[] xArray2 = { 10, 12, 13, 15, 17, 19 }; double[] yArray = { 10, 75, 25, 16, 15, 26 }; double[] yArray2 = { 54, 62, 44, 24, 44, 74 }; double[] ylArray2 = { 34, 42, 15, 0, 5, 20 }; double[] yArray3 = { 54, 62, 44, 24, 34, 74 }; double[] ylArray3 = { 44, 42, 14, 14, 14, 34 }; PointPairList list1 = new PointPairList( xArray, yArray, ylArray2 ); PointPairList list2 = new PointPairList( xArray, yArray2, ylArray2 ); PointPairList list3 = new PointPairList( xArray, yArray3, ylArray3 ); /* for ( int i = 0; i < 6; i++ ) { double x = xArray[i]; double y1 = rand.NextDouble() * 1.0 + .00001; double y2 = rand.NextDouble() * 1.0 + .00001; list1.Add( x, y1 ); list2.Add( x, y2 ); } */ //ErrorBarItem bar1 = myPane.AddErrorBar( "First", list3, Color.Blue ); //bar1.ErrorBar.Symbol.Size = 12; //bar1.ErrorBar.PenWidth = 2; //HiLowBarItem bar1 = myPane.AddHiLowBar( "First", list3, Color.Blue ); //bar1.Bar.Size = 20; BarItem bar1 = myPane.AddBar( "First", list1, Color.Blue ); BarItem bar2 = myPane.AddBar( "Second", list2, Color.Red ); //myPane.YAxis.Type = AxisType.Log; //myPane.BarType = BarType.ClusterHiLow; //myPane.XAxis.Scale.ScaleFormatEvent += new Scale.ScaleFormatHandler( CustomFormatter ); myPane.YAxis.IsTitleAtCross = false; trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; myPane.AxisChange( this.CreateGraphics() ); #endif #if false // Bars - different colors thru IsOverrideOrdinal myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); /* PointPairList list1 = new PointPairList(); list1.Add(1,13); HiLowBarItem bar1 = myPane.AddHiLowBar( "First", list1, Color.Blue ); PointPairList list2 = new PointPairList(); list2.Add(2,22); HiLowBarItem bar2 = myPane.AddHiLowBar( "Second", list2, Color.Red ); bar1.Bar.Size = 30; bar1.IsOverrideOrdinal = true; bar2.Bar.Size = 30; bar2.IsOverrideOrdinal = true; */ PointPairList list1 = new PointPairList(); list1.Add(1,13); BarItem bar1 = myPane.AddBar( "First", list1, Color.Blue ); PointPairList list2 = new PointPairList(); list2.Add(2,22); BarItem bar2 = myPane.AddBar( "Second", list2, Color.Red ); bar1.IsOverrideOrdinal = true; bar2.IsOverrideOrdinal = true; myPane.Legend.Position = LegendPos.TopFlushLeft; myPane.BarType = BarType.Overlay; myPane.XAxis.Type = AxisType.Text; string[] labels = { "Label1", "Label2" }; myPane.XAxis.TextLabels = labels; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic curve test - two text axes myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); double[] y = { 2, 4, 1, 5, 3 }; LineItem myCurve = myPane.AddCurve( "curve 1", null, y, Color.Blue, SymbolType.Diamond ); myCurve.IsOverrideOrdinal = true; myPane.XAxis.Type = AxisType.Text; myPane.YAxis.Type = AxisType.Text; string[] xLabels = { "one", "two", "three", "four", "five" }; string[] yLabels = { "alpha", "bravo", "charlie", "delta", "echo" }; //myPane.XAxis.TextLabels = xLabels; //myPane.YAxis.TextLabels = yLabels; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic horizontal bar test myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); for ( int i=0; i<5; i++ ) { double y = (double) i; //double x = new XDate( 2001, 1, i*3 ); double x = Math.Sin( i / 8.0 ) * 100000 + 100001; list.Add( x, y ); //double z = Math.Abs( Math.Cos( i / 8.0 ) ) * y; } PointPairList list2 = new PointPairList( list ); PointPairList list3 = new PointPairList( list ); BarItem myCurve = myPane.AddBar( "curve 1", list, Color.Blue ); BarItem myCurve2 = myPane.AddBar( "curve 2", list2, Color.Red ); BarItem myCurve3 = myPane.AddBar( "curve 3", list3, Color.Green ); //myPane.XAxis.IsSkipLastLabel = false; //myPane.XAxis.IsPreventLabelOverlap = false; //myPane.XAxis.ScaleFormat = "dd/MM HH:mm"; //myPane.XAxis.Type = AxisType.Date; myPane.BarType = BarType.PercentStack; myPane.BarBase = BarBase.Y; myPane.AxisChange( this.CreateGraphics() ); ValueHandler valueHandler = new ValueHandler(myPane, true); const float shift = 0; int iOrd = 0; foreach (CurveItem oCurveItem in myPane.CurveList) { BarItem oBarItem = oCurveItem as BarItem; if (oBarItem != null) { PointPairList oPointPairList = oCurveItem.Points as PointPairList; for (int i=0; i<oPointPairList.Count; i++) { double xVal = oPointPairList[i].X; string sLabel = string.Concat(xVal.ToString("F0"), "%"); double yVal = valueHandler.BarCenterValue(oCurveItem, oCurveItem.GetBarWidth(myPane), i, oPointPairList[i].Y, iOrd); double x1, x2, y; valueHandler.GetValues( oCurveItem, i, out y, out x1, out x2 ); xVal = ( x1 + x2 ) / 2.0; TextItem oTextItem = new TextItem(sLabel, (float) xVal + (xVal > 0 ? shift : -shift ), (float) yVal); oTextItem.Location.CoordinateFrame = CoordType.AxisXYScale; oTextItem.Location.AlignH = AlignH.Center; oTextItem.Location.AlignV = AlignV.Center; oTextItem.FontSpec.Border.IsVisible = true; oTextItem.FontSpec.Angle = 0; oTextItem.FontSpec.Fill.IsVisible = false; myPane.GraphItemList.Add(oTextItem); } } iOrd++; } trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // vertical bars with labels myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList list = new PointPairList(); PointPairList list2 = new PointPairList(); PointPairList list3 = new PointPairList(); Random rand = new Random(); for ( int i=0; i<5; i++ ) { double x = (double) i; double y = rand.NextDouble() * 1000; double y2 = rand.NextDouble() * 1000; double y3 = rand.NextDouble() * 1000; list.Add( x, y ); list2.Add( x, y2 ); list3.Add( x, y3 ); } BarItem myCurve = myPane.AddBar( "curve 1", list, Color.Blue ); BarItem myCurve2 = myPane.AddBar( "curve 2", list2, Color.Red ); BarItem myCurve3 = myPane.AddBar( "curve 3", list3, Color.Green ); //myPane.XAxis.IsReverse = true; myPane.AxisChange( this.CreateGraphics() ); myPane.XAxis.IsTicsBetweenLabels = true; string[] labels = { "one", "two", "three", "four", "five" }; myPane.XAxis.TextLabels = labels; myPane.XAxis.Type = AxisType.Text; //myPane.XAxis.Step = 3; myPane.XAxis.IsAllTics = false; ArrowItem tic = new ArrowItem( Color.Black, 1.0f, 2.5f, 0.99f, 2.5f, 1.01f ); tic.IsArrowHead = false; tic.Location.CoordinateFrame = CoordType.XScaleYAxisFraction; myPane.GraphItemList.Add( tic ); CreateBarLabels( myPane ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic curve test - log/exponential axis myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); PointPairList ppl1 = new PointPairList(); PointPairList ppl2 = new PointPairList(); for ( int i=0; i<100; i++ ) { double x = (double) i * 1.52 + 0.001; double x2 = x*10000; double y = Math.Sin( i / 8.0 ) * 100000 + 100001; double y2 = Math.Sin( i / 8.0 ) * 100000 + 100001; double z = Math.Abs( Math.Cos( i / 8.0 ) ) * y; ppl1.Add( x, y, z ); ppl2.Add( x2, y2, z ); } LineItem myCurve = myPane.AddCurve( "curve", ppl1, Color.Blue, SymbolType.Diamond ); LineItem myCurve2 = myPane.AddCurve( "curve2", ppl2, Color.Red, SymbolType.Triangle ); myPane.XAxis.IsUseTenPower = false; myPane.XAxis.Type = AxisType.Log; myPane.XAxis.Exponent = 0.3; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // Basic curve test myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); double[] x = new double[100]; double[] y = new double[100]; for ( int i=0; i<100; i++ ) { x[i] = (double) i; y[i] = Math.Sin( i / 8.0 ) * 100000 + 100001; double z = Math.Abs(Math.Cos( i / 8.0 )) * y[i]; } BasicArrayPointList list = new BasicArrayPointList( x, y ); //LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); LineItem myCurve = myPane.AddCurve( "curve", list, Color.Blue, SymbolType.Diamond ); //myCurve.Symbol.IsVisible = true; //myCurve.IsY2Axis = true; //myPane.Y2Axis.IsVisible = true; //myPane.YAxis.Type = AxisType.Log; //myPane.YAxis.IsScaleVisible = false; //myPane.YAxis.IsShowTitle = false; //myPane.MarginLeft = 50; //TextItem text = new TextItem("5000", -0.01f, 5000f, CoordType.XAxisFractionYScale, AlignH.Right, AlignV.Center ); //text.FontSpec.Border.IsVisible = false; //text.FontSpec.Fill.IsVisible = false; //myPane.GraphItemList.Add( text ); //TextItem text2 = new TextItem( "My Title", 0.01f, 0.5f, CoordType.XPaneFractionYAxisFraction, // AlignH.Center, AlignV.Top ); //text2.FontSpec.Border.IsVisible = false; //text2.FontSpec.Fill.IsVisible = false; //text2.FontSpec.Angle = 90f; //myPane.GraphItemList.Add( text2 ); //myPane.YAxis.IsVisible = false; //myPane.Y2Axis.Title.Text = "Y2 Axis"; //myPane.XAxis.BaseTic = 1; //myPane.XAxis.Step = 5; //myPane.Y2Axis.Cross = 60; //myPane.YAxis.IsScaleLabelsInside = true; //myPane.Y2Axis.IsShowGrid = true; //myPane.XAxis.IsShowGrid = true; //myPane.YAxis.IsSkipFirstLabel = true; myPane.XAxis.IsSkipLastLabel = true; //myPane.XAxis.IsSkipLastLabel = true; //myPane.XAxis.IsReverse = true; //myPane.AxisBorder.IsVisible = false; //myPane.XAxis.Type = AxisType.Log; PointF[] polyPts = new PointF[7]; polyPts[0] = new PointF( 30f, 0.2f ); polyPts[1] = new PointF( 25f, 0.4f ); polyPts[2] = new PointF( 27f, 0.6f ); polyPts[3] = new PointF( 30f, 0.8f ); polyPts[4] = new PointF( 35f, 0.6f ); polyPts[5] = new PointF( 37f, 0.4f ); polyPts[6] = new PointF( 30f, 0.2f ); PolyItem poly = new PolyItem( polyPts, Color.Red, Color.LightSeaGreen, Color.White ); myPane.GraphItemList.Add( poly ); myPane.AxisChange( this.CreateGraphics() ); myPane.FontSpec.IsDropShadow = true; myPane.FontSpec.DropShadowColor = Color.Red; myPane.FontSpec.Border.IsVisible = true; myPane.FontSpec.Fill = new Fill( Color.White, Color.LightGoldenrodYellow ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif #if false // repetitive points myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); double[] Track_DateTime_Xaxis = {1}; double[] Y_processed_axis = {10}; LineItem myCurve = myPane.AddCurve( "Curve Legend", Track_DateTime_Xaxis, Y_processed_axis, Color.DarkRed ); myCurve.Symbol.Fill = new Fill( Color.Red ); myPane.XAxis.Max = 1; myPane.YAxis.IsShowGrid = true; myPane.YAxis.IsShowMinorGrid = true; //myPane.AxisChange( g ); #endif #if false // masterpane test master = new MasterPane( "ZedGraph MasterPane Example", new Rectangle( 10, 10, this.Width-20, this.Height-100 ) ); master.PaneFill = new Fill( Color.White, Color.MediumSlateBlue, 45.0F ); master.Legend.IsVisible = true; master.Legend.Position = LegendPos.TopCenter; TextItem text = new TextItem( "Priority", 0.88F, 0.12F ); text.Location.CoordinateFrame = CoordType.PaneFraction; text.FontSpec.Angle = 15.0F; text.FontSpec.FontColor = Color.Red; text.FontSpec.IsBold = true; text.FontSpec.Size = 16; text.FontSpec.Border.IsVisible = false; text.FontSpec.Border.Color = Color.Red; text.FontSpec.Fill.IsVisible = false; text.Location.AlignH = AlignH.Left; text.Location.AlignV = AlignV.Bottom; master.GraphItemList.Add( text ); text = new TextItem( "DRAFT", 0.5F, 0.5F ); text.Location.CoordinateFrame = CoordType.PaneFraction; text.FontSpec.Angle = 30.0F; text.FontSpec.FontColor = Color.FromArgb( 70, 255, 100, 100 ); text.FontSpec.IsBold = true; text.FontSpec.Size = 100; text.FontSpec.Border.IsVisible = false; text.FontSpec.Fill.IsVisible = false; text.Location.AlignH = AlignH.Center; text.Location.AlignV = AlignV.Center; text.ZOrder = ZOrder.A_InFront; master.GraphItemList.Add( text ); ColorSymbolRotator rotator = new ColorSymbolRotator(); for ( int j=0; j<8; j++ ) { // Create a new graph - rect dimensions do not matter here, since it // will be resized by MasterPane.AutoPaneLayout() GraphPane newPane = new GraphPane( new Rectangle( 10, 10, 10, 10 ), "Case #" + (j+1).ToString(), "Time, Days", "Rate, m/s" ); newPane.PaneFill = new Fill( Color.PowderBlue, Color.LightYellow, 45.0F ); newPane.BaseDimension = 6.0F; // Make up some data arrays based on the Sine function double x, y; PointPairList list = new PointPairList(); for ( int i=0; i<36; i++ ) { x = (double) i + 5; y = 3.0 * ( 1.5 + Math.Sin( (double) i * 0.2 + (double) j ) ); list.Add( x, y ); } LineItem myCurve = newPane.AddCurve( "Type " + j.ToString(), list, rotator.NextColor, rotator.NextSymbol ); myCurve.Symbol.Fill = new Fill( Color.White ); master.Add( newPane ); } Graphics g = this.CreateGraphics(); master.AutoPaneLayout( g, PaneLayout.SquareRowPreferred); master.AxisChange( g ); #endif #if false // Create a new GraphPane myPane = new GraphPane( new RectangleF( 0, 0, 640, 480 ), "Title", "XAxis", "YAxis" ); string[] labels = { "Panther", "Lion", "Cheetah", "Cougar", "Tiger", "Leopard" }; double[] x = { 100, 115, 75, 22, 98, 40 }; double[] x2 = { 120, 175, 95, 57, 113, 110 }; double[] x3 = { 204, 192, 119, 80, 134, 156 }; BarItem myCurve = myPane.AddBar( "Here", x, null, Color.Red ); myCurve.Bar.Fill = new Fill( Color.Red, Color.White, Color.Red, 90f ); myCurve = myPane.AddBar( "There", x2, null, Color.Blue ); myCurve.Bar.Fill = new Fill( Color.Blue, Color.White, Color.Blue, 90f ); myCurve = myPane.AddBar( "Elsewhere", x3, null, Color.Green ); myCurve.Bar.Fill = new Fill( Color.Green, Color.White, Color.Green, 90f ); myPane.YAxis.IsTicsBetweenLabels = true; myPane.YAxis.TextLabels = labels; myPane.YAxis.Type = AxisType.Text; myPane.BarType = BarType.Stack; myPane.BarBase = BarBase.Y; myPane.Chart.Fill = new Fill( Color.White, Color.FromArgb( 255, 255, 166), 45.0F ); #endif #if false // ordinal demo myPane = new GraphPane( new RectangleF( 0, 0, 300, 200 ), "Ordinal Demo", "X Value (ordinal)", "Y Value" ); PointPairList list = new PointPairList(); list.Add( 10, 50 ); list.Add( 11, 24 ); list.Add( 20, 75 ); list.Add( 21, 62 ); LineItem myCurve = myPane.AddCurve( "Curve", list, Color.Blue, SymbolType.Diamond ); myCurve.Symbol.Fill = new Fill( Color.White ); myPane.FontSpec.Size = 24; myPane.XAxis.TitleFontSpec.Size = 18; myPane.XAxis.Scale.FontSpec.Size = 18; myPane.YAxis.TitleFontSpec.Size = 18; myPane.YAxis.Scale.FontSpec.Size = 18; myPane.XAxis.Type = AxisType.Ordinal; myPane.AxisChange( this.CreateGraphics() ); trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; #endif if ( master != null ) _crossAxis = master[0].Y2Axis; else _crossAxis = myPane.YAxisList[1]; trackBar1.Minimum = 0; trackBar1.Maximum = 100; trackBar1.Value = 50; UpdateControls(); SetSize(); //this.WindowState = FormWindowState.Maximized ; if ( this.myPane != null ) this.myPane.AxisChange( this.CreateGraphics() ); }
/// <summary> /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device as a symbol at each defined point. The routine /// only draws the symbols; the lines are draw by the /// <see cref="Line.DrawCurve"/> method. This method /// is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <param name="isSelected">Indicates that the <see cref="Symbol" /> should be drawn /// with attributes from the <see cref="Selection" /> class. /// </param> 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 ) ) { 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 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 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; } }
public HorizontalBarDemo() : base("A sideways bar demo.\n" + "This demo also shows how to add labels to the bars, in this " + "case showing the value of each bar", "HorizontalBar Demo", DemoType.Bar) { GraphPane myPane = base.GraphPane; // Set the title and axis labels myPane.Title.Text = "Horizontal Bar Graph"; myPane.XAxis.Title.Text = "Performance Factor"; myPane.YAxis.Title.Text = "Grouping"; // Enter some random data values double[] y = { 100, 115, 75, -22, 98, 40, -10 }; double[] y2 = { 90, 100, 95, -35, 80, 35, 35 }; double[] y3 = { 80, 110, 65, -15, 54, 67, 18 }; // Generate a bar with "Curve 1" in the legend BarItem myCurve = myPane.AddBar( "Curve 1", y, null, Color.Red ); // Fill the bar with a red-white-red gradient myCurve.Bar.Fill = new Fill( Color.Red, Color.White, Color.Red, 90F ); // Generate a bar with "Curve 2" in the legend myCurve = myPane.AddBar( "Curve 2", y2, null, Color.Blue ); // Fill the bar with a blue-white-blue gradient for a 3d look myCurve.Bar.Fill = new Fill( Color.Blue, Color.White, Color.Blue, 90F ); // Generate a bar with "Curve 3" in the legend myCurve = myPane.AddBar( "Curve 3", y3, null, Color.Green ); // Fill the bar with a Green-white-Green gradient for a 3d look myCurve.Bar.Fill = new Fill( Color.White, Color.Green, 90F ); // Draw the X tics between the labels instead of at the labels myPane.YAxis.MajorTic.IsBetweenLabels = true; // Set the XAxis to an ordinal type myPane.YAxis.Type = AxisType.Ordinal; // draw the X axis zero line myPane.XAxis.MajorGrid.IsZeroLine = true; //This is the part that makes the bars horizontal myPane.BarSettings.Base = BarBase.Y; // Fill the axis background with a color gradient myPane.Chart.Fill = new Fill( Color.White, Color.FromArgb( 255, 255, 166), 45.0F ); base.ZedGraphControl.AxisChange(); // The ValueHandler is a helper that does some position calculations for us. ValueHandler valueHandler = new ValueHandler( myPane, true ); // Display a value for the maximum of each bar cluster // Shift the text items by 5 user scale units above the bars const float shift = 3; int ord = 0; foreach ( CurveItem curve in myPane.CurveList ) { BarItem bar = curve as BarItem; if ( bar != null ) { IPointList points = curve.Points; for ( int i=0; i<points.Count; i++ ) { double xVal = points[i].X; // Calculate the Y value at the center of each bar double yVal = valueHandler.BarCenterValue( curve, curve.GetBarWidth( myPane ), i, points[i].Y, ord ); // format the label string to have 1 decimal place string lab = xVal.ToString( "F0" ); // create the text item (assumes the x axis is ordinal or text) // for negative bars, the label appears just above the zero value TextObj text = new TextObj( lab, (float) xVal + ( xVal > 0 ? shift : -shift ), (float) yVal ); // tell Zedgraph to use user scale units for locating the TextObj text.Location.CoordinateFrame = CoordType.AxisXYScale; text.FontSpec.Size = 10; // AlignH the left-center of the text to the specified point text.Location.AlignH = xVal > 0 ? AlignH.Left : AlignH.Right; text.Location.AlignV = AlignV.Center; text.FontSpec.Border.IsVisible = false; // rotate the text 90 degrees text.FontSpec.Angle = 0; text.FontSpec.Fill.IsVisible = false; // add the TextObj to the list myPane.GraphObjList.Add( text ); } } ord++; } }
/// <summary> /// Build an array of <see cref="PointF"/> values (pixel coordinates) that represents /// the current curve. Note that this drawing routine ignores <see cref="PointPairBase.Missing"/> /// values, but it does not "break" the line to indicate values are missing. /// </summary> /// <param name="pane">A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object.</param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> /// <param name="arrPoints">An array of <see cref="PointF"/> values in pixel /// coordinates representing the current curve.</param> /// <param name="count">The number of points contained in the "arrPoints" /// parameter.</param> /// <returns>true for a successful points array build, false for data problems</returns> public bool BuildPointsArray( GraphPane pane, CurveItem curve, out PointF[] arrPoints, out int count ) { arrPoints = null; count = 0; IPointList points = curve.Points; if ( this.IsVisible && !this.Color.IsEmpty && points != null ) { int index = 0; float curX, curY, lastX = 0, lastY = 0; double x, y, lowVal; ValueHandler valueHandler = new ValueHandler( pane, false ); // Step type plots get twice as many points. Always add three points so there is // room to close out the curve for area fills. arrPoints = new PointF[( _stepType == ZedGraph.StepType.NonStep ? 1 : 2 ) * points.Count + 1]; // Loop over all points in the curve for ( int i = 0; i < points.Count; i++ ) { // make sure that the current point is valid if ( !points[i].IsInvalid ) { // 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 x, out lowVal, out y ); } // 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 { x = points[i].X; y = points[i].Y; } if ( x == PointPair.Missing || y == PointPair.Missing ) continue; // Transform the user scale values to pixel locations Axis xAxis = curve.GetXAxis( pane ); curX = xAxis.Scale.Transform( curve.IsOverrideOrdinal, i, x ); Axis yAxis = curve.GetYAxis( pane ); curY = yAxis.Scale.Transform( curve.IsOverrideOrdinal, i, y ); if ( curX < -1000000 || curY < -1000000 || curX > 1000000 || curY > 1000000 ) continue; // Add the pixel value pair into the points array // Two points are added for step type curves // ignore step-type setting for smooth curves if ( _isSmooth || index == 0 || this.StepType == StepType.NonStep ) { arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if ( this.StepType == StepType.ForwardStep ) { arrPoints[index].X = curX; arrPoints[index].Y = lastY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } else if ( this.StepType == StepType.RearwardStep ) { arrPoints[index].X = lastX; arrPoints[index].Y = curY; index++; arrPoints[index].X = curX; arrPoints[index].Y = curY; } lastX = curX; lastY = curY; index++; } } // Make sure there is at least one valid point if ( index == 0 ) return false; // Add an extra point at the end, since the smoothing algorithm requires it arrPoints[index] = arrPoints[index - 1]; index++; count = index; return true; } else { return false; } }
/// <summary> /// Create a TextLabel for each bar in the GraphPane. /// Call this method only after calling AxisChange() /// </summary> /// <remarks> /// This method will go through the bars, create a label that corresponds to the bar value, /// and place it on the graph depending on user preferences. This works for horizontal or /// vertical bars in clusters or stacks.</remarks> /// <param name="pane">The GraphPane in which to place the text labels.</param> /// <param name="isBarCenter">true to center the labels inside the bars, false to /// place the labels just above the top of the bar.</param> /// <param name="valueFormat">The double.ToString string format to use for creating /// the labels /// </param> private void CreateBarLabels( GraphPane pane, bool isBarCenter, string valueFormat ) { bool isVertical = pane.BarSettings.Base == BarBase.X; // Make the gap between the bars and the labels = 2% of the axis range float labelOffset; if ( isVertical ) labelOffset = (float) ( pane.YAxis.Scale.Max - pane.YAxis.Scale.Min ) * 0.02f; else labelOffset = (float) ( pane.XAxis.Scale.Max - pane.XAxis.Scale.Min ) * 0.02f; // keep a count of the number of BarItems int curveIndex = 0; // Get a valuehandler to do some calculations for us ValueHandler valueHandler = new ValueHandler( pane, true ); // Loop through each curve in the list foreach ( CurveItem curve in pane.CurveList ) { // work with BarItems only BarItem bar = curve as BarItem; if ( bar != null ) { IPointList points = curve.Points; // Loop through each point in the BarItem for ( int i=0; i<points.Count; i++ ) { // Get the high, low and base values for the current bar // note that this method will automatically calculate the "effective" // values if the bar is stacked double baseVal, lowVal, hiVal; valueHandler.GetValues( curve, i, out baseVal, out lowVal, out hiVal ); // Get the value that corresponds to the center of the bar base // This method figures out how the bars are positioned within a cluster float centerVal = (float) valueHandler.BarCenterValue( bar, bar.GetBarWidth( pane ), i, baseVal, curveIndex ); // Create a text label -- note that we have to go back to the original point // data for this, since hiVal and lowVal could be "effective" values from a bar stack string barLabelText = ( isVertical ? points[i].Y : points[i].X ).ToString( valueFormat ); // Calculate the position of the label -- this is either the X or the Y coordinate // depending on whether they are horizontal or vertical bars, respectively float position; if ( isBarCenter ) position = (float) (hiVal + lowVal) / 2.0f; else position = (float) hiVal + labelOffset; // Create the new TextObj TextObj label; if ( isVertical ) label = new TextObj( barLabelText, centerVal, position ); else label = new TextObj( barLabelText, position, centerVal ); // Configure the TextObj label.Location.CoordinateFrame = CoordType.AxisXYScale; label.FontSpec.Size = 12; label.FontSpec.FontColor = Color.Black; label.FontSpec.Angle = isVertical ? 90 : 0; label.Location.AlignH = isBarCenter ? AlignH.Center : AlignH.Left; label.Location.AlignV = AlignV.Center; label.FontSpec.Border.IsVisible = false; label.FontSpec.Fill.IsVisible = false; // Add the TextObj to the GraphPane pane.GraphObjList.Add( label ); } } curveIndex++; } }
/// <summary> /// Protected internal routine that draws the specified single bar (an individual "point") /// of this series to the specified <see cref="Graphics"/> device. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn.</param> /// <param name="index"> /// The zero-based index number for the single bar to be drawn. /// </param> /// <param name="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) /// in the cluster of bars. /// </param> /// <param name="baseAxis">The <see cref="Axis"/> class instance that defines the base (independent) /// axis for the <see cref="Bar"/></param> /// <param name="valueAxis">The <see cref="Axis"/> class instance that defines the value (dependent) /// axis for the <see cref="Bar"/></param> /// <param name="barWidth"> /// The width of each bar, in pixels. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> protected virtual void DrawSingleBar(Graphics g, GraphPane pane, CurveItem curve, int index, int pos, Axis baseAxis, Axis valueAxis, float barWidth, float scaleFactor) { // pixBase = pixel value for the bar center on the base axis // pixHiVal = pixel value for the bar top on the value axis // pixLowVal = pixel value for the bar bottom on the value axis float pixBase, pixHiVal, pixLowVal; float clusterWidth = pane.BarSettings.GetClusterWidth(); //float barWidth = curve.GetBarWidth( pane ); float clusterGap = pane._barSettings.MinClusterGap*barWidth; float barGap = barWidth*pane._barSettings.MinBarGap; // curBase = the scale value on the base axis of the current bar // curHiVal = the scale value on the value axis of the current bar // curLowVal = the scale value of the bottom of the bar double curBase, curLowVal, curHiVal; var valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(curve, index, out curBase, out curLowVal, out curHiVal); // 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 (!curve.Points[index].IsInvalid) { // calculate a pixel value for the top of the bar on value axis pixLowVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curLowVal); pixHiVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curHiVal); // calculate a pixel value for the center of the bar on the base axis pixBase = baseAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curBase); // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - clusterWidth/2.0F + clusterGap/2.0F + pos*(barWidth + barGap); // Draw the bar if (pane._barSettings.Base == BarBase.X) Draw(g, pane, pixSide, pixSide + barWidth, pixLowVal, pixHiVal, scaleFactor, true, curve.Points[index]); else Draw(g, pane, pixLowVal, pixHiVal, pixSide, pixSide + barWidth, scaleFactor, true, curve.Points[index]); } }
private IPointList GetPoints(CurveItem curve, GraphPane pane) { IPointList points = curve.Points; ValueHandler valueHandler = new ValueHandler( pane, false ); if (pane.LineType == LineType.Stack) { // Loop over each point in the curve for (int i = 0; i < points.Count; i++) { double x, y, unused; if (!valueHandler.GetValues(curve, i, out x, out unused, out y)) { x = PointPair.Missing; y = PointPair.Missing; } points[i].X = x; points[i].Y = y; } } return points; }
private bool HandlePointValues(Point mousePt) { int iPt; GraphPane pane; object nearestObj; using (Graphics g = this.CreateGraphics()) { if (_masterPane.FindNearestPaneObject(mousePt, g, out pane, out nearestObj, out iPt)) { if (nearestObj is CurveItem && iPt >= 0) { CurveItem curve = (CurveItem)nearestObj; // Provide Callback for User to customize the tooltips if (this.PointValueEvent != null) { string label = this.PointValueEvent(this, pane, curve, iPt); if (label != null && label.Length > 0) { this.pointToolTip.SetToolTip(this, label); this.pointToolTip.Active = true; } else this.pointToolTip.Active = false; } else { if (curve is PieItem) { this.pointToolTip.SetToolTip(this, ((PieItem)curve).Value.ToString(_pointValueFormat)); } // else if ( curve is OHLCBarItem || curve is JapaneseCandleStickItem ) // { // StockPt spt = (StockPt)curve.Points[iPt]; // this.pointToolTip.SetToolTip( this, ( (XDate) spt.Date ).ToString( "MM/dd/yyyy" ) + "\nOpen: $" + // spt.Open.ToString( "N2" ) + // "\nHigh: $" + // spt.High.ToString( "N2" ) + "\nLow: $" + // spt.Low.ToString( "N2" ) + "\nClose: $" + // spt.Close.ToString // ( "N2" ) ); // } else { PointPair pt = curve.Points[iPt]; if (pt.Tag is string) this.pointToolTip.SetToolTip(this, (string)pt.Tag); else { double xVal, yVal, lowVal; ValueHandler valueHandler = new ValueHandler(pane, false); if ((curve is BarItem || curve is ErrorBarItem || curve is HiLowBarItem) && pane.BarSettings.Base != BarBase.X) valueHandler.GetValues(curve, iPt, out yVal, out lowVal, out xVal); else valueHandler.GetValues(curve, iPt, out xVal, out lowVal, out yVal); string xStr = MakeValueLabel(curve.GetXAxis(pane), xVal, iPt, curve.IsOverrideOrdinal); string yStr = MakeValueLabel(curve.GetYAxis(pane), yVal, iPt, curve.IsOverrideOrdinal); this.pointToolTip.SetToolTip(this, string.Format("({0}, {1})", xStr, yStr)); //this.pointToolTip.SetToolTip( this, // curve.Points[iPt].ToString( this.pointValueFormat ) ); } } this.pointToolTip.Active = true; } } else this.pointToolTip.Active = false; } else this.pointToolTip.Active = false; //g.Dispose(); } return false; }
protected override void DrawSingleBar(Graphics g, GraphPane pane, CurveItem curve, int index, int pos, Axis baseAxis, Axis valueAxis, float barWidth, float scaleFactor) { base.DrawSingleBar(g, pane, curve, index, pos, baseAxis, valueAxis, barWidth, scaleFactor); PointPair pointPair = curve.Points[index]; ErrorTag errorTag = pointPair.Tag as ErrorTag; if (pointPair.IsInvalid || errorTag == null) { return; } double curBase, curLowVal, curHiVal; ValueHandler valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(curve, index, out curBase, out curLowVal, out curHiVal); float pixBase = baseAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curBase); double lowError = curHiVal - errorTag.Error; float pixLowError = valueAxis.Scale.Transform(lowError); float pixHiError = valueAxis.Scale.Transform(lowError + errorTag.Error*2); float clusterWidth = pane.BarSettings.GetClusterWidth(); //float barWidth = curve.GetBarWidth( pane ); float clusterGap = pane.BarSettings.MinClusterGap * barWidth; float barGap = barWidth * pane.BarSettings.MinBarGap; // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - clusterWidth / 2.0F + clusterGap / 2.0F + pos * (barWidth + barGap); // Draw the bar if (pane.BarSettings.Base == BarBase.X) { if (barWidth >= 3 && errorTag.Error > 0) { // Draw whiskers float pixMidX = (float)Math.Round(pixSide + barWidth / 2); // Line g.DrawLine(ErrorPen, pixMidX, pixHiError, pixMidX, pixLowError); if (barWidth >= PIX_TERM_WIDTH) { // Ends float pixLeft = pixMidX - (float) Math.Round(PIX_TERM_WIDTH/2); float pixRight = pixLeft + PIX_TERM_WIDTH - 1; g.DrawLine(ErrorPen, pixLeft, pixHiError, pixRight, pixHiError); g.DrawLine(ErrorPen, pixLeft, pixLowError, pixRight, pixLowError); } } } else { if (barWidth >= 3 && errorTag.Error > 0) { // Draw whiskers float pixMidY = (float)Math.Round(pixSide + barWidth / 2) + 1; // Line g.DrawLine(ErrorPen, pixLowError, pixMidY, pixHiError, pixMidY); if (barWidth >= PIX_TERM_WIDTH) { // Ends float pixTop = pixMidY - (float) Math.Round(PIX_TERM_WIDTH/2); float pixBottom = pixTop + PIX_TERM_WIDTH - 1; g.DrawLine(ErrorPen, pixHiError, pixTop, pixHiError, pixBottom); g.DrawLine(ErrorPen, pixLowError, pixTop, pixLowError, pixBottom); } } } }
/// <summary> /// Create a <see cref="TextObj" /> for each bar in the <see cref="GraphPane" />. /// </summary> /// <remarks> /// This method will go through the bars, create a label that corresponds to the bar value, /// and place it on the graph depending on user preferences. This works for horizontal or /// vertical bars in clusters or stacks, but only for <see cref="BarItem" /> types. This method /// does not apply to <see cref="ErrorBarItem" /> or <see cref="HiLowBarItem" /> objects. /// Call this method only after calling <see cref="GraphPane.AxisChange()" />. /// </remarks> /// <param name="pane">The GraphPane in which to place the text labels.</param> /// <param name="isBarCenter">true to center the labels inside the bars, false to /// place the labels just above the top of the bar.</param> /// <param name="valueFormat">The double.ToString string format to use for creating /// the labels. /// </param> /// <param name="fontColor">The color in which to draw the labels</param> /// <param name="fontFamily">The string name of the font family to use for the labels</param> /// <param name="fontSize">The floating point size of the font, in scaled points</param> /// <param name="isBold">true for a bold font type, false otherwise</param> /// <param name="isItalic">true for an italic font type, false otherwise</param> /// <param name="isUnderline">true for an underline font type, false otherwise</param> public static void CreateBarLabels(GraphPane pane, bool isBarCenter, string valueFormat, string fontFamily, float fontSize, Color fontColor, bool isBold, bool isItalic, bool isUnderline) { bool isVertical = pane.BarSettings.Base == BarBase.X; // keep a count of the number of BarItems int curveIndex = 0; // Get a valuehandler to do some calculations for us ValueHandler valueHandler = new ValueHandler(pane, true); // Loop through each curve in the list foreach (CurveItem curve in pane.CurveList) { // work with BarItems only BarItem bar = curve as BarItem; if (bar != null) { IPointList points = curve.Points; // ADD JKB 9/21/07 // The labelOffset should depend on whether the curve is YAxis or Y2Axis. // JHC - Generalize to any value axis // Make the gap between the bars and the labels = 1.5% of the axis range float labelOffset; Scale scale = curve.ValueAxis(pane).Scale; labelOffset = (float) (scale._max - scale._min)*0.015f; // Loop through each point in the BarItem for (int i = 0; i < points.Count; i++) { // Get the high, low and base values for the current bar // note that this method will automatically calculate the "effective" // values if the bar is stacked double baseVal, lowVal, hiVal; valueHandler.GetValues(curve, i, out baseVal, out lowVal, out hiVal); // Get the value that corresponds to the center of the bar base // This method figures out how the bars are positioned within a cluster float centerVal = (float) valueHandler.BarCenterValue(bar, bar.GetBarWidth(pane), i, baseVal, curveIndex); // Create a text label -- note that we have to go back to the original point // data for this, since hiVal and lowVal could be "effective" values from a bar stack string barLabelText = (isVertical ? points[i].Y : points[i].X).ToString(valueFormat); // Calculate the position of the label -- this is either the X or the Y coordinate // depending on whether they are horizontal or vertical bars, respectively float position; if (isBarCenter) position = (float) (hiVal + lowVal)/2.0f; else if (hiVal >= 0) position = (float) hiVal + labelOffset; else position = (float) hiVal - labelOffset; // Create the new TextObj TextObj label; if (isVertical) label = new TextObj(barLabelText, centerVal, position); else label = new TextObj(barLabelText, position, centerVal); label.FontSpec.Family = fontFamily; // Configure the TextObj // CHANGE JKB 9/21/07 // CoordinateFrame should depend on whether curve is YAxis or Y2Axis. label.Location.CoordinateFrame = (isVertical && curve.IsY2Axis) ? CoordType.AxisXY2Scale : CoordType.AxisXYScale; label.FontSpec.Size = fontSize; label.FontSpec.FontColor = fontColor; label.FontSpec.IsItalic = isItalic; label.FontSpec.IsBold = isBold; label.FontSpec.IsUnderline = isUnderline; label.FontSpec.Angle = isVertical ? 90 : 0; label.Location.AlignH = isBarCenter ? AlignH.Center : (hiVal >= 0 ? AlignH.Left : AlignH.Right); label.Location.AlignV = AlignV.Center; label.FontSpec.Border.IsVisible = false; label.FontSpec.Fill.IsVisible = false; // Add the TextObj to the GraphPane pane.GraphObjList.Add(label); } curveIndex++; } } }
/// <summary> /// Protected internal routine that draws the specified single bar (an individual "point") /// of this series to the specified <see cref="Graphics"/> device. /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="CurveItem"/> object representing the /// <see cref="Bar"/>'s to be drawn.</param> /// <param name="index"> /// The zero-based index number for the single bar to be drawn. /// </param> /// <param name="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) /// in the cluster of bars. /// </param> /// <param name="baseAxis">The <see cref="Axis"/> class instance that defines the base (independent) /// axis for the <see cref="Bar"/></param> /// <param name="valueAxis">The <see cref="Axis"/> class instance that defines the value (dependent) /// axis for the <see cref="Bar"/></param> /// <param name="barWidth"> /// The width of each bar, in pixels. /// </param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> protected override void DrawSingleBar(Graphics g, GraphPane pane, CurveItem curve, int index, int pos, Axis baseAxis, Axis valueAxis, float barWidth, float scaleFactor) { //float scaledSize = GetBarWidth( pane, baseAxis, scaleFactor ); // pixBase = pixel value for the bar center on the base axis // pixValue = pixel value for the bar top on the value axis // pixLow = pixel value for the bar bottom on the value axis float pixBase, pixHiVal, pixLowVal; // curBase = the scale value on the base axis of the current bar // curValue = the scale value on the value axis of the current bar double curBase, curLowVal, curHiVal; var valueHandler = new ValueHandler(pane, false); valueHandler.GetValues(curve, index, out curBase, out curLowVal, out curHiVal); barWidth = GetBarWidth(pane, baseAxis, scaleFactor); // curLow = the scale value on the value axis for the bottom of the current bar // Get a "low" value for the bottom of the bar and verify validity if (curLowVal == PointPair.Missing || Double.IsNaN(curLowVal) || Double.IsInfinity(curLowVal)) curLowVal = 0; // 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 (!curve.Points[index].IsInvalid) { // calculate a pixel value for the top of the bar on value axis pixHiVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curHiVal); // calculate a pixel value for the center of the bar on the base axis pixBase = baseAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curBase); pixLowVal = valueAxis.Scale.Transform(curve.IsOverrideOrdinal, index, curLowVal); // Calculate the pixel location for the side of the bar (on the base axis) float pixSide = pixBase - barWidth/2.0F; // Draw the bar if (baseAxis is XAxis) Draw(g, pane, pixSide, pixSide + barWidth, pixLowVal, pixHiVal, scaleFactor, true, curve.Points[index]); else Draw(g, pane, pixLowVal, pixHiVal, pixSide, pixSide + barWidth, scaleFactor, true, curve.Points[index]); } }
/// <summary> /// Find the data point that lies closest to the specified mouse (screen) /// point. /// </summary> /// <remarks> /// This method will search through the specified list of curves to find which point is /// nearest. It will only consider points that are within /// <see cref="Default.NearestTol"/> pixels of the screen point, and it will /// only consider <see cref="CurveItem"/>'s that are in /// <paramref name="targetCurveList"/>. /// </remarks> /// <param name="mousePt">The screen point, in pixel coordinates.</param> /// <param name="targetCurveList">A <see cref="CurveList"/> object containing /// a subset of <see cref="CurveItem"/>'s to be searched.</param> /// <param name="nearestCurve">A reference to the <see cref="CurveItem"/> /// instance that contains the closest point. nearestCurve will be null if /// no data points are available.</param> /// <param name="iNearest">The index number of the closest point. The /// actual data vpoint will then be <see cref="CurveItem.Points">CurveItem.Points[iNearest]</see> /// . iNearest will /// be -1 if no data points are available.</param> /// <returns>true if a point was found and that point lies within /// <see cref="Default.NearestTol"/> pixels /// of the screen point, false otherwise.</returns> public bool FindNearestPoint( PointF mousePt, CurveList targetCurveList, out CurveItem nearestCurve, out int iNearest ) { CurveItem nearestBar = null; int iNearestBar = -1; nearestCurve = null; iNearest = -1; // If the point is outside the ChartRect, always return false if ( !_chart._rect.Contains( mousePt ) ) return false; double x, x2; double[] y; double[] y2; //ReverseTransform( mousePt, out x, out y, out y2 ); ReverseTransform( mousePt, out x, out x2, out y, out y2 ); if ( !AxisRangesValid() ) return false; ValueHandler valueHandler = new ValueHandler( this, false ); double xPixPerUnit = _chart._rect.Width / ( _xAxis._scale._max - _xAxis._scale._min ); //double yPixPerUnit = chartRect.Height / ( yAxis.Max - yAxis.Min ); //double y2PixPerUnit; // = chartRect.Height / ( y2Axis.Max - y2Axis.Min ); double yPixPerUnitAct, yAct, yMinAct, yMaxAct; double minDist = 1e20; double xVal, yVal, dist = 99999, distX, distY; double tolSquared = Default.NearestTol * Default.NearestTol; int iBar = 0; foreach ( CurveItem curve in targetCurveList ) { //test for pie first...if it's a pie rest of method superfluous if ( curve is PieItem && curve.IsVisible ) { if ( ( (PieItem)curve ).SlicePath != null && ( (PieItem)curve ).SlicePath.IsVisible( mousePt ) ) { nearestBar = curve; iNearestBar = 0; } continue; } else if ( curve.IsVisible ) { int yIndex = curve.GetYAxisIndex( this ); Axis yAxis = curve.GetYAxis( this ); if ( curve.IsY2Axis ) { yAct = y2[yIndex]; yMinAct = _y2AxisList[yIndex]._scale._min; yMaxAct = _y2AxisList[yIndex]._scale._max; } else { yAct = y[yIndex]; yMinAct = _yAxisList[yIndex]._scale._min; yMaxAct = _yAxisList[yIndex]._scale._max; } yPixPerUnitAct = _chart._rect.Height / ( yMaxAct - yMinAct ); IPointList points = curve.Points; float barWidth = curve.GetBarWidth( this ); double barWidthUserHalf; Axis baseAxis = curve.BaseAxis( this ); bool isXBaseAxis = ( baseAxis is XAxis || baseAxis is X2Axis ); if ( isXBaseAxis ) barWidthUserHalf = barWidth / xPixPerUnit / 2.0; else barWidthUserHalf = barWidth / yPixPerUnitAct / 2.0; if ( points != null ) { for ( int iPt = 0; iPt < curve.NPts; iPt++ ) { // xVal is the user scale X value of the current point if ( _xAxis._scale.IsAnyOrdinal && !curve.IsOverrideOrdinal ) xVal = (double)iPt + 1.0; else xVal = points[iPt].X; // yVal is the user scale Y value of the current point if ( yAxis._scale.IsAnyOrdinal && !curve.IsOverrideOrdinal ) yVal = (double)iPt + 1.0; else yVal = points[iPt].Y; if ( xVal != PointPair.Missing && yVal != PointPair.Missing ) { if ( curve.IsBar || curve is ErrorBarItem || curve is HiLowBarItem || curve is OHLCBarItem || curve is JapaneseCandleStickItem ) { double baseVal, lowVal, hiVal; valueHandler.GetValues( curve, iPt, out baseVal, out lowVal, out hiVal ); if ( lowVal > hiVal ) { double tmpVal = lowVal; lowVal = hiVal; hiVal = tmpVal; } if ( isXBaseAxis ) { double centerVal = valueHandler.BarCenterValue( curve, barWidth, iPt, xVal, iBar ); if ( x < centerVal - barWidthUserHalf || x > centerVal + barWidthUserHalf || yAct < lowVal || yAct > hiVal ) continue; } else { double centerVal = valueHandler.BarCenterValue( curve, barWidth, iPt, yVal, iBar ); if ( yAct < centerVal - barWidthUserHalf || yAct > centerVal + barWidthUserHalf || x < lowVal || x > hiVal ) continue; } if ( nearestBar == null ) { iNearestBar = iPt; nearestBar = curve; } } else if ( xVal >= _xAxis._scale._min && xVal <= _xAxis._scale._max && yVal >= yMinAct && yVal <= yMaxAct ) { if ( curve is LineItem && _lineType == LineType.Stack ) { double zVal; valueHandler.GetValues( curve, iPt, out xVal, out zVal, out yVal ); } distX = ( xVal - x ) * xPixPerUnit; distY = ( yVal - yAct ) * yPixPerUnitAct; dist = distX * distX + distY * distY; if ( dist >= minDist ) continue; minDist = dist; iNearest = iPt; nearestCurve = curve; } } } if ( curve.IsBar ) iBar++; } } } if ( nearestCurve is LineItem ) { float halfSymbol = (float)( ( (LineItem)nearestCurve ).Symbol.Size * CalcScaleFactor() / 2 ); minDist -= halfSymbol * halfSymbol; if ( minDist < 0 ) minDist = 0; } if ( minDist >= tolSquared && nearestBar != null ) { // if no point met the tolerance, but a bar was found, use it nearestCurve = nearestBar; iNearest = iNearestBar; return true; } else if ( minDist < tolSquared ) { // Did we find a close point, and is it within the tolerance? // (minDist is the square of the distance in pixel units) return true; } else // otherwise, no valid point found return false; }
/// <summary> /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/> /// device as a symbol at each defined point. The routine /// only draws the symbols; the lines are draw by the /// <see cref="Line.DrawCurve"/> method. This method /// is normally only called by the Draw method of the /// <see cref="CurveItem"/> object /// </summary> /// <param name="g"> /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// </param> /// <param name="pane"> /// A reference to the <see cref="GraphPane"/> object that is the parent or /// owner of this object. /// </param> /// <param name="curve">A <see cref="LineItem"/> representing this /// curve.</param> /// <param name="scaleFactor"> /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent <see cref="GraphPane"/> object using the /// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> public void Draw(Graphics g, GraphPane pane, LineItem curve, float scaleFactor) { float tmpX, tmpY; 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 = _border.MakePen(pane.IsPenWidthScaled, scaleFactor)) using (GraphicsPath path = MakePath(g, scaleFactor)) { RectangleF rect = path.GetBounds(); using (Brush brush = Fill.MakeBrush(rect)) { var valueHandler = new ValueHandler(pane, false); Scale xScale = pane.XAxis.Scale; Scale yScale = curve.GetYAxis(pane).Scale; bool xIsLog = xScale.IsLog; bool yIsLog = yScale.IsLog; // Loop over each defined point 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 && !Double.IsNaN(curX) && !Double.IsNaN(curY) && !Double.IsInfinity(curX) && !Double.IsInfinity(curY) && (curX > 0 || !xIsLog) && (!yIsLog || curY > 0.0)) { // Transform the user scale values to pixel locations tmpX = xScale.Transform(curve.IsOverrideOrdinal, i, curX); tmpY = yScale.Transform(curve.IsOverrideOrdinal, i, curY); // 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) { using (Brush tBrush = _fill.MakeBrush(rect, points[i])) DrawSymbol(g, tmpX, tmpY, path, pen, tBrush); } else { // Otherwise, the brush is already defined // Draw the symbol at the specified pixel location DrawSymbol(g, tmpX, tmpY, path, pen, brush); } } } } } g.SmoothingMode = sModeSave; } }