mirror of https://github.com/mhowlett/nplot.git
374 lines
12 KiB
C#
374 lines
12 KiB
C#
/*
|
|
* NPlot - A charting library for .NET
|
|
*
|
|
* LegendBase.cs
|
|
* Copyright (C) 2003-2006 Matt Howlett and others.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using System.Collections;
|
|
using System.Drawing;
|
|
|
|
namespace NPlot
|
|
{
|
|
/// <summary>
|
|
/// Provides functionality for drawing legends.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The class is quite closely tied to PlotSurface2D.
|
|
/// </remarks>
|
|
public class LegendBase
|
|
{
|
|
/// <summary>
|
|
/// The types of legend borders (enum).
|
|
/// </summary>
|
|
public enum BorderType
|
|
{
|
|
/// <summary>
|
|
/// No border.
|
|
/// </summary>
|
|
None = 0,
|
|
|
|
/// <summary>
|
|
/// Line border.
|
|
/// </summary>
|
|
Line = 1,
|
|
|
|
/// <summary>
|
|
/// Shaded border.
|
|
/// </summary>
|
|
Shadow = 2
|
|
//Curved = 3
|
|
}
|
|
|
|
private Color bgColor_;
|
|
private Color borderColor_;
|
|
private Font font_;
|
|
private int numberItemsHorizontally_ = 1;
|
|
private int numberItemsVertically_ = -1;
|
|
private Color textColor_;
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
public LegendBase()
|
|
{
|
|
Font = new Font(new FontFamily("Arial"), 10, FontStyle.Regular, GraphicsUnit.Pixel);
|
|
BackgroundColor = Color.White;
|
|
BorderColor = Color.Black;
|
|
TextColor = Color.Black;
|
|
BorderStyle = BorderType.Shadow;
|
|
AutoScaleText = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The font used to draw text in the legend.
|
|
/// </summary>
|
|
public Font Font
|
|
{
|
|
get { return font_; }
|
|
set { font_ = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The color used to draw text in the legend.
|
|
/// </summary>
|
|
public Color TextColor
|
|
{
|
|
get { return textColor_; }
|
|
set { textColor_ = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The background color of the legend.
|
|
/// </summary>
|
|
public Color BackgroundColor
|
|
{
|
|
get { return bgColor_; }
|
|
set { bgColor_ = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The color of the legend border.
|
|
/// </summary>
|
|
public Color BorderColor
|
|
{
|
|
get { return borderColor_; }
|
|
set { borderColor_ = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The border style to use for the legend.
|
|
/// </summary>
|
|
public BorderType BorderStyle { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether or not to auto scale text in the legend according the physical
|
|
/// dimensions of the plot surface.
|
|
/// </summary>
|
|
public bool AutoScaleText { get; set; }
|
|
|
|
/// <summary>
|
|
/// Setting this does two things. First of all, it sets the maximum number of
|
|
/// items in the legend vertically. Second of all, it makes the legend grow
|
|
/// horizontally (as it must given this constraint).
|
|
/// </summary>
|
|
public int NumberItemsVertically
|
|
{
|
|
set
|
|
{
|
|
numberItemsVertically_ = value;
|
|
numberItemsHorizontally_ = -1;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setting this does two things. First of all, it sets the maximum number of
|
|
/// items in the legend horizontally. Second of all, it makes the legend grow
|
|
/// vertically (as it must given this constraint).
|
|
/// </summary>
|
|
public int NumberItemsHorizontally
|
|
{
|
|
set
|
|
{
|
|
numberItemsHorizontally_ = value;
|
|
numberItemsVertically_ = -1;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the bounding box of the rectangle.
|
|
/// </summary>
|
|
/// <param name="position">the position of the top left of the legend.</param>
|
|
/// <param name="plots">Array of plot objects to appear in the legend.</param>
|
|
/// <param name="scale">if the legend is set to scale, the amount to scale by.</param>
|
|
/// >
|
|
/// <returns></returns>
|
|
/// <remarks>do implementation that doesn't call draw. Change xPos, yPos to PointF</remarks>
|
|
public Rectangle GetBoundingBox(Point position, ArrayList plots, float scale)
|
|
{
|
|
System.Drawing.Bitmap b = new System.Drawing.Bitmap(1, 1);
|
|
Graphics g = Graphics.FromImage(b);
|
|
return Draw(g, position, plots, scale);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw The legend
|
|
/// </summary>
|
|
/// <param name="g">The graphics surface on which to draw</param>
|
|
/// <param name="position">The position of the top left of the axis.</param>
|
|
/// <param name="plots">Array of plot objects to appear in the legend.</param>
|
|
/// <param name="scale">if the legend is set to scale, the amount to scale by.</param>
|
|
/// <returns>bounding box</returns>
|
|
public Rectangle Draw(Graphics g, Point position, ArrayList plots, float scale)
|
|
{
|
|
// first of all determine the Font to use in the legend.
|
|
Font textFont;
|
|
if (AutoScaleText)
|
|
{
|
|
textFont = Utils.ScaleFont(font_, scale);
|
|
}
|
|
else
|
|
{
|
|
textFont = font_;
|
|
}
|
|
|
|
// determine max width and max height of label strings and
|
|
// count the labels.
|
|
int labelCount = 0;
|
|
int maxHt = 0;
|
|
int maxWd = 0;
|
|
int unnamedCount = 0;
|
|
for (int i = 0; i < plots.Count; ++i)
|
|
{
|
|
if (!(plots[i] is IPlot))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IPlot p = (IPlot) plots[i];
|
|
|
|
if (!p.ShowInLegend)
|
|
{
|
|
continue;
|
|
}
|
|
string label = p.Label;
|
|
if (label == "")
|
|
{
|
|
unnamedCount += 1;
|
|
label = "Series " + unnamedCount.ToString();
|
|
}
|
|
SizeF labelSize = g.MeasureString(label, textFont);
|
|
if (labelSize.Height > maxHt)
|
|
{
|
|
maxHt = (int) labelSize.Height;
|
|
}
|
|
if (labelSize.Width > maxWd)
|
|
{
|
|
maxWd = (int) labelSize.Width;
|
|
}
|
|
|
|
++labelCount;
|
|
}
|
|
|
|
bool extendingHorizontally = numberItemsHorizontally_ == -1;
|
|
bool extendingVertically = numberItemsVertically_ == -1;
|
|
|
|
// determine width in legend items count units.
|
|
int widthInItemCount = 0;
|
|
if (extendingVertically)
|
|
{
|
|
if (labelCount >= numberItemsHorizontally_)
|
|
{
|
|
widthInItemCount = numberItemsHorizontally_;
|
|
}
|
|
else
|
|
{
|
|
widthInItemCount = labelCount;
|
|
}
|
|
}
|
|
else if (extendingHorizontally)
|
|
{
|
|
widthInItemCount = labelCount/numberItemsVertically_;
|
|
if (labelCount%numberItemsVertically_ != 0)
|
|
widthInItemCount += 1;
|
|
}
|
|
else
|
|
{
|
|
throw new NPlotException("logic error in legend base");
|
|
}
|
|
|
|
// determine height of legend in items count units.
|
|
int heightInItemCount = 0;
|
|
if (extendingHorizontally)
|
|
{
|
|
if (labelCount >= numberItemsVertically_)
|
|
{
|
|
heightInItemCount = numberItemsVertically_;
|
|
}
|
|
else
|
|
{
|
|
heightInItemCount = labelCount;
|
|
}
|
|
}
|
|
else // extendingVertically
|
|
{
|
|
heightInItemCount = labelCount/numberItemsHorizontally_;
|
|
if (labelCount%numberItemsHorizontally_ != 0)
|
|
heightInItemCount += 1;
|
|
}
|
|
|
|
int lineLength = 20;
|
|
int hSpacing = (int) (5.0f*scale);
|
|
int vSpacing = (int) (3.0f*scale);
|
|
int boxWidth = (int) (widthInItemCount*(lineLength + maxWd + hSpacing*2.0f) + hSpacing);
|
|
int boxHeight = (int) ((float) heightInItemCount*(maxHt + vSpacing) + vSpacing);
|
|
|
|
int totalWidth = boxWidth;
|
|
int totalHeight = boxHeight;
|
|
|
|
// draw box around the legend.
|
|
|
|
if (BorderStyle == BorderType.Line)
|
|
{
|
|
g.FillRectangle(new SolidBrush(bgColor_), position.X, position.Y, boxWidth, boxHeight);
|
|
g.DrawRectangle(new Pen(borderColor_), position.X, position.Y, boxWidth, boxHeight);
|
|
}
|
|
else if (BorderStyle == BorderType.Shadow)
|
|
{
|
|
int offset = (int) (4.0f*scale);
|
|
g.FillRectangle(new SolidBrush(Color.FromArgb(128, Color.Gray)), position.X + offset, position.Y + offset, boxWidth, boxHeight);
|
|
g.FillRectangle(new SolidBrush(bgColor_), position.X, position.Y, boxWidth, boxHeight);
|
|
g.DrawRectangle(new Pen(borderColor_), position.X, position.Y, boxWidth, boxHeight);
|
|
|
|
totalWidth += offset;
|
|
totalHeight += offset;
|
|
}
|
|
|
|
/*
|
|
else if ( this.BorderStyle == BorderType.Curved )
|
|
{
|
|
// TODO. make this nice.
|
|
}
|
|
*/
|
|
|
|
else
|
|
{
|
|
// do nothing.
|
|
}
|
|
|
|
// now draw entries in box..
|
|
labelCount = 0;
|
|
unnamedCount = 0;
|
|
|
|
int plotCount = -1;
|
|
for (int i = 0; i < plots.Count; ++i)
|
|
{
|
|
if (!(plots[i] is IPlot))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IPlot p = (IPlot) plots[i];
|
|
|
|
if (!p.ShowInLegend)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
plotCount += 1;
|
|
|
|
int xpos, ypos;
|
|
if (extendingVertically)
|
|
{
|
|
xpos = plotCount%numberItemsHorizontally_;
|
|
ypos = plotCount/numberItemsHorizontally_;
|
|
}
|
|
else
|
|
{
|
|
xpos = plotCount/numberItemsVertically_;
|
|
ypos = plotCount%numberItemsVertically_;
|
|
}
|
|
|
|
int lineXPos = (int) (position.X + hSpacing + xpos*(lineLength + maxWd + hSpacing*2.0f));
|
|
int lineYPos = (position.Y + vSpacing + ypos*(vSpacing + maxHt));
|
|
p.DrawInLegend(g, new Rectangle(lineXPos, lineYPos, lineLength, maxHt));
|
|
|
|
int textXPos = lineXPos + hSpacing + lineLength;
|
|
int textYPos = lineYPos;
|
|
string label = p.Label;
|
|
if (label == "")
|
|
{
|
|
unnamedCount += 1;
|
|
label = "Series " + unnamedCount.ToString();
|
|
}
|
|
|
|
g.DrawString(label, textFont,
|
|
new SolidBrush(textColor_), textXPos, textYPos);
|
|
|
|
++labelCount;
|
|
}
|
|
return new Rectangle(position.X, position.Y, totalWidth, totalHeight);
|
|
}
|
|
}
|
|
} |