/// <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; _numberOfColumns = 1; _legendItemWidth = 1; _legendItemHeight = 0; RectangleF clientRect = pane.CalcClientRect(g, scaleFactor); // If the legend is invisible, don't do anything if (!_isVisible) { return; } int numberOfCurvesToDraw = 0; PaneList paneList = GetPaneList(pane); _tmpSize = GetMaxHeight(paneList, g, scaleFactor); float halfGap = _tmpSize / 2.0F, maxWidth = 0, tmpWidth = 0, gapPix = _gap * _tmpSize; bool showingSecondaryLabel = false; 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]; bool atLeastOneLabelDrawn = false; 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; } atLeastOneLabelDrawn = true; } if (curve.SecondaryLabel._text != string.Empty && curve.SecondaryLabel._isVisible) { // Calculate the width of the label save the max width FontSpec tmpFont = (curve.SecondaryLabel._fontSpec != null) ? curve.SecondaryLabel._fontSpec : this.FontSpec; tmpWidth = tmpFont.GetWidth(g, curve.SecondaryLabel._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; } atLeastOneLabelDrawn = true; showingSecondaryLabel = true; } if (atLeastOneLabelDrawn) { ++numberOfCurvesToDraw; } } 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) { if (showingSecondaryLabel) { _legendItemWidth = 3.0f * _tmpSize + maxWidth * 1.6f; } else { _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) { _numberOfColumns = (int)((widthAvail - halfGap) / _legendItemWidth); } // You can never have more columns than legend entries if (_numberOfColumns > numberOfCurvesToDraw) { _numberOfColumns = numberOfCurvesToDraw; } // a saftey check if (_numberOfColumns == 0) { _numberOfColumns = 1; } } else { if (_isShowLegendSymbols) { _legendItemWidth = 4.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 = _numberOfColumns * _legendItemWidth; // The total legend height _legendItemHeight = _legendItemHeight * (float)scaleFactor + halfGap; if (_tmpSize > _legendItemHeight) { _legendItemHeight = _tmpSize; } float totLegHeight = (float)Math.Ceiling((double)numberOfCurvesToDraw / (double)_numberOfColumns) * _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 (numberOfCurvesToDraw > 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> /// 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 ) / 2; 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> /// 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="GraphPane.AxisRect"/> 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="tAxisRect"> /// The rectangle that contains the area bounded by the axes, in pixel units. /// <seealso cref="GraphPane.AxisRect">AxisRect</seealso> /// </param> public void CalcRect(Graphics g, PaneBase pane, float scaleFactor, ref RectangleF tAxisRect) { // Start with an empty rectangle this.rect = Rectangle.Empty; this.hStack = 1; this.legendItemWidth = 1; this.legendItemHeight = 0; // If the legend is invisible, don't do anything if (!this.isVisible) { return; } int nCurve = 0; PaneList paneList = GetPaneList(pane); this.gap = GetMaxHeight(paneList, g, scaleFactor); float halfGap = this.gap / 2.0F, maxWidth = 0, tmpWidth; 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) { if (curve.Label != "" && curve.IsLegendLabelVisible) { // Calculate the width of the label save the max width FontSpec tmpFont = (curve.FontSpec != null) ? curve.FontSpec : this.FontSpec; tmpWidth = tmpFont.GetWidth(g, curve.Label, scaleFactor); if (tmpWidth > maxWidth) { maxWidth = tmpWidth; } // Save the maximum symbol height for line-type curves if (curve is LineItem && ((LineItem)curve).Symbol.Size > this.legendItemHeight) { this.legendItemHeight = ((LineItem)curve).Symbol.Size; } nCurve++; } } if (pane is MasterPane && ((MasterPane)pane).HasUniformLegendEntries) { break; } } float widthAvail; // Is this legend horizontally stacked? if (this.isHStack) { // Determine the available space for horizontal stacking switch (this.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 = tAxisRect.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 = tAxisRect.Width / 2; break; // shouldn't ever happen default: widthAvail = 0; break; } // width of one legend entry this.legendItemWidth = 3 * gap + 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) { this.hStack = (int)((widthAvail - halfGap) / this.legendItemWidth); } // You can never have more columns than legend entries if (this.hStack > nCurve) { this.hStack = nCurve; } // a saftey check if (this.hStack == 0) { this.hStack = 1; } } else { this.legendItemWidth = 3.5F * gap + 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 = this.hStack * this.legendItemWidth; // The total legend height this.legendItemHeight = this.legendItemHeight * (float)scaleFactor + halfGap; if (gap > this.legendItemHeight) { this.legendItemHeight = gap; } float totLegHeight = (float)Math.Ceiling((double)nCurve / (double)hStack) * this.legendItemHeight; RectangleF newRect = new RectangleF(); // Now calculate the legend rect based on the above determined parameters // Also, adjust the axisRect to reflect the space for the legend if (nCurve > 0) { newRect = new RectangleF(0, 0, totLegWidth, totLegHeight); RectangleF clientRect = pane.CalcClientRect(g, scaleFactor); // The switch statement assigns the left and top edges, and adjusts the axisRect // as required. The right and bottom edges are calculated at the bottom of the switch. switch (this.position) { case LegendPos.Right: newRect.X = clientRect.Right - totLegWidth; newRect.Y = tAxisRect.Top; tAxisRect.Width -= totLegWidth + halfGap; break; case LegendPos.Top: newRect.X = tAxisRect.Left; newRect.Y = clientRect.Top; tAxisRect.Y += totLegHeight + halfGap; tAxisRect.Height -= totLegHeight + halfGap; break; case LegendPos.TopCenter: newRect.X = tAxisRect.Left + (tAxisRect.Width - totLegWidth) / 2; newRect.Y = tAxisRect.Top; tAxisRect.Y += totLegHeight + halfGap; tAxisRect.Height -= totLegHeight + halfGap; break; case LegendPos.Bottom: newRect.X = tAxisRect.Left + (tAxisRect.Width - totLegWidth) / 2; newRect.Y = clientRect.Bottom - totLegHeight; tAxisRect.Height -= totLegHeight + halfGap; break; case LegendPos.BottomCenter: newRect.X = tAxisRect.Left + (tAxisRect.Width - totLegWidth) / 2; newRect.Y = clientRect.Bottom - totLegHeight; tAxisRect.Height -= totLegHeight + halfGap; break; case LegendPos.Left: newRect.X = clientRect.Left; newRect.Y = tAxisRect.Top; tAxisRect.X += totLegWidth + halfGap; tAxisRect.Width -= totLegWidth + halfGap; break; case LegendPos.InsideTopRight: newRect.X = tAxisRect.Right - totLegWidth; newRect.Y = tAxisRect.Top; break; case LegendPos.InsideTopLeft: newRect.X = tAxisRect.Left; newRect.Y = tAxisRect.Top; break; case LegendPos.InsideBotRight: newRect.X = tAxisRect.Right - totLegWidth; newRect.Y = tAxisRect.Bottom - totLegHeight; break; case LegendPos.InsideBotLeft: newRect.X = tAxisRect.Left; newRect.Y = tAxisRect.Bottom - totLegHeight; break; case LegendPos.Float: newRect.Location = this.Location.TransformTopLeft(pane, totLegWidth, totLegHeight); break; } } this.rect = newRect; }