/// <summary> /// Calculate the <see cref="Legend"/> rectangle (see cref="Rect"/>), /// taking into account the number of required legend /// entries, and the legend drawing preferences. Adjust the size of the /// <see cref="GraphPane.AxisRect"/> for the parent <see cref="GraphPane"/> to accomodate the /// space required by the legend. /// </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="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="GraphPane.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <param name="axisRect"> /// The rectangle that contains the area bounded by the axes, in pixel units. /// <seealso cref="ZedGraph.AxisRect">AxisRect</seealso> /// </param> /// <param name="hStack">The number of columns (horizontal stacking) to be used /// for drawing the legend</param> /// <param name="legendWidth">The width of each column in the legend (pixels)</param> public void CalcRect(Graphics g, GraphPane pane, double scaleFactor, ref RectangleF axisRect, out int hStack, out float legendWidth) { // Start with an empty rectangle this.rect = Rectangle.Empty; hStack = 1; legendWidth = 1; // If the legend is invisible, don't do anything if (!this.isVisible) { return; } int nCurve = 0; float charHeight = this.FontSpec.GetHeight(scaleFactor), halfCharHeight = charHeight / 2.0F, charWidth = this.FontSpec.GetWidth(g, scaleFactor), gap = pane.ScaledGap(scaleFactor), maxWidth = 0, tmpWidth; // Loop through each curve in the curve list // Find the maximum width of the legend labels foreach (CurveItem curve in pane.CurveList) { // Calculate the width of the label save the max width tmpWidth = this.FontSpec.GetWidth(g, curve.Label, scaleFactor); if (tmpWidth > maxWidth) { maxWidth = tmpWidth; } nCurve++; } float widthAvail; // Is this legend horizontally stacked? if (this.isHStack) { // Determine the available space for horizontal stacking switch (this.location) { // Never stack if the legend is to the right or left case LegendLoc.Right: case LegendLoc.Left: widthAvail = 0; break; // for the top & bottom, the axis frame width is available case LegendLoc.Top: case LegendLoc.Bottom: widthAvail = pane.AxisRect.Width; break; // for inside the axis area, use 1/2 of the axis frame width case LegendLoc.InsideTopRight: case LegendLoc.InsideTopLeft: case LegendLoc.InsideBotRight: case LegendLoc.InsideBotLeft: widthAvail = pane.AxisRect.Width / 2; break; // shouldn't ever happen default: widthAvail = 0; break; } // width of one legend entry legendWidth = 3 * charHeight + 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 - halfCharHeight) / legendWidth); } // You can never have more columns than legend entries if (hStack > nCurve) { hStack = nCurve; } // a saftey check if (hStack == 0) { hStack = 1; } } else { legendWidth = 3.5F * charHeight + 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 * legendWidth; // The total legend height float legHeight = (float)Math.Ceiling((double)nCurve / (double)hStack) * charHeight; // Now calculate the legend rect based on the above determined parameters // Also, adjust the plotArea and axisRect to reflect the space for the legend if (nCurve > 0) { // 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.location) { case LegendLoc.Right: this.rect.X = pane.PaneRect.Right - totLegWidth - gap; this.rect.Y = axisRect.Top; axisRect.Width -= totLegWidth + halfCharHeight; break; case LegendLoc.Top: this.rect.X = axisRect.Left; this.rect.Y = axisRect.Top; axisRect.Y += legHeight + halfCharHeight; axisRect.Height -= legHeight + halfCharHeight; break; case LegendLoc.Bottom: this.rect.X = axisRect.Left; this.rect.Y = pane.PaneRect.Bottom - legHeight - gap; axisRect.Height -= legHeight + halfCharHeight; break; case LegendLoc.Left: this.rect.X = pane.PaneRect.Left + gap; this.rect.Y = axisRect.Top; axisRect.X += totLegWidth + halfCharHeight; axisRect.Width -= totLegWidth + halfCharHeight; break; case LegendLoc.InsideTopRight: this.rect.X = axisRect.Right - totLegWidth; this.rect.Y = axisRect.Top; break; case LegendLoc.InsideTopLeft: this.rect.X = axisRect.Left; this.rect.Y = axisRect.Top; break; case LegendLoc.InsideBotRight: this.rect.X = axisRect.Right - totLegWidth; this.rect.Y = axisRect.Bottom - legHeight; break; case LegendLoc.InsideBotLeft: this.rect.X = axisRect.Left; this.rect.Y = axisRect.Bottom - legHeight; break; } // Calculate the Right and Bottom edges of the rect this.rect.Width = totLegWidth; this.rect.Height = legHeight; } }
/// <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="GraphPane"/> 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="GraphPane.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> /// <param name="hStack">The number of columns (horizontal stacking) to be used /// for drawing the legend</param> /// <param name="legendWidth">The width of each column in the legend (pixels)</param> /// <param name="legendHeight">The height of each row in the legend (pixels)</param> public void CalcRect(Graphics g, GraphPane pane, double scaleFactor, ref RectangleF tAxisRect, out int hStack, out float legendWidth, out float legendHeight ) { // Start with an empty rectangle this.rect = Rectangle.Empty; hStack = 1; legendWidth = 1; legendHeight = 0; // If the legend is invisible, don't do anything if ( !this.isVisible ) return; int nCurve = 0; float charHeight = this.FontSpec.GetHeight( scaleFactor ), halfCharHeight = charHeight / 2.0F, charWidth = this.FontSpec.GetWidth( g, scaleFactor ), gap = pane.ScaledGap( scaleFactor ), maxWidth = 0, tmpWidth; // Loop through each curve in the curve list // Find the maximum width of the legend labels foreach( CurveItem curve in pane.CurveList ) { if ( curve.IsLegendLabelVisible && curve.Label != "" ) { // Calculate the width of the label save the max width tmpWidth = this.FontSpec.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 > legendHeight) legendHeight = ((LineItem)curve).Symbol.Size; nCurve++; } } 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.Bottom: 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 legendWidth = 3 * charHeight + 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 - halfCharHeight) / legendWidth ); // You can never have more columns than legend entries if ( hStack > nCurve ) hStack = nCurve; // a saftey check if ( hStack == 0 ) hStack = 1; } else legendWidth = 3.5F * charHeight + 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 * legendWidth; // The total legend height legendHeight = legendHeight * (float) scaleFactor + halfCharHeight; if (charHeight > legendHeight) legendHeight = charHeight; float totLegHeight = (float) Math.Ceiling( (double) nCurve / (double) hStack ) * legendHeight; 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 ); // 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 = pane.PaneRect.Right - totLegWidth - gap; newRect.Y = tAxisRect.Top; tAxisRect.Width -= totLegWidth + halfCharHeight; break; case LegendPos.Top: newRect.X = tAxisRect.Left; newRect.Y = tAxisRect.Top; tAxisRect.Y += totLegHeight + halfCharHeight; tAxisRect.Height -= totLegHeight + halfCharHeight; break; case LegendPos.Bottom: newRect.X = tAxisRect.Left; newRect.Y = pane.PaneRect.Bottom - totLegHeight - gap; tAxisRect.Height -= totLegHeight + halfCharHeight; break; case LegendPos.Left: newRect.X = pane.PaneRect.Left + gap; newRect.Y = tAxisRect.Top; tAxisRect.X += totLegWidth + halfCharHeight; tAxisRect.Width -= totLegWidth + halfCharHeight; 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; }
/// <summary> /// Render the <see cref="Legend"/> to the specified <see cref="Graphics"/> device /// This method is normally only called by the Draw method /// of the parent <see cref="GraphPane"/> 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="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="GraphPane.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <param name="hStack">The number of columns (horizontal stacking) to be used /// for drawing the legend</param> /// <param name="legendWidth">The width of each column in the legend</param> public void Draw(Graphics g, GraphPane pane, double scaleFactor, int hStack, float legendWidth) { // if the legend is not visible, do nothing if (!this.isVisible) { return; } // Fill the background with the specified color if required if (this.isFilled) { SolidBrush brush = new SolidBrush(this.fillColor); g.FillRectangle(brush, this.rect); } // Set up some scaled dimensions for calculating sizes and locations float charHeight = this.FontSpec.GetHeight(scaleFactor), halfCharHeight = charHeight / 2.0F; float charWidth = this.FontSpec.GetWidth(g, scaleFactor); float gap = pane.ScaledGap(scaleFactor); int iEntry = 0; float x, y; // Get a brush for the legend label text SolidBrush brushB = new SolidBrush(Color.Black); // Loop for each curve in the CurveList collection foreach (CurveItem curve in pane.CurveList) { // 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 // charHeight is the line spacing, with no extra margin above x = this.rect.Left + halfCharHeight + (iEntry % hStack) * legendWidth; y = this.rect.Top + (int)(iEntry / hStack) * charHeight; // Draw the legend label for the current curve this.FontSpec.Draw(g, curve.Label, x + 2.5F * charHeight, y, FontAlignH.Left, FontAlignV.Top, scaleFactor); // Draw a sample curve to the left of the label text curve.Line.Draw(g, x, y + charHeight / 2, x + 2 * charHeight, y + halfCharHeight); // Draw a sample symbol to the left of the label text curve.Symbol.Draw(g, x + charHeight, y + halfCharHeight, scaleFactor); // maintain a curve count for positioning iEntry++; } // Draw a frame around the legend if required if (iEntry > 0 && this.isFramed) { Pen pen = new Pen(this.frameColor, this.frameWidth); g.DrawRectangle(pen, Rectangle.Round(this.rect)); } }
/// <summary> /// Calculate the space required for this <see cref="Axis"/> /// object. This is the space between the paneRect and the axisRect for /// this particular axis. /// </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="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="GraphPane.CalcScaleFactor"/> method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// </param> /// <returns>Returns the space, in pixels, required for this axis (between the /// paneRect and axisRect)</returns> public float CalcSpace( Graphics g, GraphPane pane, double scaleFactor ) { float charHeight = this.ScaleFontSpec.GetHeight( scaleFactor ); float gap = pane.ScaledGap( scaleFactor ); float ticSize = this.ScaledTic( scaleFactor ); // axisRect is the actual area of the plot as bounded by the axes // Always leave 1xgap space, even if no axis is displayed float space = gap; // Account for the Axis if ( this.isVisible ) { // tic takes up 1x tic // space between tic and scale label is 0.5 tic // scale label is GetScaleMaxSpace() // space between scale label and axis label is 0.5 tic space += this.GetScaleMaxSpace( g, pane, scaleFactor ).Height + ticSize * 2.0F; // Only add space for the label if there is one // Axis Title gets actual height plus 1x gap if ( this.title.Length > 0 && this.isShowTitle ) { space += this.TitleFontSpec.BoundingBox( g, this.title, scaleFactor ).Height; } } // for the Y axes, make sure that enough space is left to fit the first // and last X axis scale label if ( ( ( this is YAxis ) || ( this is Y2Axis ) ) && pane.XAxis.IsVisible ) { float tmpSpace = pane.XAxis.GetScaleMaxSpace( g, pane, scaleFactor ).Width / 2.0F + charHeight; if ( tmpSpace > space ) space = tmpSpace; } // Verify that the minSpace property was satisfied space = Math.Max( space, this.minSpace * (float) scaleFactor ); return space; }