//============================================================================
//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
{
///
/// The Axis class is an abstract base class that encompasses all properties
/// and methods required to define a graph Axis.
///
/// This class is inherited by the
/// , , and classes
/// to define specific characteristics for those types.
///
///
/// John Champion modified by Jerry Vos
/// $Revision: 3.73 $ $Date: 2007/05/19 06:06:05 $
[Serializable]
abstract public class Axis : ISerializable, ICloneable
{
#region Class Fields
///
/// private field that stores the class, which implements all the
/// calculations and methods associated with the numeric scale for this
/// . See the public property to access this class.
///
internal Scale _scale;
///
/// Private field that stores the class, which handles all
/// the minor tic information. See the public property to access this class.
///
internal MinorTic _minorTic;
///
/// Private field that stores the class, which handles all
/// the major tic information. See the public property to access this class.
///
internal MajorTic _majorTic;
///
/// Private field that stores the class, which handles all
/// the major grid information. See the public property to access this class.
///
internal MajorGrid _majorGrid;
///
/// Private field that stores the class, which handles all
/// the minor grid information. See the public property to access this class.
///
internal MinorGrid _minorGrid;
/// Private fields for the scale rendering properties.
/// Use the public properties and
/// for access to these values.
///
internal double _cross;
/// Private field for the automatic cross position mode.
/// Use the public property for access to this value.
///
internal bool _crossAuto;
/// Private fields for the attributes.
/// Use the public properties ,
/// for access to these values.
///
protected bool _isVisible,
_isAxisSegmentVisible;
/// Private field for the title string.
/// Use the public property for access to this value.
///
protected AxisLabel _title;
///
/// A tag object for use by the user. This can be used to store additional
/// information associated with the . ZedGraph does
/// not use this value for any purpose.
///
///
/// Note that, if you are going to Serialize ZedGraph data, then any type
/// that you store in must be a serializable type (or
/// it will cause an exception).
///
public object Tag;
/// Private field for the drawing dimensions.
/// Use the public property
/// for access to these values.
private float _axisGap;
///
/// Private field for the minimum allowable space allocation.
/// Use the public property to access this value.
///
///
private float _minSpace;
/// Private fields for the colors.
/// Use the public property for access to this values.
///
private Color _color;
///
/// Temporary values for axis space calculations (see ).
///
internal float _tmpSpace;
#endregion
#region Events
///
/// A delegate that allows full custom formatting of the Axis labels
///
/// The for which the label is to be
/// formatted
/// The of interest.
/// 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;
// Revision: JCarpenter 10/06
///
/// Allow customization of title based on user preferences.
///
/// The of interest.
///
/// A string value representing the label, or null if the ZedGraph should go ahead
/// and generate the label according to the current settings. To make the title
/// blank, return "".
///
public delegate string ScaleTitleEventHandler( Axis axis );
//Revision: JCarpenter 10/06
///
/// Allow customization of the title when the scale is very large
/// Subscribe to this event to handle custom formatting of the scale axis label.
///
public event ScaleTitleEventHandler ScaleTitleEvent;
#endregion
#region Defaults
///
/// A simple struct that defines the
/// default property values for the class.
///
public struct Default
{
///
/// The default size for the gap between multiple axes
/// ( property). Units are in points (1/72 inch).
///
public static float AxisGap = 5;
///
/// The default setting for the gap between the scale labels and the axis title.
///
public static float TitleGap = 0.0f;
///
/// The default font family for the text
/// font specification
/// ( property).
///
public static string TitleFontFamily = "Arial";
///
/// The default font size for the text
/// font specification
/// ( property). Units are
/// in points (1/72 inch).
///
public static float TitleFontSize = 14;
///
/// The default font color for the text
/// font specification
/// ( property).
///
public static Color TitleFontColor = Color.Black;
///
/// The default font bold mode for the text
/// font specification
/// ( property). true
/// for a bold typeface, false otherwise.
///
public static bool TitleFontBold = true;
///
/// The default font italic mode for the text
/// font specification
/// ( property). true
/// for an italic typeface, false otherwise.
///
public static bool TitleFontItalic = false;
///
/// The default font underline mode for the text
/// font specification
/// ( property). true
/// for an underlined typeface, false otherwise.
///
public static bool TitleFontUnderline = false;
///
/// The default color for filling in the text background
/// (see property).
///
public static Color TitleFillColor = Color.White;
///
/// The default custom brush for filling in the text background
/// (see property).
///
public static Brush TitleFillBrush = null;
///
/// The default fill mode for filling in the text background
/// (see property).
///
public static FillType TitleFillType = FillType.None;
///
/// The default color for the itself
/// ( property). This color only affects the
/// the axis border.
///
public static Color BorderColor = Color.Black;
///
/// The default value for , which determines
/// whether or not the scale segment itself is visible
///
public static bool IsAxisSegmentVisible = true;
///
/// The default setting for the scale axis type
/// ( property). This value is set as per
/// the enumeration
///
public static AxisType Type = AxisType.Linear;
///
/// The default color for the axis segment.
///
public static Color Color = Color.Black;
///
/// The default setting for the axis space allocation. This term, expressed in
/// points (1/72 inch) and scaled according to for the
/// , determines the minimum amount of space an axis must
/// have between the and the
/// . This minimum space
/// applies whether is true or false.
///
public static float MinSpace = 0f;
}
#endregion
#region Constructors
///
/// Default constructor for that sets all axis properties
/// to default values as defined in the class.
///
public Axis()
{
_scale = new LinearScale( this );
_cross = 0.0;
_crossAuto = true;
_majorTic = new MajorTic();
_minorTic = new MinorTic();
_majorGrid = new MajorGrid();
_minorGrid = new MinorGrid();
_axisGap = Default.AxisGap;
_minSpace = Default.MinSpace;
_isVisible = true;
_isAxisSegmentVisible = Default.IsAxisSegmentVisible;
_title = new AxisLabel( "", Default.TitleFontFamily, Default.TitleFontSize,
Default.TitleFontColor, Default.TitleFontBold,
Default.TitleFontUnderline, Default.TitleFontItalic );
_title.FontSpec.Fill = new Fill( Default.TitleFillColor, Default.TitleFillBrush,
Default.TitleFillType );
_title.FontSpec.Border.IsVisible = false;
_color = Default.Color;
}
///
/// Constructor for that sets all axis properties
/// to default values as defined in the class,
/// except for the .
///
/// A string containing the axis title
public Axis( string title )
: this()
{
_title._text = title;
}
///
/// The Copy Constructor.
///
/// The Axis object from which to copy
public Axis( Axis rhs )
{
_scale = rhs._scale.Clone( this );
_cross = rhs._cross;
_crossAuto = rhs._crossAuto;
_majorTic = rhs.MajorTic.Clone();
_minorTic = rhs.MinorTic.Clone();
_majorGrid = rhs._majorGrid.Clone();
_minorGrid = rhs._minorGrid.Clone();
_isVisible = rhs.IsVisible;
_isAxisSegmentVisible = rhs._isAxisSegmentVisible;
_title = (AxisLabel) rhs.Title.Clone();
_axisGap = rhs._axisGap;
_minSpace = rhs.MinSpace;
_color = rhs.Color;
}
///
/// 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 );
}
#endregion
#region Serialization
///
/// Current schema value that defines the version of the serialized file
///
public const int schema = 10;
///
/// Constructor for deserializing objects
///
/// A instance that defines the serialized data
///
/// A instance that contains the serialized data
///
protected Axis( 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" );
_cross = info.GetDouble( "cross" );
_crossAuto = info.GetBoolean( "crossAuto" );
_majorTic = (MajorTic)info.GetValue( "MajorTic", typeof( MajorTic ) );
_minorTic = (MinorTic)info.GetValue( "MinorTic", typeof( MinorTic ) );
_majorGrid = (MajorGrid)info.GetValue( "majorGrid", typeof( MajorGrid ) );
_minorGrid = (MinorGrid)info.GetValue( "minorGrid", typeof( MinorGrid ) );
_isVisible = info.GetBoolean( "isVisible" );
_title = (AxisLabel) info.GetValue( "title", typeof( AxisLabel ) );
_minSpace = info.GetSingle( "minSpace" );
_color = (Color)info.GetValue( "color", typeof( Color ) );
_isAxisSegmentVisible = info.GetBoolean( "isAxisSegmentVisible" );
_axisGap = info.GetSingle( "axisGap" );
_scale = (Scale)info.GetValue( "scale", typeof( Scale ) );
_scale._ownerAxis = this;
}
///
/// 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( "cross", _cross );
info.AddValue( "crossAuto", _crossAuto );
info.AddValue( "MajorTic", MajorTic );
info.AddValue( "MinorTic", MinorTic );
info.AddValue( "majorGrid", _majorGrid );
info.AddValue( "minorGrid", _minorGrid );
info.AddValue( "isVisible", _isVisible );
info.AddValue( "title", _title );
info.AddValue( "minSpace", _minSpace );
info.AddValue( "color", _color );
info.AddValue( "isAxisSegmentVisible", _isAxisSegmentVisible );
info.AddValue( "axisGap", _axisGap );
info.AddValue( "scale", _scale );
}
#endregion
#region Scale Properties
///
/// Gets the instance associated with this .
///
public Scale Scale
{
get { return _scale; }
}
///
/// Gets or sets the scale value at which this axis should cross the "other" axis.
///
/// This property allows the axis to be shifted away from its default location.
/// For example, for a graph with an X range from -100 to +100, the Y Axis can be located
/// at the X=0 value rather than the left edge of the ChartRect. 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 "other" axis is the axis the handles the second dimension
/// for the graph. For the XAxis, the "other" axis is the YAxis. For the YAxis or
/// Y2Axis, the "other" axis is the XAxis.
///
/// The value is defined in user scale units
///
///
///
///
public double Cross
{
get { return _cross; }
set { _cross = value; _crossAuto = false; }
}
///
/// Gets or sets a value that determines whether or not the value
/// is set automatically.
///
/// Set to true to have ZedGraph put the axis in the default location, or false
/// to specify the axis location manually with a value.
///
///
///
///
public bool CrossAuto
{
get { return _crossAuto; }
set { _crossAuto = value; }
}
///
/// Gets or sets the minimum axis space allocation.
///
///
/// This term, expressed in
/// points (1/72 inch) and scaled according to
/// for the , determines the minimum amount of space
/// an axis must have between the Chart.Rect and the
/// GraphPane.Rect. This minimum space
/// applies whether is true or false.
///
public float MinSpace
{
get { return _minSpace; }
set { _minSpace = value; }
}
#endregion
#region Tic Properties
///
/// The color to use for drawing this .
///
///
/// This affects only the axis segment (see ),
/// since the ,
/// , , ,
/// , and
/// all have their own color specification.
///
/// The color is defined using the
/// class
/// .
///
public Color Color
{
get { return _color; }
set { _color = value; }
}
///
/// Gets a reference to the class instance
/// for this . This class stores all the major tic settings.
///
public MajorTic MajorTic
{
get { return _majorTic; }
}
///
/// Gets a reference to the class instance
/// for this . This class stores all the minor tic settings.
///
public MinorTic MinorTic
{
get { return _minorTic; }
}
#endregion
#region Grid Properties
///
/// Gets a reference to the class that contains the properties
/// of the major grid.
///
public MajorGrid MajorGrid
{
get { return _majorGrid; }
}
///
/// Gets a reference to the class that contains the properties
/// of the minor grid.
///
public MinorGrid MinorGrid
{
get { return _minorGrid; }
}
#endregion
#region Type Properties
///
/// This property determines whether or not the is shown.
///
///
/// Note that even if
/// the axis is not visible, it can still be actively used to draw curves on a
/// graph, it will just be invisible to the user
///
/// true to show the axis, false to disable all drawing of this axis
/// .
/// .
/// .
/// .
public bool IsVisible
{
get { return _isVisible; }
set { _isVisible = value; }
}
///
/// Gets or sets a property that determines whether or not the axis segment (the line that
/// represents the axis itself) is drawn.
///
///
/// Under normal circumstances, this value won't affect the appearance of the display because
/// the Axis segment is overlain by the Axis border (see ).
/// However, when the border is not visible, or when is set to
/// false, this value will make a difference.
///
public bool IsAxisSegmentVisible
{
get { return _isAxisSegmentVisible; }
set { _isAxisSegmentVisible = value; }
}
///
/// Gets or sets the for this .
///
///
/// The type can be either ,
/// , ,
/// or .
///
///
///
///
///
///
public AxisType Type
{
get { return _scale.Type; }
set { _scale = Scale.MakeNewScale( _scale, value ); }
}
#endregion
#region Label Properties
///
/// Gets or sets the class that contains the title of this
/// .
///
/// The title normally shows the basis and dimensions of
/// the scale range, such as "Time (Years)". The title is only shown if the
/// property is set to true. If the Title text is empty,
/// then no title is shown, and no space is "reserved" for the title on the graph.
///
/// the title is a string value
///
public AxisLabel Title
{
get { return _title; }
set { _title = value; }
}
///
/// The size of the gap between multiple axes (see and
/// ).
///
///
/// This size will be scaled
/// according to the for the
///
///
/// The axis gap is measured in points (1/72 inch)
/// .
public float AxisGap
{
get { return _axisGap; }
set { _axisGap = value; }
}
#endregion
#region Rendering Methods
///
/// Restore the scale ranging to automatic mode, and recalculate the
/// scale ranges
///
///
/// A reference to the object that is the parent or
/// owner of this object.
///
///
/// A graphic device object to be drawn into. This is normally e.Graphics from the
/// PaintEventArgs argument to the Paint() method.
///
///
///
///
///
///
public void ResetAutoScale( GraphPane pane, Graphics g )
{
_scale._minAuto = true;
_scale._maxAuto = true;
_scale._majorStepAuto = true;
_scale._minorStepAuto = true;
_crossAuto = true;
_scale._magAuto = true;
//this.numDecAuto = true;
_scale._formatAuto = true;
pane.AxisChange( g );
}
///
/// Do all rendering associated with this to the specified
/// device.
///
///
/// This method is normally only
/// called by the Draw method of the parent 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 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 .
///
public void Draw( Graphics g, GraphPane pane, float scaleFactor, float shiftPos )
{
Matrix saveMatrix = g.Transform;
_scale.SetupScaleData( pane, this );
if ( _isVisible )
{
SetTransformMatrix( g, pane, scaleFactor );
shiftPos = CalcTotalShift( pane, scaleFactor, shiftPos );
_scale.Draw( g, pane, scaleFactor, shiftPos );
//DrawTitle( g, pane, scaleFactor );
g.Transform = saveMatrix;
}
}
internal void DrawGrid( Graphics g, GraphPane pane, float scaleFactor, float shiftPos )
{
if ( _isVisible )
{
Matrix saveMatrix = g.Transform;
SetTransformMatrix( g, pane, scaleFactor );
double baseVal = _scale.CalcBaseTic();
float topPix, rightPix;
_scale.GetTopRightPix( pane, out topPix, out rightPix );
shiftPos = CalcTotalShift( pane, scaleFactor, shiftPos );
_scale.DrawGrid( g, pane, baseVal, topPix, scaleFactor );
DrawMinorTics( g, pane, baseVal, shiftPos, scaleFactor, topPix );
g.Transform = saveMatrix;
}
}
///
/// This method will set the property for this
/// using the currently required space multiplied by a fraction (bufferFraction).
///
///
/// The currently required space is calculated using , and is
/// based on current data ranges, font sizes, etc. The "space" is actually the amount of space
/// required to fit the tic marks, scale labels, and axis title.
///
/// 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 amount of space to allocate for the axis, expressed
/// as a fraction of the currently required space. For example, a value of 1.2 would
/// allow for 20% extra above the currently required space.
/// If true, then this method will only modify the
/// property if the calculated result is more than the current value.
public void SetMinSpaceBuffer( Graphics g, GraphPane pane, float bufferFraction,
bool isGrowOnly )
{
// save the original value of minSpace
float oldSpace = this.MinSpace;
// set minspace to zero, since we don't want it to affect the CalcSpace() result
this.MinSpace = 0;
// Calculate the space required for the current graph assuming scalefactor = 1.0
// and apply the bufferFraction
float fixedSpace;
float space = this.CalcSpace( g, pane, 1.0F, out fixedSpace ) * bufferFraction;
// isGrowOnly indicates the minSpace can grow but not shrink
if ( isGrowOnly )
space = Math.Max( oldSpace, space );
// Set the minSpace
this.MinSpace = space;
}
///
/// Setup the Transform Matrix to handle drawing of 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.
///
abstract public void SetTransformMatrix( Graphics g, GraphPane pane, float scaleFactor );
///
/// Calculate the "shift" size, in pixels, in order to shift the axis from its default
/// location to the value specified by .
///
///
/// A reference to the object that is the parent or
/// owner of this object.
///
/// The shift amount measured in pixels
abstract internal float CalcCrossShift( GraphPane pane );
///
/// Gets the "Cross" axis that corresponds to this axis.
///
///
/// The cross axis is the axis which determines the of this Axis when the
/// Axis.Cross property is used. The
/// cross axis for any or
/// is always the primary , and
/// the cross axis for any or is
/// always the primary .
///
///
/// A reference to the object that is the parent or
/// owner of this object.
///
abstract public Axis GetCrossAxis( GraphPane pane );
// abstract internal float GetMinPix( GraphPane pane );
//abstract internal float CalcCrossFraction( GraphPane pane );
///
/// Returns the linearized actual cross position for this axis, reflecting the settings of
/// , , and .
///
///
/// If the value of lies outside the axis range, it is
/// limited to the axis range.
///
internal double EffectiveCrossValue( GraphPane pane )
{
Axis crossAxis = GetCrossAxis( pane );
// Use Linearize here instead of _minLinTemp because this method is called
// as part of CalcRect() before scale is fully setup
double min = crossAxis._scale.Linearize( _scale._min );
double max = crossAxis._scale.Linearize( _scale._max );
if ( _crossAuto )
{
if ( crossAxis._scale.IsReverse == ( this is Y2Axis || this is X2Axis ) )
return max;
else
return min;
}
else if ( _cross < min )
return min;
else if ( _cross > max )
return max;
else
return _scale.Linearize( _cross );
}
///
/// Returns true if the axis is shifted at all due to the setting of
/// . This function will always return false if
/// is true.
///
internal bool IsCrossShifted( GraphPane pane )
{
if ( _crossAuto )
return false;
else
{
Axis crossAxis = GetCrossAxis( pane );
if ( ( ( this is XAxis || this is YAxis ) && !crossAxis._scale.IsReverse ) ||
( ( this is X2Axis || this is Y2Axis ) && crossAxis._scale.IsReverse ) )
{
if ( _cross <= crossAxis._scale._min )
return false;
}
else
{
if ( _cross >= crossAxis._scale._max )
return false;
}
}
return true;
}
///
/// Calculates the proportional fraction of the total cross axis width at which
/// this axis is located.
///
///
///
internal float CalcCrossFraction( GraphPane pane )
{
// if this axis is not shifted due to the Cross value
if ( !this.IsCrossShifted( pane ) )
{
// if it's the primary axis and the scale labels are on the inside, then we
// don't need to save any room for the axis labels (they will be inside the chart rect)
if ( IsPrimary( pane ) && _scale._isLabelsInside )
return 1.0f;
// otherwise, it's a secondary (outboard) axis and we always save room for the axis and labels.
else
return 0.0f;
}
double effCross = EffectiveCrossValue( pane );
Axis crossAxis = GetCrossAxis( pane );
// Use Linearize here instead of _minLinTemp because this method is called
// as part of CalcRect() before scale is fully setup
// double max = crossAxis._scale._maxLinTemp;
// double min = crossAxis._scale._minLinTemp;
double max = crossAxis._scale.Linearize( _scale._min );
double min = crossAxis._scale.Linearize( _scale._max );
float frac;
if ( ( ( this is XAxis || this is YAxis ) && _scale._isLabelsInside == crossAxis._scale.IsReverse ) ||
( ( this is X2Axis || this is Y2Axis ) && _scale._isLabelsInside != crossAxis._scale.IsReverse ) )
frac = (float)( ( effCross - min ) / ( max - min ) );
else
frac = (float)( ( max - effCross ) / ( max - min ) );
if ( frac < 0.0f )
frac = 0.0f;
if ( frac > 1.0f )
frac = 1.0f;
return frac;
}
private float CalcTotalShift( GraphPane pane, float scaleFactor, float shiftPos )
{
if ( !IsPrimary( pane ) )
{
// if ( CalcCrossFraction( pane ) != 0.0 )
if ( IsCrossShifted( pane ) )
{
shiftPos = 0;
}
else
{
// Scaled size (pixels) of a tic
float ticSize = _majorTic.ScaledTic( scaleFactor );
// if the scalelabels are on the inside, shift everything so the axis is drawn,
// for example, to the left side of the available space for a YAxis type
if ( _scale._isLabelsInside )
{
shiftPos += _tmpSpace;
// shift the axis to leave room for the outside tics
if ( _majorTic.IsOutside || _majorTic._isCrossOutside ||
_minorTic.IsOutside || _minorTic._isCrossOutside )
shiftPos -= ticSize;
}
else
{
// if it's not the primary axis, add a tic space for the spacing between axes
shiftPos += _axisGap * scaleFactor;
// if it has inside tics, leave another tic space
if ( _majorTic.IsInside || _majorTic._isCrossInside ||
_minorTic.IsInside || _minorTic._isCrossInside )
shiftPos += ticSize;
}
}
}
// shift is the position of the actual axis line itself
// everything else is based on that position.
float crossShift = CalcCrossShift( pane );
shiftPos += crossShift;
return shiftPos;
}
///
/// Calculate the space required (pixels) for this object.
///
///
/// This is the total space (vertical space for the X axis, horizontal space for
/// the Y axes) required to contain the axis. If is zero, then
/// this space will be the space required between the and
/// the . This method sets the internal values of
/// for use by the
/// method.
///
///
/// 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 amount of space (pixels) at the edge of the ChartRect
/// that is always required for this axis, even if the axis is shifted by the
/// value.
/// Returns the space, in pixels, required for this axis (between the
/// rect and ChartRect)
public float CalcSpace( Graphics g, GraphPane pane, float scaleFactor, out float fixedSpace )
{
//fixedSpace = 0;
//Typical character height for the scale font
float charHeight = _scale._fontSpec.GetHeight( scaleFactor );
// Scaled size (pixels) of a tic
float ticSize = _majorTic.ScaledTic( scaleFactor );
// Scaled size (pixels) of the axis gap
float axisGap = _axisGap * scaleFactor;
float scaledLabelGap = _scale._labelGap * charHeight;
float scaledTitleGap = _title.GetScaledGap( scaleFactor );
// The minimum amount of space to reserve for the NORMAL position of the axis. This would
// be the left side of the chart rect for the Y axis, the right side for the Y2 axis, etc.
// This amount of space is based on the need to reserve space for tics, etc., even if the
// Axis.Cross property causes the axis to be in a different location.
fixedSpace = 0;
// The actual space needed for this axis (ignoring the setting of Axis.Cross)
_tmpSpace = 0;
// Account for the Axis
if ( _isVisible )
{
bool hasTic = this.MajorTic.IsOutside || this.MajorTic._isCrossOutside ||
this.MinorTic.IsOutside || this.MinorTic._isCrossOutside;
// account for the tic space. Leave the tic space for any type of outside tic (Outside Tic Space)
if ( hasTic )
_tmpSpace += ticSize;
// if this is not the primary axis
if ( !IsPrimary( pane ) )
{
// always leave an extra tic space for the space between the multi-axes (Axis Gap)
_tmpSpace += axisGap;
// if it has inside tics, leave another tic space (Inside Tic Space)
if ( this.MajorTic._isInside || this.MajorTic._isCrossInside ||
this.MinorTic._isInside || this.MinorTic._isCrossInside )
_tmpSpace += ticSize;
}
// tic takes up 1x tic
// space between tic and scale label is 0.5 tic
// scale label is GetScaleMaxSpace()
// space between scale label and axis label is 0.5 tic
// account for the tic labels + 'LabelGap' tic gap between the tic and the label
_tmpSpace += _scale.GetScaleMaxSpace( g, pane, scaleFactor, true ).Height +
scaledLabelGap;
string str = MakeTitle();
// Only add space for the title if there is one
// Axis Title gets actual height
if ( str.Length > 0 && _title._isVisible )
{
//tmpSpace += this.TitleFontSpec.BoundingBox( g, str, scaleFactor ).Height;
fixedSpace = this.Title.FontSpec.BoundingBox( g, str, scaleFactor ).Height +
scaledTitleGap;
_tmpSpace += fixedSpace;
fixedSpace += scaledTitleGap;
}
if ( hasTic )
fixedSpace += ticSize;
}
// for the Y axes, make sure that enough space is left to fit the first
// and last X axis scale label
if ( this.IsPrimary( pane ) && ( (
( this is YAxis && (
( !pane.XAxis._scale._isSkipFirstLabel && !pane.XAxis._scale._isReverse ) ||
( !pane.XAxis._scale._isSkipLastLabel && pane.XAxis._scale._isReverse ) ) ) ||
( this is Y2Axis && (
( !pane.XAxis._scale._isSkipFirstLabel && pane.XAxis._scale._isReverse ) ||
( !pane.XAxis._scale._isSkipLastLabel && !pane.XAxis._scale._isReverse ) ) ) ) &&
pane.XAxis.IsVisible && pane.XAxis._scale._isVisible ) )
{
// half the width of the widest item, plus a gap of 1/2 the charheight
float tmp = pane.XAxis._scale.GetScaleMaxSpace( g, pane, scaleFactor, true ).Width / 2.0F;
//+ charHeight / 2.0F;
//if ( tmp > tmpSpace )
// tmpSpace = tmp;
fixedSpace = Math.Max( tmp, fixedSpace );
}
// Verify that the minSpace property was satisfied
_tmpSpace = Math.Max( _tmpSpace, _minSpace * (float)scaleFactor );
fixedSpace = Math.Max( fixedSpace, _minSpace * (float)scaleFactor );
return _tmpSpace;
}
///
/// Determines if this object is a "primary" one.
///
///
/// The primary axes are the (always), the first
/// in the
/// ( = 0), and the first
/// in the
/// ( = 0). Note that
/// and
/// always reference the primary axes.
///
///
/// A reference to the object that is the parent or
/// owner of this object.
///
/// true for a primary (for the ,
/// this is always true), false otherwise
abstract internal bool IsPrimary( GraphPane pane );
internal void FixZeroLine( Graphics g, GraphPane pane, float scaleFactor,
float left, float right )
{
// restore the zero line if needed (since the fill tends to cover it up)
if ( _isVisible && _majorGrid._isZeroLine &&
_scale._min < 0.0 && _scale._max > 0.0 )
{
float zeroPix = _scale.Transform( 0.0 );
using ( Pen zeroPen = new Pen( _color,
pane.ScaledPenWidth( _majorGrid._penWidth, scaleFactor ) ) )
{
g.DrawLine( zeroPen, left, zeroPix, right, zeroPix );
//zeroPen.Dispose();
}
}
}
///
/// Draw the minor tic marks 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 scale value for the first major tic position. This is the reference point
/// for all other tic marks.
///
/// 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.
///
///
/// 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.
///
public void DrawMinorTics( Graphics g, GraphPane pane, double baseVal, float shift,
float scaleFactor, float topPix )
{
if ( ( this.MinorTic.IsOutside || this.MinorTic.IsOpposite || this.MinorTic.IsInside ||
this.MinorTic._isCrossOutside || this.MinorTic._isCrossInside || _minorGrid._isVisible )
&& _isVisible )
{
double tMajor = _scale._majorStep * _scale.MajorUnitMultiplier,
tMinor = _scale._minorStep * _scale.MinorUnitMultiplier;
if ( _scale.IsLog || tMinor < tMajor )
{
float minorScaledTic = this.MinorTic.ScaledTic( scaleFactor );
// Minor tics start at the minimum value and step all the way thru
// the full scale. This means that if the minor step size is not
// an even division of the major step size, the minor tics won't
// line up with all of the scale labels and major tics.
double first = _scale._minLinTemp,
last = _scale._maxLinTemp;
double dVal = first;
float pixVal;
int iTic = _scale.CalcMinorStart( baseVal );
int MajorTic = 0;
double majorVal = _scale.CalcMajorTicValue( baseVal, MajorTic );
using ( Pen pen = new Pen( _minorTic._color,
pane.ScaledPenWidth( MinorTic._penWidth, scaleFactor ) ) )
using ( Pen minorGridPen = _minorGrid.GetPen( pane, scaleFactor ) )
{
// Draw the minor tic marks
while ( dVal < last && iTic < 5000 )
{
// Calculate the scale value for the current tic
dVal = _scale.CalcMinorTicValue( baseVal, iTic );
// Maintain a value for the current major tic
if ( dVal > majorVal )
majorVal = _scale.CalcMajorTicValue( baseVal, ++MajorTic );
// Make sure that the current value does not match up with a major tic
if ( ( ( Math.Abs( dVal ) < 1e-20 && Math.Abs( dVal - majorVal ) > 1e-20 ) ||
( Math.Abs( dVal ) > 1e-20 && Math.Abs( ( dVal - majorVal ) / dVal ) > 1e-10 ) ) &&
( dVal >= first && dVal <= last ) )
{
pixVal = _scale.LocalTransform( dVal );
_minorGrid.Draw( g, minorGridPen, pixVal, topPix );
_minorTic.Draw( g, pane, pen, pixVal, topPix, shift, minorScaledTic );
}
iTic++;
}
}
}
}
}
///
/// Draw the title for this .
///
/// On entry, it is assumed that the
/// graphics transform has been configured so that the origin is at the left side
/// of this axis, and the axis is aligned along the X coordinate direction.
///
///
/// 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 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.
///
public void DrawTitle( Graphics g, GraphPane pane, float shiftPos, float scaleFactor )
{
string str = MakeTitle();
// If the Axis is visible, draw the title
if ( _isVisible && _title._isVisible && str.Length > 0 )
{
bool hasTic = ( _scale._isLabelsInside ?
( this.MajorTic.IsInside || this.MajorTic._isCrossInside ||
this.MinorTic.IsInside || this.MinorTic._isCrossInside ) :
( this.MajorTic.IsOutside || this.MajorTic._isCrossOutside || this.MinorTic.IsOutside || this.MinorTic._isCrossOutside ) );
// Calculate the title position in screen coordinates
float x = ( _scale._maxPix - _scale._minPix ) / 2;
float scaledTic = MajorTic.ScaledTic( scaleFactor );
float scaledLabelGap = _scale._fontSpec.GetHeight( scaleFactor ) * _scale._labelGap;
float scaledTitleGap = _title.GetScaledGap( scaleFactor );
// The space for the scale labels is only reserved if the axis is not shifted due to the
// cross value. Note that this could be a problem if the axis is only shifted slightly,
// since the scale value labels may overlap the axis title. However, it's not possible to
// calculate that actual shift amount at this point, because the ChartRect rect has not yet been
// calculated, and the cross value is determined using a transform of scale values (which
// rely on ChartRect).
float gap = scaledTic * ( hasTic ? 1.0f : 0.0f ) +
this.Title.FontSpec.BoundingBox( g, str, scaleFactor ).Height / 2.0F;
float y = ( _scale._isVisible ? _scale.GetScaleMaxSpace( g, pane, scaleFactor, true ).Height
+ scaledLabelGap : 0 );
if ( _scale._isLabelsInside )
y = shiftPos - y - gap;
else
y = shiftPos + y + gap;
if ( !_crossAuto && !_title._isTitleAtCross )
y = Math.Max( y, gap );
AlignV alignV = AlignV.Center;
// Add in the TitleGap space
y += scaledTitleGap;
// Draw the title
this.Title.FontSpec.Draw( g, pane, str, x, y,
AlignH.Center, alignV, scaleFactor );
}
}
private string MakeTitle()
{
// Revision: JCarpenter 10/06
// Allow customization of the modified title when the scale is very large
// The event handler can edit the full label. If the handler returns
// null, then the title will be the default.
if ( ScaleTitleEvent != null )
{
string label = ScaleTitleEvent( this );
if ( label != null )
return label;
}
// If the Mag is non-zero and IsOmitMag == false, and IsLog == false,
// then add the mag indicator to the title.
if ( _scale._mag != 0 && !_title._isOmitMag && !_scale.IsLog )
return _title._text + String.Format( " (10^{0})", _scale._mag );
else
return _title._text;
}
///
/// Make a value label for the axis at the specified ordinal position.
///
///
/// This method properly accounts for ,
/// ,
/// and other axis format settings. It also implements the ScaleFormatEvent such that
/// custom labels can be created.
///
///
/// 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
internal string MakeLabelEventWorks( GraphPane pane, int index, double dVal )
{
// if there is a valid ScaleFormatEvent, then try to use it to create the label
// the label will be non-null if it's to be used
if ( this.ScaleFormatEvent != null )
{
string label;
label = this.ScaleFormatEvent( pane, this, dVal, index );
if ( label != null )
return label;
}
// second try. If there's no custom ScaleFormatEvent, then just call
// _scale.MakeLabel according to the type of scale
if ( this.Scale != null )
return _scale.MakeLabel( pane, index, dVal );
else
return "?";
}
#endregion
}
}