//============================================================================ //ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C# //Copyright ?2004 John Champion // //This library is free software; you can redistribute it and/or //modify it under the terms of the GNU Lesser General Public //License as published by the Free Software Foundation; either //version 2.1 of the License, or (at your option) any later version. // //This library is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //Lesser General Public License for more details. // //You should have received a copy of the GNU Lesser General Public //License along with this library; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //============================================================================= using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; using System.Security.Permissions; namespace DrawGraph { /// /// Encapsulates a bar type that displays vertical or horizontal bars /// /// /// The orientation of the bars depends on the state of /// , and the bars can be stacked or /// clustered, depending on the state of /// /// John Champion /// $Revision: 3.23 $ $Date: 2007/06/02 06:56:03 $ [Serializable] public class BarItem : CurveItem, ICloneable, ISerializable { #region Fields /// /// Private field that stores a reference to the /// class defined for this . Use the public /// property to access this value. /// protected Bar _bar; #endregion #region Properties /// /// Gets a reference to the class defined /// for this . /// public Bar Bar { get { return _bar; } } /// /// Gets a flag indicating if the Z data range should be included in the axis scaling calculations. /// /// The parent of this . /// /// true if the Z data are included, false otherwise override internal bool IsZIncluded( GraphPane pane ) { return pane._barSettings.Type == BarType.ClusterHiLow; } /// /// Gets a flag indicating if the X axis is the independent axis for this /// /// The parent of this . /// /// true if the X axis is independent, false otherwise override internal bool IsXIndependent( GraphPane pane ) { return pane._barSettings.Base == BarBase.X; } #endregion #region Constructors /// /// Create a new , specifying only the legend label for the bar. /// /// The label that will appear in the legend. public BarItem( string label ) : base( label ) { _bar = new Bar(); } /// /// Create a new using the specified properties. /// /// The label that will appear in the legend. /// An array of double precision values that define /// the independent (X axis) values for this curve /// An array of double precision values that define /// the dependent (Y axis) values for this curve /// A value that will be applied to /// the and properties. /// public BarItem( string label, double[] x, double[] y, Color color ) : this( label, new PointPairList( x, y ), color ) { } /// /// Create a new using the specified properties. /// /// The label that will appear in the legend. /// A of double precision value pairs that define /// the X and Y values for this curve /// A value that will be applied to /// the and properties. /// public BarItem( string label, IPointList points, Color color ) : base( label, points ) { _bar = new Bar( color ); } /// /// The Copy Constructor /// /// The object from which to copy public BarItem( BarItem rhs ) : base( rhs ) { //bar = new Bar( rhs.Bar ); _bar = rhs._bar.Clone(); } /// /// Implement the interface in a typesafe manner by just /// calling the typed version of /// /// A deep copy of this object object ICloneable.Clone() { return this.Clone(); } /// /// Typesafe, deep-copy clone method. /// /// A new, independent copy of this class public BarItem Clone() { return new BarItem( this ); } #endregion #region Serialization /// /// Current schema value that defines the version of the serialized file /// public const int schema2 = 10; /// /// Constructor for deserializing objects /// /// A instance that defines the serialized data /// /// A instance that contains the serialized data /// protected BarItem( SerializationInfo info, StreamingContext context ) : base( info, context ) { // The schema value is just a file version parameter. You can use it to make future versions // backwards compatible as new member variables are added to classes int sch = info.GetInt32( "schema2" ); _bar = (Bar) info.GetValue( "bar", typeof(Bar) ); } /// /// Populates a instance with the data needed to serialize the target object /// /// A instance that defines the serialized data /// A instance that contains the serialized data [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)] public override void GetObjectData( SerializationInfo info, StreamingContext context ) { base.GetObjectData( info, context ); info.AddValue( "schema2", schema2 ); info.AddValue( "bar", _bar ); } #endregion #region Methods /// /// Do all rendering associated with this to the specified /// device. This method is normally only /// called by the Draw method of the parent /// collection object. /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// The ordinal position of the current /// curve. /// /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent object using the /// method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// override public void Draw( Graphics g, GraphPane pane, int pos, float scaleFactor ) { // Pass the drawing onto the bar class if ( _isVisible ) _bar.DrawBars( g, pane, this, BaseAxis( pane ), ValueAxis( pane ), this.GetBarWidth( pane ), pos, scaleFactor ); } /// /// Draw a legend key entry for this at the specified location /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// The struct that specifies the /// location for the legend key /// /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent object using the /// method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// override public void DrawLegendKey( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor ) { _bar.Draw( g, pane, rect, scaleFactor, true, false, null ); } /// /// Create a for each bar in the . /// /// /// 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 types. This method /// does not apply to or objects. /// Call this method only after calling . /// /// The GraphPane in which to place the text labels. /// true to center the labels inside the bars, false to /// place the labels just above the top of the bar. /// The double.ToString string format to use for creating /// the labels. /// public static void CreateBarLabels( GraphPane pane, bool isBarCenter, string valueFormat ) { CreateBarLabels( pane, isBarCenter, valueFormat, TextObj.Default.FontFamily, TextObj.Default.FontSize, TextObj.Default.FontColor, TextObj.Default.FontBold, TextObj.Default.FontItalic, TextObj.Default.FontUnderline ); } /// /// Create a for each bar in the . /// /// /// 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 types. This method /// does not apply to or objects. /// Call this method only after calling . /// /// The GraphPane in which to place the text labels. /// true to center the labels inside the bars, false to /// place the labels just above the top of the bar. /// The double.ToString string format to use for creating /// the labels. /// /// The color in which to draw the labels /// The string name of the font family to use for the labels /// The floating point size of the font, in scaled points /// true for a bold font type, false otherwise /// true for an italic font type, false otherwise /// true for an underline font type, false otherwise 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; // 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.015f; else labelOffset = (float)( pane.XAxis._scale._max - pane.XAxis._scale._min ) * 0.015f; // 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 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 label.Location.CoordinateFrame = 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++; } } } /// /// Determine the coords for the rectangle associated with a specified point for /// this /// /// The to which this curve belongs /// The index of the point of interest /// A list of coordinates that represents the "rect" for /// this point (used in an html AREA tag) /// true if it's a valid point, false otherwise 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 ( pane._barSettings.Base == BarBase.X ) 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; } #endregion } }