//============================================================================ //ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C# //Copyright ?2006 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.Text; using System.Runtime.Serialization; using System.Security.Permissions; namespace DrawGraph { /// /// Class that handles the global settings for bar charts /// /// /// John Champion /// $Revision: 3.4 $ $Date: 2007/06/02 06:56:03 $ [Serializable] public class BarSettings : ISerializable { #region Fields /// Private field that determines the size of the gap between bar clusters /// for bar charts. This gap is expressed as a fraction of the bar size (1.0 means /// leave a 1-barwidth gap between clusters). /// Use the public property to access this value. private float _minClusterGap; /// Private field that determines the size of the gap between individual bars /// within a bar cluster for bar charts. This gap is expressed as a fraction of the /// bar size (1.0 means leave a 1-barwidth gap between each bar). /// Use the public property to access this value. private float _minBarGap; /// Private field that determines the base axis from which /// graphs will be displayed. The base axis is the axis from which the bars grow with /// increasing value. The value is of the enumeration type . /// To access this value, use the public property . /// /// private BarBase _base; /// Private field that determines how the /// graphs will be displayed. See the enum /// for the individual types available. /// To access this value, use the public property . /// /// private BarType _type; /// Private field that determines the width of a bar cluster (for bar charts) /// in user scale units. Normally, this value is 1.0 because bar charts are typically /// or , and the bars are /// defined at ordinal values (1.0 scale units apart). For /// or other scale types, you can use this value to scale the bars to an arbitrary /// user scale. Use the public property to access this /// value. internal double _clusterScaleWidth; /// /// Private field that determines if the will be /// calculated automatically. Use the public property /// to access this value. /// internal bool _clusterScaleWidthAuto; /// /// private field that stores the owner GraphPane that contains this BarSettings instance. /// internal GraphPane _ownerPane; #endregion #region Constructors /// /// Constructor to build a instance from the defaults. /// public BarSettings( GraphPane parentPane ) { _minClusterGap = Default.MinClusterGap; _minBarGap = Default.MinBarGap; _clusterScaleWidth = Default.ClusterScaleWidth; _clusterScaleWidthAuto = Default.ClusterScaleWidthAuto; _base = Default.Base; _type = Default.Type; _ownerPane = parentPane; } /// /// Copy constructor /// /// the instance to be copied. /// The that will be the /// parent of this new BarSettings object. public BarSettings( BarSettings rhs, GraphPane parentPane ) { _minClusterGap = rhs._minClusterGap; _minBarGap = rhs._minBarGap; _clusterScaleWidth = rhs._clusterScaleWidth; _clusterScaleWidthAuto = rhs._clusterScaleWidthAuto; _base = rhs._base; _type = rhs._type; _ownerPane = parentPane; } #endregion #region Bar Properties /// /// The minimum space between clusters, expressed as a /// fraction of the bar size. /// /// /// /// public float MinClusterGap { get { return _minClusterGap; } set { _minClusterGap = value; } } /// /// The minimum space between individual Bars /// within a cluster, expressed as a /// fraction of the bar size. /// /// /// /// public float MinBarGap { get { return _minBarGap; } set { _minBarGap = value; } } /// Determines the base axis from which /// graphs will be displayed. /// /// The base axis is the axis from which the bars grow with /// increasing value. The value is of the enumeration type . /// /// public BarBase Base { get { return _base; } set { _base = value; } } /// Determines how the /// graphs will be displayed. See the enum /// for the individual types available. /// /// public BarType Type { get { return _type; } set { _type = value; } } /// /// The width of an individual bar cluster on a graph. /// This value only applies to bar graphs plotted on non-ordinal X axis /// types (, , and /// . /// /// /// This value can be calculated automatically if /// is set to true. In this case, ClusterScaleWidth will be calculated if /// refers to an of a non-ordinal type /// ( is false). The ClusterScaleWidth is calculated /// from the minimum difference found between any two points on the /// for any in the /// . The ClusterScaleWidth is set automatically /// each time is called. Calculations are /// done by the method. /// /// /// /// /// public double ClusterScaleWidth { get { return _clusterScaleWidth; } set { _clusterScaleWidth = value; _clusterScaleWidthAuto = false; } } /// /// Gets or sets a property that determines if the will be /// calculated automatically. /// /// true for the to be calculated /// automatically based on the available data, false otherwise. This value will /// be set to false automatically if the value /// is changed by the user. /// /// /// public bool ClusterScaleWidthAuto { get { return _clusterScaleWidthAuto; } set { _clusterScaleWidthAuto = value; } } #endregion #region Serialization /// /// Current schema value that defines the version of the serialized file /// public const int schema = 10; /// /// Constructor for deserializing objects /// /// /// You MUST set the _ownerPane property after deserializing a BarSettings object. /// /// A instance that defines the /// serialized data /// /// A instance that contains /// the serialized data /// internal BarSettings( SerializationInfo info, StreamingContext 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( "schema" ); _minClusterGap = info.GetSingle( "minClusterGap" ); _minBarGap = info.GetSingle( "minBarGap" ); _clusterScaleWidth = info.GetDouble( "clusterScaleWidth" ); _clusterScaleWidthAuto = info.GetBoolean( "clusterScaleWidthAuto" ); _base = (BarBase)info.GetValue( "base", typeof( BarBase ) ); _type = (BarType)info.GetValue( "type", typeof( BarType ) ); } /// /// 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 virtual void GetObjectData( SerializationInfo info, StreamingContext context ) { info.AddValue( "schema", schema ); info.AddValue( "minClusterGap", _minClusterGap ); info.AddValue( "minBarGap", _minBarGap ); info.AddValue( "clusterScaleWidth", _clusterScaleWidth ); info.AddValue( "clusterScaleWidthAuto", _clusterScaleWidthAuto ); info.AddValue( "base", _base ); info.AddValue( "type", _type ); } #endregion #region Methods /// /// Calculate the width of an individual bar cluster on a graph. /// This value only applies to bar graphs plotted on non-ordinal X axis /// types ( is false). /// /// /// This value can be calculated automatically if /// is set to true. In this case, ClusterScaleWidth will be calculated if /// refers to an of a non-ordinal type /// ( is false). The ClusterScaleWidth is calculated /// from the minimum difference found between any two points on the /// for any in the /// . The ClusterScaleWidth is set automatically /// each time is called. /// /// /// /// /// public void CalcClusterScaleWidth() { Axis baseAxis = BarBaseAxis(); // First, calculate the clusterScaleWidth for BarItem objects if ( _clusterScaleWidthAuto && !baseAxis.Scale.IsAnyOrdinal ) { double minStep = Double.MaxValue; foreach ( CurveItem curve in _ownerPane.CurveList ) { IPointList list = curve.Points; if ( curve is BarItem ) { double step = GetMinStepSize( curve.Points, baseAxis ); minStep = step < minStep ? step : minStep; } } if ( minStep == Double.MaxValue ) minStep = 1.0; _clusterScaleWidth = minStep; } // Second, calculate the sizes of any HiLowBarItem and JapaneseCandleStickItem objects foreach ( CurveItem curve in _ownerPane.CurveList ) { IPointList list = curve.Points; if ( curve is HiLowBarItem && (curve as HiLowBarItem).Bar.IsAutoSize ) { ( curve as HiLowBarItem ).Bar._userScaleSize = GetMinStepSize( list, baseAxis ); } else if ( curve is JapaneseCandleStickItem && (curve as JapaneseCandleStickItem).Stick.IsAutoSize ) { ( curve as JapaneseCandleStickItem ).Stick._userScaleSize = GetMinStepSize( list, baseAxis ); } } } /// /// Determine the minimum increment between individual points to be used for /// calculating a bar size that fits without overlapping /// /// The list of points for the bar /// of interest /// The base axis for the bar /// The minimum increment between bars along the base axis internal static double GetMinStepSize( IPointList list, Axis baseAxis ) { double minStep = Double.MaxValue; if ( list.Count <= 0 || baseAxis._scale.IsAnyOrdinal ) return 1.0; PointPair lastPt = list[0]; for ( int i = 1; i < list.Count; i++ ) { PointPair pt = list[i]; if ( !pt.IsInvalid || !lastPt.IsInvalid ) { double step; if ( baseAxis is XAxis || baseAxis is X2Axis ) step = pt.X - lastPt.X; else step = pt.Y - lastPt.Y; if ( step > 0 && step < minStep ) minStep = step; } lastPt = pt; } double range = baseAxis.Scale._maxLinearized - baseAxis.Scale._minLinearized; if ( range <= 0 ) minStep = 1.0; else if ( minStep <= 0 || minStep < 0.001 * range || minStep > range ) minStep = 0.1 * range; return minStep; } /// /// Determine the width, in screen pixel units, of each bar cluster including /// the cluster gaps and bar gaps. /// /// This method calls the /// method for the base for graphs /// (the base is assigned by the /// property). /// /// /// /// /// /// The width of each bar cluster, in pixel units public float GetClusterWidth() { return BarBaseAxis()._scale.GetClusterWidth( _ownerPane ); } /// /// Determine the from which the charts are based. /// /// /// /// /// /// The class for the axis from which the bars are based public Axis BarBaseAxis() { Axis barAxis; if ( _base == BarBase.Y ) barAxis = _ownerPane.YAxis; else if ( _base == BarBase.Y2 ) barAxis = _ownerPane.Y2Axis; else if ( _base == BarBase.X2 ) barAxis = _ownerPane.X2Axis; else barAxis = _ownerPane.XAxis; return barAxis; } #endregion #region Defaults /// /// A simple struct that defines the /// default property values for the class. /// public struct Default { /// /// The default dimension gap between clusters of bars on a /// graph. /// This dimension is expressed in terms of the normal bar width. /// /// /// public static float MinClusterGap = 1.0F; /// /// The default dimension gap between each individual bar within a bar cluster /// on a graph. /// This dimension is expressed in terms of the normal bar width. /// /// /// public static float MinBarGap = 0.2F; /// The default value for the , which determines the base /// from which the graphs will be displayed. /// /// public static BarBase Base = BarBase.X; /// The default value for the property, which /// determines if the bars are drawn overlapping eachother in a "stacked" format, /// or side-by-side in a "cluster" format. See the /// for more information. /// /// public static BarType Type = BarType.Cluster; /// /// The default width of a bar cluster /// on a graph. This value only applies to /// graphs, and only when the /// is , /// or . /// This dimension is expressed in terms of X scale user units. /// /// /// public static double ClusterScaleWidth = 1.0; /// /// The default value for . /// public static bool ClusterScaleWidthAuto = true; } #endregion } }