Xamarin Public Jenkins (auto-signing) 536cd135cc Imported Upstream version 5.4.0.167
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
2017-08-21 15:34:15 +00:00

1015 lines
29 KiB
C#
Raw 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: 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
}
}