1 //-------------------------------------------------------------
2 // <copyright company=�Microsoft Corporation�>
3 //   Copyright � Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:		AxisScale.cs
9 //
10 //  Namespace:	System.Web.UI.WebControls[Windows.Forms].Charting
11 //
12 //	Classes:	AxisScale
13 //
14 //  Purpose:	Base class for the Axis class which defines axis
15 //				csale related properties and methods.
16 //
17 //	Reviewed:	GS Aug 8, 2002
18 //				AG Aug 8, 2002
19 //
20 //===================================================================
21 
22 #region Used namespaces
23 
24 using System;
25 using System.Collections;
26 using System.Collections.Specialized;
27 using System.ComponentModel;
28 using System.ComponentModel.Design;
29 using System.Data;
30 using System.Drawing;
31 using System.Drawing.Design;
32 using System.Drawing.Drawing2D;
33 using System.Collections.Generic;
34 #if Microsoft_CONTROL
35 
36 	using System.Windows.Forms.DataVisualization.Charting.Data;
37 	using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
38 	using System.Windows.Forms.DataVisualization.Charting.Utilities;
39 	using System.Windows.Forms.DataVisualization.Charting.Borders3D;
40 	using System.Windows.Forms.DataVisualization.Charting;
41 
42 #else
43 	using System.Web;
44 	using System.Web.UI;
45 	using System.Web.UI.DataVisualization.Charting;
46 	using System.Web.UI.DataVisualization.Charting.Data;
47 	using System.Web.UI.DataVisualization.Charting.ChartTypes;
48 	using System.Web.UI.DataVisualization.Charting.Utilities;
49 #endif
50 
51 #endregion
52 
53 #if Microsoft_CONTROL
54 	namespace System.Windows.Forms.DataVisualization.Charting
55 #else
56 namespace System.Web.UI.DataVisualization.Charting
57 
58 #endif
59 {
60 	#region Axis enumerations
61 
62 	/// <summary>
63 	/// An enumeration of the mode of automatically calculating intervals.
64 	/// </summary>
65 	public enum IntervalAutoMode
66 	{
67 		/// <summary>
68 		/// Fixed number of intervals always created on the axis.
69 		/// </summary>
70 		FixedCount,
71 
72 		/// <summary>
73 		/// Number of axis intervals depends on the axis length.
74 		/// </summary>
75 		VariableCount
76 	}
77 
78 
79 
80 	/// <summary>
81 	/// An enumeration of axis position.
82 	/// </summary>
83 	internal enum AxisPosition
84 	{
85 		/// <summary>
86 		/// Left position
87 		/// </summary>
88 		Left,
89 
90 		/// <summary>
91 		/// Right position
92 		/// </summary>
93 		Right,
94 
95 		/// <summary>
96 		/// Top position
97 		/// </summary>
98 		Top,
99 
100 		/// <summary>
101 		/// Bottom position
102 		/// </summary>
103 		Bottom
104 	}
105 
106 	/// <summary>
107 	/// An enumeration of axis arrow styles.
108 	/// </summary>
109 	public enum AxisArrowStyle
110 	{
111 		/// <summary>
112 		/// No arrow
113 		/// </summary>
114 		None,
115 		/// <summary>
116 		/// Triangle type
117 		/// </summary>
118 		Triangle,
119 		/// <summary>
120 		/// Sharp triangle type
121 		/// </summary>
122 		SharpTriangle,
123 		/// <summary>
124 		/// Lines type
125 		/// </summary>
126 		Lines
127 	}
128 
129 	#endregion
130 
131 	/// <summary>
132 	/// The Axis class keeps information about minimum, maximum
133 	/// and interval values and it is responsible for setting
134 	/// these values automatically. It also handles
135 	/// logarithmic and reversed axis.
136 	/// </summary>
137 	public partial class Axis
138 	{
139 		#region Axis scale fields
140 
141 		// Represents the distance between the data points and its
142 		// chart area margin, Measured as a percentage of default
143 		// margin size.
144 		internal double					margin = 100.0;
145 		internal double					marginView = 0.0;
146 		internal bool					offsetTempSet = false;
147 
148 		// Used for column chart margin
149 		internal double					marginTemp = 0.0;
150 		private ArrayList				_stripLineOffsets = new ArrayList();
151 
152 
153 		// Data members, which store properties values
154 		private  bool					_isLogarithmic = false;
155 		internal double					logarithmBase = 10.0;
156 		internal bool					isReversed = false;
157 		internal bool					isStartedFromZero = true;
158 		internal TickMark				minorTickMark = null;
159 		internal TickMark				majorTickMark = null;
160 		internal Grid					minorGrid = null;
161 		internal Grid					majorGrid = null;
162 		internal bool					enabled = false;
163 		internal bool					autoEnabled = true;
164 		internal LabelStyle					labelStyle = null;
165 		private	 DateTimeIntervalType	_internalIntervalType = DateTimeIntervalType.Auto;
166 		internal double					maximum = Double.NaN;
167 		internal double					crossing = Double.NaN;
168 		internal double					minimum = Double.NaN;
169 
170 		// Temporary Minimum and maximum values.
171 		internal double					tempMaximum = Double.NaN;
172 		internal double					tempMinimum = Double.NaN;
173 		internal double					tempCrossing = Double.NaN;
174 		internal CustomLabelsCollection	tempLabels;
175 		internal bool					tempAutoMaximum = true;
176 		internal bool					tempAutoMinimum = true;
177 		internal double					tempMajorGridInterval = Double.NaN;
178 		internal double					tempMinorGridInterval = 0.0;
179 		internal double					tempMajorTickMarkInterval = Double.NaN;
180 		internal double					tempMinorTickMarkInterval = 0.0;
181 		internal double					tempLabelInterval = Double.NaN;
182 		internal DateTimeIntervalType	tempGridIntervalType = DateTimeIntervalType.NotSet;
183 		internal DateTimeIntervalType	tempTickMarkIntervalType = DateTimeIntervalType.NotSet;
184 		internal DateTimeIntervalType	tempLabelIntervalType = DateTimeIntervalType.NotSet;
185 
186 		// Paint mode
187 		internal bool					paintMode = false;
188 
189 		// Axis type (X, Y, X2, Y2)
190 		internal AxisName				axisType = AxisName.X;
191 
192 		// Automatic maximum value (from data point values).
193 		private bool					_autoMaximum = true;
194 
195 		// Automatic minimum value (from data point values).
196 		private bool					_autoMinimum = true;
197 
198 		/// <summary>
199 		/// Axis position: Left, Right, Top Bottom
200 		/// </summary>
201 		private AxisPosition			_axisPosition = AxisPosition.Left;
202 
203 		/// <summary>
204 		/// Opposite Axis for this Axis. Necessary for Crossing.
205 		/// </summary>
206 		internal Axis					oppositeAxis = null;
207 
208 		// Axis data scaleView
209 		private	AxisScaleView			_scaleView = null;
210 
211 #if Microsoft_CONTROL
212 
213 		// Axis scroll bar class
214 		internal AxisScrollBar			scrollBar = null;
215 
216 #endif // Microsoft_CONTROL
217 
218 		// For scater chart X values could be rounded.
219 		internal bool roundedXValues = false;
220 
221 		// If Axis is logarithmic value shoud be converted to
222 		// linear only once.
223 		internal bool logarithmicConvertedToLinear = false;
224 
225 		// IsLogarithmic minimum value
226 		internal double logarithmicMinimum;
227 
228 		// IsLogarithmic maximum value
229 		internal double logarithmicMaximum;
230 
231 		// Correction of interval because of
232 		// 3D Rotation and perspective
233 		internal double interval3DCorrection = Double.NaN;
234 
235 		// Axis coordinate convertion optimization fields
236 		internal bool optimizedGetPosition = false;
237 		internal double paintViewMax = 0.0;
238 		internal double paintViewMin = 0.0;
239 		internal double	paintRange = 0.0;
240 		internal double	valueMultiplier = 0.0;
241 		internal RectangleF	paintAreaPosition = RectangleF.Empty;
242 		internal double paintAreaPositionBottom = 0.0;
243 		internal double paintAreaPositionRight = 0.0;
244 		internal double paintChartAreaSize = 0.0;
245 
246 
247 
248 		// Determines how number of intervals automatically calculated
249 		private IntervalAutoMode _intervalAutoMode = IntervalAutoMode.FixedCount;
250 
251 		// True if scale segments are used
252 		internal bool scaleSegmentsUsed = false;
253 
254 
255 
256 		// Preffered number of intervals on the axis
257 		internal int  prefferedNumberofIntervals = 5;
258 
259         private Stack<Double> _intervalsStore = new Stack<Double>();
260 
261 		#endregion
262 
263 		#region Axis scale properties
264 
265 		/// <summary>
266 		/// Axis position
267 		/// </summary>
268 		[
269 		Bindable(true),
270 		DefaultValue(AxisPosition.Left),
271 		NotifyParentPropertyAttribute(true),
272 		SRDescription("DescriptionAttributeReverse"),
273 		#if !Microsoft_CONTROL
274 		PersistenceMode(PersistenceMode.Attribute),
275 		#endif
276 		DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
277 		SerializationVisibilityAttribute(SerializationVisibility.Hidden)
278 		]
279 		virtual internal AxisPosition AxisPosition
280 		{
281 			get
282 			{
283                 return this._axisPosition;
284 			}
285 			set
286 			{
287                 this._axisPosition = value;
288 #if SUBAXES
289 				// Update axis position of the sub axis
290 				if( !((Axis)this).IsSubAxis )
291 				{
292 					foreach(SubAxis subAxis in ((Axis)this).SubAxes)
293 					{
294 						subAxis._axisPosition = value;
295 					}
296 				}
297 
298 #endif // SUBAXES
299                 this.Invalidate();
300 			}
301 		}
302 
303 
304 
305 		/// <summary>
306         /// Gets or sets a flag which indicates whether the number of intervals
307         /// on the axis is fixed or varies with the axis size.
308 		/// </summary>
309 		[
310 		SRCategory("CategoryAttributeInterval"),
311 		DefaultValue(IntervalAutoMode.FixedCount),
312 		SRDescription("DescriptionAttributeIntervalAutoMode"),
313 		]
314 		public IntervalAutoMode IntervalAutoMode
315 		{
316 			get
317 			{
318 				return this._intervalAutoMode;
319 			}
320 			set
321 			{
322 				this._intervalAutoMode = value;
323 				this.Invalidate();
324 			}
325 		}
326 
327 
328 
329 		/// <summary>
330 		/// Gets or sets a flag which indicates whether the axis is reversed.
331         /// If set to reversed, the values on the axis are in reversed sort order
332         /// and the direction of values on the axis is flipped.
333 		/// </summary>
334 		[
335 		SRCategory("CategoryAttributeScale"),
336 		Bindable(true),
337 		DefaultValue(false),
338 		NotifyParentPropertyAttribute(true),
339 		SRDescription("DescriptionAttributeReverse"),
340 		#if !Microsoft_CONTROL
341 		PersistenceMode(PersistenceMode.Attribute)
342 		#endif
343 		]
344 		public bool IsReversed
345 		{
346 			get
347 			{
348 				return isReversed;
349 			}
350 			set
351 			{
352 				isReversed = value;
353 				this.Invalidate();
354 			}
355 		}
356 
357 		/// <summary>
358         /// Gets or sets a flag which indicates whether the minimum value
359         /// of the axis will be automatically set to zero if all data point
360         /// values are positive.  If there are negative data point values,
361         /// the minimum value of the data points will be used.
362 		/// </summary>
363 		[
364 		SRCategory("CategoryAttributeScale"),
365 		Bindable(true),
366 		DefaultValue(true),
367 		NotifyParentPropertyAttribute(true),
368 		SRDescription("DescriptionAttributeStartFromZero3"),
369 		#if !Microsoft_CONTROL
370 		PersistenceMode(PersistenceMode.Attribute)
371 		#endif
372 		]
373 		public bool IsStartedFromZero
374 		{
375 			get
376 			{
377 				return isStartedFromZero;
378 			}
379 			set
380 			{
381 				isStartedFromZero = value;
382 				this.Invalidate();
383 			}
384 		}
385 
386 		/// <summary>
387         /// Gets or sets a flag to add a margin to the axis.
388 		/// If true, a space is added between the first/last data
389 		/// point and the border of chart area.
390 		/// </summary>
391 		[
392 		SRCategory("CategoryAttributeScale"),
393 		Bindable(true),
394 		DefaultValue(true),
395 		NotifyParentPropertyAttribute(true),
396 		SRDescription("DescriptionAttributeMargin"),
397 		#if !Microsoft_CONTROL
398 		PersistenceMode(PersistenceMode.Attribute)
399 		#endif
400 		]
401 		public bool IsMarginVisible
402 		{
403 			get
404 			{
405 				if( margin > 0 )
406 					return true;
407 				else
408 					return false;
409 			}
410 			set
411 			{
412 				if( value == true )
413 					margin = 100;
414 				else
415 					margin = 0;
416 
417 				this.Invalidate();
418 			}
419 		}
420 
421 		/// <summary>
422 		/// Date and time interval type.
423 		/// </summary>
424 		[
425 		SRCategory("CategoryAttributeScale"),
426 		Bindable(true),
427 		DefaultValue(DateTimeIntervalType.Auto),
428 		NotifyParentPropertyAttribute(true),
429 		SRDescription("DescriptionAttributeInternalIntervalType"),
430 		RefreshPropertiesAttribute(RefreshProperties.All),
431 		#if !Microsoft_CONTROL
432 		PersistenceMode(PersistenceMode.Attribute)
433 		#endif
434 		]
435 		internal DateTimeIntervalType InternalIntervalType
436 		{
437 			get
438 			{
439                 return _internalIntervalType;
440 			}
441 			set
442 			{
443 				// Set intervals for labels, grids and tick marks. ( Auto interval type )
444 				if( tempMajorGridInterval <= 0.0 ||
445 					(double.IsNaN(tempMajorGridInterval) && ((Axis)this).Interval <= 0.0) )
446 				{
447 					majorGrid.intervalType = value;
448 				}
449 
450 				if( this.tempMajorTickMarkInterval <= 0.0 ||
451 					(double.IsNaN(tempMajorTickMarkInterval) && ((Axis)this).Interval <= 0.0) )
452 				{
453 					majorTickMark.intervalType = value;
454 				}
455 
456 				if( this.tempLabelInterval <= 0.0 ||
457 					(double.IsNaN(tempLabelInterval) && ((Axis)this).Interval <= 0.0) )
458 				{
459 					labelStyle.intervalType = value;
460 				}
461 
462                 _internalIntervalType = value;
463 
464 				this.Invalidate();
465 			}
466 		}
467 
468 		/// <summary>
469 		/// Sets auto interval values to grids, tick marks
470 		/// and labels
471 		/// </summary>
472 		internal double SetInterval
473 		{
474 			set
475 			{
476 				if( tempMajorGridInterval <= 0.0 ||
477 					(double.IsNaN(tempMajorGridInterval) && ((Axis)this).Interval <= 0.0) )
478 				{
479 					majorGrid.interval = value;
480 				}
481 
482 				if( tempMajorTickMarkInterval <= 0.0 ||
483 					(double.IsNaN(tempMajorTickMarkInterval) && ((Axis)this).Interval <= 0.0) )
484 				{
485 					majorTickMark.interval = value;
486 				}
487 
488 				if( tempLabelInterval <= 0.0 ||
489 					(double.IsNaN(tempLabelInterval) && ((Axis)this).Interval <= 0.0) )
490 				{
491 					labelStyle.interval = value;
492 				}
493 
494 				this.Invalidate();
495 			}
496 		}
497 
498 		/// <summary>
499 		/// Sets auto interval values to grids, tick marks
500 		/// and labels
501 		/// </summary>
SetIntervalAndType(double newInterval, DateTimeIntervalType newIntervalType)502 		internal void SetIntervalAndType(double newInterval, DateTimeIntervalType newIntervalType)
503 		{
504 			if( tempMajorGridInterval <= 0.0 ||
505 				(double.IsNaN(tempMajorGridInterval) && ((Axis)this).Interval <= 0.0) )
506 			{
507 				majorGrid.interval = newInterval;
508 				majorGrid.intervalType = newIntervalType;
509 			}
510 
511 			if( tempMajorTickMarkInterval <= 0.0 ||
512 				(double.IsNaN(tempMajorTickMarkInterval) && ((Axis)this).Interval <= 0.0) )
513 			{
514 				majorTickMark.interval = newInterval;
515 				majorTickMark.intervalType = newIntervalType;
516 			}
517 
518 			if( tempLabelInterval <= 0.0 ||
519 				(double.IsNaN(tempLabelInterval) && ((Axis)this).Interval <= 0.0) )
520 			{
521 				labelStyle.interval = newInterval;
522 				labelStyle.intervalType = newIntervalType;
523 			}
524 
525 			this.Invalidate();
526 		}
527 
528 
529 		/// <summary>
530 		/// Gets or sets the maximum axis value.
531 		/// </summary>
532 		[
533 
534 		SRCategory("CategoryAttributeScale"),
535 		Bindable(true),
536 		DefaultValue(Double.NaN),
537 		NotifyParentPropertyAttribute(true),
538 		SRDescription("DescriptionAttributeMaximum"),
539 		#if !Microsoft_CONTROL
540 		PersistenceMode(PersistenceMode.Attribute),
541 		#endif
542         TypeConverter(typeof(AxisMinMaxAutoValueConverter))
543 		]
544 		public double Maximum
545 		{
546 			get
547 			{
548 				// Get maximum
549                 if (_isLogarithmic && logarithmicConvertedToLinear && !Double.IsNaN(maximum))
550 					return logarithmicMaximum;
551 				else
552 					return maximum;
553 			}
554 			set
555 			{
556 				// Split a value to maximum and auto maximum
557 				if( Double.IsNaN(value) )
558 				{
559 					_autoMaximum = true;
560 					maximum = Double.NaN;
561 				}
562 				else
563 				{
564 					// Set maximum
565 					maximum = value;
566 
567 					// Set non linearized Maximum for logarithmic scale
568 					logarithmicMaximum = value;
569 
570 					_autoMaximum = false;
571 				}
572 
573 				// Reset original property value fields
574 				((Axis)this).tempMaximum = maximum;
575 
576 				// This line is added because of Save ScaleView State August 29, 2003
577 				// in Web Forms. This place could cause problems with Reset Auto Values.
578 				((Axis)this).tempAutoMaximum = _autoMaximum;
579 
580 				this.Invalidate();
581 			}
582 		}
583 
584 		/// <summary>
585 		/// Gets or sets the minimum axis value
586 		/// </summary>
587 		[
588 
589 		SRCategory("CategoryAttributeScale"),
590 		Bindable(true),
591 		DefaultValue(Double.NaN),
592 		NotifyParentPropertyAttribute(true),
593 		SRDescription("DescriptionAttributeMinimum"),
594 		#if !Microsoft_CONTROL
595 		PersistenceMode(PersistenceMode.Attribute),
596 		#endif
597         TypeConverter(typeof(AxisMinMaxAutoValueConverter))
598 		]
599 		public double Minimum
600 		{
601 			get
602 			{
603 				// Get minimum
604                 if (_isLogarithmic && logarithmicConvertedToLinear && !Double.IsNaN(maximum))
605 					return logarithmicMinimum;
606 				else
607 					return minimum;
608 			}
609 			set
610 			{
611 				// Split a value to minimum and auto minimum
612 				if( Double.IsNaN(value) )
613 				{
614 					_autoMinimum = true;
615 					minimum = Double.NaN;
616 				}
617 				else
618 				{
619 					// Set maximum
620 					minimum = value;
621 					_autoMinimum = false;
622 
623 					// Set non linearized Minimum for logarithmic scale
624 					logarithmicMinimum = value;
625 				}
626 
627 				// Reset original property value fields
628 				((Axis)this).tempMinimum = minimum;
629 
630 				// This line is added because of Save ScaleView State August 29, 2003
631 				// in Web Forms. This place could cause problems with Reset Auto Values.
632 				((Axis)this).tempAutoMinimum = _autoMinimum;
633 
634 				this.Invalidate();
635 			}
636 		}
637 
638 		/// <summary>
639 		/// Gets or sets the point where axis is crossed by another axis.
640 		/// </summary>
641 		[
642 		SRCategory("CategoryAttributeScale"),
643 		Bindable(true),
644 		DefaultValue(Double.NaN),
645 		NotifyParentPropertyAttribute(true),
646 		SRDescription("DescriptionAttributeCrossing"),
647 		#if !Microsoft_CONTROL
648 		PersistenceMode(PersistenceMode.Attribute),
649 		#endif
650         TypeConverter(typeof(AxisCrossingValueConverter))
651 		]
652 		virtual public double Crossing
653 		{
654 			get
655 			{
656 				if( paintMode )
657                     if (_isLogarithmic)
658 						return Math.Pow( this.logarithmBase, GetCrossing() );
659 					else
660 						return GetCrossing();
661 				else
662 					return crossing;
663 			}
664 			set
665 			{
666 				crossing = value;
667 
668 				// Reset original property value fields
669 				((Axis)this).tempCrossing = crossing;
670 
671 				this.Invalidate();
672 			}
673 		}
674 
675 
676 		/// <summary>
677 		/// Enables or disables the axis.
678 		/// </summary>
679 		[
680 		SRCategory("CategoryAttributeMisc"),
681 		Bindable(true),
682 		DefaultValue(typeof(AxisEnabled), "Auto"),
683 		NotifyParentPropertyAttribute(true),
684 		SRDescription("DescriptionAttributeEnabled7"),
685 		#if !Microsoft_CONTROL
686 		PersistenceMode(PersistenceMode.Attribute)
687 		#endif
688 		]
689 		public AxisEnabled Enabled
690 		{
691 			get
692 			{
693 				// Take Enabled from two fields: enabled and auto enabled
694 				if( autoEnabled )
695 				{
696 					return AxisEnabled.Auto;
697 				}
698 				else if( enabled )
699 				{
700 					return AxisEnabled.True;
701 				}
702 				else
703 				{
704 					return AxisEnabled.False;
705 				}
706 			}
707 			set
708 			{ // Split Enabled to two fields: enabled and auto enabled
709 				if( value == AxisEnabled.Auto )
710 				{
711 					autoEnabled = true;
712 				}
713 				else if( value == AxisEnabled.True )
714 				{
715 					enabled = true;
716 					autoEnabled = false;
717 				}
718 				else
719 				{
720 					enabled = false;
721 					autoEnabled = false;
722 				}
723 
724 				this.Invalidate();
725 			}
726 		}
727 
728 		/// <summary>
729         /// Gets or sets a flag which indicates whether the axis is logarithmic.
730 		/// Zeros or negative data values are not allowed on logarithmic charts.
731 		/// </summary>
732 		[
733         SRCategory("CategoryAttributeScale"),
734 		Bindable(true),
735 		DefaultValue(false),
736 		NotifyParentPropertyAttribute(true),
737 		SRDescription("DescriptionAttributeLogarithmic"),
738 		#if !Microsoft_CONTROL
739 		PersistenceMode(PersistenceMode.Attribute)
740 		#endif
741 		]
742 		public bool IsLogarithmic
743 		{
744 			get
745 			{
746                 return _isLogarithmic;
747 			}
748 			set
749 			{
750                 _isLogarithmic = value;
751 				this.Invalidate();
752 			}
753 		}
754 
755 		/// <summary>
756 		/// Base of the logarithm used in logarithmic scale.
757 		/// By default, this value is 10.
758 		/// </summary>
759 		[
760         SRCategory("CategoryAttributeScale"),
761 		Bindable(true),
762 		DefaultValue(10.0),
763 		NotifyParentPropertyAttribute(true),
764 		SRDescription("DescriptionAttributeLogarithmBase"),
765 		#if !Microsoft_CONTROL
766 		PersistenceMode(PersistenceMode.Attribute)
767 		#endif
768 		]
769 		public double LogarithmBase
770 		{
771 			get
772 			{
773 				return logarithmBase;
774 			}
775 			set
776 			{
777 				if( value < 2.0 )
778 				{
779                     throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisScaleLogarithmBaseInvalid));
780 				}
781 
782 				logarithmBase = value;
783 
784 				this.Invalidate();
785 			}
786 		}
787 
788 		#endregion
789 
790 		#region Axis Segments and Scale Breaks Properties
791 
792 
793 
794 		// Field that stores Axis automatic scale breaks style.
795 		internal AxisScaleBreakStyle axisScaleBreakStyle = null;
796 
797         /// <summary>
798         /// Gets or sets the style of scale breaks.
799         /// </summary>
800 		[
801 		SRCategory("CategoryAttributeScale"),
802 		SRDescription("DescriptionAttributeScaleBreakStyle"),
803         TypeConverter(typeof(NoNameExpandableObjectConverter)),
804 		NotifyParentPropertyAttribute(true),
805 #if Microsoft_CONTROL
806 		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
807 #else
808 		PersistenceMode(PersistenceMode.InnerProperty),
809 #endif
810 		]
811 		virtual public AxisScaleBreakStyle ScaleBreakStyle
812 		{
813 			get
814 			{
815 				return this.axisScaleBreakStyle;
816 			}
817 			set
818 			{
819 				this.axisScaleBreakStyle = value;
820 				this.axisScaleBreakStyle.axis = (Axis)this;
821 				//this.Invalidate();
822 			}
823 		}
824 
825 		// Field that stores axis scale segments
826 		internal AxisScaleSegmentCollection scaleSegments = null;
827 
828 		/// <summary>
829 		/// Axis scale segment collection.
830 		/// </summary>
831 		[
832 		SRCategory("CategoryAttributeScale"),
833 		Browsable(false),
834 		EditorBrowsable(EditorBrowsableState.Never),
835 		SRDescription("DescriptionAttributeAxisScaleSegmentCollection_AxisScaleSegmentCollection"),
836 		SerializationVisibilityAttribute(SerializationVisibility.Hidden),
837 		DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
838         Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base)
839 		]
840         internal AxisScaleSegmentCollection ScaleSegments
841 		{
842 			get
843 			{
844 				return this.scaleSegments;
845 			}
846 		}
847 
848 		#endregion // Axis Segments and Scale Breaks Properties
849 
850 		#region Axis data scaleView properies and methods
851 
852 		/// <summary>
853 		/// Gets or sets the scale view settings of the axis.
854 		/// </summary>
855 		[
856 		SRCategory("CategoryAttributeDataView"),
857 		Bindable(true),
858 		SRDescription("DescriptionAttributeView"),
859 
860 #if Microsoft_CONTROL
861 		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
862 #else
863 		PersistenceMode(PersistenceMode.InnerProperty),
864 #endif
865         TypeConverter(typeof(NoNameExpandableObjectConverter))
866 		]
867  	    public AxisScaleView ScaleView
868 		{
869 			get
870 			{
871 				return _scaleView;
872 			}
873 			set
874 			{
875 				_scaleView = value;
876 				_scaleView.axis = (Axis)this;
877 				this.Invalidate();
878 			}
879 		}
880 
881 #if Microsoft_CONTROL
882 
883 		/// <summary>
884 		/// Gets or sets the scroll bar settings of the axis.
885 		/// </summary>
886 		[
887 		SRCategory("CategoryAttributeDataView"),
888 		Bindable(true),
889 		SRDescription("DescriptionAttributeScrollBar"),
890 		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
891         TypeConverter(typeof(NoNameExpandableObjectConverter))
892 		]
893         public AxisScrollBar ScrollBar
894 		{
895 			get
896 			{
897 				return scrollBar;
898 			}
899 			set
900 			{
901 				scrollBar = value;
902 				scrollBar.axis = (Axis)this;
903 				this.Invalidate();
904 			}
905 		}
906 
907 #endif // Microsoft_CONTROL
908 
909 		/// <summary>
910 		/// Gets axis data scaleView minimum position.
911 		/// </summary>
912 		/// <returns>Axis data scaleView minimum position.</returns>
913 		internal double ViewMinimum
914 		{
915             get { return _scaleView.ViewMinimum; }
916 		}
917 
918 		/// <summary>
919 		/// Gets axis data scaleView minimum position.
920 		/// </summary>
921 		/// <returns>Axis data scaleView minimum position.</returns>
922 		internal double ViewMaximum
923 		{
924             get { return _scaleView.ViewMaximum; }
925 		}
926 
927         /// <summary>
928         /// Gets automatic maximum value (from data point values).
929         /// </summary>
930         internal bool AutoMaximum
931         {
932             get { return _autoMaximum; }
933         }
934 
935         /// <summary>
936         /// Gets automatic minimum value (from data point values).
937         /// </summary>
938         internal bool AutoMinimum
939         {
940             get { return _autoMinimum; }
941         }
942 
943 		#endregion
944 
945 		#region Axis position converters methos
946 
947 		/// <summary>
948 		/// This function converts axis value to relative position (0-100%).
949 		/// If an axis has a logarithmic scale, the value is converted to a linear scale.
950 		/// </summary>
951 		/// <param name="axisValue">Value from axis.</param>
952 		/// <returns>Relative position (0-100%).</returns>
GetPosition( double axisValue )953 		public double GetPosition( double axisValue )
954 		{
955 			// Adjust for the IsLogarithmic axis
956             if (_isLogarithmic && axisValue != 0.0)
957 			{
958 				axisValue = Math.Log( axisValue, this.logarithmBase );
959 			}
960 
961 			// Get linear position
962 			return GetLinearPosition(axisValue);
963 		}
964 
965 		/// <summary>
966 		/// This function converts an axis value to relative position (0-100%).
967 		/// If an axis has a logarithmic scale, the value is converted to a linear scale.
968 		/// </summary>
969 		/// <param name="axisValue">Axis value.</param>
970 		/// <returns>Relative position (0-100%).</returns>
ValueToPosition( double axisValue )971 		public double ValueToPosition( double axisValue )
972 		{
973 			return GetPosition( axisValue );
974 		}
975 
976 		/// <summary>
977 		/// This function converts an axis value to a pixel position.
978         /// If an axis has a logarithmic scale, the value is converted to a linear scale.
979 		/// </summary>
980 		/// <param name="axisValue">Value from axis.</param>
981 		/// <returns>Pixel position.</returns>
ValueToPixelPosition( double axisValue )982 		public double ValueToPixelPosition( double axisValue )
983 		{
984 			// Get relative value
985 			double val = ValueToPosition(axisValue);
986 
987 			// Convert it to pixels
988 			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
989 			{
990 				val *= (this.Common.ChartPicture.Width - 1) / 100F;
991 			}
992 			else
993 			{
994 				val *= (this.Common.ChartPicture.Height - 1) / 100F;
995 			}
996 
997 			return val;
998 		}
999 
1000 		/// <summary>
1001 		/// This function converts a relative position to an axis value.
1002         /// If an axis has a logarithmic scale, the value is converted to a linear scale.
1003 		/// </summary>
1004 		/// <param name="position">Relative position (0-100%).</param>
1005 		/// <returns>Axis value.</returns>
PositionToValue( double position )1006 		public double PositionToValue( double position )
1007 		{
1008 			return PositionToValue(position, true);
1009 		}
1010 
1011 		/// <summary>
1012 		/// This function converts a relative position to an axis value.
1013         /// If an axis has a logarithmic scale, the value is converted to a linear scale.
1014 		/// </summary>
1015 		/// <param name="position">Relative position (0-100%).</param>
1016 		/// <param name="validateInput">Indicates if input value range should be checked.</param>
1017 		/// <returns>Axis value.</returns>
PositionToValue( double position, bool validateInput)1018 		internal double PositionToValue( double position, bool validateInput)
1019 		{
1020 			// Check parameters
1021 			if(validateInput &&
1022 				(position < 0 || position > 100) )
1023 			{
1024                 throw (new ArgumentException(SR.ExceptionAxisScalePositionInvalid, "position"));
1025 			}
1026 
1027 			// Check if plot area position was already calculated
1028 			if(PlotAreaPosition == null)
1029 			{
1030                 throw (new InvalidOperationException(SR.ExceptionAxisScalePositionToValueCallFailed));
1031 			}
1032 
1033 			// Convert chart picture position to plotting position
1034 			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
1035 				position = position - PlotAreaPosition.X;
1036 			else
1037 				position = PlotAreaPosition.Bottom - position;
1038 
1039 
1040 			// The Chart area size
1041 			double ChartArea;
1042 			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
1043 				ChartArea = PlotAreaPosition.Width;
1044 			else
1045 				ChartArea = PlotAreaPosition.Height;
1046 
1047 
1048 			// The Real range as double
1049 			double viewMax = ViewMaximum;
1050 			double viewMin = ViewMinimum;
1051 			double	range = viewMax - viewMin;
1052 
1053 			// Avoid division by zero
1054 			double axisValue = 0;
1055 			if( range != 0 )
1056 			{
1057 				// Find axis value from position
1058 				axisValue = range / ChartArea * position;
1059 			}
1060 
1061 			// Corrected axis value for reversed
1062 			if( isReversed )
1063 				axisValue = viewMax - axisValue;
1064 			else
1065 				axisValue = viewMin + axisValue;
1066 
1067 			return axisValue;
1068 		}
1069 
1070 		/// <summary>
1071 		/// This function converts a pixel position to an axis value.
1072 		/// If an axis has a logarithmic scale, the value is converted to a linear scale.
1073 		/// </summary>
1074 		/// <param name="position">Pixel position.</param>
1075 		/// <returns>Axis value.</returns>
PixelPositionToValue( double position )1076 		public double PixelPositionToValue( double position )
1077 		{
1078 			// Convert it to pixels
1079 			double val = position;
1080 			if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
1081 			{
1082 				val *= 100F / ((float)(this.Common.ChartPicture.Width - 1));
1083 			}
1084 			else
1085 			{
1086 				val *= 100F / ((float)(this.Common.ChartPicture.Height - 1));
1087 			}
1088 
1089 			// Get from relative position
1090 			return PositionToValue(val);
1091 		}
1092 
1093 		#endregion
1094 
1095 		#region Axis scale methods
1096 
1097 		/// <summary>
1098 		/// Sets axis position. Axis position depends
1099 		/// on crossing and reversed value.
1100 		/// </summary>
SetAxisPosition()1101 		internal void SetAxisPosition()
1102 		{
1103 			// Change position of the axis
1104 			if( GetOppositeAxis().isReversed )
1105 			{
1106 				if( AxisPosition == AxisPosition.Left )
1107 					AxisPosition = AxisPosition.Right;
1108 				else if( AxisPosition == AxisPosition.Right )
1109 					AxisPosition = AxisPosition.Left;
1110 				else if( AxisPosition == AxisPosition.Top )
1111 					AxisPosition = AxisPosition.Bottom;
1112 				else if( AxisPosition == AxisPosition.Bottom )
1113 					AxisPosition = AxisPosition.Top;
1114 			}
1115 		}
1116 
1117 		/// <summary>
1118 		/// Sets temporary offset value.
1119 		/// </summary>
SetTempAxisOffset( )1120 		internal void SetTempAxisOffset( )
1121 		{
1122             if (ChartArea.Series.Count == 0)
1123             {
1124                 return;
1125             }
1126 			// Conditions when this code changes margin size: Column chart,
1127 			// margin is turned off, Interval offset is not used for
1128 			// gridlines, tick marks and labels.
1129 			Series ser = ChartArea.GetFirstSeries();
1130 			if( ( ser.ChartType == SeriesChartType.Column ||
1131 				ser.ChartType == SeriesChartType.StackedColumn ||
1132 				ser.ChartType == SeriesChartType.StackedColumn100 ||
1133 				ser.ChartType == SeriesChartType.Bar ||
1134 
1135                 ser.ChartType == SeriesChartType.RangeBar ||
1136 				ser.ChartType == SeriesChartType.RangeColumn ||
1137 
1138 				ser.ChartType == SeriesChartType.StackedBar ||
1139 				ser.ChartType == SeriesChartType.StackedBar100 ) &&
1140                 margin != 100.0 && !offsetTempSet &&
1141                 this._autoMinimum)
1142 			{
1143 
1144 				// Find offset correction for Column chart margin.
1145 				double offset;
1146 				marginTemp = margin;
1147 
1148 				// Find point width
1149 				// Check if series provide custom value for point width
1150 				double pointWidthSize;
1151 				string strWidth = ser[CustomPropertyName.PointWidth];
1152 				if(strWidth != null)
1153 				{
1154 					pointWidthSize = CommonElements.ParseDouble(strWidth);
1155 				}
1156 				else
1157 				{
1158 					pointWidthSize = 0.8;
1159 				}
1160 
1161 				margin = ( pointWidthSize / 2 ) * 100;
1162 				offset = ( margin ) / 100;
1163 			    double contraOffset = ( 100 - margin ) / 100;
1164 
1165                 if (this._intervalsStore.Count == 0)
1166                 {
1167                     this._intervalsStore.Push(this.labelStyle.intervalOffset);
1168                     this._intervalsStore.Push(this.majorGrid.intervalOffset);
1169                     this._intervalsStore.Push(this.majorTickMark.intervalOffset);
1170                     this._intervalsStore.Push(this.minorGrid.intervalOffset);
1171                     this._intervalsStore.Push(this.minorTickMark.intervalOffset);
1172                 }
1173 
1174 				this.labelStyle.intervalOffset = Double.IsNaN(this.labelStyle.intervalOffset) ? offset : this.labelStyle.intervalOffset + offset;
1175 				this.majorGrid.intervalOffset = Double.IsNaN(this.majorGrid.intervalOffset) ? offset : this.majorGrid.intervalOffset + offset;
1176 				this.majorTickMark.intervalOffset = Double.IsNaN(this.majorTickMark.intervalOffset) ? offset : this.majorTickMark.intervalOffset + offset;
1177                 this.minorGrid.intervalOffset = Double.IsNaN(this.minorGrid.intervalOffset) ? offset : this.minorGrid.intervalOffset + offset;
1178                 this.minorTickMark.intervalOffset = Double.IsNaN(this.minorTickMark.intervalOffset) ? offset : this.minorTickMark.intervalOffset + offset;
1179 
1180 				foreach( StripLine strip in ((Axis)(this)).StripLines )
1181 				{
1182 					_stripLineOffsets.Add( strip.IntervalOffset );
1183 					strip.IntervalOffset -= contraOffset;
1184 				}
1185 				offsetTempSet = true;
1186 			}
1187 		}
1188 
1189 		/// <summary>
1190 		/// Resets temporary offset value.
1191 		/// </summary>
ResetTempAxisOffset( )1192 		internal void ResetTempAxisOffset(  )
1193 		{
1194 			if( this.offsetTempSet )
1195 			{
1196                 System.Diagnostics.Debug.Assert(this._intervalsStore.Count == 5, "Fail in interval store count");
1197 
1198                 this.minorTickMark.intervalOffset = this._intervalsStore.Pop();
1199                 this.minorGrid.intervalOffset = this._intervalsStore.Pop();
1200                 this.majorTickMark.intervalOffset = this._intervalsStore.Pop();
1201                 this.majorGrid.intervalOffset = this._intervalsStore.Pop();
1202                 this.labelStyle.intervalOffset = this._intervalsStore.Pop();
1203 				int index = 0;
1204 				foreach( StripLine strip in ((Axis)(this)).StripLines )
1205 				{
1206 					if( _stripLineOffsets.Count > index )
1207 					{
1208 						strip.IntervalOffset = (double)_stripLineOffsets[index];
1209 					}
1210 					index++;
1211 				}
1212 				_stripLineOffsets.Clear();
1213 				offsetTempSet = false;
1214 				margin = marginTemp;
1215 			}
1216 		}
1217 
1218 		/// <summary>
1219 		/// This function will create auto maximum and minimum values
1220 		/// using the interval. This function will make a gap between
1221 		/// data points and border of the chart area.
1222 		/// </summary>
1223 		/// <param name="inter">Interval</param>
1224 		/// <param name="shouldStartFromZero">True if minimum scale value should start from zero.</param>
1225 		/// <param name="autoMax">Maximum is auto</param>
1226 		/// <param name="autoMin">Minimum is auto</param>
1227 		/// <param name="min">Minimum value</param>
1228 		/// <param name="max">Maximum value</param>
1229 		/// <returns>Interval</returns>
RoundedValues( double inter, bool shouldStartFromZero, bool autoMax, bool autoMin, ref double min, ref double max )1230 		internal double RoundedValues(
1231 			double inter,
1232 			bool shouldStartFromZero,
1233 			bool autoMax,
1234 			bool autoMin,
1235 			ref double min,
1236 			ref double max )
1237 		{
1238 			// For X Axes
1239 			if( axisType == AxisName.X || axisType == AxisName.X2 )
1240 			{
1241 				if( margin == 0.0 && !this.roundedXValues )
1242 				{
1243 					return inter;
1244 				}
1245 			}
1246 			else // For Y Axes
1247 			{
1248 				// Avoid dividing with 0. There is no gap.
1249 				if( margin == 0.0 )
1250 				{
1251 					return inter;
1252 				}
1253 			}
1254 
1255 			if( autoMin )
1256 			{ // Set minimum value
1257 				if( min < 0.0 || ( !shouldStartFromZero && !ChartArea.stacked ) )
1258 				{
1259 					min = (double)( ((decimal)Math.Ceiling( min / inter ) - 1m ) * (decimal)inter );
1260 				}
1261 				else
1262 				{
1263 					min = 0.0;
1264 				}
1265 			}
1266 			if( autoMax )
1267 			{// Set maximum value
1268 				if( max <= 0.0 && shouldStartFromZero )
1269 				{
1270 					max = 0.0;
1271 				}
1272 				else
1273 				{
1274 					max = (double)( ((decimal)Math.Floor( max / inter ) + 1m ) *  (decimal)inter );
1275 				}
1276 			}
1277 			return inter;
1278 		}
1279 
1280 
1281 		/// <summary>
1282 		/// Recalculates an intelligent interval from real interval.
1283 		/// </summary>
1284 		/// <param name="diff">Real interval.</param>
1285 		/// <returns>Inteligent interval.</returns>
CalcInterval( double diff )1286 		internal double CalcInterval( double diff )
1287 		{
1288 			// If the interval is zero return error
1289 			if( diff == 0.0 )
1290 			{
1291                 throw (new ArgumentOutOfRangeException("diff", SR.ExceptionAxisScaleIntervalIsZero));
1292 			}
1293 
1294 			// If the real interval is > 1.0
1295 			double	step = -1;
1296 			double	temp = diff;
1297 			while( temp > 1.0 )
1298 			{
1299 				step ++;
1300 				temp = temp / 10.0;
1301 				if( step > 1000 )
1302 				{
1303                     throw (new InvalidOperationException(SR.ExceptionAxisScaleMinimumMaximumInvalid));
1304 				}
1305 			}
1306 
1307 
1308 			// If the real interval is < 1.0
1309 			temp = diff;
1310 			if( temp < 1.0 )
1311 			{
1312 				step = 0;
1313 			}
1314 
1315 			while( temp < 1.0 )
1316 			{
1317 				step --;
1318 				temp = temp * 10.0;
1319 				if( step < -1000 )
1320 				{
1321                     throw (new InvalidOperationException(SR.ExceptionAxisScaleMinimumMaximumInvalid));
1322 				}
1323 			}
1324 
1325 			double power = (this.IsLogarithmic) ? this.logarithmBase : 10.0;
1326 			double tempDiff = diff / Math.Pow( power, step );
1327 
1328 			if( tempDiff < 3 )
1329 				tempDiff = 2;
1330 			else if( tempDiff < 7 )
1331 				tempDiff = 5;
1332 			else
1333 				tempDiff = 10;
1334 
1335 			// Make a correction of the real interval
1336 			return  tempDiff * Math.Pow( power, step );
1337 		}
1338 
1339 		/// <summary>
1340 		/// Recalculates a intelligent interval from real interval
1341 		/// obtained from maximum and minimum values
1342 		/// </summary>
1343 		/// <param name="min">Minimum</param>
1344 		/// <param name="max">Maximum</param>
1345 		/// <returns>Auto Interval</returns>
CalcInterval( double min, double max )1346 		private double CalcInterval( double min, double max )
1347 		{
1348 			// Approximated interval value
1349 			return CalcInterval( ( max - min ) / 5 );
1350 		}
1351 
1352 
1353 		/// <summary>
1354 		/// Recalculates a intelligent interval from real interval
1355 		/// obtained from maximum, minimum and date type if
1356 		/// the values is date-time value.
1357 		/// </summary>
1358 		/// <param name="min">Minimum value.</param>
1359 		/// <param name="max">Maximum value.</param>
1360 		/// <param name="date">True if date.</param>
1361 		/// <param name="type">Date time interval type.</param>
1362 		/// <param name="valuesType">AxisName of date-time values.</param>
1363 		/// <returns>Auto Interval.</returns>
CalcInterval( double min, double max, bool date, out DateTimeIntervalType type, ChartValueType valuesType)1364 		internal double CalcInterval(
1365 			double min,
1366 			double max,
1367 			bool date,
1368 			out DateTimeIntervalType type,
1369 			ChartValueType valuesType)
1370 		{
1371 			// AxisName is date time
1372 			if( date )
1373 			{
1374 				DateTime dateTimeMin = DateTime.FromOADate( min );
1375 				DateTime dateTimeMax = DateTime.FromOADate( max );
1376 				TimeSpan timeSpan = dateTimeMax.Subtract( dateTimeMin );
1377 
1378 				// Minutes
1379 				double	inter = timeSpan.TotalMinutes;
1380 
1381 				// For Range less than 60 seconds interval is 5 sec
1382 				if( inter <= 1.0 && valuesType != ChartValueType.Date)
1383 				{
1384 					// Milli Seconds
1385 					double	mlSeconds = timeSpan.TotalMilliseconds;
1386 					if(mlSeconds <= 10)
1387 					{
1388 						type = DateTimeIntervalType.Milliseconds;
1389 						return 1;
1390 					}
1391 					if(mlSeconds <= 50)
1392 					{
1393 						type = DateTimeIntervalType.Milliseconds;
1394 						return 4;
1395 					}
1396 					if(mlSeconds <= 200)
1397 					{
1398 						type = DateTimeIntervalType.Milliseconds;
1399 						return 20;
1400 					}
1401 					if(mlSeconds <= 500)
1402 					{
1403 						type = DateTimeIntervalType.Milliseconds;
1404 						return 50;
1405 					}
1406 
1407 					// Seconds
1408 					double	seconds = timeSpan.TotalSeconds;
1409 
1410 					if(seconds <= 7)
1411 					{
1412 						type = DateTimeIntervalType.Seconds;
1413 						return 1;
1414 					}
1415 					else if(seconds <= 15)
1416 					{
1417 						type = DateTimeIntervalType.Seconds;
1418 						return 2;
1419 					}
1420 					else if(seconds <= 30)
1421 					{
1422 						type = DateTimeIntervalType.Seconds;
1423 						return 5;
1424 					}
1425 					else if(seconds <= 60)
1426 					{
1427 						type = DateTimeIntervalType.Seconds;
1428 						return 10;
1429 					}
1430 
1431 				}// For Range less than 120 seconds interval is 10 sec
1432 				else if( inter <= 2.0 && valuesType != ChartValueType.Date)
1433 				{
1434 					type = DateTimeIntervalType.Seconds;
1435 					return 20;
1436 				}// For Range less than 180 seconds interval is 30 sec
1437 				else if( inter <= 3.0 && valuesType != ChartValueType.Date)
1438 				{
1439 					type = DateTimeIntervalType.Seconds;
1440 					return 30;
1441 				}
1442 
1443 				// For Range less than 10 minutes interval is 1 min
1444 				else if( inter <= 10 && valuesType != ChartValueType.Date)
1445 				{
1446 					type = DateTimeIntervalType.Minutes;
1447 					return 1;
1448 				}
1449 				// For Range less than 20 minutes interval is 1 min
1450 				else if( inter <= 20 && valuesType != ChartValueType.Date)
1451 				{
1452 					type = DateTimeIntervalType.Minutes;
1453 					return 2;
1454 				}// For Range less than 60 minutes interval is 5 min
1455 				else if( inter <= 60 && valuesType != ChartValueType.Date)
1456 				{
1457 					type = DateTimeIntervalType.Minutes;
1458 					return 5;
1459 				}// For Range less than 120 minutes interval is 10 min
1460 				else if( inter <= 120 && valuesType != ChartValueType.Date)
1461 				{
1462 					type = DateTimeIntervalType.Minutes;
1463 					return 10;
1464 				}// For Range less than 180 minutes interval is 30 min
1465 				else if( inter <= 180 && valuesType != ChartValueType.Date)
1466 				{
1467 					type = DateTimeIntervalType.Minutes;
1468 					return 30;
1469 				}
1470 					// For Range less than 12 hours interval is 1 hour
1471 				else if( inter <= 60*12 && valuesType != ChartValueType.Date)
1472 				{
1473 					type = DateTimeIntervalType.Hours;
1474 					return 1;
1475 				}
1476 					// For Range less than 24 hours interval is 4 hour
1477 				else if( inter <= 60*24 && valuesType != ChartValueType.Date)
1478 				{
1479 					type = DateTimeIntervalType.Hours;
1480 					return 4;
1481 				}
1482 					// For Range less than 2 days interval is 6 hour
1483 				else if( inter <= 60*24*2 && valuesType != ChartValueType.Date)
1484 				{
1485 					type = DateTimeIntervalType.Hours;
1486 					return 6;
1487 				}
1488 					// For Range less than 3 days interval is 12 hour
1489 				else if( inter <= 60*24*3 && valuesType != ChartValueType.Date)
1490 				{
1491 					type = DateTimeIntervalType.Hours;
1492 					return 12;
1493 				}
1494 
1495 				// For Range less than 10 days interval is 1 day
1496 				else if( inter <= 60*24*10 )
1497 				{
1498 					type = DateTimeIntervalType.Days;
1499 					return 1;
1500 				}
1501 					// For Range less than 20 days interval is 2 day
1502 				else if( inter <= 60*24*20 )
1503 				{
1504 					type = DateTimeIntervalType.Days;
1505 					return 2;
1506 				}
1507 					// For Range less than 30 days interval is 3 day
1508 				else if( inter <= 60*24*30 )
1509 				{
1510 					type = DateTimeIntervalType.Days;
1511 					return 3;
1512 				}
1513 					// For Range less than 2 months interval is 1 week
1514 				else if( inter <= 60*24*30.5*2 )
1515 				{
1516 					type = DateTimeIntervalType.Weeks;
1517 					return 1;
1518 				}
1519 					// For Range less than 5 months interval is 2weeks
1520 				else if( inter <= 60*24*30.5*5 )
1521 				{
1522 					type = DateTimeIntervalType.Weeks;
1523 					return 2;
1524 				}
1525 					// For Range less than 12 months interval is 1 month
1526 				else if( inter <= 60*24*30.5*12 )
1527 				{
1528 					type = DateTimeIntervalType.Months;
1529 					return 1;
1530 				}
1531 					// For Range less than 24 months interval is 3 month
1532 				else if( inter <= 60*24*30.5*24 )
1533 				{
1534 					type = DateTimeIntervalType.Months;
1535 					return 3;
1536 				}
1537 				// For Range less than 48 months interval is 6 months
1538 				else if( inter <= 60*24*30.5*48 )
1539 				{
1540 					type = DateTimeIntervalType.Months;
1541 					return 6;
1542 				}
1543 				// For Range more than 48 months interval is year
1544 				else if( inter >= 60*24*30.5*48 )
1545 				{
1546 					type = DateTimeIntervalType.Years;
1547 					return CalcYearInterval( inter / 60 / 24 / 365 );
1548 				}
1549 			}
1550 
1551 			 // Else numbers
1552 			type = DateTimeIntervalType.Number;
1553 			return CalcInterval( min, max );
1554 
1555 		}
1556 
1557 		/// <summary>
1558 		/// Recalculates a intelligent interval for years
1559 		/// </summary>
1560 		/// <param name="years">Number of years</param>
1561 		/// <returns>Interval in years</returns>
CalcYearInterval( double years )1562 		private double CalcYearInterval( double years )
1563 		{
1564 			// If the interval is zero return error
1565 			if( years <= 1.0 )
1566 			{
1567                 throw (new ArgumentOutOfRangeException("years", SR.ExceptionAxisScaleIntervalIsLessThen1Year));
1568 			}
1569 
1570 			if( years < 5 )
1571 				return 1;
1572 			else if( years < 10 )
1573 				return 2;
1574 
1575 			// Make a correction of the interval
1576 			return Math.Floor( years / 5 );
1577 		}
1578 
1579 		/// <summary>
1580 		/// This method returns the number of units
1581 		/// between min and max.
1582 		/// </summary>
1583 		/// <param name="min">Minimum.</param>
1584 		/// <param name="max">Maximum.</param>
1585 		/// <param name="type">Date type.</param>
1586 		/// <returns>Number of units.</returns>
GetNumOfUnits( double min, double max, DateTimeIntervalType type )1587 		private int GetNumOfUnits( double min, double max, DateTimeIntervalType type )
1588 		{
1589             double current = ChartHelper.GetIntervalSize(min, 1, type);
1590 			return (int)Math.Round((max - min) / current);
1591 		}
1592 
1593 		/// <summary>
1594 		/// This method checks if value type is date-time.
1595 		/// </summary>
1596 		/// <returns>Date-time type or Auto.</returns>
GetDateTimeType()1597 		internal ChartValueType GetDateTimeType()
1598 		{
1599 			List<string> list = null;
1600 
1601 			ChartValueType dateType = ChartValueType.Auto;
1602 
1603 			// Check if Value type is date from first series in the axis
1604 			if( axisType == AxisName.X )
1605 			{
1606 				// Check X axes type
1607 				list = ChartArea.GetXAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
1608 				if( list.Count == 0 )
1609 				{
1610 					return ChartValueType.Auto;
1611 				}
1612 
1613 				if( Common.DataManager.Series[list[0]].IsXValueDateTime() )
1614 				{
1615 					dateType = Common.DataManager.Series[list[0]].XValueType;
1616 				}
1617 			}
1618 			else if( axisType == AxisName.X2 )
1619 			{
1620 				// Check X2 axes type
1621 				list = ChartArea.GetXAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
1622 				if( list.Count == 0 )
1623 				{
1624 					return ChartValueType.Auto;
1625 				}
1626 
1627 				if( Common.DataManager.Series[list[0]].IsXValueDateTime() )
1628 				{
1629 					dateType = Common.DataManager.Series[list[0]].XValueType;
1630 				}
1631 			}
1632 			else if( axisType == AxisName.Y )
1633 			{
1634 				// Check Y axes type
1635 				list = ChartArea.GetYAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
1636 				if( list.Count == 0 )
1637 				{
1638 					return ChartValueType.Auto;
1639 				}
1640 
1641 				if( Common.DataManager.Series[list[0]].IsYValueDateTime() )
1642 				{
1643 					dateType = Common.DataManager.Series[list[0]].YValueType;
1644 				}
1645 			}
1646 			else if( axisType == AxisName.Y2 )
1647 			{
1648 				// Check Y2 axes type
1649 				list = ChartArea.GetYAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
1650 				if( list.Count == 0 )
1651 				{
1652 					return ChartValueType.Auto;
1653 				}
1654 
1655 				if( Common.DataManager.Series[list[0]].IsYValueDateTime() )
1656 				{
1657 					dateType = Common.DataManager.Series[list[0]].YValueType;
1658 				}
1659 			}
1660 
1661 			return dateType;
1662 		}
1663 
1664 		/// <summary>
1665 		/// This method removes "Auto", "min", "max" from crossing
1666 		/// value and creates a double value.
1667 		/// </summary>
1668 		/// <returns>Crossing value</returns>
GetCrossing()1669 		private double GetCrossing()
1670 		{
1671 			if( Double.IsNaN(crossing) )
1672 			{
1673 				if( Common.ChartTypeRegistry.GetChartType( (string)ChartArea.ChartTypes[0] ).ZeroCrossing )
1674 				{
1675 					if( ViewMinimum > 0.0 )
1676 					{
1677 						return ViewMinimum;
1678 					}
1679 					else if( ViewMaximum < 0.0 )
1680 					{
1681 						return ViewMaximum;
1682 					}
1683 					else
1684 					{
1685 						return 0.0;
1686 					}
1687 				}
1688 				else
1689 				{
1690 					return ViewMinimum;
1691 				}
1692 			}
1693 			else if( crossing == Double.MaxValue )
1694 			{
1695 				return ViewMaximum;
1696 			}
1697 			else if(  crossing == Double.MinValue )
1698 			{
1699 				return ViewMinimum;
1700 			}
1701 
1702 			return crossing;
1703 		}
1704 
1705 		/// <summary>
1706 		/// Set auto minimum number. The minimum number
1707 		/// which was sent to this function will be used to
1708 		/// estimate a rounded minimum.
1709 		/// </summary>
1710 		/// <param name="min"> This value is a recommendation for the minimum value. </param>
SetAutoMinimum(double min)1711 		internal void SetAutoMinimum(double min)
1712 		{
1713 			// Set the minimum
1714 			if( _autoMinimum )
1715 			{
1716 				minimum = min;
1717 			}
1718 		}
1719 
1720 		/// <summary>
1721 		/// Set auto maximum number. The maximum number
1722 		/// which was sent to this function will be used to
1723 		/// estimate a rounded maximum.
1724 		/// </summary>
1725 		/// <param name="max">This value is a recommendation for the maximum value.</param>
SetAutoMaximum(double max)1726 		internal void SetAutoMaximum(double max)
1727 		{
1728 			// Set the maximum
1729 			if( _autoMaximum )
1730 			{
1731 				maximum = max;
1732 			}
1733 		}
1734 
1735 		/// <summary>
1736 		/// Find opposite axis of this axis.  What is opposite
1737 		/// axis depend on first series in chart area and primary
1738 		/// and secondary X and Y axes for the first series.
1739 		/// </summary>
1740 		/// <returns>Opposite axis</returns>
GetOppositeAxis()1741 		internal Axis GetOppositeAxis()
1742 		{
1743 			// Oppoiste axis found
1744             if (oppositeAxis != null)
1745 			{
1746                 return oppositeAxis;
1747 			}
1748 
1749 			List<string> list;
1750 
1751 			switch( axisType )
1752 			{
1753 				// X Axis
1754 				case AxisName.X:
1755 					list = ChartArea.GetXAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
1756 					// There aren't data series
1757 					if( list.Count == 0 )
1758                         oppositeAxis = ChartArea.AxisY;
1759 						// Take opposite axis from the first series from chart area
1760 					else if( Common.DataManager.Series[list[0]].YAxisType == AxisType.Primary )
1761                         oppositeAxis = ChartArea.AxisY.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
1762 					else
1763                         oppositeAxis = ChartArea.AxisY2.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
1764 					break;
1765 					// X2 Axis
1766 				case AxisName.X2:
1767 					list = ChartArea.GetXAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
1768 					// There aren't data series
1769 					if( list.Count == 0 )
1770                         oppositeAxis = ChartArea.AxisY2;
1771 						// Take opposite axis from the first series from chart area
1772 					else if( Common.DataManager.Series[list[0]].YAxisType == AxisType.Primary)
1773                         oppositeAxis = ChartArea.AxisY.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
1774 					else
1775                         oppositeAxis = ChartArea.AxisY2.GetSubAxis(Common.DataManager.Series[list[0]].YSubAxisName);
1776 					break;
1777 					// Y Axis
1778 				case AxisName.Y:
1779 					list = ChartArea.GetYAxesSeries( AxisType.Primary, ((Axis)this).SubAxisName );
1780 					// There aren't data series
1781 					if( list.Count == 0 )
1782                         oppositeAxis = ChartArea.AxisX;
1783 						// Take opposite axis from the first series from chart area
1784 					else if( Common.DataManager.Series[list[0]].XAxisType == AxisType.Primary )
1785                         oppositeAxis = ChartArea.AxisX.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
1786 					else
1787                         oppositeAxis = ChartArea.AxisX2.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
1788 					break;
1789 					// Y2 Axis
1790 				case AxisName.Y2:
1791 					list = ChartArea.GetYAxesSeries( AxisType.Secondary, ((Axis)this).SubAxisName );
1792 					// There aren't data series
1793 					if( list.Count == 0 )
1794                         oppositeAxis = ChartArea.AxisX2;
1795 						// Take opposite axis from the first series from chart area
1796 					else if( Common.DataManager.Series[list[0]].XAxisType == AxisType.Primary  )
1797                         oppositeAxis = ChartArea.AxisX.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
1798 					else
1799                         oppositeAxis = ChartArea.AxisX2.GetSubAxis(Common.DataManager.Series[list[0]].XSubAxisName);
1800 					break;
1801 			}
1802             return oppositeAxis;
1803 		}
1804 
1805 		/// <summary>
1806 		/// This function converts Values from Axes to
1807 		/// linear relative positions.
1808 		/// </summary>
1809 		/// <param name="axisValue">Value from axis.</param>
1810 		/// <returns>Relative position.</returns>
GetLinearPosition( double axisValue )1811 		internal double GetLinearPosition( double axisValue )
1812 		{
1813 			bool circularArea = (ChartArea == null || !ChartArea.chartAreaIsCurcular) ?
1814 				false : true;
1815 
1816 			// Check if some value calculation is optimized
1817 			if(!this.optimizedGetPosition)
1818 			{
1819 				paintViewMax = ViewMaximum;
1820 				paintViewMin = ViewMinimum;
1821 				paintRange = paintViewMax - paintViewMin;
1822 				paintAreaPosition = PlotAreaPosition.ToRectangleF();
1823 
1824 				// Update position for circular chart area
1825 				if(circularArea)
1826 				{
1827 					paintAreaPosition.Width /= 2.0f;
1828 					paintAreaPosition.Height /= 2.0f;
1829 				}
1830 
1831 				paintAreaPositionBottom = paintAreaPosition.Y + paintAreaPosition.Height;
1832 				paintAreaPositionRight = paintAreaPosition.X + paintAreaPosition.Width;
1833 
1834 				// The Chart area size
1835 				if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
1836 					paintChartAreaSize = paintAreaPosition.Width;
1837 				else
1838 					paintChartAreaSize = paintAreaPosition.Height;
1839 
1840 				valueMultiplier = 0.0;
1841 				if( paintRange != 0 )
1842 				{
1843 					valueMultiplier = paintChartAreaSize / paintRange;
1844 				}
1845 			}
1846 
1847 			// The Chart area pixel size
1848 			double position = valueMultiplier * ( axisValue - paintViewMin);
1849 
1850 
1851 
1852 			// Check if axis scale segments are enabled
1853 			if(this.scaleSegmentsUsed)
1854 			{
1855 				AxisScaleSegment scaleSegment = this.ScaleSegments.FindScaleSegmentForAxisValue(axisValue);
1856 				if(scaleSegment != null)
1857 				{
1858 					double segmentSize = 0.0;
1859 					double segmentPosition = 0.0;
1860 					scaleSegment.GetScalePositionAndSize(paintChartAreaSize, out segmentPosition, out segmentSize);
1861 
1862 					// Make sure value do not exceed max possible
1863 					if(!this.ScaleSegments.AllowOutOfScaleValues)
1864 					{
1865 						if(axisValue > scaleSegment.ScaleMaximum)
1866 						{
1867 							axisValue = scaleSegment.ScaleMaximum;
1868 						}
1869 						else if(axisValue < scaleSegment.ScaleMinimum)
1870 						{
1871 							axisValue = scaleSegment.ScaleMinimum;
1872 						}
1873 					}
1874 
1875 					double segmentScaleRange = scaleSegment.ScaleMaximum - scaleSegment.ScaleMinimum;
1876 
1877 					position = (segmentSize / segmentScaleRange) * (axisValue - scaleSegment.ScaleMinimum);
1878 					position += segmentPosition;
1879 				}
1880 			}
1881 
1882 
1883 			// Window position
1884 			// (Do Not use .Right or .Bottom methods below) - rounding issue!
1885 			if( isReversed )
1886 			{
1887 				if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
1888 					position = paintAreaPositionRight - position;
1889 				else
1890 					position = paintAreaPosition.Y + position;
1891 			}
1892 			else
1893 			{
1894 				if( AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom )
1895 					position = paintAreaPosition.X + position;
1896 				else
1897 					position = paintAreaPositionBottom - position;
1898 			}
1899 
1900 			return position;
1901 		}
1902 
1903 
1904 		#endregion
1905 
1906 		#region Axis estimate axis methods
1907 
1908 		/// <summary>
1909 		/// This function recalculates minimum maximum and interval.
1910 		/// The function uses current values for minimum and maximum to
1911 		/// find rounding values. If the value from the data source for the
1912 		/// maximum value is 376.5 this function will return 380. This function
1913 		/// also set interval type for date
1914 		/// </summary>
EstimateAxis()1915 		internal void EstimateAxis()
1916 		{
1917 			double axisInterval;
1918 
1919 			// Check if veiw size specified without scaleView position
1920 			if(!Double.IsNaN(this.ScaleView.Size))
1921 			{
1922 				// If size set only use axis minimum for scaleView position
1923 				if(Double.IsNaN(this.ScaleView.Position))
1924 				{
1925 					this.ScaleView.Position = this.Minimum;
1926 				}
1927 			}
1928 
1929 			// Zooming Mode
1930 			if( !Double.IsNaN(_scaleView.Position) && !Double.IsNaN(_scaleView.Size) )
1931 			{
1932 				double viewMaximum = ViewMaximum;
1933 				double viewMinimum = ViewMinimum;
1934 
1935 				// IsLogarithmic axes
1936                 if (this._isLogarithmic)
1937 				{
1938 					viewMaximum = Math.Pow( this.logarithmBase, viewMaximum );
1939 					viewMinimum = Math.Pow( this.logarithmBase, viewMinimum );
1940 				}
1941 				else
1942 				{
1943 					// Add rounding and gap for maximum and minimum
1944 					EstimateAxis( ref this.minimum, ref this.maximum, _autoMaximum, _autoMinimum );
1945 				}
1946 
1947 				// Find Interval for Zoom
1948 				axisInterval = EstimateAxis( ref viewMinimum, ref viewMaximum, true, true );
1949 			}
1950 			else // No Zooming mode
1951 			{
1952 				// Estimate axis shoud be always called for non logarithmic axis
1953 				axisInterval = EstimateAxis( ref this.minimum, ref this.maximum, _autoMaximum, _autoMinimum );
1954 			}
1955 
1956 			// Set intervals for grids, tick marks and labels
1957 			if( axisInterval <= 0.0 )
1958 			{
1959                 throw (new InvalidOperationException(SR.ExceptionAxisScaleAutoIntervalInvalid));
1960 			}
1961 			else
1962 			{
1963 				// This code checks if all series in the chart area have �integer type�
1964                 // for specified axes, which means int, uint, long and ulong and rounds interval.
1965 #if SUBAXES
1966 				if( ChartArea.SeriesIntegerType( this.axisType, ((Axis)this).SubAxisName ) )
1967 #else // SUBAXES
1968                 if ( ChartArea.SeriesIntegerType( this.axisType, string.Empty ))
1969 #endif // SUBAXES
1970                 {
1971 					axisInterval = Math.Round( axisInterval );
1972 					if( axisInterval == 0.0 )
1973 					{
1974 						axisInterval = 1.0;
1975 					}
1976 
1977 					// Round Minimum to floor value if type is integer
1978 					minimum = Math.Floor( minimum );
1979 				}
1980 
1981 				SetInterval = axisInterval;
1982 			}
1983 		}
1984 
1985 		/// <summary>
1986 		/// This function recalculates minimum maximum and interval.
1987 		/// The function uses current values for minimum and maximum to
1988 		/// find rounding values. If the value from the data source for the
1989 		/// maximum value is 376.5 this function will return 380. This function
1990 		/// also set interval type for date
1991 		/// </summary>
1992 		/// <param name="minimumValue">Minimum</param>
1993 		/// <param name="maximumValue">Maximum</param>
1994 		/// <param name="autoMaximum">Maximum value is Auto</param>
1995 		/// <param name="autoMinimum">Minimum value is Auto</param>
1996 		/// <returns>Interval</returns>
EstimateAxis( ref double minimumValue, ref double maximumValue, bool autoMaximum, bool autoMinimum )1997 		internal double EstimateAxis( ref double minimumValue, ref double maximumValue, bool autoMaximum, bool autoMinimum )
1998 		{
1999 			double axisInterval;
2000 
2001 			// The axis minimum value is greater than the maximum value.
2002 			if( maximumValue < minimumValue )
2003 			{
2004 				if(!this.Common.ChartPicture.SuppressExceptions)
2005 				{
2006                     throw (new InvalidOperationException(SR.ExceptionAxisScaleMinimumValueIsGreaterThenMaximumDataPoint));
2007 				}
2008 				else
2009 				{
2010 					// Max axis scale should be always bigger
2011 					double tempValue = maximumValue;
2012 					maximumValue = minimumValue;
2013 					minimumValue = tempValue;
2014 				}
2015 			}
2016 
2017 			// Take Value type
2018 			ChartValueType	dateType = GetDateTimeType();
2019 
2020 			// Axis type is logarithmic
2021             if (_isLogarithmic)
2022 			{
2023 				axisInterval = EstimateLogarithmicAxis( ref minimumValue, ref maximumValue, crossing, autoMaximum, autoMinimum );
2024 			}
2025 
2026 			// Axis type is date
2027 			else if( dateType !=  ChartValueType.Auto)
2028 			{
2029 				axisInterval = EstimateDateAxis( ref minimumValue, ref maximumValue, autoMaximum, autoMinimum, dateType );
2030 			}
2031 
2032 			// Axis type is number
2033 			else
2034 			{
2035 				axisInterval = EstimateNumberAxis( ref minimumValue, ref maximumValue, this.IsStartedFromZero, this.prefferedNumberofIntervals, autoMaximum, autoMinimum );
2036 			}
2037 
2038 			// Set intervals for grids, tick marks and labels
2039 			if( axisInterval <= 0.0 )
2040 			{
2041                 throw (new InvalidOperationException(SR.ExceptionAxisScaleAutoIntervalInvalid));
2042 			}
2043 			else
2044 			{
2045 				// Set interval for Grid lines Tick Marks and labels
2046 				SetInterval = axisInterval;
2047 			}
2048 
2049 			return axisInterval;
2050 
2051 		}
2052 
2053         /// <summary>
2054         /// This function recalculates minimum maximum and interval for
2055         /// logarithmic axis. The function uses current values for minimum and
2056         /// maximum to find new rounding values.
2057         /// </summary>
2058         /// <param name="minimumValue">Current Minimum value</param>
2059         /// <param name="maximumValue">Current Maximum value</param>
2060         /// <param name="crossingValue">Crossing value</param>
2061         /// <param name="autoMaximum">Maximum value is Auto</param>
2062         /// <param name="autoMinimum">Minimum value is Auto</param>
2063         /// <returns>Interval</returns>
EstimateLogarithmicAxis( ref double minimumValue, ref double maximumValue, double crossingValue, bool autoMaximum, bool autoMinimum )2064 		private double EstimateLogarithmicAxis( ref double minimumValue, ref double maximumValue, double crossingValue, bool autoMaximum, bool autoMinimum )
2065 		{
2066 			double axisInterval;
2067 
2068 			if( !logarithmicConvertedToLinear )
2069 			{
2070 				// Remember values. Do not use POW function because of rounding.
2071 				this.logarithmicMinimum = this.minimum;
2072 				this.logarithmicMaximum = this.maximum;
2073 			}
2074 
2075 			// For log axis margin always turn on.
2076 			margin = 100;
2077 
2078 			// Supress zero and negative values with logarithmic axis exceptions
2079 			if(this.Common != null && this.Common.Chart != null && this.Common.Chart.chartPicture.SuppressExceptions)
2080 			{
2081 				if (minimumValue <= 0.0 )
2082 				{
2083 					minimumValue = 1.0;
2084 				}
2085 				if (maximumValue <= 0.0 )
2086 				{
2087 					maximumValue = 1.0;
2088 				}
2089 				if (crossingValue <= 0.0 && crossingValue != Double.MinValue )
2090 				{
2091 					crossingValue = 1.0;
2092 				}
2093 			}
2094 
2095 			// The logarithmic axes can not show negative values.
2096 			if( minimumValue <= 0.0 || maximumValue <= 0.0 || crossingValue <= 0.0 )
2097 			{
2098 				if (minimumValue <= 0.0 )
2099                     throw (new ArgumentOutOfRangeException("minimumValue", SR.ExceptionAxisScaleLogarithmicNegativeValues));
2100 				if (maximumValue <= 0.0 )
2101                     throw (new ArgumentOutOfRangeException("maximumValue", SR.ExceptionAxisScaleLogarithmicNegativeValues));
2102 			}
2103 
2104 			// Change crossing to linear scale
2105 			crossingValue = Math.Log( crossingValue, this.logarithmBase );
2106 
2107 			// Change minimum and maximum to linear scale
2108 			minimumValue = Math.Log( minimumValue, this.logarithmBase );
2109 			maximumValue = Math.Log( maximumValue, this.logarithmBase );
2110 
2111 			logarithmicConvertedToLinear = true;
2112 
2113 			// Find interval - Make approximately 5 grid lines and labels.
2114 			double diff = ( maximumValue - minimumValue ) / 5;
2115 
2116 			// Make good interval for logarithmic scale
2117 			axisInterval = Math.Floor( diff );
2118 			if( axisInterval == 0 ) axisInterval = 1;
2119 
2120 			if( autoMinimum && autoMaximum )
2121 			{
2122 				// The maximum and minimum rounding with interval
2123 				RoundedValues( axisInterval, this.IsStartedFromZero, autoMaximum, autoMinimum, ref minimumValue, ref maximumValue );
2124 			}
2125 
2126 			// Do not allow min/max values more than a hundred
2127 			if(ChartArea.hundredPercent)
2128 			{
2129 				if(autoMinimum)
2130 				{
2131 					if(minimumValue < 0)
2132 						minimumValue = 0;
2133 				}
2134 
2135 				if(autoMaximum)
2136 				{
2137 					if(maximumValue > 2)
2138 						maximumValue = 2;
2139 				}
2140 			}
2141 
2142 			// Set interval for Grid lines Tick Marks and labels
2143 			return axisInterval;
2144 		}
2145 
2146 		/// <summary>
2147 		/// This function recalculates minimum maximum and interval for
2148 		/// Date axis. The function uses current values for minimum and
2149 		/// maximum to find new rounding values.
2150 		/// </summary>
2151 		/// <param name="minimumValue">Current Minimum value</param>
2152 		/// <param name="maximumValue">Current Maximum value</param>
2153 		/// <param name="autoMaximum">Maximum value is Auto</param>
2154 		/// <param name="autoMinimum">Minimum value is Auto</param>
2155 		/// <param name="valuesType">AxisName of date-time values.</param>
2156 		/// <returns>Interval</returns>
EstimateDateAxis( ref double minimumValue, ref double maximumValue, bool autoMaximum, bool autoMinimum, ChartValueType valuesType)2157 		private double EstimateDateAxis(
2158 			ref double minimumValue,
2159 			ref double maximumValue,
2160 			bool autoMaximum,
2161 			bool autoMinimum,
2162 			ChartValueType valuesType)
2163 		{
2164 			double axisInterval;
2165 
2166 			double min = minimumValue;
2167 			double max = maximumValue;
2168 
2169 			// Find interval for this date type
2170             axisInterval = CalcInterval(min, max, true, out _internalIntervalType, valuesType);
2171 
2172 
2173 			// For 3D Charts interval could be changed. After rotation
2174 			// projection of axis could be very small.
2175 			if( !double.IsNaN( this.interval3DCorrection ) &&
2176 				ChartArea.Area3DStyle.Enable3D &&
2177 				!ChartArea.chartAreaIsCurcular)
2178 			{
2179 				axisInterval = Math.Floor( axisInterval / this.interval3DCorrection );
2180 
2181 				this.interval3DCorrection = double.NaN;
2182 			}
2183 
2184 			// Find number of units between minimum and maximum
2185 			int numberOfUnits = GetNumOfUnits( min, max, _internalIntervalType );
2186 
2187 			// Make a gap between max point and axis for Y axes
2188 			if( axisType == AxisName.Y || axisType == AxisName.Y2 )
2189 			{
2190                 if (autoMinimum && minimumValue > ChartHelper.GetIntervalSize(min, axisInterval, _internalIntervalType))
2191 				{
2192 					// Add gap to the minimum value from the series
2193 					// equal half of the interval
2194                     minimumValue += ChartHelper.GetIntervalSize(
2195 						min,
2196 						- (axisInterval / 2.0) * margin / 100,
2197                         _internalIntervalType,
2198 						null,
2199 						0.0,
2200 						DateTimeIntervalType.Number,
2201 						false,
2202 						false);
2203 
2204 					// Align minimum sacale value on the interval
2205 					minimumValue = ChartHelper.AlignIntervalStart(
2206 						minimumValue,
2207 						axisInterval * margin / 100,
2208                         _internalIntervalType);
2209 				}
2210 
2211 				// Increase maximum if not zero. Make a space between chart type
2212 				// and the end of the chart area.
2213 				if( autoMaximum && max > 0 && margin != 0.0 )
2214 				{
2215 					maximumValue = minimumValue + ChartHelper.GetIntervalSize(
2216 						minimumValue,
2217 						(double)((Math.Floor(numberOfUnits / axisInterval  / margin * 100)+2) * axisInterval  * margin / 100),
2218                         _internalIntervalType);
2219 				}
2220 			}
2221 
2222             InternalIntervalType = _internalIntervalType;
2223 
2224 			// Set interval for Grid lines Tick Marks and labels
2225 			return axisInterval;
2226 		}
2227 
2228 		/// <summary>
2229 		/// This function recalculates minimum maximum and interval for
2230 		/// number type axis. The function uses current values for minimum and
2231 		/// maximum to find new rounding values.
2232 		/// </summary>
2233 		/// <param name="minimumValue">Current Minimum value</param>
2234 		/// <param name="maximumValue">Current Maximum value</param>
2235 		/// <param name="shouldStartFromZero">Should start from zero flag.</param>
2236 		/// <param name="preferredNumberOfIntervals">Preferred number of intervals. Can be set to zero for dynamic mode.</param>
2237 		/// <param name="autoMaximum">Maximum value is Auto</param>
2238 		/// <param name="autoMinimum">Minimum value is Auto</param>
2239 		/// <returns>Interval</returns>
EstimateNumberAxis( ref double minimumValue, ref double maximumValue, bool shouldStartFromZero, int preferredNumberOfIntervals, bool autoMaximum, bool autoMinimum )2240 		internal double EstimateNumberAxis(
2241 			ref double minimumValue,
2242 			ref double maximumValue,
2243 			bool shouldStartFromZero,
2244 			int preferredNumberOfIntervals,
2245 			bool autoMaximum,
2246 			bool autoMinimum )
2247 		{
2248 			double axisInterval;
2249 			double min = minimumValue;
2250 			double max = maximumValue;
2251 			double diff;
2252 
2253 			if( !roundedXValues && ( axisType == AxisName.X || axisType == AxisName.X2 ) )
2254 			{
2255 				diff = ChartArea.GetPointsInterval( false, 10 );
2256 				if( diff == 0 || ( max - min ) / diff > 20 )
2257 				{
2258 					diff = ( max - min ) / preferredNumberOfIntervals;
2259 				}
2260 
2261 			}
2262 			else
2263 			{
2264 				diff = ( max - min ) / preferredNumberOfIntervals;
2265 			}
2266 
2267             // For 3D Charts interval could be changed. After rotation
2268 			// projection of axis could be very small.
2269 			if( !double.IsNaN( this.interval3DCorrection ) &&
2270 				ChartArea.Area3DStyle.Enable3D &&
2271 				!ChartArea.chartAreaIsCurcular)
2272 			{
2273 				diff = diff / this.interval3DCorrection;
2274 
2275 				// Do not change minimum and maximum with 3D correction.
2276 				if( max - min < diff )
2277 				{
2278 					diff = max - min;
2279 				}
2280 
2281 				this.interval3DCorrection = double.NaN;
2282 
2283 				if( diff != 0.0 )
2284 				{
2285 					diff = CalcInterval( diff );
2286 				}
2287 			}
2288 
2289 
2290 			if( autoMaximum || autoMinimum )
2291 			{
2292 				if( diff == 0 )
2293 				{
2294 					// Can not find interval. Minimum and maximum are same
2295 
2296 					max = min + 1;
2297 					diff = 0.2;
2298 					axisInterval = 0.2;
2299 				}
2300 				else
2301 				{
2302 					axisInterval = CalcInterval( diff );
2303 				}
2304 			}
2305 			else
2306 			{
2307 				axisInterval = diff;
2308 			}
2309 
2310 			// Case when minimum or maximum is set and interval is > maximum.
2311 			// Reasons overflow exception.
2312 			if( ((Axis)this).interval != 0 && ((Axis)this).interval > axisInterval && minimumValue + ((Axis)this).interval > maximumValue )
2313 			{
2314 				axisInterval = ((Axis)this).interval;
2315 				if( autoMaximum )
2316 				{
2317 					maximumValue = minimumValue + axisInterval;
2318 				}
2319 
2320 				if( autoMinimum )
2321 				{
2322 					minimumValue = maximumValue - axisInterval;
2323 				}
2324 			}
2325 
2326 			// The maximum and minimum rounding for Y Axes
2327 			if( axisType == AxisName.Y || axisType == AxisName.Y2 || ( roundedXValues && ( axisType == AxisName.X || axisType == AxisName.X2 )))
2328 			{
2329 				// Start from zero for the 100% chart types
2330 				bool minIsZero = false;
2331 				bool maxIsZero = false;
2332 				if(ChartArea.hundredPercent)
2333 				{
2334 					minIsZero = (minimumValue == 0.0);
2335 					maxIsZero = (maximumValue == 0.0);
2336 				}
2337 
2338 				// Round min/max values
2339 				RoundedValues( axisInterval, shouldStartFromZero, autoMaximum, autoMinimum, ref minimumValue, ref maximumValue );
2340 
2341 				// Do not allow min/max values more than a hundred
2342 				if(ChartArea.hundredPercent)
2343 				{
2344 					if(autoMinimum)
2345 					{
2346 						if(minimumValue < -100)
2347 							minimumValue = -100;
2348 						if(minIsZero)
2349 							minimumValue = 0;
2350 					}
2351 
2352 					if(autoMaximum)
2353 					{
2354 						if(maximumValue > 100)
2355 							maximumValue = 100;
2356 						if(maxIsZero)
2357 							maximumValue = 0;
2358 					}
2359 				}
2360 			}
2361 
2362 			// Set interval for Grid lines Tick Marks and labels
2363 			return axisInterval;
2364 		}
2365 
2366 		#endregion
2367 	}
2368 }
2369 
2370