536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1015 lines
29 KiB
C#
1015 lines
29 KiB
C#
//-------------------------------------------------------------
|
||
// <copyright company=’Microsoft Corporation’>
|
||
// Copyright © Microsoft Corporation. All Rights Reserved.
|
||
// </copyright>
|
||
//-------------------------------------------------------------
|
||
// @owner=alexgor, deliant
|
||
//=================================================================
|
||
// File: AxisScaleSegments.cs
|
||
//
|
||
// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
|
||
//
|
||
// Classes: AxisScaleSegment, AxisScaleSegmentCollection
|
||
//
|
||
// Purpose:
|
||
//
|
||
// Reviewed:
|
||
//
|
||
//===================================================================
|
||
|
||
#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;
|
||
#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;
|
||
|
||
#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;
|
||
#endif
|
||
|
||
#endregion
|
||
|
||
#if Microsoft_CONTROL
|
||
namespace System.Windows.Forms.DataVisualization.Charting
|
||
#else
|
||
namespace System.Web.UI.DataVisualization.Charting
|
||
|
||
#endif
|
||
{
|
||
/// <summary>
|
||
/// <b>AxisScaleSegment</b> class represents a single segment of the axis with
|
||
/// it's own scale and intervals.
|
||
/// </summary>
|
||
[
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_AxisScaleSegment"),
|
||
]
|
||
internal class AxisScaleSegment
|
||
{
|
||
#region Fields
|
||
|
||
// Associated axis
|
||
internal Axis axis = null;
|
||
|
||
// Axis segment position in percent of the axis size
|
||
private double _position = 0.0;
|
||
|
||
// Axis segment size in percent of the axis size
|
||
private double _size = 0.0;
|
||
|
||
// Axis segment spacing in percent of the axis size
|
||
private double _spacing = 0.0;
|
||
|
||
// Axis segment scale minimum value
|
||
private double _scaleMinimum = 0.0;
|
||
|
||
// Axis segment scale maximum value
|
||
private double _scaleMaximum = 0.0;
|
||
|
||
// Axis segment interval offset.
|
||
private double _intervalOffset = 0;
|
||
|
||
// Axis segment interval.
|
||
private double _interval = 0;
|
||
|
||
// Axis segment interval units type.
|
||
private DateTimeIntervalType _intervalType = DateTimeIntervalType.Auto;
|
||
|
||
// Axis segment interval offset units type.
|
||
private DateTimeIntervalType _intervalOffsetType = DateTimeIntervalType.Auto;
|
||
|
||
// Object associated with the segment
|
||
private object _tag = null;
|
||
|
||
// Stack used to save/load axis settings
|
||
private Stack _oldAxisSettings = new Stack();
|
||
|
||
#endregion // Fields
|
||
|
||
#region Constructor
|
||
|
||
/// <summary>
|
||
/// Default object constructor.
|
||
/// </summary>
|
||
public AxisScaleSegment()
|
||
{
|
||
}
|
||
|
||
#endregion // Constructor
|
||
|
||
#region Properties
|
||
|
||
/// <summary>
|
||
/// Axis segment position in axis size percentage.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeMisc"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_Position"),
|
||
]
|
||
public double Position
|
||
{
|
||
get
|
||
{
|
||
return this._position;
|
||
}
|
||
set
|
||
{
|
||
if(value < 0.0 || value > 100.0)
|
||
{
|
||
throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleSegmentsPositionInvalid));
|
||
}
|
||
this._position = value;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment size in axis size percentage.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeMisc"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_Size"),
|
||
]
|
||
public double Size
|
||
{
|
||
get
|
||
{
|
||
return this._size;
|
||
}
|
||
set
|
||
{
|
||
if(value < 0.0 || value > 100.0)
|
||
{
|
||
throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleSegmentsSizeInvalid));
|
||
}
|
||
this._size = value;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment spacing in axis size percentage.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeMisc"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_Spacing"),
|
||
]
|
||
public double Spacing
|
||
{
|
||
get
|
||
{
|
||
return this._spacing;
|
||
}
|
||
set
|
||
{
|
||
if(value < 0.0 || value > 100.0)
|
||
{
|
||
throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleSegmentsSpacingInvalid));
|
||
}
|
||
this._spacing = value;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment scale maximum value.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeMisc"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_ScaleMaximum"),
|
||
]
|
||
public double ScaleMaximum
|
||
{
|
||
get
|
||
{
|
||
return this._scaleMaximum;
|
||
}
|
||
set
|
||
{
|
||
this._scaleMaximum = value;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment scale minimum value.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeMisc"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_ScaleMinimum"),
|
||
]
|
||
public double ScaleMinimum
|
||
{
|
||
get
|
||
{
|
||
return this._scaleMinimum;
|
||
}
|
||
set
|
||
{
|
||
this._scaleMinimum = value;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Axis segment interval size.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeInterval"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_Interval"),
|
||
TypeConverter(typeof(AxisIntervalValueConverter)),
|
||
]
|
||
public double Interval
|
||
{
|
||
get
|
||
{
|
||
return this._interval;
|
||
}
|
||
set
|
||
{
|
||
// Axis interval properties must be set
|
||
if(double.IsNaN(value))
|
||
{
|
||
this._interval = 0;
|
||
}
|
||
else
|
||
{
|
||
this._interval = value;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment interval offset.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeInterval"),
|
||
DefaultValue(0.0),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_IntervalOffset"),
|
||
TypeConverter(typeof(AxisIntervalValueConverter))
|
||
]
|
||
public double IntervalOffset
|
||
{
|
||
get
|
||
{
|
||
return _intervalOffset;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment interval type.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeInterval"),
|
||
DefaultValue(DateTimeIntervalType.Auto),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_IntervalType"),
|
||
]
|
||
public DateTimeIntervalType IntervalType
|
||
{
|
||
get
|
||
{
|
||
return this._intervalType;
|
||
}
|
||
set
|
||
{
|
||
// Axis interval properties must be set
|
||
if(value == DateTimeIntervalType.NotSet)
|
||
{
|
||
this._intervalType = DateTimeIntervalType.Auto;
|
||
}
|
||
else
|
||
{
|
||
_intervalType = value;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Axis segment interval offset type.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeInterval"),
|
||
DefaultValue(DateTimeIntervalType.Auto),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_IntervalOffsetType"),
|
||
]
|
||
public DateTimeIntervalType IntervalOffsetType
|
||
{
|
||
get
|
||
{
|
||
return this._intervalOffsetType;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Object associated with axis scale segment.
|
||
/// </summary>
|
||
[
|
||
SRCategory("CategoryAttributeMisc"),
|
||
Browsable(false),
|
||
DefaultValue(null),
|
||
SRDescription("DescriptionAttributeAxisScaleSegment_Tag"),
|
||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
|
||
SerializationVisibility(SerializationVisibility.Hidden),
|
||
]
|
||
public object Tag
|
||
{
|
||
get
|
||
{
|
||
return this._tag;
|
||
}
|
||
set
|
||
{
|
||
this._tag = value;
|
||
}
|
||
}
|
||
|
||
#endregion // Properties
|
||
|
||
#region Break Line Painting Methods
|
||
|
||
/// <summary>
|
||
/// Paints the axis break line.
|
||
/// </summary>
|
||
/// <param name="graph">Chart graphics to use.</param>
|
||
/// <param name="nextSegment">Axis scale segment next to current.</param>
|
||
internal void PaintBreakLine(ChartGraphics graph, AxisScaleSegment nextSegment)
|
||
{
|
||
// Get break line position
|
||
RectangleF breakPosition = this.GetBreakLinePosition(graph, nextSegment);
|
||
|
||
// Get top line graphics path
|
||
GraphicsPath breakLinePathTop = this.GetBreakLinePath(breakPosition, true);
|
||
GraphicsPath breakLinePathBottom = null;
|
||
|
||
// Clear break line space using chart color behind the area
|
||
if(breakPosition.Width > 0f && breakPosition.Height > 0f)
|
||
{
|
||
// Get bottom line graphics path
|
||
breakLinePathBottom = this.GetBreakLinePath(breakPosition, false);
|
||
|
||
// Clear plotting area background
|
||
using(GraphicsPath fillPath = new GraphicsPath())
|
||
{
|
||
// Create fill path out of top and bottom break lines
|
||
fillPath.AddPath(breakLinePathTop, true);
|
||
fillPath.Reverse();
|
||
fillPath.AddPath(breakLinePathBottom, true);
|
||
fillPath.CloseAllFigures();
|
||
|
||
// Use chart back color to fill the area
|
||
using(Brush fillBrush = this.GetChartFillBrush(graph))
|
||
{
|
||
graph.FillPath(fillBrush, fillPath);
|
||
|
||
// Check if shadow exsits in chart area
|
||
if( this.axis.ChartArea.ShadowOffset != 0 && !this.axis.ChartArea.ShadowColor.IsEmpty)
|
||
{
|
||
// Clear shadow
|
||
RectangleF shadowPartRect = breakPosition;
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
shadowPartRect.Y += this.axis.ChartArea.ShadowOffset;
|
||
shadowPartRect.Height -= this.axis.ChartArea.ShadowOffset;
|
||
shadowPartRect.X = shadowPartRect.Right - 1;
|
||
shadowPartRect.Width = this.axis.ChartArea.ShadowOffset + 2;
|
||
}
|
||
else
|
||
{
|
||
shadowPartRect.X += this.axis.ChartArea.ShadowOffset;
|
||
shadowPartRect.Width -= this.axis.ChartArea.ShadowOffset;
|
||
shadowPartRect.Y = shadowPartRect.Bottom - 1;
|
||
shadowPartRect.Height = this.axis.ChartArea.ShadowOffset + 2;
|
||
}
|
||
graph.FillRectangle(fillBrush, shadowPartRect);
|
||
|
||
// Draw new shadow
|
||
using(GraphicsPath shadowPath = new GraphicsPath())
|
||
{
|
||
shadowPath.AddPath(breakLinePathTop, false);
|
||
|
||
// Define maximum size
|
||
float size = this.axis.ChartArea.ShadowOffset;
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
size = Math.Min(size, breakPosition.Height);
|
||
}
|
||
else
|
||
{
|
||
size = Math.Min(size, breakPosition.Width);
|
||
}
|
||
|
||
// Define step to increase transperancy
|
||
int transparencyStep = (int)(this.axis.ChartArea.ShadowColor.A / size);
|
||
|
||
// Set clip region to achieve spacing of the shadow
|
||
// Start with the plotting rectangle position
|
||
RectangleF clipRegion = graph.GetAbsoluteRectangle(this.axis.PlotAreaPosition.ToRectangleF());
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
clipRegion.X += this.axis.ChartArea.ShadowOffset;
|
||
clipRegion.Width += this.axis.ChartArea.ShadowOffset;
|
||
}
|
||
else
|
||
{
|
||
clipRegion.Y += this.axis.ChartArea.ShadowOffset;
|
||
clipRegion.Height += this.axis.ChartArea.ShadowOffset;
|
||
}
|
||
graph.SetClip(graph.GetRelativeRectangle(clipRegion));
|
||
|
||
// Draw several lines to form shadow
|
||
for(int index = 0; index < size; index ++)
|
||
{
|
||
using(Matrix newMatrix = new Matrix())
|
||
{
|
||
// Shift top break line by 1 pixel
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
newMatrix.Translate(0f, 1f);
|
||
}
|
||
else
|
||
{
|
||
newMatrix.Translate(1f, 0f);
|
||
}
|
||
shadowPath.Transform(newMatrix);
|
||
}
|
||
|
||
// Get line color
|
||
Color color = Color.FromArgb(
|
||
this.axis.ChartArea.ShadowColor.A - transparencyStep * index,
|
||
this.axis.ChartArea.ShadowColor);
|
||
|
||
using(Pen shadowPen = new Pen(color, 1))
|
||
{
|
||
// Draw shadow
|
||
graph.DrawPath(shadowPen, shadowPath);
|
||
}
|
||
}
|
||
|
||
graph.ResetClip();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Draw Separator Line(s)
|
||
if(this.axis.ScaleBreakStyle.BreakLineStyle != BreakLineStyle.None)
|
||
{
|
||
using(Pen pen = new Pen(this.axis.ScaleBreakStyle.LineColor, this.axis.ScaleBreakStyle.LineWidth))
|
||
{
|
||
// Set line style
|
||
pen.DashStyle = graph.GetPenStyle(this.axis.ScaleBreakStyle.LineDashStyle);
|
||
|
||
// Draw break lines
|
||
graph.DrawPath(pen, breakLinePathTop);
|
||
if(breakPosition.Width > 0f && breakPosition.Height > 0f)
|
||
{
|
||
graph.DrawPath(pen, breakLinePathBottom);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Dispose break line paths
|
||
breakLinePathTop.Dispose();
|
||
breakLinePathTop = null;
|
||
if(breakLinePathBottom != null)
|
||
{
|
||
breakLinePathBottom.Dispose();
|
||
breakLinePathBottom = null;
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// Get fill brush used to fill axis break lines spacing.
|
||
/// </summary>
|
||
/// <param name="graph">chart graphics.</param>
|
||
/// <returns>Fill brush.</returns>
|
||
private Brush GetChartFillBrush(ChartGraphics graph)
|
||
{
|
||
Chart chart = this.axis.ChartArea.Common.Chart;
|
||
Brush brush = null;
|
||
|
||
if( chart.BackGradientStyle == GradientStyle.None )
|
||
{
|
||
brush = new SolidBrush(chart.BackColor);
|
||
}
|
||
else
|
||
{
|
||
// If a gradient type is set create a brush with gradient
|
||
brush = graph.GetGradientBrush(new RectangleF(0, 0, chart.chartPicture.Width - 1, chart.chartPicture.Height - 1), chart.BackColor, chart.BackSecondaryColor, chart.BackGradientStyle);
|
||
}
|
||
|
||
if( chart.BackHatchStyle != ChartHatchStyle.None )
|
||
{
|
||
brush = graph.GetHatchBrush( chart.BackHatchStyle, chart.BackColor, chart.BackSecondaryColor );
|
||
}
|
||
|
||
if( chart.BackImage.Length > 0 &&
|
||
chart.BackImageWrapMode != ChartImageWrapMode.Unscaled &&
|
||
chart.BackImageWrapMode != ChartImageWrapMode.Scaled)
|
||
{
|
||
brush = graph.GetTextureBrush(chart.BackImage, chart.BackImageTransparentColor, chart.BackImageWrapMode, chart.BackColor );
|
||
}
|
||
|
||
return brush;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gets a path of the break line for specified position.
|
||
/// </summary>
|
||
/// <param name="breakLinePosition">Break line position.</param>
|
||
/// <param name="top">Indicates if top or bottom break line path should be retrieved.</param>
|
||
/// <returns>Graphics path with break line path.</returns>
|
||
private GraphicsPath GetBreakLinePath(RectangleF breakLinePosition, bool top)
|
||
{
|
||
GraphicsPath path = new GraphicsPath();
|
||
|
||
if(this.axis.ScaleBreakStyle.BreakLineStyle == BreakLineStyle.Wave)
|
||
{
|
||
PointF[] points = null;
|
||
int pointNumber = 0;
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
float startX = breakLinePosition.X;
|
||
float endX = breakLinePosition.Right;
|
||
float y = (top) ? breakLinePosition.Y : breakLinePosition.Bottom;
|
||
pointNumber = ((int)(endX - startX) / 40) * 2 ;
|
||
if(pointNumber < 2)
|
||
{
|
||
pointNumber = 2;
|
||
}
|
||
float step = (endX - startX) / pointNumber;
|
||
points = new PointF[pointNumber + 1];
|
||
for(int pointIndex = 1; pointIndex < pointNumber + 1; pointIndex++)
|
||
{
|
||
points[pointIndex] = new PointF(
|
||
startX + pointIndex * step,
|
||
y + ((pointIndex%2 == 0) ? -2f : 2f) );
|
||
}
|
||
|
||
points[0] = new PointF(startX, y);
|
||
points[points.Length - 1] = new PointF(endX, y);
|
||
}
|
||
else
|
||
{
|
||
float startY = breakLinePosition.Y;
|
||
float endY = breakLinePosition.Bottom;
|
||
float x = (top) ? breakLinePosition.X : breakLinePosition.Right;
|
||
pointNumber = ((int)(endY - startY) / 40) * 2 ;
|
||
if(pointNumber < 2)
|
||
{
|
||
pointNumber = 2;
|
||
}
|
||
float step = (endY - startY) / pointNumber;
|
||
points = new PointF[pointNumber + 1];
|
||
for(int pointIndex = 1; pointIndex < pointNumber + 1; pointIndex++)
|
||
{
|
||
points[pointIndex] = new PointF(
|
||
x + ((pointIndex%2 == 0) ? -2f : 2f),
|
||
startY + pointIndex * step);
|
||
}
|
||
|
||
points[0] = new PointF(x, startY);
|
||
points[points.Length - 1] = new PointF(x, endY);
|
||
}
|
||
|
||
path.AddCurve(points, 0, pointNumber, 0.8f);
|
||
}
|
||
else if(this.axis.ScaleBreakStyle.BreakLineStyle == BreakLineStyle.Ragged)
|
||
{
|
||
PointF[] points = null;
|
||
Random rand = new Random(435657);
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
float startX = breakLinePosition.X;
|
||
float endX = breakLinePosition.Right;
|
||
float y = (top) ? breakLinePosition.Y : breakLinePosition.Bottom;
|
||
float step = 10f;
|
||
int pointNumber = (int)((endX - startX) / step);
|
||
if(pointNumber < 2)
|
||
{
|
||
pointNumber = 2;
|
||
}
|
||
points = new PointF[pointNumber];
|
||
|
||
for(int pointIndex = 1; pointIndex < pointNumber - 1; pointIndex++)
|
||
{
|
||
points[pointIndex] = new PointF(
|
||
startX + pointIndex * step,
|
||
y + rand.Next(-3, 3) );
|
||
}
|
||
|
||
points[0] = new PointF(startX, y);
|
||
points[points.Length - 1] = new PointF(endX, y);
|
||
}
|
||
else
|
||
{
|
||
float startY = breakLinePosition.Y;
|
||
float endY = breakLinePosition.Bottom;
|
||
float x = (top) ? breakLinePosition.X : breakLinePosition.Right;
|
||
float step = 10f;
|
||
int pointNumber = (int)((endY - startY) / step);
|
||
if(pointNumber < 2)
|
||
{
|
||
pointNumber = 2;
|
||
}
|
||
points = new PointF[pointNumber];
|
||
|
||
for(int pointIndex = 1; pointIndex < pointNumber - 1; pointIndex++)
|
||
{
|
||
points[pointIndex] = new PointF(
|
||
x + rand.Next(-3, 3),
|
||
startY + pointIndex * step );
|
||
}
|
||
|
||
points[0] = new PointF(x, startY);
|
||
points[points.Length - 1] = new PointF(x, endY);
|
||
}
|
||
|
||
path.AddLines(points);
|
||
}
|
||
else
|
||
{
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
if(top)
|
||
{
|
||
path.AddLine(breakLinePosition.X, breakLinePosition.Y, breakLinePosition.Right, breakLinePosition.Y);
|
||
}
|
||
else
|
||
{
|
||
path.AddLine(breakLinePosition.X, breakLinePosition.Bottom, breakLinePosition.Right, breakLinePosition.Bottom);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if(top)
|
||
{
|
||
path.AddLine(breakLinePosition.X, breakLinePosition.Y, breakLinePosition.X, breakLinePosition.Bottom);
|
||
}
|
||
else
|
||
{
|
||
path.AddLine(breakLinePosition.Right, breakLinePosition.Y, breakLinePosition.Right, breakLinePosition.Bottom);
|
||
}
|
||
}
|
||
}
|
||
return path;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Gets position of the axis break line. Break line may be shown as a single
|
||
/// line or two lines separated with a spacing.
|
||
/// </summary>
|
||
/// <param name="graph">Chart graphics.</param>
|
||
/// <param name="nextSegment">Next segment reference.</param>
|
||
/// <returns>Position of the axis break line in pixel coordinates.</returns>
|
||
internal RectangleF GetBreakLinePosition(ChartGraphics graph, AxisScaleSegment nextSegment)
|
||
{
|
||
// Start with the plotting rectangle position
|
||
RectangleF breakPosition = this.axis.PlotAreaPosition.ToRectangleF();
|
||
|
||
// Find maximum scale value of the current segment and minimuj of the next
|
||
double from = this.axis.GetLinearPosition(nextSegment.ScaleMinimum);
|
||
double to = this.axis.GetLinearPosition(this.ScaleMaximum);
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
breakPosition.Y = (float)Math.Min(from, to);
|
||
breakPosition.Height = (float)Math.Max(from, to);
|
||
}
|
||
else
|
||
{
|
||
breakPosition.X = (float)Math.Min(from, to);
|
||
breakPosition.Width = (float)Math.Max(from, to);;
|
||
}
|
||
|
||
// Convert to pixels
|
||
breakPosition = Rectangle.Round(graph.GetAbsoluteRectangle(breakPosition));
|
||
|
||
// Add border width
|
||
if( this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left )
|
||
{
|
||
breakPosition.Height = (float)Math.Abs(breakPosition.Y - breakPosition.Height);
|
||
breakPosition.X -= this.axis.ChartArea.BorderWidth;
|
||
breakPosition.Width += 2 * this.axis.ChartArea.BorderWidth;
|
||
}
|
||
else
|
||
{
|
||
breakPosition.Width = (float)Math.Abs(breakPosition.X - breakPosition.Width);
|
||
breakPosition.Y -= this.axis.ChartArea.BorderWidth;
|
||
breakPosition.Height += 2 * this.axis.ChartArea.BorderWidth;
|
||
}
|
||
|
||
return breakPosition;
|
||
}
|
||
|
||
#endregion // Break Line Painting Methods
|
||
|
||
#region Helper Methods
|
||
|
||
/// <summary>
|
||
/// Gets segment scale position and size in relative coordinates.
|
||
/// Method takes in consideration segment spacing and space required fro separatorType.
|
||
/// </summary>
|
||
/// <param name="plotAreaSize">Plotting area size in relative coordinates.</param>
|
||
/// <param name="scalePosition">Returns scale position.</param>
|
||
/// <param name="scaleSize">Returns scale size.</param>
|
||
internal void GetScalePositionAndSize(double plotAreaSize, out double scalePosition, out double scaleSize)
|
||
{
|
||
scaleSize = (this.Size - this.Spacing) * (plotAreaSize / 100.0);
|
||
scalePosition = this.Position * (plotAreaSize / 100.0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Saves axis settings and set them from the current segment.
|
||
/// </summary>
|
||
internal void SetTempAxisScaleAndInterval()
|
||
{
|
||
// Save current axis settings
|
||
if(_oldAxisSettings.Count == 0)
|
||
{
|
||
_oldAxisSettings.Push(this.axis.maximum);
|
||
_oldAxisSettings.Push(this.axis.minimum);
|
||
|
||
_oldAxisSettings.Push(this.axis.majorGrid.interval);
|
||
_oldAxisSettings.Push(this.axis.majorGrid.intervalType);
|
||
_oldAxisSettings.Push(this.axis.majorGrid.intervalOffset);
|
||
_oldAxisSettings.Push(this.axis.majorGrid.intervalOffsetType);
|
||
|
||
_oldAxisSettings.Push(this.axis.majorTickMark.interval);
|
||
_oldAxisSettings.Push(this.axis.majorTickMark.intervalType);
|
||
_oldAxisSettings.Push(this.axis.majorTickMark.intervalOffset);
|
||
_oldAxisSettings.Push(this.axis.majorTickMark.intervalOffsetType);
|
||
|
||
_oldAxisSettings.Push(this.axis.LabelStyle.interval);
|
||
_oldAxisSettings.Push(this.axis.LabelStyle.intervalType);
|
||
_oldAxisSettings.Push(this.axis.LabelStyle.intervalOffset);
|
||
_oldAxisSettings.Push(this.axis.LabelStyle.intervalOffsetType);
|
||
}
|
||
|
||
// Copy settings from the segment into the axis
|
||
this.axis.maximum = this.ScaleMaximum;
|
||
this.axis.minimum = this.ScaleMinimum;
|
||
|
||
this.axis.majorGrid.interval = this.Interval;
|
||
this.axis.majorGrid.intervalType = this.IntervalType;
|
||
this.axis.majorGrid.intervalOffset = this.IntervalOffset;
|
||
this.axis.majorGrid.intervalOffsetType = this.IntervalOffsetType;
|
||
|
||
this.axis.majorTickMark.interval = this.Interval;
|
||
this.axis.majorTickMark.intervalType = this.IntervalType;
|
||
this.axis.majorTickMark.intervalOffset = this.IntervalOffset;
|
||
this.axis.majorTickMark.intervalOffsetType = this.IntervalOffsetType;
|
||
|
||
this.axis.LabelStyle.interval = this.Interval;
|
||
this.axis.LabelStyle.intervalType = this.IntervalType;
|
||
this.axis.LabelStyle.intervalOffset = this.IntervalOffset;
|
||
this.axis.LabelStyle.intervalOffsetType = this.IntervalOffsetType;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Restore previously saved axis settings.
|
||
/// </summary>
|
||
internal void RestoreAxisScaleAndInterval()
|
||
{
|
||
if(_oldAxisSettings.Count > 0)
|
||
{
|
||
this.axis.LabelStyle.intervalOffsetType = (DateTimeIntervalType)_oldAxisSettings.Pop();
|
||
this.axis.LabelStyle.intervalOffset = (double)_oldAxisSettings.Pop();
|
||
this.axis.LabelStyle.intervalType = (DateTimeIntervalType)_oldAxisSettings.Pop();
|
||
this.axis.LabelStyle.interval = (double)_oldAxisSettings.Pop();
|
||
|
||
this.axis.majorTickMark.intervalOffsetType = (DateTimeIntervalType)_oldAxisSettings.Pop();
|
||
this.axis.majorTickMark.intervalOffset = (double)_oldAxisSettings.Pop();
|
||
this.axis.majorTickMark.intervalType = (DateTimeIntervalType)_oldAxisSettings.Pop();
|
||
this.axis.majorTickMark.interval = (double)_oldAxisSettings.Pop();
|
||
|
||
this.axis.majorGrid.intervalOffsetType = (DateTimeIntervalType)_oldAxisSettings.Pop();
|
||
this.axis.majorGrid.intervalOffset = (double)_oldAxisSettings.Pop();
|
||
this.axis.majorGrid.intervalType = (DateTimeIntervalType)_oldAxisSettings.Pop();
|
||
this.axis.majorGrid.interval = (double)_oldAxisSettings.Pop();
|
||
|
||
this.axis.minimum = (double)_oldAxisSettings.Pop();
|
||
this.axis.maximum = (double)_oldAxisSettings.Pop();
|
||
}
|
||
}
|
||
|
||
#endregion // Helper Methods
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// <b>AxisScaleSegmentCollection</b> is a class that stores collection of axis segments.
|
||
/// </summary>
|
||
[
|
||
SRDescription("DescriptionAttributeAxisScaleSegmentCollection_AxisScaleSegmentCollection"),
|
||
]
|
||
internal class AxisScaleSegmentCollection : CollectionBase
|
||
{
|
||
#region Fields
|
||
|
||
// Axis this segment collection belongs to.
|
||
private Axis _axis = null;
|
||
|
||
// Segment which is always used to convert scale values.
|
||
// This value is set tmporarly when only one segment has
|
||
// to handle all the values.
|
||
private AxisScaleSegment _enforcedSegment = null;
|
||
|
||
// Indicates that values allowed to be outside of the scale segment.
|
||
// Otherwise they will be rounded to Min and Max values.
|
||
internal bool AllowOutOfScaleValues = false;
|
||
|
||
#endregion // Fields
|
||
|
||
#region Construction and Initialization
|
||
|
||
/// <summary>
|
||
/// Default public constructor.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// This constructor is for internal use and should not be part of documentation.
|
||
/// </remarks>
|
||
public AxisScaleSegmentCollection()
|
||
{
|
||
}
|
||
|
||
/// <summary>
|
||
/// Default public constructor.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// This constructor is for internal use and should not be part of documentation.
|
||
/// </remarks>
|
||
/// <param name="axis">
|
||
/// Chart axis this collection belongs to
|
||
/// </param>
|
||
internal AxisScaleSegmentCollection(Axis axis)
|
||
{
|
||
this._axis = axis;
|
||
}
|
||
|
||
#endregion // Construction and Initialization
|
||
|
||
#region Indexer
|
||
|
||
/// <summary>
|
||
/// Axis scale segment collection indexer.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// The <b>AxisScaleSegment</b> object index can be provided as a parameter. Returns the <see cref="AxisScaleSegment"/> object.
|
||
/// </remarks>
|
||
[
|
||
SRDescription("DescriptionAttributeAxisScaleSegmentCollection_Item"),
|
||
]
|
||
public AxisScaleSegment this[int index]
|
||
{
|
||
get
|
||
{
|
||
return (AxisScaleSegment)this.List[(int)index];
|
||
}
|
||
}
|
||
|
||
#endregion // Indexer
|
||
|
||
#region Collection Add and Insert methods
|
||
|
||
/// <summary>
|
||
/// Adds a segment to the end of the collection.
|
||
/// </summary>
|
||
/// <param name="segment">
|
||
/// <see cref="AxisScaleSegment"/> object to add.
|
||
/// </param>
|
||
/// <returns>
|
||
/// Index of the newly added object.
|
||
/// </returns>
|
||
public int Add(AxisScaleSegment segment)
|
||
{
|
||
return this.List.Add(segment);
|
||
}
|
||
|
||
|
||
#endregion // Collection Add and Insert methods
|
||
|
||
#region Items Inserting and Removing Notification methods
|
||
|
||
/// <summary>
|
||
/// After new item inserted.
|
||
/// </summary>
|
||
/// <param name="index">Item index.</param>
|
||
/// <param name="value">Item object.</param>
|
||
/// <remarks>
|
||
/// This is an internal method and should not be part of the documentation.
|
||
/// </remarks>
|
||
protected override void OnInsertComplete(int index, object value)
|
||
{
|
||
((AxisScaleSegment)value).axis = this._axis;
|
||
}
|
||
|
||
/// <summary>
|
||
/// After items is set.
|
||
/// </summary>
|
||
/// <param name="index">The zero-based index at which oldValue can be found.</param>
|
||
/// <param name="oldValue">The value to replace with newValue.</param>
|
||
/// <param name="newValue">The new value of the element at index.</param>
|
||
/// <remarks>
|
||
/// This is an internal method and should not be part of the documentation.
|
||
/// </remarks>
|
||
protected override void OnSetComplete(int index, object oldValue, object newValue)
|
||
{
|
||
((AxisScaleSegment)newValue).axis = this._axis;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Helper Methods
|
||
|
||
/// <summary>
|
||
/// Ensures that specified axis scale segment is used for all coordinate transformations.
|
||
/// Set tot NULL to reset.
|
||
/// </summary>
|
||
/// <param name="segment"></param>
|
||
internal void EnforceSegment(AxisScaleSegment segment)
|
||
{
|
||
this._enforcedSegment = segment;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Find axis scale segment that should be used to translate axis value to relative coordinates.
|
||
/// </summary>
|
||
/// <param name="axisValue">Axis value to convert.</param>
|
||
/// <returns>Scale segment to use for the convertion.</returns>
|
||
public AxisScaleSegment FindScaleSegmentForAxisValue(double axisValue)
|
||
{
|
||
// Check if no segments defined
|
||
if(this.List.Count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// Check if segment enforcment is enabled
|
||
if(_enforcedSegment != null)
|
||
{
|
||
return _enforcedSegment;
|
||
}
|
||
|
||
// Iterate through all segments
|
||
for(int index = 0; index < this.Count; index++)
|
||
{
|
||
if(axisValue < this[index].ScaleMinimum)
|
||
{
|
||
if(index == 0)
|
||
{
|
||
return this[index];
|
||
}
|
||
else
|
||
{
|
||
// Find the segment which is "closer" to the value
|
||
if( Math.Abs(this[index].ScaleMinimum - axisValue) < Math.Abs(axisValue - this[index - 1].ScaleMaximum))
|
||
{
|
||
return this[index];
|
||
}
|
||
else
|
||
{
|
||
return this[index - 1];
|
||
}
|
||
}
|
||
}
|
||
|
||
if(axisValue <= this[index].ScaleMaximum)
|
||
{
|
||
return this[index];
|
||
}
|
||
else if(index == this.Count - 1)
|
||
{
|
||
return this[index];
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
#endregion // Helper Methods
|
||
}
|
||
}
|
||
|