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