|
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
// Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
// File: ChartElement.cs
//
// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
//
// Classes: ChartHelper
//
// Purpose: The chart element is base class for the big number
// of classes. It stores common methods and data.
//
// Reviewed: GS - August 2, 2002
// AG - August 8, 2002
// AG - Microsoft 16, 2007
//
//===================================================================
#region Used namespaces
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
using System.Collections.ObjectModel;
#endregion
#if Microsoft_CONTROL
namespace System.Windows.Forms.DataVisualization.Charting
#else
namespace System.Web.UI.DataVisualization.Charting
#endif
{
#region Enumerations
/// <summary>
/// An enumeration that specifies a label alignment.
/// </summary>
[
Flags
]
public enum LabelAlignmentStyles
{
/// <summary>
/// Label is aligned to the top of the data point.
/// </summary>
Top = 1,
/// <summary>
/// Label is aligned to the bottom of the data point.
/// </summary>
Bottom = 2,
/// <summary>
/// Label is aligned to the right of the data point.
/// </summary>
Right = 4,
/// <summary>
/// Label is aligned to the left of the data point.
/// </summary>
Left = 8,
/// <summary>
/// Label is aligned to the top-left corner of the data point.
/// </summary>
TopLeft = 16,
/// <summary>
/// Label is aligned to the top-right corner of the data point.
/// </summary>
TopRight = 32,
/// <summary>
/// Label is aligned to the bottom-left of the data point.
/// </summary>
BottomLeft = 64,
/// <summary>
/// Label is aligned to the bottom-right of the data point.
/// </summary>
BottomRight = 128,
/// <summary>
/// Label is aligned to the center of the data point.
/// </summary>
Center = 256,
}
/// <summary>
/// An enumeration of chart types.
/// </summary>
public enum SeriesChartType
{
/// <summary>
/// Point chart type.
/// </summary>
Point,
/// <summary>
/// FastPoint chart type.
/// </summary>
FastPoint,
/// <summary>
/// Bubble chart type.
/// </summary>
Bubble,
/// <summary>
/// Line chart type.
/// </summary>
Line,
/// <summary>
/// Spline chart type.
/// </summary>
Spline,
/// <summary>
/// StepLine chart type.
/// </summary>
StepLine,
/// <summary>
/// FastLine chart type.
/// </summary>
FastLine,
/// <summary>
/// Bar chart type.
/// </summary>
Bar,
/// <summary>
/// Stacked bar chart type.
/// </summary>
StackedBar,
/// <summary>
/// Hundred percent stacked bar chart type.
/// </summary>
StackedBar100,
/// <summary>
/// Column chart type.
/// </summary>
Column,
/// <summary>
/// Stacked column chart type.
/// </summary>
StackedColumn,
/// <summary>
/// Hundred percent stacked column chart type.
/// </summary>
StackedColumn100,
/// <summary>
/// Area chart type.
/// </summary>
Area,
/// <summary>
/// Spline area chart type.
/// </summary>
SplineArea,
/// <summary>
/// Stacked area chart type.
/// </summary>
StackedArea,
/// <summary>
/// Hundred percent stacked area chart type.
/// </summary>
StackedArea100,
/// <summary>
/// Pie chart type.
/// </summary>
Pie,
/// <summary>
/// Doughnut chart type.
/// </summary>
Doughnut,
/// <summary>
/// Stock chart type.
/// </summary>
Stock,
/// <summary>
/// CandleStick chart type.
/// </summary>
Candlestick,
/// <summary>
/// Range chart type.
/// </summary>
Range,
/// <summary>
/// Spline range chart type.
/// </summary>
SplineRange,
/// <summary>
/// RangeBar chart type.
/// </summary>
RangeBar,
/// <summary>
/// Range column chart type.
/// </summary>
RangeColumn,
/// <summary>
/// Radar chart type.
/// </summary>
Radar,
/// <summary>
/// Polar chart type.
/// </summary>
Polar,
/// <summary>
/// Error bar chart type.
/// </summary>
ErrorBar,
/// <summary>
/// Box plot chart type.
/// </summary>
BoxPlot,
/// <summary>
/// Renko chart type.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Renko")]
Renko,
/// <summary>
/// ThreeLineBreak chart type.
/// </summary>
ThreeLineBreak,
/// <summary>
/// Kagi chart type.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Kagi")]
Kagi,
/// <summary>
/// PointAndFigure chart type.
/// </summary>
PointAndFigure,
/// <summary>
/// Funnel chart type.
/// </summary>
Funnel,
/// <summary>
/// Pyramid chart type.
/// </summary>
Pyramid,
}
/// <summary>
/// Axis Arrow orientation
/// </summary>
internal enum ArrowOrientation
{
/// <summary>
/// Arrow direction is Right - Left
/// </summary>
Left,
/// <summary>
/// Arrow direction is Left - Right
/// </summary>
Right,
/// <summary>
/// Arrow direction is Bottom - Top
/// </summary>
Top,
/// <summary>
/// Arrow direction is Top - Bottom
/// </summary>
Bottom
}
/// <summary>
/// An enumeration of image alignment.
/// </summary>
public enum ChartImageAlignmentStyle
{
/// <summary>
/// The mage is aligned to the top left corner of the chart element.
/// </summary>
TopLeft,
/// <summary>
/// The image is aligned to the top boundary of the chart element.
/// </summary>
Top,
/// <summary>
/// The image is aligned to the top right corner of the chart element.
/// </summary>
TopRight,
/// <summary>
/// The image is aligned to the right boundary of the chart element.
/// </summary>
Right,
/// <summary>
/// The image is aligned to the bottom right corner of the chart element.
/// </summary>
BottomRight,
/// <summary>
/// The image is aligned to the bottom boundary of the chart element.
/// </summary>
Bottom,
/// <summary>
/// The image is aligned to the bottom left corner of the chart element.
/// </summary>
BottomLeft,
/// <summary>
/// The image is aligned to the left boundary of the chart element.
/// </summary>
Left,
/// <summary>
/// The image is aligned in the center of the chart element.
/// </summary>
Center
};
/// <summary>
/// An enumeration that specifies a background image drawing mode.
/// </summary>
public enum ChartImageWrapMode
{
/// <summary>
/// Background image is scaled to fit the entire chart element.
/// </summary>
Scaled = WrapMode.Clamp,
/// <summary>
/// Background image is tiled to fit the entire chart element.
/// </summary>
Tile = WrapMode.Tile,
/// <summary>
/// Every other tiled image is reversed around the X-axis.
/// </summary>
TileFlipX = WrapMode.TileFlipX,
/// <summary>
/// Every other tiled image is reversed around the X-axis and Y-axis.
/// </summary>
TileFlipXY = WrapMode.TileFlipXY,
/// <summary>
/// Every other tiled image is reversed around the Y-axis.
/// </summary>
TileFlipY = WrapMode.TileFlipY,
/// <summary>
/// Background image is not scaled.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Unscaled")]
Unscaled = 100
};
/// <summary>
/// An enumeration that specifies the state of an axis.
/// </summary>
public enum AxisEnabled
{
/// <summary>
/// The axis is only enabled if it used to plot a Series.
/// </summary>
Auto,
/// <summary>
/// The axis is always enabled.
/// </summary>
True,
/// <summary>
/// The axis is never enabled.
/// </summary>
False
};
/// <summary>
/// An enumeration of units of measurement of an interval.
/// </summary>
public enum DateTimeIntervalType
{
/// <summary>
/// Automatically determined by the Chart control.
/// </summary>
Auto,
/// <summary>
/// The interval is numerical.
/// </summary>
Number,
/// <summary>
/// The interval is years.
/// </summary>
Years,
/// <summary>
/// The interval is months.
/// </summary>
Months,
/// <summary>
/// The interval is weeks.
/// </summary>
Weeks,
/// <summary>
/// The interval is days.
/// </summary>
Days,
/// <summary>
/// The interval is hours.
/// </summary>
Hours,
/// <summary>
/// The interval is minutes.
/// </summary>
Minutes,
/// <summary>
/// The interval is seconds.
/// </summary>
Seconds,
/// <summary>
/// The interval is milliseconds.
/// </summary>
Milliseconds,
/// <summary>
/// The interval type is not defined.
/// </summary>
NotSet,
}
/// <summary>
/// An enumeration that specifies value types for various chart properties
/// </summary>
public enum ChartValueType
{
/// <summary>
/// Property type is set automatically by the Chart control.
/// </summary>
Auto,
/// <summary>
/// Double value.
/// </summary>
Double,
/// <summary>
/// Single value.
/// </summary>
Single,
/// <summary>
/// Int32 value.
/// </summary>
Int32,
/// <summary>
/// Int64 value.
/// </summary>
Int64,
/// <summary>
/// UInt32 value.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly",
Justification = "These names are patterned after the standard CLR types for consistency")]
UInt32,
/// <summary>
/// UInt64 value.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly",
Justification = "These names are patterned after the standard CLR types for consistency")]
UInt64,
/// <summary>
/// String value.
/// </summary>
String,
/// <summary>
/// DateTime value.
/// </summary>
DateTime,
/// <summary>
/// Date portion of the DateTime value.
/// </summary>
Date,
/// <summary>
/// Time portion of the DateTime value.
/// </summary>
Time,
/// <summary>
/// DateTime with offset
/// </summary>
DateTimeOffset
};
/// <summary>
/// An enumeration that specifies a hatching style.
/// </summary>
public enum ChartHatchStyle
{
/// <summary>
/// No hatching style.
/// </summary>
None,
/// <summary>
/// Backward diagonal style.
/// </summary>
BackwardDiagonal,
/// <summary>
/// Cross style.
/// </summary>
Cross,
/// <summary>
/// Dark downward diagonal style.
/// </summary>
DarkDownwardDiagonal,
/// <summary>
/// Dark horizontal style.
/// </summary>
DarkHorizontal,
/// <summary>
/// Dark upward diagonal style.
/// </summary>
DarkUpwardDiagonal,
/// <summary>
/// Dark vertical style.
/// </summary>
DarkVertical,
/// <summary>
/// Dashed downward diagonal style.
/// </summary>
DashedDownwardDiagonal,
/// <summary>
/// Dashed horizontal style.
/// </summary>
DashedHorizontal,
/// <summary>
/// Dashed upward diagonal style.
/// </summary>
DashedUpwardDiagonal,
/// <summary>
/// Dashed vertical style.
/// </summary>
DashedVertical,
/// <summary>
/// Diagonal brick style.
/// </summary>
DiagonalBrick,
/// <summary>
/// Diagonal cross style.
/// </summary>
DiagonalCross,
/// <summary>
/// Divot style.
/// </summary>
Divot,
/// <summary>
/// Dotted diamond style.
/// </summary>
DottedDiamond,
/// <summary>
/// Dotted grid style.
/// </summary>
DottedGrid,
/// <summary>
/// Forward diagonal style.
/// </summary>
ForwardDiagonal,
/// <summary>
/// Horizontal style.
/// </summary>
Horizontal,
/// <summary>
/// Horizontal brick style.
/// </summary>
HorizontalBrick,
/// <summary>
/// Large checker board style.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "CheckerBoard")]
LargeCheckerBoard,
/// <summary>
/// Large confetti style.
/// </summary>
LargeConfetti,
/// <summary>
/// Large grid style.
/// </summary>
LargeGrid,
/// <summary>
/// Light downward diagonal style.
/// </summary>
LightDownwardDiagonal,
/// <summary>
/// Light horizontal style.
/// </summary>
LightHorizontal,
/// <summary>
/// Light upward diagonal style.
/// </summary>
LightUpwardDiagonal,
/// <summary>
/// Light vertical style.
/// </summary>
LightVertical,
/// <summary>
/// Narrow horizontal style.
/// </summary>
NarrowHorizontal,
/// <summary>
/// Narrow vertical style.
/// </summary>
NarrowVertical,
/// <summary>
/// Outlined diamond style.
/// </summary>
OutlinedDiamond,
/// <summary>
/// Percent05 style.
/// </summary>
Percent05,
/// <summary>
/// Percent10 style.
/// </summary>
Percent10,
/// <summary>
/// Percent20 style.
/// </summary>
Percent20,
/// <summary>
/// Percent25 style.
/// </summary>
Percent25,
/// <summary>
/// Percent30 style.
/// </summary>
Percent30,
/// <summary>
/// Percent40 style.
/// </summary>
Percent40,
/// <summary>
/// Percent50 style.
/// </summary>
Percent50,
/// <summary>
/// Percent60 style.
/// </summary>
Percent60,
/// <summary>
/// Percent70 style.
/// </summary>
Percent70,
/// <summary>
/// Percent75 style.
/// </summary>
Percent75,
/// <summary>
/// Percent80 style.
/// </summary>
Percent80,
/// <summary>
/// Percent90 style.
/// </summary>
Percent90,
/// <summary>
/// Plaid style.
/// </summary>
Plaid,
/// <summary>
/// Shingle style.
/// </summary>
Shingle,
/// <summary>
/// Small checker board style.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "CheckerBoard")]
SmallCheckerBoard,
/// <summary>
/// Small confetti style.
/// </summary>
SmallConfetti,
/// <summary>
/// Small grid style.
/// </summary>
SmallGrid,
/// <summary>
/// Solid diamond style.
/// </summary>
SolidDiamond,
/// <summary>
/// Sphere style.
/// </summary>
Sphere,
/// <summary>
/// Trellis style.
/// </summary>
Trellis,
/// <summary>
/// Vertical style.
/// </summary>
Vertical,
/// <summary>
/// Wave style.
/// </summary>
Wave,
/// <summary>
/// Weave style.
/// </summary>
Weave,
/// <summary>
/// Wide downward diagonal style.
/// </summary>
WideDownwardDiagonal,
/// <summary>
/// Wide upward diagonal style.
/// </summary>
WideUpwardDiagonal,
/// <summary>
/// ZigZag style.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ZigZag")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Zig")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Zag")]
ZigZag
};
/// <summary>
/// An enumeration that specifies the level of anti-aliasing quality.
/// </summary>
public enum TextAntiAliasingQuality
{
/// <summary>
/// Normal anti-aliasing quality.
/// </summary>
Normal,
/// <summary>
/// High anti-aliasing quality.
/// </summary>
High,
/// <summary>
/// System default anti-aliasing quality.
/// </summary>
SystemDefault
}
/// <summary>
/// An enumeration of anti-aliasing flags.
/// </summary>
[Flags]
public enum AntiAliasingStyles
{
/// <summary>
/// No anti-aliasing.
/// </summary>
None = 0,
/// <summary>
/// Use anti-aliasing when drawing text.
/// </summary>
Text = 1,
/// <summary>
/// Use anti-aliasing when drawing grahics primitives (e.g. lines, rectangle)
/// </summary>
Graphics = 2,
/// <summary>
/// Use anti-alias for everything.
/// </summary>
All = Text | Graphics
};
/// <summary>
/// An enumeration of marker styles.
/// </summary>
public enum MarkerStyle
{
/// <summary>
/// No marker is displayed for the series/data point.
/// </summary>
None = 0,
/// <summary>
/// A square marker is displayed.
/// </summary>
Square = 1,
/// <summary>
/// A circle marker is displayed.
/// </summary>
Circle = 2,
/// <summary>
/// A diamond-shaped marker is displayed.
/// </summary>
Diamond = 3,
/// <summary>
/// A triangular marker is displayed.
/// </summary>
Triangle = 4,
/// <summary>
/// A cross-shaped marker is displayed.
/// </summary>
Cross = 5,
/// <summary>
/// A 4-point star-shaped marker is displayed.
/// </summary>
Star4 = 6,
/// <summary>
/// A 5-point star-shaped marker is displayed.
/// </summary>
Star5 = 7,
/// <summary>
/// A 6-point star-shaped marker is displayed.
/// </summary>
Star6 = 8,
/// <summary>
/// A 10-point star-shaped marker is displayed.
/// </summary>
Star10 = 9
};
/// <summary>
/// An enumeration of gradient styles.
/// </summary>
public enum GradientStyle
{
/// <summary>
/// No gradient is used.
/// </summary>
None,
/// <summary>
/// Gradient is applied from left to right.
/// </summary>
LeftRight,
/// <summary>
/// Gradient is applied from top to bottom.
/// </summary>
TopBottom,
/// <summary>
/// Gradient is applied from the center outwards.
/// </summary>
Center,
/// <summary>
/// Gradient is applied diagonally from left to right.
/// </summary>
DiagonalLeft,
/// <summary>
/// Gradient is applied diagonally from right to left.
/// </summary>
DiagonalRight,
/// <summary>
/// Gradient is applied horizontally from the center outwards.
/// </summary>
HorizontalCenter,
/// <summary>
/// Gradient is applied vertically from the center outwards.
/// </summary>
VerticalCenter
};
#endregion
#region ChartElement
/// <summary>
/// Common chart helper methods used across different chart elements.
/// </summary>
internal class ChartHelper
{
#region Fields
/// <summary>
/// Maximum number of grid lines per Axis
/// </summary>
internal const int MaxNumOfGridlines = 10000;
#endregion // Fields
#region Constructor
/// <summary>
/// Private constructor to avoid instantiating the class
/// </summary>
private ChartHelper() { }
#endregion // Constructor
#region Methods
/// <summary>
/// Adjust the beginnin of the first interval depending on the type and size.
/// </summary>
/// <param name="start">Original start point.</param>
/// <param name="intervalSize">Interval size.</param>
/// <param name="type">AxisName of the interval (Month, Year, ...).</param>
/// <returns>Adjusted interval start position as double.</returns>
internal static double AlignIntervalStart(double start, double intervalSize, DateTimeIntervalType type)
{
return AlignIntervalStart(start, intervalSize, type, null);
}
/// <summary>
/// Adjust the beginnin of the first interval depending on the type and size.
/// </summary>
/// <param name="start">Original start point.</param>
/// <param name="intervalSize">Interval size.</param>
/// <param name="type">AxisName of the interval (Month, Year, ...).</param>
/// <param name="series">First series connected to the axis.</param>
/// <returns>Adjusted interval start position as double.</returns>
internal static double AlignIntervalStart(double start, double intervalSize, DateTimeIntervalType type, Series series)
{
return AlignIntervalStart( start, intervalSize, type, series, true );
}
/// <summary>
/// Adjust the beginnin of the first interval depending on the type and size.
/// </summary>
/// <param name="start">Original start point.</param>
/// <param name="intervalSize">Interval size.</param>
/// <param name="type">AxisName of the interval (Month, Year, ...).</param>
/// <param name="series">First series connected to the axis.</param>
/// <param name="majorInterval">Interval is used for major gridlines or tickmarks.</param>
/// <returns>Adjusted interval start position as double.</returns>
internal static double AlignIntervalStart(double start, double intervalSize, DateTimeIntervalType type, Series series, bool majorInterval)
{
// Special case for indexed series
if(series != null && series.IsXValueIndexed)
{
if(type == DateTimeIntervalType.Auto ||
type == DateTimeIntervalType.Number)
{
if( majorInterval )
{
return 1;
}
else
{
return 0;
}
}
return -(series.Points.Count + 1);
}
// Non indexed series
else
{
// Do not adjust start position for these interval type
if(type == DateTimeIntervalType.Auto ||
type == DateTimeIntervalType.Number)
{
return start;
}
// Get the beginning of the interval depending on type
DateTime newStartDate = DateTime.FromOADate(start);
// Adjust the months interval depending on size
if(intervalSize > 0.0 && intervalSize != 1.0)
{
if(type == DateTimeIntervalType.Months && intervalSize <= 12.0 && intervalSize > 1)
{
// Make sure that the beginning is aligned correctly for cases
// like quarters and half years
DateTime resultDate = newStartDate;
DateTime sizeAdjustedDate = new DateTime(newStartDate.Year, 1, 1, 0, 0, 0);
while(sizeAdjustedDate < newStartDate)
{
resultDate = sizeAdjustedDate;
sizeAdjustedDate = sizeAdjustedDate.AddMonths((int)intervalSize);
}
newStartDate = resultDate;
return newStartDate.ToOADate();
}
}
// Check interval type
switch(type)
{
case(DateTimeIntervalType.Years):
int year = (int)((int)(newStartDate.Year / intervalSize) * intervalSize);
if(year <= 0)
{
year = 1;
}
newStartDate = new DateTime(year,
1, 1, 0, 0, 0);
break;
case(DateTimeIntervalType.Months):
int month = (int)((int)(newStartDate.Month / intervalSize) * intervalSize);
if(month <= 0)
{
month = 1;
}
newStartDate = new DateTime(newStartDate.Year,
month, 1, 0, 0, 0);
break;
case(DateTimeIntervalType.Days):
int day = (int)((int)(newStartDate.Day / intervalSize) * intervalSize);
if(day <= 0)
{
day = 1;
}
newStartDate = new DateTime(newStartDate.Year,
newStartDate.Month, day, 0, 0, 0);
break;
case(DateTimeIntervalType.Hours):
int hour = (int)((int)(newStartDate.Hour / intervalSize) * intervalSize);
newStartDate = new DateTime(newStartDate.Year,
newStartDate.Month, newStartDate.Day, hour, 0, 0);
break;
case(DateTimeIntervalType.Minutes):
int minute = (int)((int)(newStartDate.Minute / intervalSize) * intervalSize);
newStartDate = new DateTime(newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
minute,
0);
break;
case(DateTimeIntervalType.Seconds):
int second = (int)((int)(newStartDate.Second / intervalSize) * intervalSize);
newStartDate = new DateTime(newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
newStartDate.Minute,
second,
0);
break;
case(DateTimeIntervalType.Milliseconds):
int milliseconds = (int)((int)(newStartDate.Millisecond / intervalSize) * intervalSize);
newStartDate = new DateTime(newStartDate.Year,
newStartDate.Month,
newStartDate.Day,
newStartDate.Hour,
newStartDate.Minute,
newStartDate.Second,
milliseconds);
break;
case(DateTimeIntervalType.Weeks):
// NOTE: Code below was changed to fix issue #5962
// Elements that have interval set to weeks should be aligned to the
// nearest Monday no matter how many weeks is the interval.
//newStartDate = newStartDate.AddDays(-((int)newStartDate.DayOfWeek * intervalSize));
newStartDate = newStartDate.AddDays(-((int)newStartDate.DayOfWeek));
newStartDate = new DateTime(newStartDate.Year,
newStartDate.Month, newStartDate.Day, 0, 0, 0);
break;
}
return newStartDate.ToOADate();
}
}
/// <summary>
/// Gets interval size as double number.
/// </summary>
/// <param name="current">Current value.</param>
/// <param name="interval">Interval size.</param>
/// <param name="type">AxisName of the interval (Month, Year, ...).</param>
/// <returns>Interval size as double.</returns>
internal static double GetIntervalSize(double current, double interval, DateTimeIntervalType type)
{
return GetIntervalSize(
current,
interval,
type,
null,
0,
DateTimeIntervalType.Number,
true,
true);
}
/// <summary>
/// Gets interval size as double number.
/// </summary>
/// <param name="current">Current value.</param>
/// <param name="interval">Interval size.</param>
/// <param name="type">AxisName of the interval (Month, Year, ...).</param>
/// <param name="series">First series connected to the axis.</param>
/// <param name="intervalOffset">Offset size.</param>
/// <param name="intervalOffsetType">Offset type(Month, Year, ...).</param>
/// <param name="forceIntIndex">Force Integer indexed</param>
/// <returns>Interval size as double.</returns>
internal static double GetIntervalSize(
double current,
double interval,
DateTimeIntervalType type,
Series series,
double intervalOffset,
DateTimeIntervalType intervalOffsetType,
bool forceIntIndex)
{
return GetIntervalSize(
current,
interval,
type,
series,
intervalOffset,
intervalOffsetType,
forceIntIndex,
true);
}
/// <summary>
/// Gets interval size as double number.
/// </summary>
/// <param name="current">Current value.</param>
/// <param name="interval">Interval size.</param>
/// <param name="type">AxisName of the interval (Month, Year, ...).</param>
/// <param name="series">First series connected to the axis.</param>
/// <param name="intervalOffset">Offset size.</param>
/// <param name="intervalOffsetType">Offset type(Month, Year, ...).</param>
/// <param name="forceIntIndex">Force Integer indexed</param>
/// <param name="forceAbsInterval">Force Integer indexed</param>
/// <returns>Interval size as double.</returns>
internal static double GetIntervalSize(
double current,
double interval,
DateTimeIntervalType type,
Series series,
double intervalOffset,
DateTimeIntervalType intervalOffsetType,
bool forceIntIndex,
bool forceAbsInterval)
{
// AxisName is not date.
if( type == DateTimeIntervalType.Number || type == DateTimeIntervalType.Auto )
{
return interval;
}
// Special case for indexed series
if(series != null && series.IsXValueIndexed)
{
// Check point index
int pointIndex = (int)Math.Ceiling(current - 1);
if(pointIndex < 0)
{
pointIndex = 0;
}
if(pointIndex >= series.Points.Count || series.Points.Count <= 1)
{
return interval;
}
// Get starting and ending values of the closest interval
double adjuster = 0;
double xValue = series.Points[pointIndex].XValue;
xValue = AlignIntervalStart(xValue, 1, type, null);
double xEndValue = xValue + GetIntervalSize(xValue, interval, type);
xEndValue += GetIntervalSize(xEndValue, intervalOffset, intervalOffsetType);
xValue += GetIntervalSize(xValue, intervalOffset, intervalOffsetType);
if(intervalOffset < 0)
{
xValue = xValue + GetIntervalSize(xValue, interval, type);
xEndValue = xEndValue + GetIntervalSize(xEndValue, interval, type);
}
// The first point in the series
if(pointIndex == 0 && current < 0)
{
// Round the first point value depending on the interval type
DateTime dateValue = DateTime.FromOADate(series.Points[pointIndex].XValue);
DateTime roundedDateValue = dateValue;
switch(type)
{
case(DateTimeIntervalType.Years): // Ignore hours,...
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month, dateValue.Day, 0, 0, 0);
break;
case(DateTimeIntervalType.Months): // Ignore hours,...
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month, dateValue.Day, 0, 0, 0);
break;
case(DateTimeIntervalType.Days): // Ignore hours,...
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month, dateValue.Day, 0, 0, 0);
break;
case(DateTimeIntervalType.Hours): //
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month, dateValue.Day, dateValue.Hour,
dateValue.Minute, 0);
break;
case(DateTimeIntervalType.Minutes):
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month,
dateValue.Day,
dateValue.Hour,
dateValue.Minute,
dateValue.Second);
break;
case(DateTimeIntervalType.Seconds):
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month,
dateValue.Day,
dateValue.Hour,
dateValue.Minute,
dateValue.Second,
0);
break;
case(DateTimeIntervalType.Weeks):
roundedDateValue = new DateTime(dateValue.Year,
dateValue.Month, dateValue.Day, 0, 0, 0);
break;
}
// The first point value is exactly on the interval boundaries
if(roundedDateValue.ToOADate() == xValue || roundedDateValue.ToOADate() == xEndValue)
{
return - current + 1;
}
}
// Adjuster of 0.5 means that position should be between points
++pointIndex;
while(pointIndex < series.Points.Count)
{
if(series.Points[pointIndex].XValue >= xEndValue)
{
if(series.Points[pointIndex].XValue > xEndValue && !forceIntIndex)
{
adjuster = -0.5;
}
break;
}
++pointIndex;
}
// If last point outside of the max series index
if(pointIndex == series.Points.Count)
{
pointIndex += series.Points.Count/5 + 1;
}
double size = (pointIndex + 1) - current + adjuster;
return (size != 0) ? size : interval;
}
// Non indexed series
else
{
DateTime date = DateTime.FromOADate(current);
TimeSpan span = new TimeSpan(0);
if(type == DateTimeIntervalType.Days)
{
span = TimeSpan.FromDays(interval);
}
else if(type == DateTimeIntervalType.Hours)
{
span = TimeSpan.FromHours(interval);
}
else if(type == DateTimeIntervalType.Milliseconds)
{
span = TimeSpan.FromMilliseconds(interval);
}
else if(type == DateTimeIntervalType.Seconds)
{
span = TimeSpan.FromSeconds(interval);
}
else if(type == DateTimeIntervalType.Minutes)
{
span = TimeSpan.FromMinutes(interval);
}
else if(type == DateTimeIntervalType.Weeks)
{
span = TimeSpan.FromDays(7.0 * interval);
}
else if(type == DateTimeIntervalType.Months)
{
// Special case handling when current date points
// to the last day of the month
bool lastMonthDay = false;
if(date.Day == DateTime.DaysInMonth(date.Year, date.Month))
{
lastMonthDay = true;
}
// Add specified amount of months
date = date.AddMonths((int)Math.Floor(interval));
span = TimeSpan.FromDays(30.0 * ( interval - Math.Floor(interval) ));
// Check if last month of the day was used
if(lastMonthDay && span.Ticks == 0)
{
// Make sure the last day of the month is selected
int daysInMobth = DateTime.DaysInMonth(date.Year, date.Month);
date = date.AddDays(daysInMobth - date.Day);
}
}
else if(type == DateTimeIntervalType.Years)
{
date = date.AddYears((int)Math.Floor(interval));
span = TimeSpan.FromDays(365.0 * ( interval - Math.Floor(interval) ));
}
// Check if an absolute interval size must be returned
double result = date.Add(span).ToOADate() - current;
if(forceAbsInterval)
{
result = Math.Abs(result);
}
return result;
}
}
/// <summary>
/// Check if series is indexed. IsXValueIndexed flag is set or all X values are zeros.
/// </summary>
/// <param name="series">Data series to test.</param>
/// <returns>True if series is indexed.</returns>
static internal bool IndexedSeries( Series series)
{
// X value indexed flag set
if (series.IsXValueIndexed)
{
return true;
}
if (Utilities.CustomPropertyRegistry.IsXAxisQuantitativeChartTypes.Contains(series.ChartType) &&
series.IsCustomPropertySet(Utilities.CustomPropertyName.IsXAxisQuantitative))
{
string attribValue = series[Utilities.CustomPropertyName.IsXAxisQuantitative];
if (String.Compare(attribValue, "True", StringComparison.OrdinalIgnoreCase) == 0)
{
return false;
}
}
// Check if series has all X values set to zero
return SeriesXValuesZeros(series);
}
/// <summary>
/// Check if all data points in the series have X value set to 0.
/// </summary>
/// <param name="series">Data series to check.</param>
static private bool SeriesXValuesZeros( Series series )
{
// Check if X value zeros check was already done
if(series.xValuesZerosChecked)
{
return series.xValuesZeros;
}
// Data point loop
series.xValuesZerosChecked = true;
series.xValuesZeros = true;
foreach( DataPoint point in series.Points )
{
if( point.XValue != 0.0 )
{
// If any data point has value different than 0 return false
series.xValuesZeros = false;
break;
}
}
return series.xValuesZeros;
}
/// <summary>
/// Check if any series is indexed. IsXValueIndexed flag is set or all X values are zeros.
/// </summary>
/// <param name="common">Reference to common chart classes.</param>
/// <param name="series">Data series names.</param>
/// <returns>True if any series is indexed.</returns>
static internal bool IndexedSeries(CommonElements common, params string[] series)
{
// Data series loop
bool zeroXValues = true;
foreach (string ser in series)
{
Series localSeries = common.DataManager.Series[ser];
// Check series indexed flag
if (localSeries.IsXValueIndexed)
{
// If flag set in at least one series - all series are indexed
return true;
}
// Check if series has all X values set to zero
if (zeroXValues && !IndexedSeries(localSeries))
{
zeroXValues = false;
}
}
return zeroXValues;
}
/// <summary>
/// Check if all data points in many series have X value set to 0.
/// </summary>
/// <param name="common">Reference to common chart classes.</param>
/// <param name="series">Data series.</param>
/// <returns>True if all data points have value 0.</returns>
static internal bool SeriesXValuesZeros(CommonElements common, params string[] series)
{
// Data series loop
foreach( string ser in series )
{
// Check one series X values
if(!SeriesXValuesZeros(common.DataManager.Series[ ser ]))
{
return false;
}
}
return true;
}
#endregion
}
#endregion //ChartElement
}
|