/// <summary> /// Draw the this <see cref="Bar"/> to the specified <see cref="Graphics"/> /// device as a bar at each defined point. This method /// is normally only called by the <see cref="BarItem.Draw"/> method of the /// <see cref="BarItem"/> 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="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="barWidth"> /// The width of each bar, in pixels. /// </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="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 DrawBars(Graphics g, GraphPane pane, CurveItem curve, Axis baseAxis, Axis valueAxis, float barWidth, int pos, float scaleFactor) { // For non-cluster bar types, the position is always zero since the bars are on top // of eachother BarType barType = pane._barSettings.Type; if (barType == BarType.Overlay || barType == BarType.Stack || barType == BarType.PercentStack || barType == BarType.SortedOverlay) { pos = 0; } // Loop over each defined point and draw the corresponding bar for (int i = 0; i < curve.Points.Count; i++) { DrawSingleBar(g, pane, curve, i, pos, baseAxis, valueAxis, barWidth, scaleFactor); } }
/// <summary> /// Draw the specified single bar (an individual "point") of this series to the specified /// <see cref="Graphics"/> device. This method is not as efficient as /// <see cref="DrawBars"/>, which draws the bars for all points. It is intended to be used /// only for <see cref="BarType.SortedOverlay"/>, which requires special handling of each bar. /// </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="pos"> /// The ordinal position of the this bar series (0=first bar, 1=second bar, etc.) /// in the cluster of bars. /// </param> /// <param name="index"> /// The zero-based index number for the single bar to be drawn. /// </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> public void DrawSingleBar(Graphics g, GraphPane pane, CurveItem curve, Axis baseAxis, Axis valueAxis, int pos, int index, float barWidth, float scaleFactor) { // Make sure that a bar value exists for the current curve and current ordinal position if (index >= curve.Points.Count) { return; } // For Overlay and Stack bars, the position is always zero since the bars are on top // of eachother if (pane._barSettings.Type == BarType.Overlay || pane._barSettings.Type == BarType.Stack || pane._barSettings.Type == BarType.PercentStack) { pos = 0; } // Draw the specified bar DrawSingleBar(g, pane, curve, index, pos, baseAxis, valueAxis, barWidth, scaleFactor); }
/// <summary> /// The Copy Constructor /// </summary> /// <param name="rhs">The CurveItem object from which to copy</param> public CurveItem(CurveItem rhs) : base(rhs) { _label = rhs._label.Clone(); //_isY2Axis = rhs.IsY2Axis; //_isX2Axis = rhs.IsX2Axis; _isVisible = rhs.IsVisible; _isOverrideOrdinal = rhs._isOverrideOrdinal; _yAxisIndex = rhs._yAxisIndex; if (rhs.Tag is ICloneable) { this.Tag = ((ICloneable)rhs.Tag).Clone(); } else { this.Tag = rhs.Tag; } _points = (IPointList)rhs.Points.Clone(); //_link = rhs._link.Clone(); }
/// <summary> /// Calculate the <see cref="Legend"/> rectangle (<see cref="Rect"/>), /// taking into account the number of required legend /// entries, and the legend drawing preferences. /// </summary> /// <remarks>Adjust the size of the /// <see cref="Chart.Rect"/> for the parent <see cref="GraphPane"/> to accomodate the /// space required by the legend. /// </remarks> /// <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="PaneBase"/> object that is the parent or /// owner of this object. /// </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="tChartRect"> /// The rectangle that contains the area bounded by the axes, in pixel units. /// <seealso cref="Chart.Rect" /> /// </param> public void CalcRect(Graphics g, PaneBase pane, float scaleFactor, ref RectangleF tChartRect) { // Start with an empty rectangle _rect = Rectangle.Empty; _hStack = 1; _legendItemWidth = 1; _legendItemHeight = 0; RectangleF clientRect = pane.CalcClientRect(g, scaleFactor); // If the legend is invisible, don't do anything if (!_isVisible) { return; } int nCurve = 0; PaneList paneList = GetPaneList(pane); _tmpSize = GetMaxHeight(paneList, g, scaleFactor); float halfGap = _tmpSize / 2.0F, maxWidth = 0, tmpWidth, gapPix = _gap * _tmpSize; foreach (GraphPane tmpPane in paneList) { // Loop through each curve in the curve list // Find the maximum width of the legend labels //foreach ( CurveItem curve in tmpPane.CurveList ) //foreach ( CurveItem curve in GetIterator( tmpPane.CurveList, _isReverse ) ) int count = tmpPane.CurveList.Count; for (int i = 0; i < count; i++) { CurveItem curve = tmpPane.CurveList[_isReverse ? count - i - 1 : i]; if (curve._label._text != string.Empty && curve._label._isVisible) { // Calculate the width of the label save the max width FontSpec tmpFont = (curve._label._fontSpec != null) ? curve._label._fontSpec : this.FontSpec; tmpWidth = tmpFont.GetWidth(g, curve._label._text, scaleFactor); if (tmpWidth > maxWidth) { maxWidth = tmpWidth; } // Save the maximum symbol height for line-type curves if (curve is LineItem && ((LineItem)curve).Symbol.Size > _legendItemHeight) { _legendItemHeight = ((LineItem)curve).Symbol.Size; } nCurve++; } } if (pane is MasterPane && ((MasterPane)pane).IsUniformLegendEntries) { break; } } float widthAvail; // Is this legend horizontally stacked? if (_isHStack) { // Determine the available space for horizontal stacking switch (_position) { // Never stack if the legend is to the right or left case LegendPos.Right: case LegendPos.Left: widthAvail = 0; break; // for the top & bottom, the axis border width is available case LegendPos.Top: case LegendPos.TopCenter: case LegendPos.Bottom: case LegendPos.BottomCenter: widthAvail = tChartRect.Width; break; // for the top & bottom flush left, the panerect less margins is available case LegendPos.TopFlushLeft: case LegendPos.BottomFlushLeft: widthAvail = clientRect.Width; break; // for inside the axis area or Float, use 1/2 of the axis border width case LegendPos.InsideTopRight: case LegendPos.InsideTopLeft: case LegendPos.InsideBotRight: case LegendPos.InsideBotLeft: case LegendPos.Float: widthAvail = tChartRect.Width / 2; break; // shouldn't ever happen default: widthAvail = 0; break; } // width of one legend entry if (_isShowLegendSymbols) { _legendItemWidth = 3.0f * _tmpSize + maxWidth; } else { _legendItemWidth = 0.5f * _tmpSize + maxWidth; } // Calculate the number of columns in the legend // Normally, the legend is: // available width / ( max width of any entry + space for line&symbol ) if (maxWidth > 0) { _hStack = (int)((widthAvail - halfGap) / _legendItemWidth); } // You can never have more columns than legend entries if (_hStack > nCurve) { _hStack = nCurve; } // a saftey check if (_hStack == 0) { _hStack = 1; } } else { if (_isShowLegendSymbols) { _legendItemWidth = 3.0F * _tmpSize + maxWidth; } else { _legendItemWidth = 0.5F * _tmpSize + maxWidth; } } // legend is: // item: space line space text space // width: wid 4*wid wid maxWid wid // The symbol is centered on the line // // legend begins 3 * wid to the right of the plot rect // // The height of the legend is the actual height of the lines of text // (nCurve * hite) plus wid on top and wid on the bottom // total legend width float totLegWidth = _hStack * _legendItemWidth; // The total legend height _legendItemHeight = _legendItemHeight * (float)scaleFactor + halfGap; if (_tmpSize > _legendItemHeight) { _legendItemHeight = _tmpSize; } float totLegHeight = (float)Math.Ceiling((double)nCurve / (double)_hStack) * _legendItemHeight; RectangleF newRect = new RectangleF(); // Now calculate the legend rect based on the above determined parameters // Also, adjust the ChartRect to reflect the space for the legend if (nCurve > 0) { newRect = new RectangleF(0, 0, totLegWidth, totLegHeight); // The switch statement assigns the left and top edges, and adjusts the ChartRect // as required. The right and bottom edges are calculated at the bottom of the switch. switch (_position) { case LegendPos.Right: newRect.X = clientRect.Right - totLegWidth; newRect.Y = tChartRect.Top; tChartRect.Width -= totLegWidth + gapPix; break; case LegendPos.Top: newRect.X = tChartRect.Left; newRect.Y = clientRect.Top; tChartRect.Y += totLegHeight + gapPix; tChartRect.Height -= totLegHeight + gapPix; break; case LegendPos.TopFlushLeft: newRect.X = clientRect.Left; newRect.Y = clientRect.Top; tChartRect.Y += totLegHeight + gapPix * 1.5f; tChartRect.Height -= totLegHeight + gapPix * 1.5f; break; case LegendPos.TopCenter: newRect.X = tChartRect.Left + (tChartRect.Width - totLegWidth) / 2; newRect.Y = tChartRect.Top; tChartRect.Y += totLegHeight + gapPix; tChartRect.Height -= totLegHeight + gapPix; break; case LegendPos.Bottom: newRect.X = tChartRect.Left; newRect.Y = clientRect.Bottom - totLegHeight; tChartRect.Height -= totLegHeight + gapPix; break; case LegendPos.BottomFlushLeft: newRect.X = clientRect.Left; newRect.Y = clientRect.Bottom - totLegHeight; tChartRect.Height -= totLegHeight + gapPix; break; case LegendPos.BottomCenter: newRect.X = tChartRect.Left + (tChartRect.Width - totLegWidth) / 2; newRect.Y = clientRect.Bottom - totLegHeight; tChartRect.Height -= totLegHeight + gapPix; break; case LegendPos.Left: newRect.X = clientRect.Left; newRect.Y = tChartRect.Top; tChartRect.X += totLegWidth + halfGap; tChartRect.Width -= totLegWidth + gapPix; break; case LegendPos.InsideTopRight: newRect.X = tChartRect.Right - totLegWidth; newRect.Y = tChartRect.Top; break; case LegendPos.InsideTopLeft: newRect.X = tChartRect.Left; newRect.Y = tChartRect.Top; break; case LegendPos.InsideBotRight: newRect.X = tChartRect.Right - totLegWidth; newRect.Y = tChartRect.Bottom - totLegHeight; break; case LegendPos.InsideBotLeft: newRect.X = tChartRect.Left; newRect.Y = tChartRect.Bottom - totLegHeight; break; case LegendPos.Float: newRect.Location = this.Location.TransformTopLeft(pane, totLegWidth, totLegHeight); break; } } _rect = newRect; }
/// <summary> /// Render the <see cref="Legend"/> to the specified <see cref="Graphics"/> device. /// </summary> /// <remarks> /// This method is normally only called by the Draw method /// of the parent <see cref="GraphPane"/> object. /// </remarks> /// <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="PaneBase"/> object that is the parent or /// owner of this object. /// </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, PaneBase pane, float scaleFactor) { // if the legend is not visible, do nothing if (!_isVisible) { return; } // Fill the background with the specified color if required _fill.Draw(g, _rect); PaneList paneList = GetPaneList(pane); float halfGap = _tmpSize / 2.0F; // Check for bad data values if (_hStack <= 0) { _hStack = 1; } if (_legendItemWidth <= 0) { _legendItemWidth = 100; } if (_legendItemHeight <= 0) { _legendItemHeight = _tmpSize; } //float gap = pane.ScaledGap( scaleFactor ); int iEntry = 0; float x, y; // Get a brush for the legend label text using (SolidBrush brushB = new SolidBrush(Color.Black)) { foreach (GraphPane tmpPane in paneList) { // Loop for each curve in the CurveList collection //foreach ( CurveItem curve in tmpPane.CurveList ) int count = tmpPane.CurveList.Count; for (int i = 0; i < count; i++) { CurveItem curve = tmpPane.CurveList[_isReverse ? count - i - 1 : i]; if (curve._label._text != "" && curve._label._isVisible) { // Calculate the x,y (TopLeft) location of the current // curve legend label // assuming: // charHeight/2 for the left margin, plus legendWidth for each // horizontal column // legendHeight is the line spacing, with no extra margin above x = _rect.Left + halfGap / 2.0F + (iEntry % _hStack) * _legendItemWidth; y = _rect.Top + (int)(iEntry / _hStack) * _legendItemHeight; // Draw the legend label for the current curve FontSpec tmpFont = (curve._label._fontSpec != null) ? curve._label._fontSpec : this.FontSpec; // This is required because, for long labels, the centering can affect the // position in GDI+. tmpFont.StringAlignment = StringAlignment.Near; if (_isShowLegendSymbols) { tmpFont.Draw(g, pane, curve._label._text, x + 2.5F * _tmpSize, y + _legendItemHeight / 2.0F, AlignH.Left, AlignV.Center, scaleFactor); RectangleF rect = new RectangleF(x, y + _legendItemHeight / 4.0F, 2 * _tmpSize, _legendItemHeight / 2.0F); curve.DrawLegendKey(g, tmpPane, rect, scaleFactor); } else { if (curve._label._fontSpec == null) { tmpFont.FontColor = curve.Color; } tmpFont.Draw(g, pane, curve._label._text, x + 0.0F * _tmpSize, y + _legendItemHeight / 2.0F, AlignH.Left, AlignV.Center, scaleFactor); } // maintain a curve count for positioning iEntry++; } } if (pane is MasterPane && ((MasterPane)pane).IsUniformLegendEntries) { break; } } // Draw a border around the legend if required if (iEntry > 0) { this.Border.Draw(g, pane, scaleFactor, _rect); } } }
/// <summary> /// Get the user scale values associate with a particular point of a /// particular curve.</summary> /// <remarks>The main purpose of this method is to handle /// stacked bars and lines, in which case the stacked values are returned rather /// than the individual data values. However, this method works generically for any /// curve type. /// </remarks> /// <param name="pane">The parent <see cref="GraphPane"/> object.</param> /// <param name="curve">A <see cref="CurveItem"/> object of interest.</param> /// <param name="iPt">The zero-based point index for the point of interest.</param> /// <param name="baseVal">A <see cref="Double"/> value representing the value /// for the independent axis.</param> /// <param name="lowVal">A <see cref="Double"/> value representing the lower /// value for the dependent axis.</param> /// <param name="hiVal">A <see cref="Double"/> value representing the upper /// value for the dependent axis.</param> /// <returns>true if the data point is value, false for /// <see cref="PointPairBase.Missing"/>, invalid, etc. data.</returns> public static bool GetValues(GraphPane pane, CurveItem curve, int iPt, out double baseVal, out double lowVal, out double hiVal) { hiVal = PointPair.Missing; lowVal = PointPair.Missing; baseVal = PointPair.Missing; if (curve == null || curve.Points.Count <= iPt || !curve.IsVisible) { return(false); } Axis baseAxis = curve.BaseAxis(pane); Axis valueAxis = curve.ValueAxis(pane); if (baseAxis is XAxis || baseAxis is X2Axis) { baseVal = curve.Points[iPt].X; } else { baseVal = curve.Points[iPt].Y; } // is it a stacked bar type? if (curve is BarItem && (pane._barSettings.Type == BarType.Stack || pane._barSettings.Type == BarType.PercentStack)) { double positiveStack = 0; double negativeStack = 0; double curVal; // loop through all the curves, summing up the values to get a total (only // for the current ordinal position iPt) foreach (CurveItem tmpCurve in pane.CurveList) { // Sum the value for the current curve only if it is a bar if (tmpCurve.IsBar && tmpCurve.IsVisible) { curVal = PointPair.Missing; // For non-ordinal curves, find a matching base value (must match exactly) if (curve.IsOverrideOrdinal || !baseAxis._scale.IsAnyOrdinal) { IPointList points = tmpCurve.Points; for (int i = 0; i < points.Count; i++) { if ((baseAxis is XAxis || baseAxis is X2Axis) && points[i].X == baseVal) { curVal = points[i].Y; break; } else if (!(baseAxis is XAxis || baseAxis is X2Axis) && points[i].Y == baseVal) { curVal = points[i].X; break; } } } // otherwise, it's an ordinal type so use the value at the same ordinal position else if (iPt < tmpCurve.Points.Count) { // Get the value for the appropriate value axis if (baseAxis is XAxis || baseAxis is X2Axis) { curVal = tmpCurve.Points[iPt].Y; } else { curVal = tmpCurve.Points[iPt].X; } } // If it's a missing value, skip it if (curVal == PointPair.Missing) { positiveStack = PointPair.Missing; negativeStack = PointPair.Missing; } // the current curve is the target curve, save the summed values for later if (tmpCurve == curve) { // if the value is positive, use the positive stack if (curVal >= 0) { lowVal = positiveStack; hiVal = (curVal == PointPair.Missing || positiveStack == PointPair.Missing) ? PointPair.Missing : positiveStack + curVal; } // otherwise, use the negative stack else { hiVal = negativeStack; lowVal = (curVal == PointPair.Missing || negativeStack == PointPair.Missing) ? PointPair.Missing : negativeStack + curVal; } } // Add all positive values to the positive stack, and negative values to the // negative stack if (curVal >= 0) { positiveStack = (curVal == PointPair.Missing || positiveStack == PointPair.Missing) ? PointPair.Missing : positiveStack + curVal; } else { negativeStack = (curVal == PointPair.Missing || negativeStack == PointPair.Missing) ? PointPair.Missing : negativeStack + curVal; } } } // if the curve is a PercentStack type, then calculate the percent for this bar // based on the total height of the stack if (pane._barSettings.Type == BarType.PercentStack && hiVal != PointPair.Missing && lowVal != PointPair.Missing) { // Use the total magnitude of the positive plus negative bar stacks to determine // the percentage value positiveStack += Math.Abs(negativeStack); // just to avoid dividing by zero... if (positiveStack != 0) { // calculate the percentage values lowVal = lowVal / positiveStack * 100.0; hiVal = hiVal / positiveStack * 100.0; } else { lowVal = 0; hiVal = 0; } } if (baseVal == PointPair.Missing || lowVal == PointPair.Missing || hiVal == PointPair.Missing) { return(false); } else { return(true); } } // If the curve is a stacked line type, then sum up the values similar to the stacked bar type else if (curve is LineItem && pane.LineType == LineType.Stack) { double stack = 0; double curVal; // loop through all the curves, summing up the values to get a total (only // for the current ordinal position iPt) foreach (CurveItem tmpCurve in pane.CurveList) { // make sure the curve is a Line type if (tmpCurve is LineItem && tmpCurve.IsVisible) { curVal = PointPair.Missing; // For non-ordinal curves, find a matching base value (must match exactly) if (curve.IsOverrideOrdinal || !baseAxis._scale.IsAnyOrdinal) { IPointList points = tmpCurve.Points; for (int i = 0; i < points.Count; i++) { if (points[i].X == baseVal) { curVal = points[i].Y; break; } } } // otherwise, it's an ordinal type so use the value at the same ordinal position else if (iPt < tmpCurve.Points.Count) { // For line types, the Y axis is always the value axis curVal = tmpCurve.Points[iPt].Y; } // if the current value is missing, then the rest of the stack is missing if (curVal == PointPair.Missing) { stack = PointPair.Missing; } // if the current curve is the target curve, save the values if (tmpCurve == curve) { lowVal = stack; hiVal = (curVal == PointPair.Missing || stack == PointPair.Missing) ? PointPair.Missing : stack + curVal; } // sum all the curves to a single total. This includes both positive and // negative values (unlike the bar stack type). stack = (curVal == PointPair.Missing || stack == PointPair.Missing) ? PointPair.Missing : stack + curVal; } } if (baseVal == PointPair.Missing || lowVal == PointPair.Missing || hiVal == PointPair.Missing) { return(false); } else { return(true); } } // otherwise, the curve is not a stacked type (not a stacked bar or stacked line) else { if ((!(curve is HiLowBarItem)) && (!(curve is ErrorBarItem))) { lowVal = 0; } else { lowVal = curve.Points[iPt].LowValue; } if (baseAxis is XAxis || baseAxis is X2Axis) { hiVal = curve.Points[iPt].Y; } else { hiVal = curve.Points[iPt].X; } } // Special Exception: Bars on log scales should always plot from the Min value upwards, // since they can never be zero if (curve is BarItem && valueAxis._scale.IsLog && lowVal == 0) { lowVal = valueAxis._scale._min; } if (baseVal == PointPair.Missing || hiVal == PointPair.Missing || (lowVal == PointPair.Missing && (curve is ErrorBarItem || curve is HiLowBarItem))) { return(false); } else { return(true); } }
/// <summary> /// Get the user scale values associate with a particular point of a /// particular curve.</summary> /// <remarks>The main purpose of this method is to handle /// stacked bars, in which case the stacked values are returned rather /// than the individual data values. /// </remarks> /// <param name="curve">A <see cref="CurveItem"/> object of interest.</param> /// <param name="iPt">The zero-based point index for the point of interest.</param> /// <param name="baseVal">A <see cref="Double"/> value representing the value /// for the independent axis.</param> /// <param name="lowVal">A <see cref="Double"/> value representing the lower /// value for the dependent axis.</param> /// <param name="hiVal">A <see cref="Double"/> value representing the upper /// value for the dependent axis.</param> /// <returns>true if the data point is value, false for /// <see cref="PointPairBase.Missing"/>, invalid, etc. data.</returns> public bool GetValues(CurveItem curve, int iPt, out double baseVal, out double lowVal, out double hiVal) { return(GetValues(_pane, curve, iPt, out baseVal, out lowVal, out hiVal)); }