Files
Pat Tullmann 0cb742dafb binfmt-detector-cli: rewrite to support PE32+ binaries (#38)
Rewrite with hard-coded offsets into the PE file format to discern
if a binary is PE32 or PE32+, and then to determine if it contains
a "CLR Data Directory" entry that looks valid.

Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native
binaries, and a random assortment of garbage files.

Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
2023-10-16 20:16:47 +02:00

3158 lines
102 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//-------------------------------------------------------------
// <copyright company=Microsoft Corporation>
// Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
// File: ChartArea.cs
//
// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
//
// Classes: ChartArea
//
// Purpose: The ChartArea class represents one chart area within
// a chart image, and is used to plot one or more chart
// series. The number of chart series that can be plotted
// in a chart area is unlimited.
//
// Reviewed: GS - August 6, 2002
// AG - August 7, 2002
// AG - Microsoft 16, 2007
//
//===================================================================
#region Used namespaces
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Data;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
#if Microsoft_CONTROL
using System.Windows.Forms.DataVisualization.Charting.Data;
using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
using System.Windows.Forms.DataVisualization.Charting.Utilities;
using System.Windows.Forms.DataVisualization.Charting.Borders3D;
using System.Windows.Forms.DataVisualization.Charting;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
using System.Windows.Forms.Design;
#else
using System.Web;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.DataVisualization.Charting.Data;
using System.Web.UI.DataVisualization.Charting.ChartTypes;
using System.Web.UI.DataVisualization.Charting.Utilities;
using System.Web.UI.DataVisualization.Charting.Borders3D;
#endif
#endregion
#if Microsoft_CONTROL
namespace System.Windows.Forms.DataVisualization.Charting
#else
namespace System.Web.UI.DataVisualization.Charting
#endif
{
#region Chart area aligment enumerations
/// <summary>
/// An enumeration of the alignment orientations of a ChartArea
/// </summary>
[Flags]
public enum AreaAlignmentOrientations
{
/// <summary>
/// Chart areas are not automatically aligned.
/// </summary>
None = 0,
/// <summary>
/// Chart areas are aligned vertically.
/// </summary>
Vertical = 1,
/// <summary>
/// Chart areas are aligned horizontally.
/// </summary>
Horizontal = 2,
/// <summary>
/// Chart areas are aligned using all values (horizontally and vertically).
/// </summary>
All = Vertical | Horizontal
}
/// <summary>
/// An enumeration of the alignment styles of a ChartArea
/// </summary>
[Flags]
public enum AreaAlignmentStyles
{
/// <summary>
/// Chart areas are not automatically aligned.
/// </summary>
None = 0,
/// <summary>
/// Chart areas are aligned by positions.
/// </summary>
Position = 1,
/// <summary>
/// Chart areas are aligned by inner plot positions.
/// </summary>
PlotPosition = 2,
/// <summary>
/// Chart areas are aligned by axes views.
/// </summary>
AxesView = 4,
#if Microsoft_CONTROL
/// <summary>
/// Cursor and Selection alignment.
/// </summary>
Cursor = 8,
/// <summary>
/// Complete alignment.
/// </summary>
All = Position | PlotPosition | Cursor | AxesView
#else // Microsoft_CONTROL
/// <summary>
/// Complete alignment.
/// </summary>
All = Position | PlotPosition | AxesView
#endif // Microsoft_CONTROL
}
#endregion
/// <summary>
/// The ChartArea class is used to create and display a chart
/// area within a chart image. The chart area is a rectangular
/// area on a chart image. It has 4 axes, horizontal and vertical grids.
/// A chart area can contain more than one different chart type.
/// The number of chart series that can be plotted in a chart area
/// is unlimited.
///
/// ChartArea class exposes all the properties and methods
/// of its base ChartArea3D class.
/// </summary>
[
DefaultProperty("Axes"),
SRDescription("DescriptionAttributeChartArea_ChartArea"),
]
#if ASPPERM_35
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
#endif
public partial class ChartArea : ChartNamedElement
{
#region Chart Area Fields
/// <summary>
/// Plot area position
/// </summary>
internal ElementPosition PlotAreaPosition;
// Private data members, which store properties values
private Axis[] _axisArray = new Axis[4];
private Color _backColor = Color.Empty;
private ChartHatchStyle _backHatchStyle = ChartHatchStyle.None;
private string _backImage = "";
private ChartImageWrapMode _backImageWrapMode = ChartImageWrapMode.Tile;
private Color _backImageTransparentColor = Color.Empty;
private ChartImageAlignmentStyle _backImageAlignment = ChartImageAlignmentStyle.TopLeft;
private GradientStyle _backGradientStyle = GradientStyle.None;
private Color _backSecondaryColor = Color.Empty;
private Color _borderColor = Color.Black;
private int _borderWidth = 1;
private ChartDashStyle _borderDashStyle = ChartDashStyle.NotSet;
private int _shadowOffset = 0;
private Color _shadowColor = Color.FromArgb(128, 0, 0, 0);
private ElementPosition _areaPosition = null;
private ElementPosition _innerPlotPosition = null;
internal int IterationCounter = 0;
private bool _isSameFontSizeForAllAxes = false;
internal float axesAutoFontSize = 8f;
private string _alignWithChartArea = Constants.NotSetValue;
private AreaAlignmentOrientations _alignmentOrientation = AreaAlignmentOrientations.Vertical;
private AreaAlignmentStyles _alignmentStyle = AreaAlignmentStyles.All;
private int _circularSectorNumber = int.MinValue;
private int _circularUsePolygons = int.MinValue;
// Flag indicates that chart area is acurrently aligned
internal bool alignmentInProcess = false;
// Chart area position before adjustments
internal RectangleF originalAreaPosition = RectangleF.Empty;
// Chart area inner plot position before adjustments
internal RectangleF originalInnerPlotPosition = RectangleF.Empty;
// Chart area position before adjustments
internal RectangleF lastAreaPosition = RectangleF.Empty;
// Center of the circulat chart area
internal PointF circularCenter = PointF.Empty;
private ArrayList _circularAxisList = null;
#if Microsoft_CONTROL
// Buffered plotting area image
internal Bitmap areaBufferBitmap = null;
private Cursor _cursorX = new Cursor();
private Cursor _cursorY = new Cursor();
#endif
// Area SmartLabel class
internal SmartLabel smartLabels = new SmartLabel();
// Gets or sets a flag that specifies whether the chart area is visible.
private bool _visible = true;
#endregion
#region Chart Area Cursor properties
#if Microsoft_CONTROL
/// <summary>
/// Gets or sets a Cursor object that is used for cursors and selected ranges along the X-axis.
/// </summary>
[
SRCategory("CategoryAttributeCursor"),
Bindable(true),
DefaultValue(null),
SRDescription("DescriptionAttributeChartArea_CursorX"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
TypeConverter(typeof(NoNameExpandableObjectConverter)),
]
public Cursor CursorX
{
get
{
return _cursorX;
}
set
{
_cursorX = value;
// Initialize chart object
_cursorX.Initialize(this, AxisName.X);
}
}
/// <summary>
/// Gets or sets a Cursor object that is used for cursors and selected ranges along the Y-axis.
/// </summary>
[
SRCategory("CategoryAttributeCursor"),
Bindable(true),
DefaultValue(null),
SRDescription("DescriptionAttributeChartArea_CursorY"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
TypeConverter(typeof(NoNameExpandableObjectConverter)),
]
public Cursor CursorY
{
get
{
return _cursorY;
}
set
{
_cursorY = value;
// Initialize chart object
_cursorY.Initialize(this, AxisName.Y);
}
}
#endif // Microsoft_CONTROL
#endregion
#region Chart Area properties
/// <summary>
/// Gets or sets a flag that specifies whether the chart area is visible.
/// </summary>
/// <remarks>
/// When this flag is set to false, all series, legends, titles and annotation objects
/// associated with the chart area will also be hidden.
/// </remarks>
/// <value>
/// <b>True</b> if the chart area is visible; <b>false</b> otherwise.
/// </value>
[
SRCategory("CategoryAttributeAppearance"),
DefaultValue(true),
SRDescription("DescriptionAttributeChartArea_Visible"),
ParenthesizePropertyNameAttribute(true),
]
virtual public bool Visible
{
get
{
return _visible;
}
set
{
_visible = value;
this.Invalidate();
}
}
/// <summary>
/// Gets or sets the name of the ChartArea object to which this chart area should be aligned.
/// </summary>
[
SRCategory("CategoryAttributeAlignment"),
Bindable(true),
DefaultValue(Constants.NotSetValue),
SRDescription("DescriptionAttributeChartArea_AlignWithChartArea"),
TypeConverter(typeof(LegendAreaNameConverter)),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public string AlignWithChartArea
{
get
{
return _alignWithChartArea;
}
set
{
if (value != _alignWithChartArea)
{
if (String.IsNullOrEmpty(value))
{
_alignWithChartArea = Constants.NotSetValue;
}
else
{
if (Chart != null && Chart.ChartAreas != null)
{
Chart.ChartAreas.VerifyNameReference(value);
}
_alignWithChartArea = value;
}
Invalidate();
}
}
}
/// <summary>
/// Gets or sets the alignment orientation of a chart area.
/// </summary>
[
SRCategory("CategoryAttributeAlignment"),
Bindable(true),
DefaultValue(AreaAlignmentOrientations.Vertical),
SRDescription("DescriptionAttributeChartArea_AlignOrientation"),
Editor(Editors.FlagsEnumUITypeEditor.Editor, Editors.FlagsEnumUITypeEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public AreaAlignmentOrientations AlignmentOrientation
{
get
{
return _alignmentOrientation;
}
set
{
_alignmentOrientation = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the alignment style of the ChartArea.
/// </summary>
[
SRCategory("CategoryAttributeAlignment"),
Bindable(true),
DefaultValue(AreaAlignmentStyles.All),
SRDescription("DescriptionAttributeChartArea_AlignType"),
Editor(Editors.FlagsEnumUITypeEditor.Editor, Editors.FlagsEnumUITypeEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public AreaAlignmentStyles AlignmentStyle
{
get
{
return _alignmentStyle;
}
set
{
_alignmentStyle = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets an array that represents all axes for a chart area.
/// </summary>
[
SRCategory("CategoryAttributeAxes"),
Bindable(true),
SRDescription("DescriptionAttributeChartArea_Axes"),
TypeConverter(typeof(AxesArrayConverter)),
Editor(Editors.AxesArrayEditor.Editor, Editors.AxesArrayEditor.Base),
DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
SerializationVisibilityAttribute(SerializationVisibility.Hidden)
]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public Axis[] Axes
{
get
{
return _axisArray;
}
set
{
AxisX = value[0];
AxisY = value[1];
AxisX2 = value[2];
AxisY2 = value[3];
Invalidate();
}
}
/// <summary>
/// Avoid serialization of the axes array
/// </summary>
[EditorBrowsableAttribute(EditorBrowsableState.Never)]
internal bool ShouldSerializeAxes()
{
return false;
}
/// <summary>
/// Gets or sets an Axis object that represents the primary Y-axis.
/// </summary>
[
SRCategory("CategoryAttributeAxis"),
Bindable(true),
Browsable(false),
SRDescription("DescriptionAttributeChartArea_AxisY"),
#if Microsoft_CONTROL
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
#else
PersistenceMode(PersistenceMode.InnerProperty),
#endif
TypeConverter(typeof(NoNameExpandableObjectConverter))
]
public Axis AxisY
{
get
{
return axisY;
}
set
{
axisY = value;
axisY.Initialize(this, AxisName.Y);
_axisArray[1] = axisY;
Invalidate();
}
}
/// <summary>
/// Gets or sets an Axis object that represents the primary X-axis.
/// </summary>
[
SRCategory("CategoryAttributeAxis"),
Bindable(true),
Browsable(false),
SRDescription("DescriptionAttributeChartArea_AxisX"),
#if Microsoft_CONTROL
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
#else
PersistenceMode(PersistenceMode.InnerProperty),
#endif
TypeConverter(typeof(NoNameExpandableObjectConverter))
]
public Axis AxisX
{
get
{
return axisX;
}
set
{
axisX = value;
axisX.Initialize(this, AxisName.X);
_axisArray[0] = axisX;
Invalidate();
}
}
/// <summary>
/// Gets or sets an Axis object that represents the secondary X-axis.
/// </summary>
[
SRCategory("CategoryAttributeAxis"),
Bindable(true),
Browsable(false),
SRDescription("DescriptionAttributeChartArea_AxisX2"),
#if Microsoft_CONTROL
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
#else
PersistenceMode(PersistenceMode.InnerProperty),
#endif
TypeConverter(typeof(NoNameExpandableObjectConverter))
]
public Axis AxisX2
{
get
{
return axisX2;
}
set
{
axisX2 = value;
axisX2.Initialize(this, AxisName.X2);
_axisArray[2] = axisX2;
Invalidate();
}
}
/// <summary>
/// Gets or sets an Axis object that represents the secondary Y-axis.
/// </summary>
[
SRCategory("CategoryAttributeAxis"),
Bindable(true),
Browsable(false),
SRDescription("DescriptionAttributeChartArea_AxisY2"),
#if Microsoft_CONTROL
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
#else
PersistenceMode(PersistenceMode.InnerProperty),
#endif
TypeConverter(typeof(NoNameExpandableObjectConverter))
]
public Axis AxisY2
{
get
{
return axisY2;
}
set
{
axisY2 = value;
axisY2.Initialize(this, AxisName.Y2);
_axisArray[3] = axisY2;
Invalidate();
}
}
/// <summary>
/// Gets or sets an ElementPosition object, which defines the position of a chart area object within the chart image.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
SRDescription("DescriptionAttributeChartArea_Position"),
#if Microsoft_CONTROL
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
#else
PersistenceMode(PersistenceMode.InnerProperty),
#endif
NotifyParentPropertyAttribute(true),
TypeConverter(typeof(ElementPositionConverter)),
SerializationVisibilityAttribute(SerializationVisibility.Element)
]
public ElementPosition Position
{
get
{
// Serialize only position values if Auto set to false
if(this.Chart != null && this.Chart.serializationStatus == SerializationStatus.Saving )
{
if(_areaPosition.Auto)
{
return new ElementPosition();
}
else
{
ElementPosition newPosition = new ElementPosition();
#if Microsoft_CONTROL
newPosition.Auto = false;
#else
newPosition.Auto = true;
#endif
newPosition.SetPositionNoAuto(_areaPosition.X, _areaPosition.Y, _areaPosition.Width, _areaPosition.Height);
return newPosition;
}
}
return _areaPosition;
}
set
{
_areaPosition = value;
_areaPosition.Parent = this;
_areaPosition.resetAreaAutoPosition = true;
Invalidate();
}
}
/// <summary>
/// Determoines if this position should be serialized.
/// </summary>
/// <returns></returns>
internal bool ShouldSerializePosition()
{
return !this.Position.Auto;
}
/// <summary>
/// Gets or sets an ElementPosition object, which defines the inner plot position of a chart area object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
SRDescription("DescriptionAttributeChartArea_InnerPlotPosition"),
#if Microsoft_CONTROL
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
#else
PersistenceMode(PersistenceMode.InnerProperty),
#endif
NotifyParentPropertyAttribute(true),
TypeConverter(typeof(ElementPositionConverter)),
SerializationVisibilityAttribute(SerializationVisibility.Element)
]
public ElementPosition InnerPlotPosition
{
get
{
// Serialize only position values if Auto set to false
if (this.Common != null && this.Common.Chart != null && this.Common.Chart.serializationStatus == SerializationStatus.Saving)
{
if(_innerPlotPosition.Auto)
{
return new ElementPosition();
}
else
{
ElementPosition newPosition = new ElementPosition();
#if Microsoft_CONTROL
newPosition.Auto = false;
#else
newPosition.Auto = true;
#endif
newPosition.SetPositionNoAuto(_innerPlotPosition.X, _innerPlotPosition.Y, _innerPlotPosition.Width, _innerPlotPosition.Height);
return newPosition;
}
}
return _innerPlotPosition;
}
set
{
_innerPlotPosition = value;
_innerPlotPosition.Parent = this;
Invalidate();
}
}
/// <summary>
/// Determoines if this position should be serialized.
/// </summary>
/// <returns></returns>
internal bool ShouldSerializeInnerPlotPosition()
{
return !this.InnerPlotPosition.Auto;
}
/// <summary>
/// Gets or sets the background color of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(typeof(Color), ""),
SRDescription("DescriptionAttributeBackColor"),
NotifyParentPropertyAttribute(true),
TypeConverter(typeof(ColorConverter)),
Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public Color BackColor
{
get
{
return _backColor;
}
set
{
_backColor = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the hatching style of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(ChartHatchStyle.None),
NotifyParentPropertyAttribute(true),
SRDescription("DescriptionAttributeBackHatchStyle"),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute),
#endif
Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
]
public ChartHatchStyle BackHatchStyle
{
get
{
return _backHatchStyle;
}
set
{
_backHatchStyle = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the background image of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(""),
SRDescription("DescriptionAttributeBackImage"),
Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute),
#endif
NotifyParentPropertyAttribute(true)
]
public string BackImage
{
get
{
return _backImage;
}
set
{
_backImage = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the drawing mode of the background image of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(ChartImageWrapMode.Tile),
NotifyParentPropertyAttribute(true),
SRDescription("DescriptionAttributeImageWrapMode"),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public ChartImageWrapMode BackImageWrapMode
{
get
{
return _backImageWrapMode;
}
set
{
_backImageWrapMode = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the color of a ChartArea object's background image that will be drawn as transparent.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(typeof(Color), ""),
NotifyParentPropertyAttribute(true),
SRDescription("DescriptionAttributeImageTransparentColor"),
TypeConverter(typeof(ColorConverter)),
Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public Color BackImageTransparentColor
{
get
{
return _backImageTransparentColor;
}
set
{
_backImageTransparentColor = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the alignment of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(ChartImageAlignmentStyle.TopLeft),
NotifyParentPropertyAttribute(true),
SRDescription("DescriptionAttributeBackImageAlign"),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public ChartImageAlignmentStyle BackImageAlignment
{
get
{
return _backImageAlignment;
}
set
{
_backImageAlignment = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the orientation of a chart element's gradient,
/// and also determines whether or not a gradient is used.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(GradientStyle.None),
NotifyParentPropertyAttribute(true),
SRDescription("DescriptionAttributeBackGradientStyle"),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute),
#endif
Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
]
public GradientStyle BackGradientStyle
{
get
{
return _backGradientStyle;
}
set
{
_backGradientStyle = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the secondary color of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(typeof(Color), ""),
NotifyParentPropertyAttribute(true),
SRDescription("DescriptionAttributeBackSecondaryColor"),
TypeConverter(typeof(ColorConverter)),
Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public Color BackSecondaryColor
{
get
{
return _backSecondaryColor;
}
set
{
_backSecondaryColor = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the shadow color of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(typeof(Color), "128,0,0,0"),
SRDescription("DescriptionAttributeShadowColor"),
NotifyParentPropertyAttribute(true),
TypeConverter(typeof(ColorConverter)),
Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public Color ShadowColor
{
get
{
return _shadowColor;
}
set
{
_shadowColor = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the shadow offset (in pixels) of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(0),
SRDescription("DescriptionAttributeShadowOffset"),
NotifyParentPropertyAttribute(true),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public int ShadowOffset
{
get
{
return _shadowOffset;
}
set
{
_shadowOffset = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the border color of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(typeof(Color), "Black"),
SRDescription("DescriptionAttributeBorderColor"),
NotifyParentPropertyAttribute(true),
TypeConverter(typeof(ColorConverter)),
Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public Color BorderColor
{
get
{
return _borderColor;
}
set
{
_borderColor = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the border width of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(1),
SRDescription("DescriptionAttributeBorderWidth"),
NotifyParentPropertyAttribute(true),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public int BorderWidth
{
get
{
return _borderWidth;
}
set
{
if(value < 0)
{
throw (new ArgumentOutOfRangeException("value", SR.ExceptionBorderWidthIsNegative));
}
_borderWidth = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the style of the border line of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(ChartDashStyle.NotSet),
SRDescription("DescriptionAttributeBorderDashStyle"),
NotifyParentPropertyAttribute(true),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public ChartDashStyle BorderDashStyle
{
get
{
return _borderDashStyle;
}
set
{
_borderDashStyle = value;
Invalidate();
}
}
/// <summary>
/// Gets or sets the unique name of a ChartArea object.
/// </summary>
[
SRCategory("CategoryAttributeMisc"),
Bindable(true),
SRDescription("DescriptionAttributeChartArea_Name"),
NotifyParentPropertyAttribute(true),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public override string Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
/// <summary>
/// Gets or sets a Boolean that determines if the labels of the axes for all chart area
/// , which have LabelsAutoFit property set to true, are of equal size.
/// </summary>
[
SRCategory("CategoryAttributeAppearance"),
Bindable(true),
DefaultValue(false),
SRDescription("DescriptionAttributeChartArea_EquallySizedAxesFont"),
NotifyParentPropertyAttribute(true),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public bool IsSameFontSizeForAllAxes
{
get
{
return _isSameFontSizeForAllAxes;
}
set
{
_isSameFontSizeForAllAxes = value;
Invalidate();
}
}
#endregion
#region Constructors
/// <summary>
/// ChartArea constructor.
/// </summary>
public ChartArea()
{
Initialize();
}
/// <summary>
/// ChartArea constructor.
/// </summary>
/// <param name="name">The name.</param>
public ChartArea(string name) : base(name)
{
Initialize();
}
#endregion
#region Chart Area Methods
/// <summary>
/// Restores series order and X axis reversed mode for the 3D charts.
/// </summary>
internal void Restore3DAnglesAndReverseMode()
{
if(this.Area3DStyle.Enable3D && !this.chartAreaIsCurcular)
{
// Restore axis "IsReversed" property and old Y angle
this.AxisX.IsReversed = oldReverseX;
this.AxisX2.IsReversed = oldReverseX;
this.AxisY.IsReversed = oldReverseY;
this.AxisY2.IsReversed = oldReverseY;
this.Area3DStyle.Rotation = oldYAngle;
}
}
/// <summary>
/// Sets series order and X axis reversed mode for the 3D charts.
/// </summary>
internal void Set3DAnglesAndReverseMode()
{
// Clear series reversed flag
_reverseSeriesOrder = false;
// If 3D charting is enabled
if(this.Area3DStyle.Enable3D)
{
// Make sure primary & secondary axis has the same IsReversed settings
// This is a limitation for the 3D chart required for labels drawing.
this.AxisX2.IsReversed = this.AxisX.IsReversed;
this.AxisY2.IsReversed = this.AxisY.IsReversed;
// Remember reversed order of X & Y axis and Angles
oldReverseX = this.AxisX.IsReversed;
oldReverseY = this.AxisY.IsReversed;
oldYAngle = this.Area3DStyle.Rotation;
// Check if Y angle
if(this.Area3DStyle.Rotation > 90 || this.Area3DStyle.Rotation < -90)
{
// This method depends on the 'switchValueAxes' field which is calculated based on the chart types
// of the series associated with the chart area. We need to call SetData method to make sure this field
// is correctly initialized. Because we only need to collect information about the series, we pass 'false'
// as parameters to limit the amount of work this function does.
this.SetData(false, false);
// Reversed series order
_reverseSeriesOrder = true;
// Reversed primary and secondary X axis
if(!this.switchValueAxes)
{
this.AxisX.IsReversed = !this.AxisX.IsReversed;
this.AxisX2.IsReversed = !this.AxisX2.IsReversed;
}
// Reversed primary and secondary Y axis for chart types like Bar
else
{
this.AxisY.IsReversed = !this.AxisY.IsReversed;
this.AxisY2.IsReversed = !this.AxisY2.IsReversed;
}
// Adjust Y angle
if(this.Area3DStyle.Rotation > 90)
{
this.Area3DStyle.Rotation = (this.Area3DStyle.Rotation - 90) - 90;
}
else if(this.Area3DStyle.Rotation < -90)
{
this.Area3DStyle.Rotation = (this.Area3DStyle.Rotation + 90) + 90;
}
}
}
}
/// <summary>
/// Save all automatic values like Minimum and Maximum.
/// </summary>
internal void SetTempValues()
{
// Save non automatic area position
if(!this.Position.Auto)
{
this.originalAreaPosition = this.Position.ToRectangleF();
}
// Save non automatic area inner plot position
if(!this.InnerPlotPosition.Auto)
{
this.originalInnerPlotPosition = this.InnerPlotPosition.ToRectangleF();
}
this._circularSectorNumber = int.MinValue;
this._circularUsePolygons = int.MinValue;
this._circularAxisList = null;
// Save Minimum and maximum values for all axes
axisX.StoreAxisValues();
axisX2.StoreAxisValues();
axisY.StoreAxisValues();
axisY2.StoreAxisValues();
}
/// <summary>
/// Load all automatic values like Minimum and Maximum with original values.
/// </summary>
internal void GetTempValues()
{
// Take Minimum and maximum values for all axes
axisX.ResetAxisValues();
axisX2.ResetAxisValues();
axisY.ResetAxisValues();
axisY2.ResetAxisValues();
// Restore non automatic area position
if(!this.originalAreaPosition.IsEmpty)
{
this.lastAreaPosition = this.Position.ToRectangleF();
this.Position.SetPositionNoAuto(this.originalAreaPosition.X, this.originalAreaPosition.Y, this.originalAreaPosition.Width, this.originalAreaPosition.Height);
this.originalAreaPosition = RectangleF.Empty;
}
// Save non automatic area inner plot position
if(!this.originalInnerPlotPosition.IsEmpty)
{
this.InnerPlotPosition.SetPositionNoAuto(this.originalInnerPlotPosition.X, this.originalInnerPlotPosition.Y, this.originalInnerPlotPosition.Width, this.originalInnerPlotPosition.Height);
this.originalInnerPlotPosition = RectangleF.Empty;
}
}
/// <summary>
/// Initialize Chart area and axes
/// </summary>
internal void Initialize()
{
// Initialize 3D style class
_area3DStyle = new ChartArea3DStyle(this);
// Create axes for this chart area.
axisY = new Axis( );
axisX = new Axis( );
axisX2 = new Axis( );
axisY2 = new Axis( );
// Initialize axes;
axisX.Initialize(this, AxisName.X);
axisY.Initialize(this, AxisName.Y);
axisX2.Initialize(this, AxisName.X2);
axisY2.Initialize(this, AxisName.Y2);
// Initialize axes array
_axisArray[0] = axisX;
_axisArray[1] = axisY;
_axisArray[2] = axisX2;
_axisArray[3] = axisY2;
// Set flag to reset auto values for all areas
_areaPosition = new ElementPosition(this);
_areaPosition.resetAreaAutoPosition = true;
_innerPlotPosition = new ElementPosition(this);
// Set the position of the new chart area
if( PlotAreaPosition == null )
{
PlotAreaPosition = new ElementPosition(this);
}
#if Microsoft_CONTROL
// Initialize cursor class
this._cursorX.Initialize(this, AxisName.X);
this._cursorY.Initialize(this, AxisName.Y);
#endif // Microsoft_CONTROL
}
/// <summary>
/// Minimum and maximum do not have to be calculated
/// from data series every time. It is very time
/// consuming. Minimum and maximum are buffered
/// and only when this flags are set Minimum and
/// Maximum are refreshed from data.
/// </summary>
internal void ResetMinMaxFromData()
{
_axisArray[0].refreshMinMaxFromData = true;
_axisArray[1].refreshMinMaxFromData = true;
_axisArray[2].refreshMinMaxFromData = true;
_axisArray[3].refreshMinMaxFromData = true;
}
/// <summary>
/// Recalculates the axes scale of a chart area.
/// </summary>
public void RecalculateAxesScale()
{
// Read axis Max/Min from data
ResetMinMaxFromData();
#if Microsoft_CONTROL
Set3DAnglesAndReverseMode();
SetTempValues();
#endif
// Initialize area position
_axisArray[0].ReCalc( PlotAreaPosition );
_axisArray[1].ReCalc( PlotAreaPosition );
_axisArray[2].ReCalc( PlotAreaPosition );
_axisArray[3].ReCalc( PlotAreaPosition );
// Find all Data and chart types which belong
// to this chart area an set default values
SetData();
#if Microsoft_CONTROL
Restore3DAnglesAndReverseMode();
GetTempValues();
#endif
}
/// <summary>
/// RecalculateAxesScale the chart area
/// </summary>
internal void ReCalcInternal()
{
// Initialize area position
_axisArray[0].ReCalc( PlotAreaPosition );
_axisArray[1].ReCalc( PlotAreaPosition );
_axisArray[2].ReCalc( PlotAreaPosition );
_axisArray[3].ReCalc( PlotAreaPosition );
// Find all Data and chart types which belong
// to this chart area an set default values
SetData();
}
/// <summary>
/// Reset auto calculated chart area values.
/// </summary>
internal void ResetAutoValues()
{
_axisArray[0].ResetAutoValues();
_axisArray[1].ResetAutoValues();
_axisArray[2].ResetAutoValues();
_axisArray[3].ResetAutoValues();
}
/// <summary>
/// Calculates Position for the background.
/// </summary>
/// <param name="withScrollBars">Calculate with scroll bars</param>
/// <returns>Background rectangle</returns>
internal RectangleF GetBackgroundPosition( bool withScrollBars )
{
// For pie and doughnut, which do not have axes, the position
// for the background is Chart area position not plotting
// area position.
RectangleF backgroundPosition = PlotAreaPosition.ToRectangleF();
if( !requireAxes )
{
backgroundPosition = Position.ToRectangleF();
}
// Without scroll bars
if( !withScrollBars )
{
return backgroundPosition;
}
// Add scroll bar rectangles to the area background
RectangleF backgroundPositionWithScrollBars = new RectangleF(backgroundPosition.Location, backgroundPosition.Size);
#if Microsoft_CONTROL
if( requireAxes )
{
// Loop through all axis
foreach(Axis axis in this.Axes)
{
// Find axis with visible scroll bars
if(axis.ScrollBar.IsVisible && axis.ScrollBar.IsPositionedInside)
{
// Change size of the background rectangle depending on the axis position
if(axis.AxisPosition == AxisPosition.Bottom)
{
backgroundPositionWithScrollBars.Height += (float)axis.ScrollBar.GetScrollBarRelativeSize();
}
else if(axis.AxisPosition == AxisPosition.Top)
{
backgroundPositionWithScrollBars.Y -= (float)axis.ScrollBar.GetScrollBarRelativeSize();
backgroundPositionWithScrollBars.Height += (float)axis.ScrollBar.GetScrollBarRelativeSize();
}
else if(axis.AxisPosition == AxisPosition.Left)
{
backgroundPositionWithScrollBars.X -= (float)axis.ScrollBar.GetScrollBarRelativeSize();
backgroundPositionWithScrollBars.Width += (float)axis.ScrollBar.GetScrollBarRelativeSize();
}
else if(axis.AxisPosition == AxisPosition.Left)
{
backgroundPositionWithScrollBars.Width += (float)axis.ScrollBar.GetScrollBarRelativeSize();
}
}
}
}
#endif // Microsoft_CONTROL
return backgroundPositionWithScrollBars;
}
/// <summary>
/// Call when the chart area is resized.
/// </summary>
/// <param name="chartGraph">Chart graphics object.</param>
internal void Resize(ChartGraphics chartGraph)
{
// Initialize plotting area position
RectangleF plottingRect = Position.ToRectangleF();
if(!InnerPlotPosition.Auto)
{
plottingRect.X += (Position.Width / 100F) * InnerPlotPosition.X;
plottingRect.Y += (Position.Height / 100F) * InnerPlotPosition.Y;
plottingRect.Width = (Position.Width / 100F) * InnerPlotPosition.Width;
plottingRect.Height = (Position.Height / 100F) * InnerPlotPosition.Height;
}
//******************************************************
//** Calculate number of vertical and horizontal axis
//******************************************************
int verticalAxes = 0;
int horizontalAxes = 0;
foreach(Axis axis in this.Axes)
{
if(axis.enabled)
{
if(axis.AxisPosition == AxisPosition.Bottom)
{
++horizontalAxes;
}
else if(axis.AxisPosition == AxisPosition.Top)
{
++horizontalAxes;
}
else if(axis.AxisPosition == AxisPosition.Left)
{
++verticalAxes;
}
else if(axis.AxisPosition == AxisPosition.Right)
{
++verticalAxes;
}
}
}
if(horizontalAxes <= 0 )
{
horizontalAxes = 1;
}
if(verticalAxes <= 0 )
{
verticalAxes = 1;
}
//******************************************************
//** Find same auto-fit font size
//******************************************************
Axis[] axisArray = (this.switchValueAxes) ?
new Axis[] {this.AxisX, this.AxisX2, this.AxisY, this.AxisY2} :
new Axis[] {this.AxisY, this.AxisY2, this.AxisX, this.AxisX2};
if(this.IsSameFontSizeForAllAxes)
{
axesAutoFontSize = 20;
foreach(Axis axis in axisArray)
{
// Process only enabled axis
if(axis.enabled)
{
// Resize axis
if(axis.AxisPosition == AxisPosition.Bottom || axis.AxisPosition == AxisPosition.Top)
{
axis.Resize(chartGraph, this.PlotAreaPosition, plottingRect, horizontalAxes, InnerPlotPosition.Auto);
}
else
{
axis.Resize(chartGraph, this.PlotAreaPosition, plottingRect, verticalAxes, InnerPlotPosition.Auto);
}
// Calculate smallest font size
if(axis.IsLabelAutoFit && axis.autoLabelFont != null)
{
axesAutoFontSize = Math.Min(axesAutoFontSize, axis.autoLabelFont.Size);
}
}
}
}
//******************************************************
//** Adjust plotting area position according to the axes
//** elements (title, labels, tick marks) size.
//******************************************************
RectangleF rectLabelSideSpacing = RectangleF.Empty;
foreach(Axis axis in axisArray)
{
// Process only enabled axis
if( ! axis.enabled )
{
//******************************************************
//** Adjust for the 3D Wall Width for disabled axis
//******************************************************
if(InnerPlotPosition.Auto && this.Area3DStyle.Enable3D && !this.chartAreaIsCurcular)
{
SizeF areaWallSize = chartGraph.GetRelativeSize(new SizeF(this.Area3DStyle.WallWidth, this.Area3DStyle.WallWidth));
if(axis.AxisPosition == AxisPosition.Bottom)
{
plottingRect.Height -= areaWallSize.Height;
}
else if(axis.AxisPosition == AxisPosition.Top)
{
plottingRect.Y += areaWallSize.Height;
plottingRect.Height -= areaWallSize.Height;
}
else if(axis.AxisPosition == AxisPosition.Right)
{
plottingRect.Width -= areaWallSize.Width;
}
else if(axis.AxisPosition == AxisPosition.Left)
{
plottingRect.X += areaWallSize.Width;
plottingRect.Width -= areaWallSize.Width;
}
}
continue;
}
//******************************************************
//** Calculate axes elements position
//******************************************************
if(axis.AxisPosition == AxisPosition.Bottom || axis.AxisPosition == AxisPosition.Top)
{
axis.Resize(chartGraph, this.PlotAreaPosition, plottingRect, horizontalAxes, InnerPlotPosition.Auto);
}
else
{
axis.Resize(chartGraph, this.PlotAreaPosition, plottingRect, verticalAxes, InnerPlotPosition.Auto);
}
// Shift top/bottom labels so they will not overlap with left/right labels
PreventTopBottomAxesLabelsOverlapping(axis);
//******************************************************
//** Check axis position
//******************************************************
float axisPosition = (float)axis.GetAxisPosition();
if(axis.AxisPosition == AxisPosition.Bottom)
{
if(!axis.GetIsMarksNextToAxis())
{
axisPosition = plottingRect.Bottom;
}
axisPosition = plottingRect.Bottom - axisPosition;
}
else if(axis.AxisPosition == AxisPosition.Top)
{
if(!axis.GetIsMarksNextToAxis())
{
axisPosition = plottingRect.Y;
}
axisPosition = axisPosition - plottingRect.Top;
}
else if(axis.AxisPosition == AxisPosition.Right)
{
if(!axis.GetIsMarksNextToAxis())
{
axisPosition = plottingRect.Right;
}
axisPosition = plottingRect.Right - axisPosition;
}
else if(axis.AxisPosition == AxisPosition.Left)
{
if(!axis.GetIsMarksNextToAxis())
{
axisPosition = plottingRect.X;
}
axisPosition = axisPosition - plottingRect.Left;
}
//******************************************************
//** Adjust axis elements size with axis position
//******************************************************
// Calculate total size of axis elements
float axisSize = axis.markSize + axis.labelSize;
#if SUBAXES
// Add sub-axis size
if(!this.chartAreaIsCurcular && !this.Area3DStyle.Enable3D)
{
foreach(SubAxis subAxis in axis.SubAxes)
{
axisSize += subAxis.markSize + subAxis.labelSize + subAxis.titleSize;
}
}
#endif // SUBAXES
// Adjust depending on the axis position
axisSize -= axisPosition;
if(axisSize < 0)
{
axisSize = 0;
}
// Add axis title and scroll bar size (always outside of plotting area)
axisSize += axis.titleSize + axis.scrollBarSize;
// Calculate horizontal axes size for circualar area
if(this.chartAreaIsCurcular &&
(axis.AxisPosition == AxisPosition.Top || axis.AxisPosition == AxisPosition.Bottom) )
{
axisSize = axis.titleSize + axis.markSize + axis.scrollBarSize;
}
//******************************************************
//** Adjust plotting area
//******************************************************
if(InnerPlotPosition.Auto)
{
if(axis.AxisPosition == AxisPosition.Bottom)
{
plottingRect.Height -= axisSize;
}
else if(axis.AxisPosition == AxisPosition.Top)
{
plottingRect.Y += axisSize;
plottingRect.Height -= axisSize;
}
else if(axis.AxisPosition == AxisPosition.Left)
{
plottingRect.X += axisSize;
plottingRect.Width -= axisSize;
}
else if(axis.AxisPosition == AxisPosition.Right)
{
plottingRect.Width -= axisSize;
}
// Check if labels side offset should be processed
bool addLabelsSideOffsets = true;
// Update the plotting area depending on the size required for labels on the sides
if (addLabelsSideOffsets)
{
if (axis.AxisPosition == AxisPosition.Bottom || axis.AxisPosition == AxisPosition.Top)
{
if (axis.labelNearOffset != 0 && axis.labelNearOffset < Position.X)
{
float offset = Position.X - axis.labelNearOffset;
if (Math.Abs(offset) > plottingRect.Width * 0.3f)
{
offset = plottingRect.Width * 0.3f;
}
// NOTE: Code was removed to solve an issue with extra space when labels angle = 45
//rectLabelSideSpacing.Width = (float)Math.Max(offset, rectLabelSideSpacing.Width);
rectLabelSideSpacing.X = (float)Math.Max(offset, rectLabelSideSpacing.X);
}
if (axis.labelFarOffset > Position.Right)
{
if ((axis.labelFarOffset - Position.Right) < plottingRect.Width * 0.3f)
{
rectLabelSideSpacing.Width = (float)Math.Max(axis.labelFarOffset - Position.Right, rectLabelSideSpacing.Width);
}
else
{
rectLabelSideSpacing.Width = (float)Math.Max(plottingRect.Width * 0.3f, rectLabelSideSpacing.Width);
}
}
}
else
{
if (axis.labelNearOffset != 0 && axis.labelNearOffset < Position.Y)
{
float offset = Position.Y - axis.labelNearOffset;
if (Math.Abs(offset) > plottingRect.Height * 0.3f)
{
offset = plottingRect.Height * 0.3f;
}
// NOTE: Code was removed to solve an issue with extra space when labels angle = 45
//rectLabelSideSpacing.Height = (float)Math.Max(offset, rectLabelSideSpacing.Height);
rectLabelSideSpacing.Y = (float)Math.Max(offset, rectLabelSideSpacing.Y);
}
if (axis.labelFarOffset > Position.Bottom)
{
if ((axis.labelFarOffset - Position.Bottom) < plottingRect.Height * 0.3f)
{
rectLabelSideSpacing.Height = (float)Math.Max(axis.labelFarOffset - Position.Bottom, rectLabelSideSpacing.Height);
}
else
{
rectLabelSideSpacing.Height = (float)Math.Max(plottingRect.Height * 0.3f, rectLabelSideSpacing.Height);
}
}
}
}
}
}
//******************************************************
//** Make sure there is enough space
//** for labels on the chart sides
//******************************************************
if (!this.chartAreaIsCurcular)
{
if (rectLabelSideSpacing.Y > 0 && rectLabelSideSpacing.Y > plottingRect.Y - Position.Y)
{
float delta = (plottingRect.Y - Position.Y) - rectLabelSideSpacing.Y;
plottingRect.Y -= delta;
plottingRect.Height += delta;
}
if (rectLabelSideSpacing.X > 0 && rectLabelSideSpacing.X > plottingRect.X - Position.X)
{
float delta = (plottingRect.X - Position.X) - rectLabelSideSpacing.X;
plottingRect.X -= delta;
plottingRect.Width += delta;
}
if (rectLabelSideSpacing.Height > 0 && rectLabelSideSpacing.Height > Position.Bottom - plottingRect.Bottom)
{
plottingRect.Height += (Position.Bottom - plottingRect.Bottom) - rectLabelSideSpacing.Height;
}
if (rectLabelSideSpacing.Width > 0 && rectLabelSideSpacing.Width > Position.Right - plottingRect.Right)
{
plottingRect.Width += (Position.Right - plottingRect.Right) - rectLabelSideSpacing.Width;
}
}
//******************************************************
//** Plotting area must be square for the circular
//** chart area (in pixels).
//******************************************************
if(this.chartAreaIsCurcular)
{
// Adjust area to fit the axis title
float xTitleSize = (float)Math.Max(this.AxisY.titleSize, this.AxisY2.titleSize);
if(xTitleSize > 0)
{
plottingRect.X += xTitleSize;
plottingRect.Width -= 2f * xTitleSize;
}
float yTitleSize = (float)Math.Max(this.AxisX.titleSize, this.AxisX2.titleSize);
if(yTitleSize > 0)
{
plottingRect.Y += yTitleSize;
plottingRect.Height -= 2f * yTitleSize;
}
// Make a square plotting rect
RectangleF rect = chartGraph.GetAbsoluteRectangle( plottingRect );
if(rect.Width > rect.Height)
{
rect.X += (rect.Width - rect.Height) / 2f;
rect.Width = rect.Height;
}
else
{
rect.Y += (rect.Height - rect.Width) / 2f;
rect.Height = rect.Width;
}
plottingRect = chartGraph.GetRelativeRectangle( rect );
// Remember circular chart area center
this.circularCenter = new PointF(plottingRect.X + plottingRect.Width/2f, plottingRect.Y + plottingRect.Height/2f);
// Calculate auto-fit font of the circular axis labels and update area position
FitCircularLabels(chartGraph, this.PlotAreaPosition, ref plottingRect, xTitleSize, yTitleSize);
}
//******************************************************
//** Set plotting area position
//******************************************************
if(plottingRect.Width < 0f)
{
plottingRect.Width = 0f;
}
if(plottingRect.Height < 0f)
{
plottingRect.Height = 0f;
}
PlotAreaPosition.FromRectangleF(plottingRect);
InnerPlotPosition.SetPositionNoAuto(
(float)Math.Round((plottingRect.X - Position.X) / (Position.Width / 100F), 5),
(float)Math.Round((plottingRect.Y - Position.Y) / (Position.Height / 100F), 5),
(float)Math.Round(plottingRect.Width / (Position.Width / 100F), 5),
(float)Math.Round(plottingRect.Height / (Position.Height / 100F), 5));
//******************************************************
//** Adjust label font size for axis, which were
//** automatically calculated after the opposite axis
//** change the size of plotting area.
//******************************************************
this.AxisY2.AdjustLabelFontAtSecondPass(chartGraph, InnerPlotPosition.Auto);
this.AxisY.AdjustLabelFontAtSecondPass(chartGraph, InnerPlotPosition.Auto);
if(InnerPlotPosition.Auto)
{
this.AxisX2.AdjustLabelFontAtSecondPass(chartGraph, InnerPlotPosition.Auto);
this.AxisX.AdjustLabelFontAtSecondPass(chartGraph, InnerPlotPosition.Auto);
}
}
/// <summary>
/// Finds axis by it's position. Can be Null.
/// </summary>
/// <param name="axisPosition">Axis position to find</param>
/// <returns>Found axis.</returns>
private Axis FindAxis(AxisPosition axisPosition)
{
foreach(Axis axis in this.Axes)
{
if(axis.AxisPosition == axisPosition)
{
return axis;
}
}
return null;
}
/// <summary>
/// Shift top/bottom labels so they will not overlap with left/right labels.
/// </summary>
/// <param name="axis">Axis to shift up/down.</param>
private void PreventTopBottomAxesLabelsOverlapping(Axis axis)
{
// If axis is not on the edge of the chart area do not
// try to adjust it's position when axis labels overlap
// labels of the oppositie axis.
if( !axis.IsAxisOnAreaEdge )
{
return;
}
// Shift bottom axis labels down
if(axis.AxisPosition == AxisPosition.Bottom)
{
// Get labels position
float labelsPosition = (float)axis.GetAxisPosition();
if( !axis.GetIsMarksNextToAxis() )
{
labelsPosition = axis.PlotAreaPosition.Bottom;
}
// Only adjust labels outside plotting area
if(Math.Round(labelsPosition, 2) < Math.Round(axis.PlotAreaPosition.Bottom, 2))
{
return;
}
// Check if labels may overlap with Left axis
Axis leftAxis = FindAxis(AxisPosition.Left);
if(leftAxis != null &&
leftAxis.enabled &&
leftAxis.labelFarOffset != 0 &&
leftAxis.labelFarOffset > labelsPosition &&
axis.labelNearOffset != 0 &&
axis.labelNearOffset < PlotAreaPosition.X)
{
float overlap = (float)(leftAxis.labelFarOffset - labelsPosition) * 0.75f;
if(overlap > axis.markSize)
{
axis.markSize += overlap - axis.markSize;
}
}
// Check if labels may overlap with Right axis
Axis rightAxis = FindAxis(AxisPosition.Right);
if(rightAxis != null &&
rightAxis.enabled &&
rightAxis.labelFarOffset != 0 &&
rightAxis.labelFarOffset > labelsPosition &&
axis.labelFarOffset != 0 &&
axis.labelFarOffset > PlotAreaPosition.Right)
{
float overlap = (float)(rightAxis.labelFarOffset - labelsPosition) * 0.75f;
if(overlap > axis.markSize)
{
axis.markSize += overlap - axis.markSize;
}
}
}
// Shift top axis labels up
else if(axis.AxisPosition == AxisPosition.Top)
{
// Get labels position
float labelsPosition = (float)axis.GetAxisPosition();
if( !axis.GetIsMarksNextToAxis() )
{
labelsPosition = axis.PlotAreaPosition.Y;
}
// Only adjust labels outside plotting area
if(Math.Round(labelsPosition, 2) < Math.Round(axis.PlotAreaPosition.Y, 2))
{
return;
}
// Check if labels may overlap with Left axis
Axis leftAxis = FindAxis(AxisPosition.Left);
if(leftAxis != null &&
leftAxis.enabled &&
leftAxis.labelNearOffset != 0 &&
leftAxis.labelNearOffset < labelsPosition &&
axis.labelNearOffset != 0 &&
axis.labelNearOffset < PlotAreaPosition.X)
{
float overlap = (float)(labelsPosition - leftAxis.labelNearOffset) * 0.75f;
if(overlap > axis.markSize)
{
axis.markSize += overlap - axis.markSize;
}
}
// Check if labels may overlap with Right axis
Axis rightAxis = FindAxis(AxisPosition.Right);
if(rightAxis != null &&
rightAxis.enabled &&
rightAxis.labelNearOffset != 0 &&
rightAxis.labelNearOffset < labelsPosition &&
axis.labelFarOffset != 0 &&
axis.labelFarOffset > PlotAreaPosition.Right)
{
float overlap = (float)(labelsPosition - rightAxis.labelNearOffset) * 0.75f;
if(overlap > axis.markSize)
{
axis.markSize += overlap - axis.markSize;
}
}
}
}
#endregion
#region Painting and Selection Methods
/// <summary>
/// Draws chart area background and/or border.
/// </summary>
/// <param name="graph">Chart graphics.</param>
/// <param name="position">Background position.</param>
/// <param name="borderOnly">Draws chart area border only.</param>
private void PaintAreaBack(ChartGraphics graph, RectangleF position, bool borderOnly)
{
if(!borderOnly)
{
// Draw background
if(!this.Area3DStyle.Enable3D || !requireAxes || chartAreaIsCurcular)
{
// 3D Pie Chart doesn't need scene
// Draw 2D background
graph.FillRectangleRel(
position,
BackColor,
BackHatchStyle,
BackImage,
BackImageWrapMode,
BackImageTransparentColor,
BackImageAlignment,
BackGradientStyle,
BackSecondaryColor,
(requireAxes) ? Color.Empty : BorderColor,
(requireAxes) ? 0 : BorderWidth,
BorderDashStyle,
ShadowColor,
ShadowOffset,
PenAlignment.Outset,
chartAreaIsCurcular,
(chartAreaIsCurcular && this.CircularUsePolygons) ? this.CircularSectorsNumber : 0,
this.Area3DStyle.Enable3D);
}
else
{
// Draw chart area 3D scene
this.DrawArea3DScene(graph, position);
}
}
else
{
if(!this.Area3DStyle.Enable3D || !requireAxes || chartAreaIsCurcular)
{
// Draw chart area border
if(BorderColor != Color.Empty && BorderWidth > 0)
{
graph.FillRectangleRel( position,
Color.Transparent,
ChartHatchStyle.None,
"",
ChartImageWrapMode.Tile,
Color.Empty,
ChartImageAlignmentStyle.Center,
GradientStyle.None,
Color.Empty,
BorderColor,
BorderWidth,
BorderDashStyle,
Color.Empty,
0,
PenAlignment.Outset,
chartAreaIsCurcular,
(chartAreaIsCurcular && this.CircularUsePolygons) ? this.CircularSectorsNumber : 0,
this.Area3DStyle.Enable3D);
}
}
}
}
/// <summary>
/// Paint the chart area.
/// </summary>
/// <param name="graph">Chart graphics.</param>
internal void Paint( ChartGraphics graph )
{
// Check if plot area position was recalculated.
// If not and non-auto InnerPlotPosition & Position were
// specified - do all needed calculations
if (PlotAreaPosition.Width == 0 &&
PlotAreaPosition.Height == 0 &&
!InnerPlotPosition.Auto
&& !Position.Auto)
{
// Initialize plotting area position
RectangleF plottingRect = Position.ToRectangleF();
if (!InnerPlotPosition.Auto)
{
plottingRect.X += (Position.Width / 100F) * InnerPlotPosition.X;
plottingRect.Y += (Position.Height / 100F) * InnerPlotPosition.Y;
plottingRect.Width = (Position.Width / 100F) * InnerPlotPosition.Width;
plottingRect.Height = (Position.Height / 100F) * InnerPlotPosition.Height;
}
PlotAreaPosition.FromRectangleF(plottingRect);
}
// Get background position rectangle.
RectangleF backgroundPositionWithScrollBars = GetBackgroundPosition(true);
RectangleF backgroundPosition = GetBackgroundPosition(false);
// Add hot region for plotting area.
if (Common.ProcessModeRegions)
{
Common.HotRegionsList.AddHotRegion(backgroundPosition, this, ChartElementType.PlottingArea, true);
}
// Draw background
PaintAreaBack(graph, backgroundPositionWithScrollBars, false);
// Call BackPaint event
Common.Chart.CallOnPrePaint(new ChartPaintEventArgs(this, graph, Common, PlotAreaPosition));
// Draw chart types without axes - Pie.
if (!requireAxes && ChartTypes.Count != 0)
{
// Find first chart type that do not require axis (like Pie) and draw it.
// Chart types that do not require axes (circular charts) cannot be combined with
// any other chart types.
// NOTE: Fixes issues #4672 and #4692
for (int chartTypeIndex = 0; chartTypeIndex < ChartTypes.Count; chartTypeIndex++)
{
IChartType chartType = Common.ChartTypeRegistry.GetChartType((string)ChartTypes[chartTypeIndex]);
if (!chartType.RequireAxes)
{
chartType.Paint(graph, Common, this, null);
break;
}
}
// Call Paint event
Common.Chart.CallOnPostPaint(new ChartPaintEventArgs(this, graph, Common, PlotAreaPosition));
return;
}
// Reset Smart Labels
this.smartLabels.Reset();
// Set values for optimized drawing
foreach (Axis currentAxis in this._axisArray)
{
currentAxis.optimizedGetPosition = true;
currentAxis.paintViewMax = currentAxis.ViewMaximum;
currentAxis.paintViewMin = currentAxis.ViewMinimum;
currentAxis.paintRange = currentAxis.paintViewMax - currentAxis.paintViewMin;
currentAxis.paintAreaPosition = PlotAreaPosition.ToRectangleF();
if (currentAxis.ChartArea != null && currentAxis.ChartArea.chartAreaIsCurcular)
{
// Update position for circular chart area
currentAxis.paintAreaPosition.Width /= 2.0f;
currentAxis.paintAreaPosition.Height /= 2.0f;
}
currentAxis.paintAreaPositionBottom = currentAxis.paintAreaPosition.Y + currentAxis.paintAreaPosition.Height;
currentAxis.paintAreaPositionRight = currentAxis.paintAreaPosition.X + currentAxis.paintAreaPosition.Width;
if (currentAxis.AxisPosition == AxisPosition.Top || currentAxis.AxisPosition == AxisPosition.Bottom)
currentAxis.paintChartAreaSize = currentAxis.paintAreaPosition.Width;
else
currentAxis.paintChartAreaSize = currentAxis.paintAreaPosition.Height;
currentAxis.valueMultiplier = 0.0;
if (currentAxis.paintRange != 0)
{
currentAxis.valueMultiplier = currentAxis.paintChartAreaSize / currentAxis.paintRange;
}
}
// Draw Axis Striplines (only when StripWidth > 0)
bool useScaleSegments = false;
Axis[] axesArray = new Axis[] { axisY, axisY2, axisX, axisX2 };
foreach (Axis currentAxis in axesArray)
{
useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
if (!useScaleSegments)
{
currentAxis.PaintStrips(graph, false);
}
else
{
foreach (AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
{
scaleSegment.SetTempAxisScaleAndInterval();
currentAxis.PaintStrips(graph, false);
scaleSegment.RestoreAxisScaleAndInterval();
}
}
}
// Draw Axis Grids
axesArray = new Axis[] { axisY, axisX2, axisY2, axisX };
foreach (Axis currentAxis in axesArray)
{
useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
if (!useScaleSegments)
{
currentAxis.PaintGrids(graph);
}
else
{
foreach (AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
{
scaleSegment.SetTempAxisScaleAndInterval();
currentAxis.PaintGrids(graph);
scaleSegment.RestoreAxisScaleAndInterval();
}
}
}
// Draw Axis Striplines (only when StripWidth == 0)
foreach (Axis currentAxis in axesArray)
{
useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
if (!useScaleSegments)
{
currentAxis.PaintStrips(graph, true);
}
else
{
foreach (AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
{
scaleSegment.SetTempAxisScaleAndInterval();
currentAxis.PaintStrips(graph, true);
scaleSegment.RestoreAxisScaleAndInterval();
}
}
}
// Draw Axis elements on the back of the 3D scene
if (this.Area3DStyle.Enable3D && !this.chartAreaIsCurcular)
{
foreach (Axis currentAxis in axesArray)
{
useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
if (!useScaleSegments)
{
currentAxis.PrePaint(graph);
}
else
{
foreach (AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
{
scaleSegment.SetTempAxisScaleAndInterval();
currentAxis.PrePaint(graph);
scaleSegment.RestoreAxisScaleAndInterval();
}
}
}
}
// Draws chart area border
bool borderDrawn = false;
if (this.Area3DStyle.Enable3D || !IsBorderOnTopSeries())
{
borderDrawn = true;
PaintAreaBack(graph, backgroundPosition, true);
}
// Draw chart types
if (!this.Area3DStyle.Enable3D || this.chartAreaIsCurcular)
{
// Drawing in 2D space
// NOTE: Fixes issue #6443 and #5385
// If two chart series of the same type (for example Line) are separated
// by other series (for example Area) the order is not correct.
// Old implementation draws ALL series that belongs to the chart type.
ArrayList typeAndSeries = this.GetChartTypesAndSeriesToDraw();
// Draw series by chart type or by series
foreach (ChartTypeAndSeriesInfo chartTypeInfo in typeAndSeries)
{
this.IterationCounter = 0;
IChartType type = Common.ChartTypeRegistry.GetChartType(chartTypeInfo.ChartType);
// If 'chartTypeInfo.Series' set to NULL all series of that chart type are drawn at once
type.Paint(graph, Common, this, chartTypeInfo.Series);
}
}
else
{
// Drawing in 3D space
PaintChartSeries3D(graph);
}
// Draw area border if it wasn't drawn prior to the series
if (!borderDrawn)
{
PaintAreaBack(graph, backgroundPosition, true);
}
// Draw Axis
foreach (Axis currentAxis in axesArray)
{
useScaleSegments = (currentAxis.ScaleSegments.Count > 0);
if (!useScaleSegments)
{
// Paint axis and Reset temp axis offset for side-by-side charts like column
currentAxis.Paint(graph);
}
else
{
// Some of the axis elements like grid lines and tickmarks
// are drawn for each segment
foreach (AxisScaleSegment scaleSegment in currentAxis.ScaleSegments)
{
scaleSegment.SetTempAxisScaleAndInterval();
currentAxis.PaintOnSegmentedScalePassOne(graph);
scaleSegment.RestoreAxisScaleAndInterval();
}
// Other elements like labels, title, axis line are drawn once
currentAxis.PaintOnSegmentedScalePassTwo(graph);
}
}
// Call Paint event
Common.Chart.CallOnPostPaint(new ChartPaintEventArgs(this, graph, Common, PlotAreaPosition));
// Draw axis scale break lines
axesArray = new Axis[] { axisY, axisY2 };
foreach (Axis currentAxis in axesArray)
{
for (int segmentIndex = 0; segmentIndex < (currentAxis.ScaleSegments.Count - 1); segmentIndex++)
{
currentAxis.ScaleSegments[segmentIndex].PaintBreakLine(graph, currentAxis.ScaleSegments[segmentIndex + 1]);
}
}
// Reset values for optimized drawing
foreach (Axis curentAxis in this._axisArray)
{
curentAxis.optimizedGetPosition = false;
// Reset preffered number of intervals on the axis
curentAxis.prefferedNumberofIntervals = 5;
// Reset flag that scale segments are used
curentAxis.scaleSegmentsUsed = false;
}
}
/// <summary>
/// Checks if chart area border should be drawn on top of series.
/// </summary>
/// <returns>True if border should be darwn on top.</returns>
private bool IsBorderOnTopSeries()
{
// For most of the chart types chart area border is drawn on top.
bool result = true;
foreach( Series series in this.Common.Chart.Series )
{
if(series.ChartArea == this.Name)
{
// It is common for the Bubble and Point chart types to draw markers
// partially outside of the chart area. By drawing the border before
// series we avoiding the possibility of drawing the border line on
// top of the marker.
if(series.ChartType == SeriesChartType.Bubble ||
series.ChartType == SeriesChartType.Point)
{
return false;
}
}
}
return result;
}
/// <summary>
/// Paint the chart area cursors.
/// </summary>
/// <param name="graph">Chart graphics.</param>
/// <param name="cursorOnly">Indicates that only cursors are redrawn.</param>
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "These parameters are used when compiling for the Microsoft version of Chart")]
internal void PaintCursors( ChartGraphics graph, bool cursorOnly )
{
// Cursors and selection are supoorted only in 2D charts
if(this.Area3DStyle.Enable3D == true)
{
return;
}
// Do not draw cursor/selection for chart types that do not require axis (like Pie)
if(!this.requireAxes)
{
return;
}
// Cursors and selection are not supoorted in circular areas
if(this.chartAreaIsCurcular)
{
return;
}
// Do not draw cursor/selection while printing
if(this.Common != null &&
this.Common.ChartPicture != null &&
this.Common.ChartPicture.isPrinting)
{
return;
}
// Do not draw cursor/selection when chart area is not visible
// because either width or height is set to zero
if(this.Position.Width == 0f ||
this.Position.Height == 0f)
{
return;
}
#if Microsoft_CONTROL
Chart chart = this.Common.Chart;
ChartPicture chartPicture = Common.ChartPicture;
// Check if cursor should be drawn
if(!double.IsNaN(_cursorX.SelectionStart) ||
!double.IsNaN(_cursorX.SelectionEnd) ||
!double.IsNaN(_cursorX.Position) ||
!double.IsNaN(_cursorY.SelectionStart) ||
!double.IsNaN(_cursorY.SelectionEnd) ||
!double.IsNaN(_cursorY.Position))
{
if(!chartPicture.backgroundRestored &&
!chartPicture.isSelectionMode )
{
chartPicture.backgroundRestored = true;
Rectangle chartPosition = new Rectangle(0, 0, chartPicture.Width, chartPicture.Height);
// Get chart area position
Rectangle absAreaPlotPosition = Rectangle.Round(graph.GetAbsoluteRectangle(PlotAreaPosition.ToRectangleF()));
int maxCursorWidth = (CursorY.LineWidth > CursorX.LineWidth) ? CursorY.LineWidth + 1 : CursorX.LineWidth + 1;
absAreaPlotPosition.Inflate(maxCursorWidth, maxCursorWidth);
absAreaPlotPosition.Intersect(new Rectangle(0, 0, chart.Width, chart.Height));
// Create area buffer bitmap
if(areaBufferBitmap == null ||
chartPicture.nonTopLevelChartBuffer == null ||
!cursorOnly)
{
// Dispose previous bitmap
if(areaBufferBitmap != null)
{
areaBufferBitmap.Dispose();
areaBufferBitmap = null;
}
if(chartPicture.nonTopLevelChartBuffer != null)
{
chartPicture.nonTopLevelChartBuffer.Dispose();
chartPicture.nonTopLevelChartBuffer = null;
}
// Copy chart area plotting rectangle from the chart's dubble buffer image into area dubble buffer image
if(chart.paintBufferBitmap != null)
{
areaBufferBitmap = chart.paintBufferBitmap.Clone(absAreaPlotPosition, chart.paintBufferBitmap.PixelFormat);
}
// Copy whole chart from the chart's dubble buffer image into area dubble buffer image
if(chart.paintBufferBitmap != null &&
chart.paintBufferBitmap.Size.Width >= chartPosition.Size.Width &&
chart.paintBufferBitmap.Size.Height >= chartPosition.Size.Height)
{
chartPicture.nonTopLevelChartBuffer = chart.paintBufferBitmap.Clone(
chartPosition, chart.paintBufferBitmap.PixelFormat);
}
}
else if(cursorOnly && chartPicture.nonTopLevelChartBuffer != null)
{
// Restore previous background
chart.paintBufferBitmapGraphics.DrawImageUnscaled(
chartPicture.nonTopLevelChartBuffer,
chartPosition);
}
}
// Draw chart area cursors and range selection
_cursorY.Paint(graph);
_cursorX.Paint(graph);
}
#endif // Microsoft_CONTROL
}
#endregion
#region Circular chart area methods
/// <summary>
/// Gets a circular chart type interface that belongs to this chart area.
/// </summary>
/// <returns>ICircularChartType interface or null.</returns>
internal ICircularChartType GetCircularChartType()
{
// Get number of sectors in circular chart area
foreach(Series series in this.Common.DataManager.Series)
{
if(series.IsVisible() && series.ChartArea == this.Name)
{
ICircularChartType type = Common.ChartTypeRegistry.GetChartType(series.ChartTypeName) as ICircularChartType;;
if(type != null)
{
return type;
}
}
}
return null;
}
/// <summary>
/// Calculate size of the circular axis labels and sets auto-fit font.
/// </summary>
/// <param name="chartGraph">Chart graphics object.</param>
/// <param name="chartAreaPosition">The Chart area position.</param>
/// <param name="plotArea">Plotting area size.</param>
/// <param name="xTitleSize">Size of title on the axis.</param>
/// <param name="yTitleSize">Size of title on the axis.</param>
internal void FitCircularLabels(
ChartGraphics chartGraph,
ElementPosition chartAreaPosition,
ref RectangleF plotArea,
float xTitleSize,
float yTitleSize)
{
// Check if axis labels are enabled
if(!this.AxisX.LabelStyle.Enabled)
{
return;
}
// Get absolute titles size
SizeF titleSize = chartGraph.GetAbsoluteSize(new SizeF(xTitleSize, yTitleSize));
// Get absolute position of area
RectangleF plotAreaRectAbs = chartGraph.GetAbsoluteRectangle( plotArea );
RectangleF areaRectAbs = chartGraph.GetAbsoluteRectangle( chartAreaPosition.ToRectangleF());
// Get absolute markers size and spacing
float spacing = chartGraph.GetAbsolutePoint(new PointF(0, this.AxisX.markSize + Axis.elementSpacing)).Y;
// Get circular axis list
ArrayList axisList = GetCircularAxisList();
// Get circular axis labels style
CircularAxisLabelsStyle labelsStyle = GetCircularAxisLabelsStyle();
//*****************************************************************
//** Calculate the auto-fit font if required
//*****************************************************************
if(this.AxisX.LabelStyle.Enabled && this.AxisX.IsLabelAutoFit)
{
// Set max auto fit font
this.AxisX.autoLabelFont = Common.ChartPicture.FontCache.GetFont(
this.AxisX.LabelStyle.Font.FontFamily,
14,
this.AxisX.LabelStyle.Font.Style,
GraphicsUnit.Point);
// Get estimated labels size
float labelsSizeEstimate = GetCircularLabelsSize(chartGraph, areaRectAbs, plotAreaRectAbs, titleSize);
labelsSizeEstimate = (float)Math.Min(labelsSizeEstimate * 1.1f, plotAreaRectAbs.Width / 5f);
labelsSizeEstimate += spacing;
// Calculate auto-fit font
this.AxisX.GetCircularAxisLabelsAutoFitFont(chartGraph, axisList, labelsStyle, plotAreaRectAbs, areaRectAbs, labelsSizeEstimate);
}
//*****************************************************************
//** Shrink plot area size proportionally
//*****************************************************************
// Get labels size
float labelsSize = GetCircularLabelsSize(chartGraph, areaRectAbs, plotAreaRectAbs, titleSize);
// Check if change size is smaller than radius
labelsSize = (float)Math.Min(labelsSize, plotAreaRectAbs.Width / 2.5f);
labelsSize += spacing;
plotAreaRectAbs.X += labelsSize;
plotAreaRectAbs.Width -= 2f * labelsSize;
plotAreaRectAbs.Y += labelsSize;
plotAreaRectAbs.Height -= 2f * labelsSize;
// Restrict minimum plot area size
if(plotAreaRectAbs.Width < 1.0f)
{
plotAreaRectAbs.Width = 1.0f;
}
if(plotAreaRectAbs.Height < 1.0f)
{
plotAreaRectAbs.Height = 1.0f;
}
plotArea = chartGraph.GetRelativeRectangle( plotAreaRectAbs );
//*****************************************************************
//** Set axes labels size
//*****************************************************************
SizeF relativeLabelSize = chartGraph.GetRelativeSize(new SizeF(labelsSize, labelsSize));
this.AxisX.labelSize = relativeLabelSize.Height;
this.AxisX2.labelSize = relativeLabelSize.Height;
this.AxisY.labelSize = relativeLabelSize.Width;
this.AxisY2.labelSize = relativeLabelSize.Width;
}
/// <summary>
/// Calculate size of the circular axis labels.
/// </summary>
/// <param name="chartGraph">Chart graphics object.</param>
/// <param name="areaRectAbs">The Chart area position.</param>
/// <param name="plotAreaRectAbs">Plotting area size.</param>
/// <param name="titleSize">Size of title on the axes.</param>
/// <returns>Circulat labels style.</returns>
internal float GetCircularLabelsSize(
ChartGraphics chartGraph,
RectangleF areaRectAbs,
RectangleF plotAreaRectAbs,
SizeF titleSize)
{
// Find current horiz. and vert. spacing between plotting and chart areas
SizeF areaDiff = new SizeF(plotAreaRectAbs.X - areaRectAbs.X, plotAreaRectAbs.Y - areaRectAbs.Y);
areaDiff.Width -= titleSize.Width;
areaDiff.Height -= titleSize.Height;
// Get absolute center of the area
PointF areaCenterAbs = chartGraph.GetAbsolutePoint(this.circularCenter);
// Get circular axis list
ArrayList axisList = GetCircularAxisList();
// Get circular axis labels style
CircularAxisLabelsStyle labelsStyle = GetCircularAxisLabelsStyle();
// Defines on how much (pixels) the circular chart area radius should be reduced
float labelsSize = 0f;
//*****************************************************************
//** Loop through all axis labels
//*****************************************************************
foreach(CircularChartAreaAxis axis in axisList)
{
//*****************************************************************
//** Measure label text
//*****************************************************************
SizeF textSize = chartGraph.MeasureString(
axis.Title.Replace("\\n", "\n"),
(this.AxisX.autoLabelFont == null) ? this.AxisX.LabelStyle.Font : this.AxisX.autoLabelFont);
textSize.Width = (float)Math.Ceiling(textSize.Width * 1.1f);
textSize.Height = (float)Math.Ceiling(textSize.Height * 1.1f);
//*****************************************************************
//** Calculate area size change depending on labels style
//*****************************************************************
if(labelsStyle == CircularAxisLabelsStyle.Circular)
{
labelsSize = (float)Math.Max(
labelsSize,
textSize.Height);
}
else if(labelsStyle == CircularAxisLabelsStyle.Radial)
{
float textAngle = axis.AxisPosition + 90;
// For angled text find it's X and Y components
float width = (float)Math.Cos(textAngle/180F*Math.PI) * textSize.Width;
float height = (float)Math.Sin(textAngle/180F*Math.PI) * textSize.Width;
width = (float)Math.Abs(Math.Ceiling(width));
height = (float)Math.Abs(Math.Ceiling(height));
// Reduce text size by current spacing between plotting area and chart area
width -= areaDiff.Width;
height -= areaDiff.Height;
if(width < 0)
width = 0;
if(height < 0)
height = 0;
labelsSize = (float)Math.Max(
labelsSize,
Math.Max(width, height));
}
else if(labelsStyle == CircularAxisLabelsStyle.Horizontal)
{
// Get text angle
float textAngle = axis.AxisPosition;
if(textAngle > 180f)
{
textAngle -= 180f;
}
// Get label rotated position
PointF[] labelPosition = new PointF[] { new PointF(areaCenterAbs.X, plotAreaRectAbs.Y) };
Matrix newMatrix = new Matrix();
newMatrix.RotateAt(textAngle, areaCenterAbs);
newMatrix.TransformPoints(labelPosition);
// Calculate width
float width = textSize.Width;
width -= areaRectAbs.Right - labelPosition[0].X;
if(width < 0f)
{
width = 0f;
}
labelsSize = (float)Math.Max(
labelsSize,
Math.Max(width, textSize.Height));
}
}
return labelsSize;
}
/// <summary>
/// True if polygons should be used instead of the circles for the chart area.
/// </summary>
internal bool CircularUsePolygons
{
get
{
// Check if value was precalculated
if(this._circularUsePolygons == int.MinValue)
{
_circularUsePolygons = 0;
// Look for custom properties in series
foreach(Series series in this.Common.DataManager.Series)
{
if(series.ChartArea == this.Name && series.IsVisible())
{
// Get custom attribute
if (series.IsCustomPropertySet(CustomPropertyName.AreaDrawingStyle))
{
if(String.Compare(series[CustomPropertyName.AreaDrawingStyle], "Polygon", StringComparison.OrdinalIgnoreCase) == 0)
{
_circularUsePolygons = 1;
}
else if (String.Compare(series[CustomPropertyName.AreaDrawingStyle], "Circle", StringComparison.OrdinalIgnoreCase) == 0)
{
_circularUsePolygons = 0;
}
else
{
throw(new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid( series[CustomPropertyName.AreaDrawingStyle], "AreaDrawingStyle")));
}
break;
}
}
}
}
return (this._circularUsePolygons == 1);
}
}
/// <summary>
/// Gets circular area axis labels style.
/// </summary>
/// <returns>Axis labels style.</returns>
internal CircularAxisLabelsStyle GetCircularAxisLabelsStyle()
{
CircularAxisLabelsStyle style = CircularAxisLabelsStyle.Auto;
// Get maximum number of points in all series
foreach(Series series in this.Common.DataManager.Series)
{
if(series.IsVisible() && series.ChartArea == this.Name && series.IsCustomPropertySet(CustomPropertyName.CircularLabelsStyle))
{
string styleName = series[CustomPropertyName.CircularLabelsStyle];
if(String.Compare( styleName, "Auto", StringComparison.OrdinalIgnoreCase) == 0 )
{
style = CircularAxisLabelsStyle.Auto;
}
else if(String.Compare( styleName,"Circular", StringComparison.OrdinalIgnoreCase) == 0)
{
style = CircularAxisLabelsStyle.Circular;
}
else if(String.Compare( styleName,"Radial", StringComparison.OrdinalIgnoreCase) == 0)
{
style = CircularAxisLabelsStyle.Radial;
}
else if (String.Compare(styleName, "Horizontal", StringComparison.OrdinalIgnoreCase) == 0)
{
style = CircularAxisLabelsStyle.Horizontal;
}
else
{
throw(new InvalidOperationException(SR.ExceptionCustomAttributeValueInvalid( styleName, "CircularLabelsStyle")));
}
}
}
// Get auto style
if(style == CircularAxisLabelsStyle.Auto)
{
int sectorNumber = CircularSectorsNumber;
style = CircularAxisLabelsStyle.Horizontal;
if(sectorNumber > 30)
{
style = CircularAxisLabelsStyle.Radial;
}
}
return style;
}
/// <summary>
/// Number of sectors in the circular area.
/// </summary>
internal int CircularSectorsNumber
{
get
{
// Check if value was precalculated
if(this._circularSectorNumber == int.MinValue)
{
this._circularSectorNumber = GetCircularSectorNumber();
}
return this._circularSectorNumber;
}
}
/// <summary>
/// Gets number of sectors in the circular chart area.
/// </summary>
/// <returns>Number of sectors.</returns>
private int GetCircularSectorNumber()
{
ICircularChartType type = this.GetCircularChartType();
if(type != null)
{
return type.GetNumerOfSectors(this, this.Common.DataManager.Series);
}
return 0;
}
/// <summary>
/// Fills a list of circular axis.
/// </summary>
/// <returns>Axes list.</returns>
internal ArrayList GetCircularAxisList()
{
// Check if list was already created
if(_circularAxisList == null)
{
_circularAxisList = new ArrayList();
// Loop through all sectors
int sectorNumber = GetCircularSectorNumber();
for(int sectorIndex = 0; sectorIndex < sectorNumber; sectorIndex++)
{
// Create new axis object
CircularChartAreaAxis axis = new CircularChartAreaAxis(sectorIndex * 360f/sectorNumber);
// Check if custom X axis labels will be used
if(this.AxisX.CustomLabels.Count > 0)
{
if(sectorIndex < this.AxisX.CustomLabels.Count)
{
axis.Title = this.AxisX.CustomLabels[sectorIndex].Text;
axis.TitleForeColor = this.AxisX.CustomLabels[sectorIndex].ForeColor;
}
}
else
{
// Get axis title from all series
foreach(Series series in this.Common.DataManager.Series)
{
if(series.IsVisible() && series.ChartArea == this.Name && sectorIndex < series.Points.Count)
{
if(series.Points[sectorIndex].AxisLabel.Length > 0)
{
axis.Title = series.Points[sectorIndex].AxisLabel;
break;
}
}
}
}
// Add axis into the list
_circularAxisList.Add(axis);
}
}
return _circularAxisList;
}
/// <summary>
/// Converts circular position of the X axis to angle in degrees.
/// </summary>
/// <param name="position">X axis position.</param>
/// <returns>Angle in degrees.</returns>
internal float CircularPositionToAngle(double position)
{
// Get X axis scale size
double scaleRatio = 360.0 / Math.Abs(this.AxisX.Maximum - this.AxisX.Minimum);
return (float)(position * scaleRatio + this.AxisX.Crossing);
}
#endregion
#region 2D Series drawing order methods
/// <summary>
/// Helper method that returns a list of 'ChartTypeAndSeriesInfo' objects.
/// This list is used for chart area series drawing in 2D mode. Each
/// object may represent an individual series or all series that belong
/// to one chart type.
///
/// This method is intended to fix issues #6443 and #5385 when area chart
/// type incorrectly overlaps point or line chart type.
/// </summary>
/// <returns>List of 'ChartTypeAndSeriesInfo' objects.</returns>
private ArrayList GetChartTypesAndSeriesToDraw()
{
ArrayList resultList = new ArrayList();
// Build chart type or series position based lists
if (this.ChartTypes.Count > 1 &&
(this.ChartTypes.Contains(ChartTypeNames.Area)
|| this.ChartTypes.Contains(ChartTypeNames.SplineArea)
)
)
{
// Array of chart type names that do not require furher processing
ArrayList processedChartType = new ArrayList();
ArrayList splitChartType = new ArrayList();
// Draw using the exact order in the series collection
int seriesIndex = 0;
foreach (Series series in this.Common.DataManager.Series)
{
// Check if series is visible and belongs to the chart area
if (series.ChartArea==this.Name && series.IsVisible() && series.Points.Count > 0)
{
// Check if this chart type was already processed
if (!processedChartType.Contains(series.ChartTypeName))
{
// Check if curent chart type can be individually processed
bool canBeIndividuallyProcessed = false;
if (series.ChartType == SeriesChartType.Point ||
series.ChartType == SeriesChartType.Line ||
series.ChartType == SeriesChartType.Spline ||
series.ChartType == SeriesChartType.StepLine)
{
canBeIndividuallyProcessed = true;
}
if (!canBeIndividuallyProcessed)
{
// Add a record to process all series of that chart type at once
resultList.Add(new ChartTypeAndSeriesInfo(series.ChartTypeName));
processedChartType.Add(series.ChartTypeName);
}
else
{
// Check if curent chart type has more that 1 series and they are split
// by other series
bool chartTypeIsSplit = false;
if (splitChartType.Contains(series.ChartTypeName))
{
chartTypeIsSplit = true;
}
else
{
bool otherChartTypeFound = false;
for (int curentSeriesIndex = seriesIndex + 1; curentSeriesIndex < this.Common.DataManager.Series.Count; curentSeriesIndex++)
{
if (series.ChartTypeName == this.Common.DataManager.Series[curentSeriesIndex].ChartTypeName)
{
if (otherChartTypeFound)
{
chartTypeIsSplit = true;
splitChartType.Add(series.ChartTypeName);
}
}
else
{
if (this.Common.DataManager.Series[curentSeriesIndex].ChartType == SeriesChartType.Area ||
this.Common.DataManager.Series[curentSeriesIndex].ChartType == SeriesChartType.SplineArea)
{
otherChartTypeFound = true;
}
}
}
}
if (chartTypeIsSplit)
{
// Add a record to process this series individually
resultList.Add(new ChartTypeAndSeriesInfo(series));
}
else
{
// Add a record to process all series of that chart type at once
resultList.Add(new ChartTypeAndSeriesInfo(series.ChartTypeName));
processedChartType.Add(series.ChartTypeName);
}
}
}
}
++seriesIndex;
}
}
else
{
foreach (string chartType in this.ChartTypes)
{
resultList.Add(new ChartTypeAndSeriesInfo(chartType));
}
}
return resultList;
}
/// <summary>
/// Internal data structure that stores chart type name and optionally series object.
/// </summary>
internal class ChartTypeAndSeriesInfo
{
/// <summary>
/// Object constructor.
/// </summary>
public ChartTypeAndSeriesInfo()
{
}
/// <summary>
/// Object constructor.
/// </summary>
/// <param name="chartType">Chart type name to initialize with.</param>
public ChartTypeAndSeriesInfo(string chartType)
{
this.ChartType = chartType;
}
/// <summary>
/// Object constructor.
/// </summary>
/// <param name="series">Series to initialize with.</param>
public ChartTypeAndSeriesInfo(Series series)
{
this.ChartType = series.ChartTypeName;
this.Series = series;
}
// Chart type name
internal string ChartType = string.Empty;
// Series object. Can be set to NULL!
internal Series Series = null;
}
#endregion // 2D Series drawing order methods
#region IDisposable Members
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "axisX")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "axisX2")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "axisY")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "axisY2")]
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Dispose managed resources
if (this._axisArray != null)
{
foreach (Axis axis in this._axisArray)
{
axis.Dispose();
}
this._axisArray = null;
}
if ( this._areaPosition != null)
{
this._areaPosition.Dispose();
this._areaPosition = null;
}
if (this._innerPlotPosition != null)
{
this._innerPlotPosition.Dispose();
this._innerPlotPosition = null;
}
if (this.PlotAreaPosition != null)
{
this.PlotAreaPosition.Dispose();
this.PlotAreaPosition = null;
}
#if Microsoft_CONTROL
if (this.areaBufferBitmap != null)
{
this.areaBufferBitmap.Dispose();
this.areaBufferBitmap = null;
}
if (this._cursorX != null)
{
this._cursorX.Dispose();
this._cursorX = null;
}
if (this._cursorY != null)
{
this._cursorY.Dispose();
this._cursorY = null;
}
#endif
}
base.Dispose(disposing);
}
#endregion
}
}