AIMS/DrawGraph/Graph/BarSettings.cs
2022-08-23 21:12:59 +08:00

484 lines
18 KiB
C#

//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright ?2006 John Champion
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//=============================================================================
using System;
using System.Drawing;
using System.Text;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace DrawGraph
{
/// <summary>
/// Class that handles the global settings for bar charts
/// </summary>
///
/// <author> John Champion </author>
/// <version> $Revision: 3.4 $ $Date: 2007/06/02 06:56:03 $ </version>
[Serializable]
public class BarSettings : ISerializable
{
#region Fields
/// <summary>Private field that determines the size of the gap between bar clusters
/// for bar charts. This gap is expressed as a fraction of the bar size (1.0 means
/// leave a 1-barwidth gap between clusters).
/// Use the public property <see cref="MinClusterGap"/> to access this value. </summary>
private float _minClusterGap;
/// <summary>Private field that determines the size of the gap between individual bars
/// within a bar cluster for bar charts. This gap is expressed as a fraction of the
/// bar size (1.0 means leave a 1-barwidth gap between each bar).
/// Use the public property <see cref="MinBarGap"/> to access this value. </summary>
private float _minBarGap;
/// <summary>Private field that determines the base axis from which <see cref="Bar"/>
/// graphs will be displayed. The base axis is the axis from which the bars grow with
/// increasing value. The value is of the enumeration type <see cref="ZedGraph.BarBase"/>.
/// To access this value, use the public property <see cref="Base"/>.
/// </summary>
/// <seealso cref="Default.Base"/>
private BarBase _base;
/// <summary>Private field that determines how the <see cref="BarItem"/>
/// graphs will be displayed. See the <see cref="ZedGraph.BarType"/> enum
/// for the individual types available.
/// To access this value, use the public property <see cref="Type"/>.
/// </summary>
/// <seealso cref="Default.Type"/>
private BarType _type;
/// <summary>Private field that determines the width of a bar cluster (for bar charts)
/// in user scale units. Normally, this value is 1.0 because bar charts are typically
/// <see cref="AxisType.Ordinal"/> or <see cref="AxisType.Text"/>, and the bars are
/// defined at ordinal values (1.0 scale units apart). For <see cref="AxisType.Linear"/>
/// or other scale types, you can use this value to scale the bars to an arbitrary
/// user scale. Use the public property <see cref="ClusterScaleWidth"/> to access this
/// value. </summary>
internal double _clusterScaleWidth;
/// <summary>
/// Private field that determines if the <see cref="ClusterScaleWidth" /> will be
/// calculated automatically. Use the public property <see cref="ClusterScaleWidthAuto" />
/// to access this value.
/// </summary>
internal bool _clusterScaleWidthAuto;
/// <summary>
/// private field that stores the owner GraphPane that contains this BarSettings instance.
/// </summary>
internal GraphPane _ownerPane;
#endregion
#region Constructors
/// <summary>
/// Constructor to build a <see cref="BarSettings" /> instance from the defaults.
/// </summary>
public BarSettings( GraphPane parentPane )
{
_minClusterGap = Default.MinClusterGap;
_minBarGap = Default.MinBarGap;
_clusterScaleWidth = Default.ClusterScaleWidth;
_clusterScaleWidthAuto = Default.ClusterScaleWidthAuto;
_base = Default.Base;
_type = Default.Type;
_ownerPane = parentPane;
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="rhs">the <see cref="BarSettings" /> instance to be copied.</param>
/// <param name="parentPane">The <see cref="GraphPane" /> that will be the
/// parent of this new BarSettings object.</param>
public BarSettings( BarSettings rhs, GraphPane parentPane )
{
_minClusterGap = rhs._minClusterGap;
_minBarGap = rhs._minBarGap;
_clusterScaleWidth = rhs._clusterScaleWidth;
_clusterScaleWidthAuto = rhs._clusterScaleWidthAuto;
_base = rhs._base;
_type = rhs._type;
_ownerPane = parentPane;
}
#endregion
#region Bar Properties
/// <summary>
/// The minimum space between <see cref="Bar"/> clusters, expressed as a
/// fraction of the bar size.
/// </summary>
/// <seealso cref="Default.MinClusterGap"/>
/// <seealso cref="MinBarGap"/>
/// <seealso cref="ClusterScaleWidth"/>
public float MinClusterGap
{
get { return _minClusterGap; }
set { _minClusterGap = value; }
}
/// <summary>
/// The minimum space between individual <see cref="Bar">Bars</see>
/// within a cluster, expressed as a
/// fraction of the bar size.
/// </summary>
/// <seealso cref="Default.MinBarGap"/>
/// <seealso cref="MinClusterGap"/>
/// <seealso cref="ClusterScaleWidth"/>
public float MinBarGap
{
get { return _minBarGap; }
set { _minBarGap = value; }
}
/// <summary>Determines the base axis from which <see cref="Bar"/>
/// graphs will be displayed.
/// </summary>
/// <remarks>The base axis is the axis from which the bars grow with
/// increasing value. The value is of the enumeration type <see cref="ZedGraph.BarBase"/>.
/// </remarks>
/// <seealso cref="Default.Base"/>
public BarBase Base
{
get { return _base; }
set { _base = value; }
}
/// <summary>Determines how the <see cref="BarItem"/>
/// graphs will be displayed. See the <see cref="ZedGraph.BarType"/> enum
/// for the individual types available.
/// </summary>
/// <seealso cref="Default.Type"/>
public BarType Type
{
get { return _type; }
set { _type = value; }
}
/// <summary>
/// The width of an individual bar cluster on a <see cref="Bar"/> graph.
/// This value only applies to bar graphs plotted on non-ordinal X axis
/// types (<see cref="AxisType.Linear"/>, <see cref="AxisType.Log"/>, and
/// <see cref="AxisType.Date"/>.
/// </summary>
/// <remarks>
/// This value can be calculated automatically if <see cref="ClusterScaleWidthAuto" />
/// is set to true. In this case, ClusterScaleWidth will be calculated if
/// <see cref="Base" /> refers to an <see cref="Axis" /> of a non-ordinal type
/// (<see cref="Scale.IsAnyOrdinal" /> is false). The ClusterScaleWidth is calculated
/// from the minimum difference found between any two points on the <see cref="Base" />
/// <see cref="Axis" /> for any <see cref="BarItem" /> in the
/// <see cref="GraphPane.CurveList" />. The ClusterScaleWidth is set automatically
/// each time <see cref="GraphPane.AxisChange()" /> is called. Calculations are
/// done by the <see cref="BarSettings.CalcClusterScaleWidth" /> method.
/// </remarks>
/// <seealso cref="Default.ClusterScaleWidth"/>
/// <seealso cref="ClusterScaleWidthAuto"/>
/// <seealso cref="MinBarGap"/>
/// <seealso cref="MinClusterGap"/>
public double ClusterScaleWidth
{
get { return _clusterScaleWidth; }
set { _clusterScaleWidth = value; _clusterScaleWidthAuto = false; }
}
/// <summary>
/// Gets or sets a property that determines if the <see cref="ClusterScaleWidth" /> will be
/// calculated automatically.
/// </summary>
/// <remarks>true for the <see cref="ClusterScaleWidth" /> to be calculated
/// automatically based on the available data, false otherwise. This value will
/// be set to false automatically if the <see cref="ClusterScaleWidth" /> value
/// is changed by the user.
/// </remarks>
/// <seealso cref="Default.ClusterScaleWidthAuto"/>
/// <seealso cref="ClusterScaleWidth"/>
public bool ClusterScaleWidthAuto
{
get { return _clusterScaleWidthAuto; }
set { _clusterScaleWidthAuto = value; }
}
#endregion
#region Serialization
/// <summary>
/// Current schema value that defines the version of the serialized file
/// </summary>
public const int schema = 10;
/// <summary>
/// Constructor for deserializing objects
/// </summary>
/// <remarks>
/// You MUST set the _ownerPane property after deserializing a BarSettings object.
/// </remarks>
/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the
/// serialized data
/// </param>
/// <param name="context">A <see cref="StreamingContext"/> instance that contains
/// the serialized data
/// </param>
internal BarSettings( SerializationInfo info, StreamingContext context )
{
// The schema value is just a file version parameter. You can use it to make future versions
// backwards compatible as new member variables are added to classes
int sch = info.GetInt32( "schema" );
_minClusterGap = info.GetSingle( "minClusterGap" );
_minBarGap = info.GetSingle( "minBarGap" );
_clusterScaleWidth = info.GetDouble( "clusterScaleWidth" );
_clusterScaleWidthAuto = info.GetBoolean( "clusterScaleWidthAuto" );
_base = (BarBase)info.GetValue( "base", typeof( BarBase ) );
_type = (BarType)info.GetValue( "type", typeof( BarType ) );
}
/// <summary>
/// Populates a <see cref="SerializationInfo"/> instance with the data needed to serialize the target object
/// </summary>
/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data</param>
/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data</param>
[SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true )]
public virtual void GetObjectData( SerializationInfo info, StreamingContext context )
{
info.AddValue( "schema", schema );
info.AddValue( "minClusterGap", _minClusterGap );
info.AddValue( "minBarGap", _minBarGap );
info.AddValue( "clusterScaleWidth", _clusterScaleWidth );
info.AddValue( "clusterScaleWidthAuto", _clusterScaleWidthAuto );
info.AddValue( "base", _base );
info.AddValue( "type", _type );
}
#endregion
#region Methods
/// <summary>
/// Calculate the width of an individual bar cluster on a <see cref="BarItem"/> graph.
/// This value only applies to bar graphs plotted on non-ordinal X axis
/// types (<see cref="Scale.IsAnyOrdinal" /> is false).
/// </summary>
/// <remarks>
/// This value can be calculated automatically if <see cref="ClusterScaleWidthAuto" />
/// is set to true. In this case, ClusterScaleWidth will be calculated if
/// <see cref="Base" /> refers to an <see cref="Axis" /> of a non-ordinal type
/// (<see cref="Scale.IsAnyOrdinal" /> is false). The ClusterScaleWidth is calculated
/// from the minimum difference found between any two points on the <see cref="Base" />
/// <see cref="Axis" /> for any <see cref="BarItem" /> in the
/// <see cref="GraphPane.CurveList" />. The ClusterScaleWidth is set automatically
/// each time <see cref="GraphPane.AxisChange()" /> is called.
/// </remarks>
/// <seealso cref="Default.ClusterScaleWidth"/>
/// <seealso cref="ClusterScaleWidthAuto"/>
/// <seealso cref="MinBarGap"/>
/// <seealso cref="MinClusterGap"/>
public void CalcClusterScaleWidth()
{
Axis baseAxis = BarBaseAxis();
// First, calculate the clusterScaleWidth for BarItem objects
if ( _clusterScaleWidthAuto && !baseAxis.Scale.IsAnyOrdinal )
{
double minStep = Double.MaxValue;
foreach ( CurveItem curve in _ownerPane.CurveList )
{
IPointList list = curve.Points;
if ( curve is BarItem )
{
double step = GetMinStepSize( curve.Points, baseAxis );
minStep = step < minStep ? step : minStep;
}
}
if ( minStep == Double.MaxValue )
minStep = 1.0;
_clusterScaleWidth = minStep;
}
// Second, calculate the sizes of any HiLowBarItem and JapaneseCandleStickItem objects
foreach ( CurveItem curve in _ownerPane.CurveList )
{
IPointList list = curve.Points;
if ( curve is HiLowBarItem &&
(curve as HiLowBarItem).Bar.IsAutoSize )
{
( curve as HiLowBarItem ).Bar._userScaleSize =
GetMinStepSize( list, baseAxis );
}
else if ( curve is JapaneseCandleStickItem &&
(curve as JapaneseCandleStickItem).Stick.IsAutoSize )
{
( curve as JapaneseCandleStickItem ).Stick._userScaleSize =
GetMinStepSize( list, baseAxis );
}
}
}
/// <summary>
/// Determine the minimum increment between individual points to be used for
/// calculating a bar size that fits without overlapping
/// </summary>
/// <param name="list">The <see cref="IPointList" /> list of points for the bar
/// of interest</param>
/// <param name="baseAxis">The base axis for the bar</param>
/// <returns>The minimum increment between bars along the base axis</returns>
internal static double GetMinStepSize( IPointList list, Axis baseAxis )
{
double minStep = Double.MaxValue;
if ( list.Count <= 0 || baseAxis._scale.IsAnyOrdinal )
return 1.0;
PointPair lastPt = list[0];
for ( int i = 1; i < list.Count; i++ )
{
PointPair pt = list[i];
if ( !pt.IsInvalid || !lastPt.IsInvalid )
{
double step;
if ( baseAxis is XAxis || baseAxis is X2Axis )
step = pt.X - lastPt.X;
else
step = pt.Y - lastPt.Y;
if ( step > 0 && step < minStep )
minStep = step;
}
lastPt = pt;
}
double range = baseAxis.Scale._maxLinearized - baseAxis.Scale._minLinearized;
if ( range <= 0 )
minStep = 1.0;
else if ( minStep <= 0 || minStep < 0.001 * range || minStep > range )
minStep = 0.1 * range;
return minStep;
}
/// <summary>
/// Determine the width, in screen pixel units, of each bar cluster including
/// the cluster gaps and bar gaps.
/// </summary>
/// <remarks>This method calls the <see cref="Scale.GetClusterWidth(GraphPane)"/>
/// method for the base <see cref="Axis"/> for <see cref="Bar"/> graphs
/// (the base <see cref="Axis"/> is assigned by the <see cref="ZedGraph.BarSettings.Base"/>
/// property).
/// </remarks>
/// <seealso cref="ZedGraph.BarBase"/>
/// <seealso cref="ZedGraph.BarSettings"/>
/// <seealso cref="Scale.GetClusterWidth(GraphPane)"/>
/// <seealso cref="ZedGraph.BarSettings.Type"/>
/// <returns>The width of each bar cluster, in pixel units</returns>
public float GetClusterWidth()
{
return BarBaseAxis()._scale.GetClusterWidth( _ownerPane );
}
/// <summary>
/// Determine the <see cref="Axis"/> from which the <see cref="Bar"/> charts are based.
/// </summary>
/// <seealso cref="ZedGraph.BarBase"/>
/// <seealso cref="BarSettings"/>
/// <seealso cref="ZedGraph.BarSettings.Base"/>
/// <seealso cref="Scale.GetClusterWidth(GraphPane)"/>
/// <returns>The <see cref="Axis"/> class for the axis from which the bars are based</returns>
public Axis BarBaseAxis()
{
Axis barAxis;
if ( _base == BarBase.Y )
barAxis = _ownerPane.YAxis;
else if ( _base == BarBase.Y2 )
barAxis = _ownerPane.Y2Axis;
else if ( _base == BarBase.X2 )
barAxis = _ownerPane.X2Axis;
else
barAxis = _ownerPane.XAxis;
return barAxis;
}
#endregion
#region Defaults
/// <summary>
/// A simple struct that defines the
/// default property values for the <see cref="BarSettings"/> class.
/// </summary>
public struct Default
{
/// <summary>
/// The default dimension gap between clusters of bars on a
/// <see cref="Bar"/> graph.
/// This dimension is expressed in terms of the normal bar width.
/// </summary>
/// <seealso cref="Default.MinBarGap"/>
/// <seealso cref="BarSettings.MinClusterGap"/>
public static float MinClusterGap = 1.0F;
/// <summary>
/// The default dimension gap between each individual bar within a bar cluster
/// on a <see cref="Bar"/> graph.
/// This dimension is expressed in terms of the normal bar width.
/// </summary>
/// <seealso cref="Default.MinClusterGap"/>
/// <seealso cref="BarSettings.MinBarGap"/>
public static float MinBarGap = 0.2F;
/// <summary>The default value for the <see cref="BarSettings.Base"/>, which determines the base
/// <see cref="Axis"/> from which the <see cref="Bar"/> graphs will be displayed.
/// </summary>
/// <seealso cref="BarSettings.Base"/>
public static BarBase Base = BarBase.X;
/// <summary>The default value for the <see cref="BarSettings.Type"/> property, which
/// determines if the bars are drawn overlapping eachother in a "stacked" format,
/// or side-by-side in a "cluster" format. See the <see cref="ZedGraph.BarType"/>
/// for more information.
/// </summary>
/// <seealso cref="BarSettings.Type"/>
public static BarType Type = BarType.Cluster;
/// <summary>
/// The default width of a bar cluster
/// on a <see cref="Bar"/> graph. This value only applies to
/// <see cref="Bar"/> graphs, and only when the
/// <see cref="Axis.Type"/> is <see cref="AxisType.Linear"/>,
/// <see cref="AxisType.Log"/> or <see cref="AxisType.Date"/>.
/// This dimension is expressed in terms of X scale user units.
/// </summary>
/// <seealso cref="Default.MinClusterGap"/>
/// <seealso cref="BarSettings.MinBarGap"/>
public static double ClusterScaleWidth = 1.0;
/// <summary>
/// The default value for <see cref="BarSettings.ClusterScaleWidthAuto" />.
/// </summary>
public static bool ClusterScaleWidthAuto = true;
}
#endregion
}
}