//============================================================================ //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.Collections; using System.Text; using System.Runtime.Serialization; using System.Security.Permissions; using System.Drawing; using System.Drawing.Drawing2D; namespace DrawGraph { /// /// The Scale class is an abstract base class that encompasses the properties /// and methods associated with a scale of data. /// /// This class is inherited by the /// , , , /// , , , /// , and /// classes to define specific characteristics for those types. /// /// /// John Champion /// $Revision: 1.30 $ $Date: 2007/06/02 06:56:03 $ [Serializable] abstract public class Scale : ISerializable { #region Fields /// Private fields for the scale definitions. /// Use the public properties , , /// , , and /// for access to these values. /// internal double _min, _max, _majorStep, _minorStep, _exponent, _baseTic; /// Private fields for the automatic scaling modes. /// Use the public properties , , /// , , /// and /// for access to these values. /// internal bool _minAuto, _maxAuto, _majorStepAuto, _minorStepAuto, _magAuto, _formatAuto; /// Private fields for the "grace" settings. /// These values determine how much extra space is left before the first data value /// and after the last data value. /// Use the public properties and /// for access to these values. /// internal double _minGrace, _maxGrace; /// Private field for the scale value display. /// Use the public property for access to this value. /// internal int _mag; /// Private fields for the attributes. /// Use the public properties and /// for access to these values. /// internal bool _isReverse, _isPreventLabelOverlap, _isUseTenPower, _isLabelsInside, _isSkipFirstLabel, _isSkipLastLabel, _isSkipCrossLabel, _isVisible; /// Private field for the array of text labels. /// This property is only used if is set to /// internal string[] _textLabels = null; /// Private field for the format of the tic labels. /// Use the public property for access to this value. /// internal string _format; /// /// Private fields for Unit types to be used for the major and minor tics. /// See and for the corresponding /// public properties. /// These types only apply for date-time scales (). /// /// The value of these types is of enumeration type /// internal DateUnit _majorUnit, _minorUnit; /// Private field for the alignment of the tic labels. /// This fields controls whether the inside, center, or outside edges of the text labels are aligned. /// Use the public property /// for access to this value. /// internal AlignP _align; /// Private field for the alignment of the tic labels. /// This fields controls whether the left, center, or right edges of the text labels are aligned. /// Use the public property /// for access to this value. /// internal AlignH _alignH; /// Private fields for the font specificatios. /// Use the public properties and /// for access to these values. internal FontSpec _fontSpec; /// /// Internal field that stores the amount of space between the scale labels and the /// major tics. Use the public property to access this /// value. /// internal float _labelGap; /// /// Data range temporary values, used by GetRange(). /// internal double _rangeMin, _rangeMax, _lBound, _uBound; /// /// Pixel positions at the minimum and maximum value for this scale. /// These are temporary values used/valid only during the Draw process. /// internal float _minPix, _maxPix; /// /// Scale values for calculating transforms. These are temporary values /// used ONLY during the Draw process. /// /// /// These values are just and /// for normal linear scales, but for log or exponent scales they will be a /// linear representation. For , it is the /// of the value, and for , /// it is the /// of the value. /// internal double _minLinTemp, _maxLinTemp; /// /// Gets or sets the linearized version of the scale range. /// /// /// This value is valid at any time, whereas is an optimization /// pre-set that is only valid during draw operations. /// internal double _minLinearized { get { return Linearize( _min ); } set { _min = DeLinearize( value ); } } /// /// Gets or sets the linearized version of the scale range. /// /// /// This value is valid at any time, whereas is an optimization /// pre-set that is only valid during draw operations. /// internal double _maxLinearized { get { return Linearize( _max ); } set { _max = DeLinearize( value ); } } /// /// private field that stores the owner Axis that contains this Scale instance. /// internal Axis _ownerAxis; #endregion #region Defaults /// /// A simple struct that defines the /// default property values for the class. /// public struct Default { /// /// The default "zero lever" for automatically selecting the axis /// scale range (see ). This number is /// used to determine when an axis scale range should be extended to /// include the zero value. This value is maintained only in the /// class, and cannot be changed after compilation. /// public static double ZeroLever = 0.25; /// The default "grace" value applied to the minimum data range. /// This value is /// expressed as a fraction of the total data range. For example, assume the data /// range is from 4.0 to 16.0, leaving a range of 12.0. If MinGrace is set to /// 0.1, then 10% of the range, or 1.2 will be subtracted from the minimum data value. /// The scale will then be ranged to cover at least 2.8 to 16.0. /// /// public static double MinGrace = 0.1; /// The default "grace" value applied to the maximum data range. /// This value is /// expressed as a fraction of the total data range. For example, assume the data /// range is from 4.0 to 16.0, leaving a range of 12.0. If MaxGrace is set to /// 0.1, then 10% of the range, or 1.2 will be added to the maximum data value. /// The scale will then be ranged to cover at least 4.0 to 17.2. /// /// /// public static double MaxGrace = 0.1; /// /// The maximum number of text labels (major tics) that will be allowed on the plot by /// the automatic scaling logic. This value applies only to /// axes. If there are more than MaxTextLabels on the plot, then /// will be increased to reduce the number of labels. That is, /// the step size might be increased to 2.0 to show only every other label. /// public static double MaxTextLabels = 12.0; /// /// The default target number of steps for automatically selecting the X axis /// scale step size (see ). /// This number is an initial target value for the number of major steps /// on an axis. This value is maintained only in the /// class, and cannot be changed after compilation. /// public static double TargetXSteps = 7.0; /// /// The default target number of steps for automatically selecting the Y or Y2 axis /// scale step size (see ). /// This number is an initial target value for the number of major steps /// on an axis. This value is maintained only in the /// class, and cannot be changed after compilation. /// public static double TargetYSteps = 7.0; /// /// The default target number of minor steps for automatically selecting the X axis /// scale minor step size (see ). /// This number is an initial target value for the number of minor steps /// on an axis. This value is maintained only in the /// class, and cannot be changed after compilation. /// public static double TargetMinorXSteps = 5.0; /// /// The default target number of minor steps for automatically selecting the Y or Y2 axis /// scale minor step size (see ). /// This number is an initial target value for the number of minor steps /// on an axis. This value is maintained only in the /// class, and cannot be changed after compilation. /// public static double TargetMinorYSteps = 5.0; /// /// The default reverse mode for the scale /// ( property). true for a reversed scale /// (X decreasing to the left, Y/Y2 decreasing upwards), false otherwise. /// public static bool IsReverse = false; /// /// The default setting for the scale format string /// ( property). For numeric values, this value is /// setting according to the format strings. For date /// type values, this value is set as per the function. /// //public static string ScaleFormat = "&dd-&mmm-&yy &hh:&nn"; public static string Format = "g"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 1825 days (5 years). /// This value is used by the method. /// public static double RangeYearYear = 1825; // 5 years /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 365 days (1 year). /// This value is used by the method. /// public static double RangeYearMonth = 365; // 1 year /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 90 days (3 months). /// This value is used by the method. /// public static double RangeMonthMonth = 90; // 3 months /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 10 days. /// This value is used by the method. /// public static double RangeDayDay = 10; // 10 days /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 3 days. /// This value is used by the method. /// public static double RangeDayHour = 3; // 3 days /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 0.4167 days (10 hours). /// This value is used by the method. /// public static double RangeHourHour = 0.4167; // 10 hours /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 0.125 days (3 hours). /// This value is used by the method. /// public static double RangeHourMinute = 0.125; // 3 hours /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 6.94e-3 days (10 minutes). /// This value is used by the method. /// public static double RangeMinuteMinute = 6.94e-3; // 10 Minutes /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 2.083e-3 days (3 minutes). /// This value is used by the method. /// public static double RangeMinuteSecond = 2.083e-3; // 3 Minutes /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// If the total span of data exceeds this number (in days), then the auto-range /// code will select = /// and = . /// This value normally defaults to 3.472e-5 days (3 seconds). /// This value is used by the method. /// public static double RangeSecondSecond = 3.472e-5; // 3 Seconds /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatYearYear = "yyyy"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatYearMonth = "MMM-yyyy"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatMonthMonth = "MMM-yyyy"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatDayDay = "d-MMM"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatDayHour = "d-MMM HH:mm"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatHourHour = "HH:mm"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatHourMinute = "HH:mm"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatMinuteMinute = "HH:mm"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatMinuteSecond = "mm:ss"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatSecondSecond = "mm:ss"; /// /// A default setting for the auto-ranging code. /// This values applies only to Date-Time type axes. /// This is the format used for the scale values when auto-ranging code /// selects a of /// for and for /// for . /// This value is used by the method. /// /// public static string FormatMillisecond = "ss.fff"; /* Prior format assignments using original XDate.ToString() this.scaleFormat = "&yyyy"; this.scaleFormat = "&mmm-&yy"; this.scaleFormat = "&mmm-&yy"; scaleFormat = "&d-&mmm"; this.scaleFormat = "&d-&mmm &hh:&nn"; scaleFormat = "&hh:&nn"; scaleFormat = "&hh:&nn"; scaleFormat = "&hh:&nn"; scaleFormat = "&nn:&ss"; scaleFormat = "&nn:&ss"; */ /// The default alignment of the tic labels. /// This value controls whether the inside, center, or outside edges of the text labels are aligned. /// /// public static AlignP Align = AlignP.Center; /// The default alignment of the tic labels. /// This value controls whether the left, center, or right edges of the text labels are aligned. /// /// public static AlignH AlignH = AlignH.Center; /// /// The default font family for the scale values /// font specification /// ( property). /// public static string FontFamily = "Arial"; /// /// The default font size for the scale values /// font specification /// ( property). Units are /// in points (1/72 inch). /// public static float FontSize = 14; /// /// The default font color for the scale values /// font specification /// ( property). /// public static Color FontColor = Color.Black; /// /// The default font bold mode for the scale values /// font specification /// ( property). true /// for a bold typeface, false otherwise. /// public static bool FontBold = false; /// /// The default font italic mode for the scale values /// font specification /// ( property). true /// for an italic typeface, false otherwise. /// public static bool FontItalic = false; /// /// The default font underline mode for the scale values /// font specification /// ( property). true /// for an underlined typeface, false otherwise. /// public static bool FontUnderline = false; /// /// The default color for filling in the scale text background /// (see property). /// public static Color FillColor = Color.White; /// /// The default custom brush for filling in the scale text background /// (see property). /// public static Brush FillBrush = null; /// /// The default fill mode for filling in the scale text background /// (see property). /// public static FillType FillType = FillType.None; /// /// The default value for , which determines /// whether or not the scale values are displayed. /// public static bool IsVisible = true; /// /// The default value for , which determines /// whether or not the scale labels and title for the will appear /// on the opposite side of the that it normally appears. /// public static bool IsLabelsInside = false; /// /// Determines the size of the band at the beginning and end of the axis that will have labels /// omitted if the axis is shifted due to a non-default location using the /// property. /// /// /// This parameter applies only when is false. It is scaled according /// to the size of the graph based on . When a non-default /// axis location is selected, the first and last labels on that axis will overlap the opposing /// axis frame. This parameter allows those labels to be omitted to avoid the overlap. Set this /// parameter to zero to turn off the effect. /// public static float EdgeTolerance = 6; /// /// The default setting for the gap between the outside tics (or the axis edge /// if there are no outside tics) and the scale labels, expressed as a fraction of /// the major tic size. /// public static float LabelGap = 0.3f; } #endregion #region constructors /// /// Basic constructor -- requires that the object be intialized with /// a pre-existing owner . /// /// The object that is the owner of this /// instance. public Scale( Axis ownerAxis ) { _ownerAxis = ownerAxis; _min = 0.0; _max = 1.0; _majorStep = 0.1; _minorStep = 0.1; _exponent = 1.0; _mag = 0; _baseTic = PointPair.Missing; _minGrace = Default.MinGrace; _maxGrace = Default.MaxGrace; _minAuto = true; _maxAuto = true; _majorStepAuto = true; _minorStepAuto = true; _magAuto = true; _formatAuto = true; _isReverse = Default.IsReverse; _isUseTenPower = true; _isPreventLabelOverlap = true; _isVisible = true; _isSkipFirstLabel = false; _isSkipLastLabel = false; _isSkipCrossLabel = false; _majorUnit = DateUnit.Day; _minorUnit = DateUnit.Day; _format = null; _textLabels = null; _isLabelsInside = Default.IsLabelsInside; _align = Default.Align; _alignH = Default.AlignH; _fontSpec = new FontSpec( Default.FontFamily, Default.FontSize, Default.FontColor, Default.FontBold, Default.FontUnderline, Default.FontItalic, Default.FillColor, Default.FillBrush, Default.FillType ); _fontSpec.Border.IsVisible = false; _labelGap = Default.LabelGap; } /// /// Copy Constructor. Create a new object based on the specified /// existing one. /// /// The object to be copied. /// The object that will own the /// new instance of public Scale( Scale rhs, Axis owner ) { _ownerAxis = owner; _min = rhs._min; _max = rhs._max; _majorStep = rhs._majorStep; _minorStep = rhs._minorStep; _exponent = rhs._exponent; _baseTic = rhs._baseTic; _minAuto = rhs._minAuto; _maxAuto = rhs._maxAuto; _majorStepAuto = rhs._majorStepAuto; _minorStepAuto = rhs._minorStepAuto; _magAuto = rhs._magAuto; _formatAuto = rhs._formatAuto; _minGrace = rhs._minGrace; _maxGrace = rhs._maxGrace; _mag = rhs._mag; _isUseTenPower = rhs._isUseTenPower; _isReverse = rhs._isReverse; _isPreventLabelOverlap = rhs._isPreventLabelOverlap; _isVisible = rhs._isVisible; _isSkipFirstLabel = rhs._isSkipFirstLabel; _isSkipLastLabel = rhs._isSkipLastLabel; _isSkipCrossLabel = rhs._isSkipCrossLabel; _majorUnit = rhs._majorUnit; _minorUnit = rhs._minorUnit; _format = rhs._format; _isLabelsInside = rhs._isLabelsInside; _align = rhs._align; _alignH = rhs._alignH; _fontSpec = (FontSpec) rhs._fontSpec.Clone(); _labelGap = rhs._labelGap; if ( rhs._textLabels != null ) _textLabels = (string[])rhs._textLabels.Clone(); else _textLabels = null; } /// /// Create a new clone of the current item, with a new owner assignment /// /// The new instance that will be /// the owner of the new Scale /// A new clone. abstract public Scale Clone( Axis owner ); /* /// /// Implement the interface in a typesafe manner by just /// calling the typed version of Clone /> /// /// /// Note that this method must be called with an explicit cast to ICloneable, and /// that it is inherently virtual. For example: /// /// ParentClass foo = new ChildClass(); /// ChildClass bar = (ChildClass) ((ICloneable)foo).Clone(); /// /// Assume that ChildClass is inherited from ParentClass. Even though foo is declared with /// ParentClass, it is actually an instance of ChildClass. Calling the ICloneable implementation /// of Clone() on foo actually calls ChildClass.Clone() as if it were a virtual function. /// /// A deep copy of this object object ICloneable.Clone() { throw new NotImplementedException( "Can't clone an abstract base type -- child types must implement ICloneable" ); //return new PaneBase( this ); } */ /// /// A construction method that creates a new object using the /// properties of an existing object, but specifying a new /// . /// /// /// This constructor is used to change the type of an existing . /// By specifying the old object, you are giving a set of properties /// (which encompasses all fields associated with the scale, since the derived types /// have no fields) to be used in creating a new object, only this /// time having the newly specified object type. /// The existing object from which to /// copy the field data. /// An representing the type of derived type /// of new object to create. /// The new object. public Scale MakeNewScale( Scale oldScale, AxisType type ) { switch ( type ) { case AxisType.Linear: return new LinearScale( oldScale, _ownerAxis ); case AxisType.Date: return new DateScale( oldScale, _ownerAxis ); case AxisType.Log: return new LogScale( oldScale, _ownerAxis ); case AxisType.Exponent: return new ExponentScale( oldScale, _ownerAxis ); case AxisType.Ordinal: return new OrdinalScale( oldScale, _ownerAxis ); case AxisType.Text: return new TextScale( oldScale, _ownerAxis ); case AxisType.DateAsOrdinal: return new DateAsOrdinalScale( oldScale, _ownerAxis ); case AxisType.LinearAsOrdinal: return new LinearAsOrdinalScale( oldScale, _ownerAxis ); default: throw new Exception( "Implementation Error: Invalid AxisType" ); } } #endregion #region Serialization /// /// Current schema value that defines the version of the serialized file /// // schema changed to 2 with isScaleVisible public const int schema = 11; /// /// Constructor for deserializing objects /// /// A instance that defines the serialized data /// /// A instance that contains the serialized data /// protected Scale( 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" ); _min = info.GetDouble( "min" ); _max = info.GetDouble( "max" ); _majorStep = info.GetDouble( "majorStep" ); _minorStep = info.GetDouble( "minorStep" ); _exponent = info.GetDouble( "exponent" ); _baseTic = info.GetDouble( "baseTic" ); _minAuto = info.GetBoolean( "minAuto" ); _maxAuto = info.GetBoolean( "maxAuto" ); _majorStepAuto = info.GetBoolean( "majorStepAuto" ); _minorStepAuto = info.GetBoolean( "minorStepAuto" ); _magAuto = info.GetBoolean( "magAuto" ); _formatAuto = info.GetBoolean( "formatAuto" ); _minGrace = info.GetDouble( "minGrace" ); _maxGrace = info.GetDouble( "maxGrace" ); _mag = info.GetInt32( "mag" ); _isReverse = info.GetBoolean( "isReverse" ); _isPreventLabelOverlap = info.GetBoolean( "isPreventLabelOverlap" ); _isUseTenPower = info.GetBoolean( "isUseTenPower" ); _isVisible = true; _isVisible = info.GetBoolean( "isVisible" ); _isSkipFirstLabel = info.GetBoolean( "isSkipFirstLabel" ); _isSkipLastLabel = info.GetBoolean( "isSkipLastLabel" ); _isSkipCrossLabel = info.GetBoolean( "isSkipCrossLabel" ); _textLabels = (string[]) info.GetValue( "textLabels", typeof(string[]) ); _format = info.GetString( "format" ); _majorUnit = (DateUnit) info.GetValue( "majorUnit", typeof(DateUnit) ); _minorUnit = (DateUnit) info.GetValue( "minorUnit", typeof(DateUnit) ); _isLabelsInside = info.GetBoolean( "isLabelsInside" ); _align = (AlignP)info.GetValue( "align", typeof( AlignP ) ); if ( schema >= 11 ) _alignH = (AlignH)info.GetValue( "alignH", typeof( AlignH ) ); _fontSpec = (FontSpec)info.GetValue( "fontSpec", typeof( FontSpec ) ); _labelGap = info.GetSingle( "labelGap" ); } /// /// Populates a instance with the data needed to /// serialize the target object /// /// /// You MUST set the _ownerAxis property after deserializing a BarSettings 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( "min", _min ); info.AddValue( "max", _max ); info.AddValue( "majorStep", _majorStep ); info.AddValue( "minorStep", _minorStep ); info.AddValue( "exponent", _exponent ); info.AddValue( "baseTic", _baseTic ); info.AddValue( "minAuto", _minAuto ); info.AddValue( "maxAuto", _maxAuto ); info.AddValue( "majorStepAuto", _majorStepAuto ); info.AddValue( "minorStepAuto", _minorStepAuto ); info.AddValue( "magAuto", _magAuto ); info.AddValue( "formatAuto", _formatAuto ); info.AddValue( "minGrace", _minGrace ); info.AddValue( "maxGrace", _maxGrace ); info.AddValue( "mag", _mag ); info.AddValue( "isReverse", _isReverse ); info.AddValue( "isPreventLabelOverlap", _isPreventLabelOverlap ); info.AddValue( "isUseTenPower", _isUseTenPower ); info.AddValue( "isVisible", _isVisible ); info.AddValue( "isSkipFirstLabel", _isSkipFirstLabel ); info.AddValue( "isSkipLastLabel", _isSkipLastLabel ); info.AddValue( "isSkipCrossLabel", _isSkipCrossLabel ); info.AddValue( "textLabels", _textLabels ); info.AddValue( "format", _format ); info.AddValue( "majorUnit", _majorUnit ); info.AddValue( "minorUnit", _minorUnit ); info.AddValue( "isLabelsInside", _isLabelsInside ); info.AddValue( "align", _align ); info.AddValue( "alignH", _alignH ); info.AddValue( "fontSpec", _fontSpec ); info.AddValue( "labelGap", _labelGap ); } #endregion #region properties /// /// Get an enumeration that indicates the type of this scale. /// abstract public AxisType Type { get; } /// /// True if this scale is , false otherwise. /// public bool IsLog { get { return this is LogScale; } } /// /// True if this scale is , false otherwise. /// public bool IsExponent { get { return this is ExponentScale; } } /// /// True if this scale is , false otherwise. /// public bool IsDate { get { return this is DateScale; } } /// /// True if this scale is , false otherwise. /// public bool IsText { get { return this is TextScale; } } /// /// True if this scale is , false otherwise. /// /// /// Note that this is only true for an actual class. /// This property will be false for other ordinal types such as /// , , /// or . Use the /// as a "catchall" for all ordinal type axes. /// public bool IsOrdinal { get { return this is OrdinalScale; } } /// /// Gets a value that indicates if this is of any of the /// ordinal types in the enumeration. /// /// public bool IsAnyOrdinal { get { AxisType type = this.Type; return type == AxisType.Ordinal || type == AxisType.Text || type == AxisType.LinearAsOrdinal || type == AxisType.DateAsOrdinal; } } /* /// /// The pixel position at the minimum value for this axis. This read-only /// value is used/valid only during the Draw process. /// public float MinPix { get { return _minPix; } } /// /// The pixel position at the maximum value for this axis. This read-only /// value is used/valid only during the Draw process. /// public float MaxPix { get { return _maxPix; } } */ /// /// Gets or sets the minimum scale value for this . /// /// This value can be set /// automatically based on the state of . If /// this value is set manually, then will /// also be set to false. /// /// The value is defined in user scale units for /// and axes. For /// and axes, /// this value is an ordinal starting with 1.0. For /// axes, this value is in XL Date format (see , which is the /// number of days since the reference date of January 1, 1900. /// /// /// /// public virtual double Min { get { return _min; } set { _min = value; _minAuto = false; } } /// /// Gets or sets the maximum scale value for this . /// /// /// This value can be set /// automatically based on the state of . If /// this value is set manually, then will /// also be set to false. /// /// The value is defined in user scale units for /// and axes. For /// and axes, /// this value is an ordinal starting with 1.0. For /// axes, this value is in XL Date format (see , which is the /// number of days since the reference date of January 1, 1900. /// /// /// /// public virtual double Max { get { return _max; } set { _max = value; _maxAuto = false; } } /// /// Gets or sets the scale step size for this (the increment between /// labeled axis values). /// /// /// This value can be set /// automatically based on the state of . If /// this value is set manually, then will /// also be set to false. This value is ignored for /// axes. For axes, this /// value is defined in units of . /// /// The value is defined in user scale units /// /// /// /// /// /// /// /// public double MajorStep { get { return _majorStep; } set { _majorStep = value; _majorStepAuto = false; } } /// /// Gets or sets the scale minor step size for this (the spacing between /// minor tics). /// /// This value can be set /// automatically based on the state of . If /// this value is set manually, then will /// also be set to false. This value is ignored for and /// axes. For axes, this /// value is defined in units of . /// /// The value is defined in user scale units /// /// /// /// public double MinorStep { get { return _minorStep; } set { _minorStep = value; _minorStepAuto = false; } } /// /// Gets or sets the scale exponent value. This only applies to . /// /// /// /// /// /// /// /// /// public double Exponent { get { return _exponent; } set { _exponent = value; } } /// /// Gets or sets the scale value at which the first major tic label will appear. /// /// This property allows the scale labels to start at an irregular value. /// For example, on a scale range with = 0, = 1000, /// and = 200, a value of 50 would cause /// the scale labels to appear at values 50, 250, 450, 650, and 850. Note that the /// default value for this property is , which means the /// value is not used. Setting this property to any value other than /// will activate the effect. The value specified must /// coincide with the first major tic. That is, if were set to /// 650 in the example above, then the major tics would only occur at 650 and 850. This /// setting may affect the minor tics, since the minor tics are always referenced to the /// . That is, in the example above, if the /// were set to 30 (making it a non-multiple of the major step), then the minor tics would /// occur at 20, 50 (so it lines up with the BaseTic), 80, 110, 140, etc. /// /// The value is defined in user scale units /// /// /// /// /// public double BaseTic { get { return _baseTic; } set { _baseTic = value; } } /// /// Gets or sets the type of units used for the major step size (). /// /// /// This unit type only applies to Date-Time axes ( = true). /// The axis is set to date type with the property. /// The unit types are defined as . /// /// The value is a enum type /// /// /// /// /// public DateUnit MajorUnit { get { return _majorUnit; } set { _majorUnit = value; } } /// /// Gets or sets the type of units used for the minor step size (). /// /// /// This unit type only applies to Date-Time axes ( = true). /// The axis is set to date type with the property. /// The unit types are defined as . /// /// The value is a enum type /// /// /// /// /// public DateUnit MinorUnit { get { return _minorUnit; } set { _minorUnit = value; } } /// /// Gets the major unit multiplier for this scale type, if any. /// /// The major unit multiplier will correct the units of /// to match the units of /// and . This reflects the setting of /// . /// virtual internal double MajorUnitMultiplier { get { return 1.0; } } /// /// Gets the minor unit multiplier for this scale type, if any. /// /// The minor unit multiplier will correct the units of /// to match the units of /// and . This reflects the setting of /// . /// virtual internal double MinorUnitMultiplier { get { return 1.0; } } /// /// Gets or sets a value that determines whether or not the minimum scale value /// is set automatically. /// /// /// This value will be set to false if /// is manually changed. /// /// true for automatic mode, false for manual mode /// public bool MinAuto { get { return _minAuto; } set { _minAuto = value; } } /// /// Gets or sets a value that determines whether or not the maximum scale value /// is set automatically. /// /// /// This value will be set to false if /// is manually changed. /// /// true for automatic mode, false for manual mode /// public bool MaxAuto { get { return _maxAuto; } set { _maxAuto = value; } } /// /// Gets or sets a value that determines whether or not the scale step size /// is set automatically. /// /// /// This value will be set to false if /// is manually changed. /// /// true for automatic mode, false for manual mode /// public bool MajorStepAuto { get { return _majorStepAuto; } set { _majorStepAuto = value; } } /// /// Gets or sets a value that determines whether or not the minor scale step size /// is set automatically. /// /// /// This value will be set to false if /// is manually changed. /// /// true for automatic mode, false for manual mode /// public bool MinorStepAuto { get { return _minorStepAuto; } set { _minorStepAuto = value; } } /// /// Determines whether or not the scale label format /// is determined automatically based on the range of data values. /// /// /// This value will be set to false if /// is manually changed. /// /// true if will be set automatically, false /// if it is to be set manually by the user /// /// /// public bool FormatAuto { get { return _formatAuto; } set { _formatAuto = value; } } /// /// The format of the tic labels. /// /// /// This property may be a date format or a numeric format, depending on the setting of /// Scale.Type. /// This property may be set automatically by ZedGraph, depending on the state of /// . /// /// The format string conforms to the /// for date formats, and /// for numeric formats. /// /// /// /// // /// public string Format { get { return _format; } set { _format = value; _formatAuto = false; } } /// /// The magnitude multiplier for scale values. /// /// /// This is used to limit /// the size of the displayed value labels. For example, if the value /// is really 2000000, then the graph will display 2000 with a 10^3 /// magnitude multiplier. This value can be determined automatically /// depending on the state of . /// If this value is set manually by the user, /// then will also be set to false. /// /// The magnitude multiplier (power of 10) for the scale /// value labels /// /// /// /// // /// public int Mag { get { return _mag; } set { _mag = value; _magAuto = false; } } /// /// Determines whether the value will be set /// automatically based on the data, or manually by the user. /// /// /// If the user manually sets the value, then this /// flag will be set to false. /// /// true to have set automatically, /// false otherwise /// /// /// public bool MagAuto { get { return _magAuto; } set { _magAuto = value; } } /// Gets or sets the "grace" value applied to the minimum data range. /// /// /// This value is /// expressed as a fraction of the total data range. For example, assume the data /// range is from 4.0 to 16.0, leaving a range of 12.0. If MinGrace is set to /// 0.1, then 10% of the range, or 1.2 will be subtracted from the minimum data value. /// The scale will then be ranged to cover at least 2.8 to 16.0. /// /// /// /// public double MinGrace { get { return _minGrace; } set { _minGrace = value; } } /// Gets or sets the "grace" value applied to the maximum data range. /// /// /// This values determines how much extra space is left after the last data value. /// This value is /// expressed as a fraction of the total data range. For example, assume the data /// range is from 4.0 to 16.0, leaving a range of 12.0. If MaxGrace is set to /// 0.1, then 10% of the range, or 1.2 will be added to the maximum data value. /// The scale will then be ranged to cover at least 4.0 to 17.2. /// /// /// /// public double MaxGrace { get { return _maxGrace; } set { _maxGrace = value; } } /// Controls the alignment of the tic labels. /// /// /// This property controls whether the inside, center, or outside edges of the /// text labels are aligned. /// public AlignP Align { get { return _align; } set { _align = value; } } /// Controls the alignment of the tic labels. /// /// /// This property controls whether the left, center, or right edges of the /// text labels are aligned. /// public AlignH AlignH { get { return _alignH; } set { _alignH = value; } } /// /// Gets a reference to the class used to render /// the scale values /// /// /// /// /// /// /// public FontSpec FontSpec { get { return _fontSpec; } set { if ( value == null ) throw new ArgumentNullException( "Uninitialized FontSpec in Scale" ); _fontSpec = value; } } /// /// The gap between the scale labels and the tics. /// public float LabelGap { get { return _labelGap; } set { _labelGap = value; } } /// /// Gets or sets a value that causes the axis scale labels and title to appear on the /// opposite side of the axis. /// /// /// For example, setting this flag to true for the will shift the /// axis labels and title to the right side of the instead of the /// normal left-side location. Set this property to true for the , /// and set the property for the to an arbitrarily /// large value (assuming is false for the ) in /// order to have the appear at the top of the . /// /// /// public bool IsLabelsInside { get { return _isLabelsInside; } set { _isLabelsInside = value; } } /// /// Gets or sets a value that causes the first scale label for this to be /// hidden. /// /// /// Often, for axis that have an active setting (e.g., /// is false), the first and/or last scale label are overlapped by opposing axes. Use this /// property to hide the first scale label to avoid the overlap. Note that setting this value /// to true will hide any scale label that appears within of the /// beginning of the . /// public bool IsSkipFirstLabel { get { return _isSkipFirstLabel; } set { _isSkipFirstLabel = value; } } /// /// Gets or sets a value that causes the last scale label for this to be /// hidden. /// /// /// Often, for axis that have an active setting (e.g., /// is false), the first and/or last scale label are overlapped by opposing axes. Use this /// property to hide the last scale label to avoid the overlap. Note that setting this value /// to true will hide any scale label that appears within of the /// end of the . /// public bool IsSkipLastLabel { get { return _isSkipLastLabel; } set { _isSkipLastLabel = value; } } /// /// Gets or sets a value that causes the scale label that is located at the /// value for this to be hidden. /// /// /// For axes that have an active setting (e.g., /// is false), the scale label at the value is overlapped by opposing axes. /// Use this property to hide the scale label to avoid the overlap. /// public bool IsSkipCrossLabel { get { return _isSkipCrossLabel; } set { _isSkipCrossLabel = value; } } /// /// Determines if the scale values are reversed for this /// /// true for the X values to decrease to the right or the Y values to /// decrease upwards, false otherwise /// . public bool IsReverse { get { return _isReverse; } set { _isReverse = value; } } /// /// Determines if powers-of-ten notation will be used for the numeric value labels. /// /// /// The powers-of-ten notation is just the text "10" followed by a superscripted value /// indicating the magnitude. This mode is only valid for log scales (see /// and ). /// /// boolean value; true to show the title as a power of ten, false to /// show a regular numeric value (e.g., "0.01", "10", "1000") public bool IsUseTenPower { get { return _isUseTenPower; } set { _isUseTenPower = value; } } /// /// Gets or sets a value that determines if ZedGraph will check to /// see if the scale labels are close enough to overlap. If so, /// ZedGraph will adjust the step size to prevent overlap. /// /// /// The process of checking for overlap is done during the /// method call, and affects the selection of the major step size (). /// /// boolean value; true to check for overlap, false otherwise public bool IsPreventLabelOverlap { get { return _isPreventLabelOverlap; } set { _isPreventLabelOverlap = value; } } /// /// Gets or sets a property that determines whether or not the scale values will be shown. /// /// true to show the scale values, false otherwise /// . public bool IsVisible { get { return _isVisible; } set { _isVisible = value; } } /// /// The text labels for this . /// /// /// This property is only /// applicable if is set to . /// public string[] TextLabels { get { return _textLabels; } set { _textLabels = value; } } #endregion /* #region events /// /// A delegate that allows full custom formatting of the Axis labels /// /// The for which the label is to be /// formatted /// The for which the label is to be formatted /// The value to be formatted /// The zero-based index of the label to be formatted /// /// A string value representing the label, or null if the ZedGraph should go ahead /// and generate the label according to the current settings /// public delegate string ScaleFormatHandler( GraphPane pane, Axis axis, double val, int index ); /// /// Subscribe to this event to handle custom formatting of the scale labels. /// public event ScaleFormatHandler ScaleFormatEvent; #endregion */ #region Methods /// /// Setup some temporary transform values in preparation for rendering the /// . /// /// /// This method is typically called by the parent /// object as part of the method. It is also /// called by and /// /// methods to setup for coordinate transformations. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// /// The parent for this /// virtual public void SetupScaleData( GraphPane pane, Axis axis ) { // save the ChartRect data for transforming scale values to pixels if ( axis is XAxis || axis is X2Axis ) { _minPix = pane.Chart._rect.Left; _maxPix = pane.Chart._rect.Right; } else { _minPix = pane.Chart._rect.Top; _maxPix = pane.Chart._rect.Bottom; } _minLinTemp = Linearize( _min ); _maxLinTemp = Linearize( _max ); } /* internal void ResetScaleData() { _minPix = float.NaN; _maxPix = float.NaN; _minLinTemp = double.NaN; _maxLinTemp = double.NaN; } */ /// /// Convert a value to its linear equivalent for this type of scale. /// /// /// The default behavior is to just return the value unchanged. However, /// for and , /// it returns the log or power equivalent. /// /// The value to be converted virtual public double Linearize( double val ) { return val; } /// /// Convert a value from its linear equivalent to its actual scale value /// for this type of scale. /// /// /// The default behavior is to just return the value unchanged. However, /// for and , /// it returns the anti-log or inverse-power equivalent. /// /// The value to be converted virtual public double DeLinearize( double val ) { return val; } /* /// /// Make a value label for the axis at the specified ordinal position. /// /// /// This method properly accounts for , , /// and other axis format settings. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// /// The zero-based, ordinal index of the label to be generated. For example, a value of 2 would /// cause the third value label on the axis to be generated. /// /// /// The numeric value associated with the label. This value is ignored for log () /// and text () type axes. /// /// The resulting value label as a virtual internal string MakeLabel( GraphPane pane, int index, double dVal ) { if ( this.ScaleFormatEvent != null ) { string label; label = this.ScaleFormatEvent( pane, _ownerAxis, dVal, index ); if ( label != null ) return label; } if ( _format == null ) _format = Scale.Default.Format; // linear or ordinal is the default behavior // this method is overridden for other Scale types double scaleMult = Math.Pow( (double) 10.0, _mag ); return ( dVal / scaleMult ).ToString( _format ); } */ /// /// Make a value label for the axis at the specified ordinal position. /// /// /// This method properly accounts for , , /// and other axis format settings. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// /// The zero-based, ordinal index of the label to be generated. For example, a value of 2 would /// cause the third value label on the axis to be generated. /// /// /// The numeric value associated with the label. This value is ignored for log () /// and text () type axes. /// /// The resulting value label as a virtual internal string MakeLabel( GraphPane pane, int index, double dVal ) { if ( _format == null ) _format = Scale.Default.Format; // linear or ordinal is the default behavior // this method is overridden for other Scale types double scaleMult = Math.Pow( (double)10.0, _mag ); return ( dVal / scaleMult ).ToString( _format ); } /// /// Get the maximum width of the scale value text that is required to label this /// . /// The results of this method are used to determine how much space is required for /// the axis labels. /// /// /// 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 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. /// /// /// true to get the bounding box of the text using the , /// false to just get the bounding box without rotation /// /// the maximum width of the text in pixel units internal SizeF GetScaleMaxSpace( Graphics g, GraphPane pane, float scaleFactor, bool applyAngle ) { if ( _isVisible ) { double dVal, scaleMult = Math.Pow( (double)10.0, _mag ); int i; float saveAngle = _fontSpec.Angle; if ( !applyAngle ) _fontSpec.Angle = 0; int nTics = CalcNumTics(); double startVal = CalcBaseTic(); SizeF maxSpace = new SizeF( 0, 0 ); // Repeat for each tic for ( i = 0; i < nTics; i++ ) { dVal = CalcMajorTicValue( startVal, i ); // draw the label //string tmpStr = MakeLabel( pane, i, dVal ); string tmpStr = _ownerAxis.MakeLabelEventWorks( pane, i, dVal ); SizeF sizeF; if ( this.IsLog && _isUseTenPower ) sizeF = _fontSpec.BoundingBoxTenPower( g, tmpStr, scaleFactor ); else sizeF = _fontSpec.BoundingBox( g, tmpStr, scaleFactor ); if ( sizeF.Height > maxSpace.Height ) maxSpace.Height = sizeF.Height; if ( sizeF.Width > maxSpace.Width ) maxSpace.Width = sizeF.Width; } _fontSpec.Angle = saveAngle; return maxSpace; } else return new SizeF(0,0); } /// /// Determine the value for any major tic. /// /// /// This method properly accounts for , , /// and other axis format settings. /// /// /// The value of the first major tic (floating point double) /// /// /// The major tic number (0 = first major tic). For log scales, this is the actual power of 10. /// /// /// The specified major tic value (floating point double). /// virtual internal double CalcMajorTicValue( double baseVal, double tic ) { // Default behavior is a normal linear scale (also works for ordinal types) return baseVal + (double) _majorStep * tic; } /// /// Determine the value for any minor tic. /// /// /// This method properly accounts for , , /// and other axis format settings. /// /// /// The value of the first major tic (floating point double). This tic value is the base /// reference for all tics (including minor ones). /// /// /// The major tic number (0 = first major tic). For log scales, this is the actual power of 10. /// /// /// The specified minor tic value (floating point double). /// virtual internal double CalcMinorTicValue( double baseVal, int iTic ) { // default behavior is a linear axis (works for ordinal types too return baseVal + (double) _minorStep * (double) iTic; } /// /// Internal routine to determine the ordinals of the first minor tic mark /// /// /// The value of the first major tic for the axis. /// /// /// The ordinal position of the first minor tic, relative to the first major tic. /// This value can be negative (e.g., -3 means the first minor tic is 3 minor step /// increments before the first major tic. /// virtual internal int CalcMinorStart( double baseVal ) { // Default behavior is for a linear scale (works for ordinal as well return (int) ( ( _min - baseVal ) / _minorStep ); } /// /// Determine the value for the first major tic. /// /// /// This is done by finding the first possible value that is an integral multiple of /// the step size, taking into account the date/time units if appropriate. /// This method properly accounts for , , /// and other axis format settings. /// /// /// First major tic value (floating point double). /// virtual internal double CalcBaseTic() { if ( _baseTic != PointPair.Missing ) return _baseTic; else if ( IsAnyOrdinal ) { // basetic is always 1 for ordinal types return 1; } else { // default behavior is linear or ordinal type // go to the nearest even multiple of the step size return Math.Ceiling( (double)_min / (double)_majorStep - 0.00000001 ) * (double)_majorStep; } } /// /// Draw the value labels, tic marks, and grid lines as /// required for this . /// /// /// 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 first major tic value for the axis /// /// /// The total number of major tics for the axis /// /// /// The pixel location of the far side of the ChartRect from this axis. /// This value is the ChartRect.Height for the XAxis, or the ChartRect.Width /// for the YAxis and Y2Axis. /// /// The number of pixels to shift this axis, based on the /// value of . A positive value is into the ChartRect relative to /// the default axis position. /// /// 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. /// internal void DrawLabels( Graphics g, GraphPane pane, double baseVal, int nTics, float topPix, float shift, float scaleFactor ) { MajorTic tic = _ownerAxis._majorTic; // MajorGrid grid = _ownerAxis._majorGrid; double dVal, dVal2; float pixVal, pixVal2; float scaledTic = tic.ScaledTic( scaleFactor ); double scaleMult = Math.Pow( (double)10.0, _mag ); using ( Pen ticPen = tic.GetPen( pane, scaleFactor ) ) // using ( Pen gridPen = grid.GetPen( pane, scaleFactor ) ) { // get the Y position of the center of the axis labels // (the axis itself is referenced at zero) SizeF maxLabelSize = GetScaleMaxSpace( g, pane, scaleFactor, true ); float charHeight = _fontSpec.GetHeight( scaleFactor ); float maxSpace = maxLabelSize.Height; float edgeTolerance = Default.EdgeTolerance * scaleFactor; double rangeTol = ( _maxLinTemp - _minLinTemp ) * 0.001; int firstTic = (int)( ( _minLinTemp - baseVal ) / _majorStep + 0.99 ); if ( firstTic < 0 ) firstTic = 0; // save the position of the previous tic float lastPixVal = -10000; // loop for each major tic for ( int i = firstTic; i < nTics + firstTic; i++ ) { dVal = CalcMajorTicValue( baseVal, i ); // If we're before the start of the scale, just go to the next tic if ( dVal < _minLinTemp ) continue; // if we've already past the end of the scale, then we're done if ( dVal > _maxLinTemp + rangeTol ) break; // convert the value to a pixel position pixVal = LocalTransform( dVal ); // see if the tic marks will be drawn between the labels instead of at the labels // (this applies only to AxisType.Text if ( tic._isBetweenLabels && IsText ) { // We need one extra tic in order to draw the tics between labels // so provide an exception here if ( i == 0 ) { dVal2 = CalcMajorTicValue( baseVal, -0.5 ); if ( dVal2 >= _minLinTemp ) { pixVal2 = LocalTransform( dVal2 ); tic.Draw( g, pane, ticPen, pixVal2, topPix, shift, scaledTic ); // grid.Draw( g, gridPen, pixVal2, topPix ); } } dVal2 = CalcMajorTicValue( baseVal, (double)i + 0.5 ); if ( dVal2 > _maxLinTemp ) break; pixVal2 = LocalTransform( dVal2 ); } else pixVal2 = pixVal; tic.Draw( g, pane, ticPen, pixVal2, topPix, shift, scaledTic ); // draw the grid // grid.Draw( g, gridPen, pixVal2, topPix ); bool isMaxValueAtMaxPix = ( ( _ownerAxis is XAxis || _ownerAxis is Y2Axis ) && !IsReverse ) || ( _ownerAxis is Y2Axis && IsReverse ); bool isSkipZone = ( ( ( _isSkipFirstLabel && isMaxValueAtMaxPix ) || ( _isSkipLastLabel && !isMaxValueAtMaxPix ) ) && pixVal < edgeTolerance ) || ( ( ( _isSkipLastLabel && isMaxValueAtMaxPix ) || ( _isSkipFirstLabel && !isMaxValueAtMaxPix ) ) && pixVal > _maxPix - _minPix - edgeTolerance ); bool isSkipCross = _isSkipCrossLabel && !_ownerAxis._crossAuto && Math.Abs( _ownerAxis._cross - dVal ) < rangeTol * 10.0; isSkipZone = isSkipZone || isSkipCross; if ( _isVisible && !isSkipZone ) { // For exponential scales, just skip any label that would overlap with the previous one // This is because exponential scales have varying label spacing if ( IsPreventLabelOverlap && Math.Abs( pixVal - lastPixVal ) < maxLabelSize.Width ) continue; DrawLabel( g, pane, i, dVal, pixVal, shift, maxSpace, scaledTic, charHeight, scaleFactor ); lastPixVal = pixVal; } } } } internal void DrawGrid( Graphics g, GraphPane pane, double baseVal, float topPix, float scaleFactor ) { MajorTic tic = _ownerAxis._majorTic; MajorGrid grid = _ownerAxis._majorGrid; int nTics = CalcNumTics(); double dVal, dVal2; float pixVal, pixVal2; using ( Pen gridPen = grid.GetPen( pane, scaleFactor ) ) { // get the Y position of the center of the axis labels // (the axis itself is referenced at zero) // SizeF maxLabelSize = GetScaleMaxSpace( g, pane, scaleFactor, true ); // float charHeight = _fontSpec.GetHeight( scaleFactor ); // float maxSpace = maxLabelSize.Height; // float edgeTolerance = Default.EdgeTolerance * scaleFactor; double rangeTol = ( _maxLinTemp - _minLinTemp ) * 0.001; int firstTic = (int)( ( _minLinTemp - baseVal ) / _majorStep + 0.99 ); if ( firstTic < 0 ) firstTic = 0; // save the position of the previous tic // float lastPixVal = -10000; // loop for each major tic for ( int i = firstTic; i < nTics + firstTic; i++ ) { dVal = CalcMajorTicValue( baseVal, i ); // If we're before the start of the scale, just go to the next tic if ( dVal < _minLinTemp ) continue; // if we've already past the end of the scale, then we're done if ( dVal > _maxLinTemp + rangeTol ) break; // convert the value to a pixel position pixVal = LocalTransform( dVal ); // see if the tic marks will be drawn between the labels instead of at the labels // (this applies only to AxisType.Text if ( tic._isBetweenLabels && IsText ) { // We need one extra tic in order to draw the tics between labels // so provide an exception here if ( i == 0 ) { dVal2 = CalcMajorTicValue( baseVal, -0.5 ); if ( dVal2 >= _minLinTemp ) { pixVal2 = LocalTransform( dVal2 ); grid.Draw( g, gridPen, pixVal2, topPix ); } } dVal2 = CalcMajorTicValue( baseVal, (double)i + 0.5 ); if ( dVal2 > _maxLinTemp ) break; pixVal2 = LocalTransform( dVal2 ); } else pixVal2 = pixVal; // draw the grid grid.Draw( g, gridPen, pixVal2, topPix ); } } } internal void DrawLabel( Graphics g, GraphPane pane, int i, double dVal, float pixVal, float shift, float maxSpace, float scaledTic, float charHeight, float scaleFactor ) { float textTop, textCenter; if ( _ownerAxis.MajorTic.IsOutside ) textTop = scaledTic + charHeight * _labelGap; else textTop = charHeight * _labelGap; // draw the label //string tmpStr = MakeLabel( pane, i, dVal ); string tmpStr = _ownerAxis.MakeLabelEventWorks( pane, i, dVal ); float height; if ( this.IsLog && _isUseTenPower ) height = _fontSpec.BoundingBoxTenPower( g, tmpStr, scaleFactor ).Height; else height = _fontSpec.BoundingBox( g, tmpStr, scaleFactor ).Height; if ( _align == AlignP.Center ) textCenter = textTop + maxSpace / 2.0F; else if ( _align == AlignP.Outside ) textCenter = textTop + maxSpace - height / 2.0F; else // inside textCenter = textTop + height / 2.0F; if ( _isLabelsInside ) textCenter = shift - textCenter; else textCenter = shift + textCenter; AlignV av = AlignV.Center; AlignH ah = AlignH.Center; if ( _ownerAxis is XAxis || _ownerAxis is X2Axis ) ah = _alignH; else av = _alignH == AlignH.Left ? AlignV.Top : ( _alignH == AlignH.Right ? AlignV.Bottom : AlignV.Center ); if ( this.IsLog && _isUseTenPower ) _fontSpec.DrawTenPower( g, pane, tmpStr, pixVal, textCenter, ah, av, scaleFactor ); else _fontSpec.Draw( g, pane, tmpStr, pixVal, textCenter, ah, av, scaleFactor ); } /// /// Draw the scale, including the tic marks, value labels, and grid lines as /// required for this . /// /// /// 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 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. /// /// /// The number of pixels to shift to account for non-primary axis position (e.g., /// the second, third, fourth, etc. or . /// internal void Draw( Graphics g, GraphPane pane, float scaleFactor, float shiftPos ) { MajorGrid majorGrid = _ownerAxis._majorGrid; MajorTic majorTic = _ownerAxis._majorTic; MinorTic minorTic = _ownerAxis._minorTic; float rightPix, topPix; GetTopRightPix( pane, out topPix, out rightPix ); // calculate the total number of major tics required int nTics = CalcNumTics(); // get the first major tic value double baseVal = CalcBaseTic(); using ( Pen pen = new Pen( _ownerAxis.Color, pane.ScaledPenWidth( majorTic._penWidth, scaleFactor ) ) ) { // redraw the axis border if ( _ownerAxis.IsAxisSegmentVisible ) g.DrawLine( pen, 0.0F, shiftPos, rightPix, shiftPos ); // Draw a zero-value line if needed if ( majorGrid._isZeroLine && _min < 0.0 && _max > 0.0 ) { float zeroPix = LocalTransform( 0.0 ); g.DrawLine( pen, zeroPix, 0.0F, zeroPix, topPix ); } } // draw the major tics and labels DrawLabels( g, pane, baseVal, nTics, topPix, shiftPos, scaleFactor ); // _ownerAxis.DrawMinorTics( g, pane, baseVal, shiftPos, scaleFactor, topPix ); _ownerAxis.DrawTitle( g, pane, shiftPos, scaleFactor ); } internal void GetTopRightPix( GraphPane pane, out float topPix, out float rightPix ) { if ( _ownerAxis is XAxis || _ownerAxis is X2Axis ) { rightPix = pane.Chart._rect.Width; topPix = -pane.Chart._rect.Height; } else { rightPix = pane.Chart._rect.Height; topPix = -pane.Chart._rect.Width; } // sanity check if ( _min >= _max ) return; // if the step size is outrageous, then quit // (step size not used for log scales) if ( !IsLog ) { if ( _majorStep <= 0 || _minorStep <= 0 ) return; double tMajor = ( _max - _min ) / ( _majorStep * MajorUnitMultiplier ); double tMinor = ( _max - _min ) / ( _minorStep * MinorUnitMultiplier ); MinorTic minorTic = _ownerAxis._minorTic; if ( tMajor > 1000 || ( ( minorTic.IsOutside || minorTic.IsInside || minorTic.IsOpposite ) && tMinor > 5000 ) ) return; } } /// /// Determine the width, in pixel units, of each bar cluster including /// the cluster gaps and bar gaps. /// /// /// This method uses the for /// non-ordinal axes, or a cluster width of 1.0 for ordinal axes. /// /// A reference to the object /// associated with this /// The width of each bar cluster, in pixel units public float GetClusterWidth( GraphPane pane ) { double basisVal = _min; return Math.Abs( Transform( basisVal + ( IsAnyOrdinal ? 1.0 : pane._barSettings._clusterScaleWidth ) ) - Transform( basisVal ) ); } /// /// Calculates the cluster width, in pixels, by transforming the specified /// clusterScaleWidth. /// /// The width in user scale units of each /// bar cluster /// The equivalent pixel size of the bar cluster public float GetClusterWidth( double clusterScaleWidth ) { double basisVal = _min; return Math.Abs( Transform( basisVal + clusterScaleWidth ) - Transform( basisVal ) ); } #endregion #region Scale Picker Methods /// /// Select a reasonable scale given a range of data values. /// /// /// The scale range is chosen /// based on increments of 1, 2, or 5 (because they are even divisors of 10). This /// routine honors the , , /// and autorange settings as well as the /// setting. In the event that any of the autorange settings are false, the /// corresponding , , or /// setting is explicitly honored, and the remaining autorange settings (if any) will /// be calculated to accomodate the non-autoranged values. The basic defaults for /// scale selection are defined using , /// , and /// from the default class. /// On Exit: /// is set to scale minimum (if = true) /// is set to scale maximum (if = true) /// is set to scale step size (if = true) /// is set to scale minor step size (if = true) /// is set to a magnitude multiplier according to the data /// is set to the display format for the values (this controls the /// number of decimal places, whether there are thousands separators, currency types, etc.) /// /// A reference to the object /// associated with this /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// 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. /// virtual public void PickScale( GraphPane pane, Graphics g, float scaleFactor ) { double minVal = _rangeMin; double maxVal = _rangeMax; // Make sure that minVal and maxVal are legitimate values if ( Double.IsInfinity( minVal ) || Double.IsNaN( minVal ) || minVal == Double.MaxValue ) minVal = 0.0; if ( Double.IsInfinity( maxVal ) || Double.IsNaN( maxVal ) || maxVal == Double.MaxValue ) maxVal = 0.0; // if the scales are autoranged, use the actual data values for the range double range = maxVal - minVal; // "Grace" is applied to the numeric axis types only bool numType = !this.IsAnyOrdinal; // For autoranged values, assign the value. If appropriate, adjust the value by the // "Grace" value. if ( _minAuto ) { _min = minVal; // Do not let the grace value extend the axis below zero when all the values were positive if ( numType && ( _min < 0 || minVal - _minGrace * range >= 0.0 ) ) _min = minVal - _minGrace * range; } if ( _maxAuto ) { _max = maxVal; // Do not let the grace value extend the axis above zero when all the values were negative if ( numType && ( _max > 0 || maxVal + _maxGrace * range <= 0.0 ) ) _max = maxVal + _maxGrace * range; } if ( _max == _min && _maxAuto && _minAuto ) { if ( Math.Abs( _max ) > 1e-100 ) { _max *= ( _min < 0 ? 0.95 : 1.05 ); _min *= ( _min < 0 ? 1.05 : 0.95 ); } else { _max = 1.0; _min = -1.0; } } if ( _max <= _min ) { if ( _maxAuto ) _max = _min + 1.0; else if ( _minAuto ) _min = _max - 1.0; } } /// /// Calculate the maximum number of labels that will fit on this axis. /// /// /// This method works for /// both X and Y direction axes, and it works for angled text (assuming that a bounding box /// is an appropriate measure). Technically, labels at 45 degree angles could fit better than /// the return value of this method since the bounding boxes can overlap without the labels actually /// overlapping. /// /// A reference to the object /// associated with this /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// 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. /// public int CalcMaxLabels( Graphics g, GraphPane pane, float scaleFactor ) { SizeF size = this.GetScaleMaxSpace( g, pane, scaleFactor, false ); // The font angles are already set such that the Width is parallel to the appropriate (X or Y) // axis. Therefore, we always use size.Width. // use the minimum of 1/4 the max Width or 1 character space // double allowance = this.Scale.FontSpec.GetWidth( g, scaleFactor ); // if ( allowance > size.Width / 4 ) // allowance = size.Width / 4; float maxWidth = 1000; float temp = 1000; float costh = (float) Math.Abs( Math.Cos( _fontSpec.Angle * Math.PI / 180.0 ) ); float sinth = (float) Math.Abs( Math.Sin( _fontSpec.Angle * Math.PI / 180.0 ) ); if ( costh > 0.001 ) maxWidth = size.Width / costh; if ( sinth > 0.001 ) temp = size.Height / sinth; if ( temp < maxWidth ) maxWidth = temp; //maxWidth = size.Width; /* if ( this is XAxis ) // Add an extra character width to leave a minimum of 1 character space between labels maxWidth = size.Width + this.Scale.FontSpec.GetWidth( g, scaleFactor ); else // For vertical spacing, we only need 1/2 character maxWidth = size.Width + this.Scale.FontSpec.GetWidth( g, scaleFactor ) / 2.0; */ if ( maxWidth <= 0 ) maxWidth = 1; // Calculate the maximum number of labels double width; RectangleF chartRect = pane.Chart._rect; if ( _ownerAxis is XAxis || _ownerAxis is X2Axis ) width = ( chartRect.Width == 0 ) ? pane.Rect.Width * 0.75 : chartRect.Width; else width = ( chartRect.Height == 0 ) ? pane.Rect.Height * 0.75 : chartRect.Height; int maxLabels = (int) ( width / maxWidth ); if ( maxLabels <= 0 ) maxLabels = 1; return maxLabels; } internal void SetScaleMag( double min, double max, double step ) { // set the scale magnitude if required if ( _magAuto ) { // Find the optimal scale display multiple double mag = -100; double mag2 = -100; if ( Math.Abs( _min ) > 1.0e-30 ) mag = Math.Floor( Math.Log10( Math.Abs( _min ) ) ); if ( Math.Abs( _max ) > 1.0e-30 ) mag2 = Math.Floor( Math.Log10( Math.Abs( _max ) ) ); mag = Math.Max( mag2, mag ); // Do not use scale multiples for magnitudes below 4 if ( mag == -100 || Math.Abs( mag ) <= 3 ) mag = 0; // Use a power of 10 that is a multiple of 3 (engineering scale) _mag = (int) ( Math.Floor( mag / 3.0 ) * 3.0 ); } // Calculate the appropriate number of dec places to display if required if ( _formatAuto ) { int numDec = 0 - (int) ( Math.Floor( Math.Log10( _majorStep ) ) - _mag ); if ( numDec < 0 ) numDec = 0; _format = "f" + numDec.ToString(); } } /// /// Calculate a step size based on a data range. /// /// /// This utility method /// will try to honor the and /// number of /// steps while using a rational increment (1, 2, or 5 -- which are /// even divisors of 10). This method is used by . /// /// The range of data in user scale units. This can /// be a full range of the data for the major step size, or just the /// value of the major step size to calculate the minor step size /// The desired "typical" number of steps /// to divide the range into /// The calculated step size for the specified data range. protected static double CalcStepSize( double range, double targetSteps ) { // Calculate an initial guess at step size double tempStep = range / targetSteps; // Get the magnitude of the step size double mag = Math.Floor( Math.Log10( tempStep ) ); double magPow = Math.Pow( (double) 10.0, mag ); // Calculate most significant digit of the new step size double magMsd = ( (int) ( tempStep / magPow + .5 ) ); // promote the MSD to either 1, 2, or 5 if ( magMsd > 5.0 ) magMsd = 10.0; else if ( magMsd > 2.0 ) magMsd = 5.0; else if ( magMsd > 1.0 ) magMsd = 2.0; return magMsd * magPow; } /// /// Calculate a step size based on a data range, limited to a maximum number of steps. /// /// /// This utility method /// will calculate a step size, of no more than maxSteps, /// using a rational increment (1, 2, or 5 -- which are /// even divisors of 10). This method is used by . /// /// The range of data in user scale units. This can /// be a full range of the data for the major step size, or just the /// value of the major step size to calculate the minor step size /// The maximum allowable number of steps /// to divide the range into /// The calculated step size for the specified data range. protected double CalcBoundedStepSize( double range, double maxSteps ) { // Calculate an initial guess at step size double tempStep = range / maxSteps; // Get the magnitude of the step size double mag = Math.Floor( Math.Log10( tempStep ) ); double magPow = Math.Pow( (double) 10.0, mag ); // Calculate most significant digit of the new step size double magMsd = Math.Ceiling( tempStep / magPow ); // promote the MSD to either 1, 2, or 5 if ( magMsd > 5.0 ) magMsd = 10.0; else if ( magMsd > 2.0 ) magMsd = 5.0; else if ( magMsd > 1.0 ) magMsd = 2.0; return magMsd * magPow; } /// /// Internal routine to determine the ordinals of the first and last major axis label. /// /// /// This is the total number of major tics for this axis. /// virtual internal int CalcNumTics() { int nTics = 1; // default behavior is for a linear or ordinal scale nTics = (int) ( ( _max - _min ) / _majorStep + 0.01 ) + 1; if ( nTics < 1 ) nTics = 1; else if ( nTics > 1000 ) nTics = 1000; return nTics; } /// /// Calculate the modulus (remainder) in a safe manner so that divide /// by zero errors are avoided /// /// The divisor /// The dividend /// the value of the modulus, or zero for the divide-by-zero /// case protected double MyMod( double x, double y ) { double temp; if ( y == 0 ) return 0; temp = x / y; return y * ( temp - Math.Floor( temp ) ); } /// /// Define suitable default ranges for an axis in the event that /// no data were available /// /// The of interest /// The for which to set the range internal void SetRange( GraphPane pane, Axis axis ) { if ( _rangeMin >= Double.MaxValue || _rangeMax <= Double.MinValue ) { // If this is a Y axis, and the main Y axis is valid, use it for defaults if ( axis != pane.XAxis && axis != pane.X2Axis && pane.YAxis.Scale._rangeMin < double.MaxValue && pane.YAxis.Scale._rangeMax > double.MinValue ) { _rangeMin = pane.YAxis.Scale._rangeMin; _rangeMax = pane.YAxis.Scale._rangeMax; } // Otherwise, if this is a Y axis, and the main Y2 axis is valid, use it for defaults else if ( axis != pane.XAxis && axis != pane.X2Axis && pane.Y2Axis.Scale._rangeMin < double.MaxValue && pane.Y2Axis.Scale._rangeMax > double.MinValue ) { _rangeMin = pane.Y2Axis.Scale._rangeMin; _rangeMax = pane.Y2Axis.Scale._rangeMax; } // Otherwise, just use 0 and 1 else { _rangeMin = 0; _rangeMax = 1; } } /* if ( yMinVal >= Double.MaxValue || yMaxVal <= Double.MinValue ) { if ( y2MinVal < Double.MaxValue && y2MaxVal > Double.MinValue ) { yMinVal = y2MinVal; yMaxVal = y2MaxVal; } else { yMinVal = 0; yMaxVal = 0.01; } } if ( y2MinVal >= Double.MaxValue || y2MaxVal <= Double.MinValue ) { if ( yMinVal < Double.MaxValue && yMaxVal > Double.MinValue ) { y2MinVal = yMinVal; y2MaxVal = yMaxVal; } else { y2MinVal = 0; y2MaxVal = 1; } } */ } #endregion #region Coordinate Transform Methods /// /// Transform the coordinate value from user coordinates (scale value) /// to graphics device coordinates (pixels). /// /// This method takes into /// account the scale range ( and ), /// logarithmic state (), scale reverse state /// () and axis type (, /// , or ). /// Note that the must be valid, and /// must be called for the /// current configuration before using this method (this is called everytime /// the graph is drawn (i.e., is called). /// /// The coordinate value, in user scale units, to /// be transformed /// the coordinate value transformed to screen coordinates /// for use in calling the draw routines public float Transform( double x ) { // Must take into account Log, and Reverse Axes double ratio = ( Linearize( x ) - _minLinTemp ) / ( _maxLinTemp - _minLinTemp ); // _isReverse axisType Eqn // T XAxis _maxPix - ... // F YAxis _maxPix - ... // F Y2Axis _maxPix - ... // T YAxis _minPix + ... // T Y2Axis _minPix + ... // F XAxis _minPix + ... if ( _isReverse == ( _ownerAxis is XAxis || _ownerAxis is X2Axis ) ) return (float) ( _maxPix - ( _maxPix - _minPix ) * ratio ); else return (float) ( _minPix + ( _maxPix - _minPix ) * ratio ); } /// /// Transform the coordinate value from user coordinates (scale value) /// to graphics device coordinates (pixels). /// /// /// This method takes into /// account the scale range ( and ), /// logarithmic state (), scale reverse state /// () and axis type (, /// , or ). /// Note that the must be valid, and /// must be called for the /// current configuration before using this method (this is called everytime /// the graph is drawn (i.e., is called). /// /// true to force the axis to honor the data /// value, rather than replacing it with the ordinal value /// The ordinal value of this point, just in case /// this is an axis /// The coordinate value, in user scale units, to /// be transformed /// the coordinate value transformed to screen coordinates /// for use in calling the draw routines public float Transform( bool isOverrideOrdinal, int i, double x ) { // ordinal types ignore the X value, and just use the ordinal position if ( this.IsAnyOrdinal && i >= 0 && !isOverrideOrdinal ) x = (double) i + 1.0; return Transform( x ); } /// /// Reverse transform the user coordinates (scale value) /// given a graphics device coordinate (pixels). /// /// /// This method takes into /// account the scale range ( and ), /// logarithmic state (), scale reverse state /// () and axis type (, /// , or ). /// Note that the must be valid, and /// must be called for the /// current configuration before using this method (this is called everytime /// the graph is drawn (i.e., is called). /// /// The screen pixel value, in graphics device coordinates to /// be transformed /// The user scale value that corresponds to the screen pixel location public double ReverseTransform( float pixVal ) { double val; // see if the sign of the equation needs to be reversed if ( ( _isReverse ) == ( _ownerAxis is XAxis || _ownerAxis is X2Axis ) ) val = (double) ( pixVal - _maxPix ) / (double) ( _minPix - _maxPix ) * ( _maxLinTemp - _minLinTemp ) + _minLinTemp; else val = (double) ( pixVal - _minPix ) / (double) ( _maxPix - _minPix ) * ( _maxLinTemp - _minLinTemp ) + _minLinTemp; return DeLinearize( val ); } /// /// Transform the coordinate value from user coordinates (scale value) /// to graphics device coordinates (pixels). /// /// Assumes that the origin /// has been set to the "left" of this axis, facing from the label side. /// Note that the left side corresponds to the scale minimum for the X and /// Y2 axes, but it is the scale maximum for the Y axis. /// This method takes into /// account the scale range ( and ), /// logarithmic state (), scale reverse state /// () and axis type (, /// , or ). Note that /// the must be valid, and /// must be called for the /// current configuration before using this method. /// /// The coordinate value, in linearized user scale units, to /// be transformed /// the coordinate value transformed to screen coordinates /// for use in calling the method public float LocalTransform( double x ) { // Must take into account Log, and Reverse Axes double ratio; float rv; // Coordinate values for log scales are already in exponent form, so no need // to take the log here ratio = ( x - _minLinTemp ) / ( _maxLinTemp - _minLinTemp ); if ( _isReverse == ( _ownerAxis is YAxis || _ownerAxis is X2Axis ) ) rv = (float) ( ( _maxPix - _minPix ) * ratio ); else rv = (float)( ( _maxPix - _minPix ) * ( 1.0F - ratio ) ); return rv; } /// /// Calculate a base 10 logarithm in a safe manner to avoid math exceptions /// /// The value for which the logarithm is to be calculated /// The value of the logarithm, or 0 if the /// argument was negative or zero public static double SafeLog( double x ) { if ( x > 1.0e-20 ) return Math.Log10( x ); else return 0.0; } /// ///Calculate an exponential in a safe manner to avoid math exceptions /// /// The value for which the exponential is to be calculated /// The exponent value to use for calculating the exponential. public static double SafeExp( double x, double exponent ) { if ( x > 1.0e-20 ) return Math.Pow( x, exponent ); else return 0.0; } #endregion } }