1 //------------------------------------------------------------- 2 // <copyright company=�Microsoft Corporation�> 3 // Copyright � Microsoft Corporation. All Rights Reserved. 4 // </copyright> 5 //------------------------------------------------------------- 6 // @owner=alexgor, deliant 7 //================================================================= 8 // File: Axis.cs 9 // 10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting 11 // 12 // Classes: Axis 13 // 14 // Purpose: Axis related properties and methods. Axis class gives 15 // information to Common.Chart series about 16 // position in the Common.Chart area and keeps all necessary 17 // information about axes. 18 // 19 // Reviewed: GS - August 6, 2002 20 // AG - August 7, 2002 21 // 22 //=================================================================== 23 24 #region Used namespace 25 using System; 26 using System.Collections; 27 using System.Collections.Specialized; 28 using System.ComponentModel; 29 using System.ComponentModel.Design; 30 using System.Data; 31 using System.Drawing; 32 using System.Drawing.Design; 33 using System.Drawing.Text; 34 using System.Drawing.Drawing2D; 35 using System.Diagnostics.CodeAnalysis; 36 #if Microsoft_CONTROL 37 using System.Windows.Forms.DataVisualization.Charting; 38 using System.Windows.Forms.DataVisualization.Charting.Data; 39 using System.Windows.Forms.DataVisualization.Charting.ChartTypes; 40 using System.Windows.Forms.DataVisualization.Charting.Utilities; 41 using System.Windows.Forms.DataVisualization.Charting.Borders3D; 42 43 #else 44 using System.Web; 45 using System.Web.UI; 46 using System.Web.UI.DataVisualization.Charting; 47 using System.Web.UI.DataVisualization.Charting.Data; 48 using System.Web.UI.DataVisualization.Charting.Utilities; 49 using System.Web.UI.DataVisualization.Charting.ChartTypes; 50 #endif 51 52 53 #endregion 54 55 #if Microsoft_CONTROL 56 namespace System.Windows.Forms.DataVisualization.Charting 57 #else 58 namespace System.Web.UI.DataVisualization.Charting 59 60 #endif 61 { 62 #region Axis name enumeration 63 64 /// <summary> 65 /// An enumeration of auto-fitting styles of the axis labels. 66 /// </summary> 67 [Flags] 68 public enum LabelAutoFitStyles 69 { 70 /// <summary> 71 /// No auto-fitting. 72 /// </summary> 73 None = 0, 74 /// <summary> 75 /// Allow font size increasing. 76 /// </summary> 77 IncreaseFont = 1, 78 /// <summary> 79 /// Allow font size decreasing. 80 /// </summary> 81 DecreaseFont = 2, 82 /// <summary> 83 /// Allow using staggered labels. 84 /// </summary> 85 StaggeredLabels = 4, 86 /// <summary> 87 /// Allow changing labels angle using values of 0, 30, 60 and 90 degrees. 88 /// </summary> 89 LabelsAngleStep30 = 8, 90 /// <summary> 91 /// Allow changing labels angle using values of 0, 45, 90 degrees. 92 /// </summary> 93 LabelsAngleStep45 = 16, 94 /// <summary> 95 /// Allow changing labels angle using values of 0 and 90 degrees. 96 /// </summary> 97 LabelsAngleStep90 = 32, 98 /// <summary> 99 /// Allow replacing spaces with the new line character. 100 /// </summary> 101 WordWrap = 64, 102 } 103 104 /// <summary> 105 /// An enumeration of axis names. 106 /// </summary> 107 public enum AxisName 108 { 109 /// <summary> 110 /// Primary X Axis. 111 /// </summary> 112 113 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "X")] 114 X = 0, 115 /// <summary> 116 /// Primary Y Axis. 117 /// </summary> 118 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Y")] 119 Y = 1, 120 /// <summary> 121 /// Secondary X Axis. 122 /// </summary> 123 X2 = 2, 124 /// <summary> 125 /// Secondary Y Axis. 126 /// </summary> 127 Y2 = 3 128 } 129 130 #endregion 131 132 /// <summary> 133 /// The Axis class gives information to the Common.Chart series 134 /// about positions in the Common.Chart area and keeps all of 135 /// the data about the axis. 136 /// </summary> 137 [ 138 SRDescription("DescriptionAttributeAxis_Axis"), 139 DefaultProperty("Enabled"), 140 ] 141 #if ASPPERM_35 142 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] 143 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] 144 #endif 145 #if Microsoft_CONTROL 146 public partial class Axis : ChartNamedElement 147 #else 148 public partial class Axis : ChartNamedElement, IChartMapArea 149 #endif 150 { 151 #region Axis fields 152 153 /// <summary> 154 /// Plot area position 155 /// </summary> 156 internal ElementPosition PlotAreaPosition; 157 158 // This field synchronies Store and Reset temporary values 159 private bool _storeValuesEnabled = true; 160 161 private FontCache _fontCache = new FontCache(); 162 private Font _titleFont; 163 private Color _titleForeColor = Color.Black; 164 private StringAlignment _titleAlignment = StringAlignment.Center; 165 private string _title = ""; 166 private int _lineWidth = 1; 167 private ChartDashStyle _lineDashStyle = ChartDashStyle.Solid; 168 private Color _lineColor = Color.Black; 169 private bool _isLabelAutoFit = true; 170 private AxisArrowStyle _arrowStyle = AxisArrowStyle.None; 171 private StripLinesCollection _stripLines = null; 172 private bool _isMarksNextToAxis = true; 173 174 // Default text orientation 175 private TextOrientation _textOrientation = TextOrientation.Auto; 176 177 // Size of the axis elements in percentage 178 internal float titleSize = 0F; 179 internal float labelSize = 0F; 180 internal float labelNearOffset = 0F; 181 internal float labelFarOffset = 0F; 182 internal float unRotatedLabelSize = 0F; 183 internal float markSize = 0F; 184 internal float scrollBarSize = 0F; 185 internal float totlaGroupingLabelsSize = 0F; 186 internal float[] groupingLabelSizes = null; 187 internal float totlaGroupingLabelsSizeAdjustment = 0f; 188 private LabelAutoFitStyles _labelAutoFitStyle = LabelAutoFitStyles.DecreaseFont | 189 LabelAutoFitStyles.IncreaseFont | 190 LabelAutoFitStyles.LabelsAngleStep30 | 191 LabelAutoFitStyles.StaggeredLabels | 192 LabelAutoFitStyles.WordWrap; 193 194 // Auto calculated font for labels 195 internal Font autoLabelFont = null; 196 internal int autoLabelAngle = -1000; 197 internal int autoLabelOffset = -1; 198 199 // Labels auto fitting constants 200 private float _aveLabelFontSize = 10F; 201 private float _minLabelFontSize = 5F; 202 // Determines maximum label size of the chart area. 203 private float _maximumAutoSize = 75f; 204 205 // Chart title position rectangle 206 private RectangleF _titlePosition = RectangleF.Empty; 207 208 // Element spacing size 209 internal const float elementSpacing = 1F; 210 211 // Maximum total size of the axis's elements in percentage 212 private const float maxAxisElementsSize = 75F; 213 214 // Maximum size of the axis title in percentage 215 private const float maxAxisTitleSize = 20F; 216 217 // Maximum size of the axis second row of labels in percentage 218 // of the total labels size 219 private const float maxAxisLabelRow2Size = 45F; 220 221 // Maximum size of the axis tick marks in percentage 222 private const float maxAxisMarkSize = 20F; 223 224 // Minimum cached value from data series. 225 internal double minimumFromData = double.NaN; 226 227 // Maximum cached value from data series. 228 internal double maximumFromData = double.NaN; 229 230 // Flag, which tells to Set Data method to take, again values from 231 // data source and not to use cached values. 232 internal bool refreshMinMaxFromData = true; 233 234 // Flag, which tells to Set Data method to take, again values from 235 // data source and not to use cached values. 236 internal int numberOfPointsInAllSeries = 0; 237 238 // Original axis scaleView position 239 private double _originalViewPosition = double.NaN; 240 241 242 /// <summary> 243 /// Indicates that isInterlaced strip lines will be displayed for the axis. 244 /// </summary> 245 private bool _isInterlaced = false; 246 247 /// <summary> 248 /// Color used to draw isInterlaced strip lines for the axis. 249 /// </summary> 250 private Color _interlacedColor = Color.Empty; 251 252 /// <summary> 253 /// Axis interval offset. 254 /// </summary> 255 private double _intervalOffset = 0; 256 257 /// <summary> 258 /// Axis interval. 259 /// </summary> 260 internal double interval = 0; 261 262 /// <summary> 263 /// Axis interval units type. 264 /// </summary> 265 internal DateTimeIntervalType intervalType = DateTimeIntervalType.Auto; 266 267 /// <summary> 268 /// Axis interval offset units type. 269 /// </summary> 270 internal DateTimeIntervalType intervalOffsetType = DateTimeIntervalType.Auto; 271 272 /// <summary> 273 /// Minimum font size that can be used by the labels auto-fitting algorithm. 274 /// </summary> 275 internal int labelAutoFitMinFontSize = 6; 276 277 /// <summary> 278 /// Maximum font size that can be used by the labels auto-fitting algorithm. 279 /// </summary> 280 internal int labelAutoFitMaxFontSize = 10; 281 282 /// <summary> 283 /// Axis tooltip 284 /// </summary> 285 private string _toolTip = String.Empty; 286 287 /// <summary> 288 /// Axis HREF 289 /// </summary> 290 private string _url = String.Empty; 291 292 #if !Microsoft_CONTROL 293 294 295 /// <summary> 296 /// Axis map area attributes 297 /// </summary> 298 private string _mapAreaAttributes = String.Empty; 299 300 private string _postbackValue = String.Empty; 301 302 #endif 303 304 #endregion 305 306 #region Axis constructor and initialization 307 308 /// <summary> 309 /// Default constructor of Axis. 310 /// </summary> Axis()311 public Axis() 312 : base(null, GetName(AxisName.X)) 313 { 314 Initialize(AxisName.X); 315 } 316 317 /// <summary> 318 /// Axis constructor. 319 /// </summary> 320 /// <param name="chartArea">The chart area the axis belongs to.</param> 321 /// <param name="axisTypeName">The type of the axis.</param> Axis(ChartArea chartArea, AxisName axisTypeName)322 public Axis(ChartArea chartArea, AxisName axisTypeName) 323 : base(chartArea, GetName(axisTypeName)) 324 { 325 Initialize(axisTypeName); 326 } 327 328 /// <summary> 329 /// Initialize axis class 330 /// </summary> 331 /// <param name="axisTypeName">Name of the axis type.</param> Initialize(AxisName axisTypeName)332 private void Initialize(AxisName axisTypeName) 333 { 334 // DT: Axis could be already created. Don't recreate new labelstyle and other objects. 335 // Initialize axis labels 336 if (labelStyle == null) 337 { 338 labelStyle = new LabelStyle(this); 339 } 340 if (_customLabels == null) 341 { 342 _customLabels = new CustomLabelsCollection(this); 343 } 344 if (_scaleView == null) 345 { 346 // Create axis data scaleView object 347 _scaleView = new AxisScaleView(this); 348 } 349 #if Microsoft_CONTROL 350 if (scrollBar == null) 351 { 352 // Create axis croll bar class 353 scrollBar = new AxisScrollBar(this); 354 } 355 #endif // Microsoft_CONTROL 356 357 this.axisType = axisTypeName; 358 359 // Create grid & tick marks objects 360 if (minorTickMark == null) 361 { 362 minorTickMark = new TickMark(this, false); 363 } 364 if (majorTickMark == null) 365 { 366 majorTickMark = new TickMark(this, true); 367 majorTickMark.Interval = double.NaN; 368 majorTickMark.IntervalOffset = double.NaN; 369 majorTickMark.IntervalType = DateTimeIntervalType.NotSet; 370 majorTickMark.IntervalOffsetType = DateTimeIntervalType.NotSet; 371 } 372 if (minorGrid == null) 373 { 374 minorGrid = new Grid(this, false); 375 } 376 if (majorGrid == null) 377 { 378 majorGrid = new Grid(this, true); 379 majorGrid.Interval = double.NaN; 380 majorGrid.IntervalOffset = double.NaN; 381 majorGrid.IntervalType = DateTimeIntervalType.NotSet; 382 majorGrid.IntervalOffsetType = DateTimeIntervalType.NotSet; 383 } 384 if (this._stripLines == null) 385 { 386 this._stripLines = new StripLinesCollection(this); 387 } 388 389 if (_titleFont == null) 390 { 391 _titleFont = _fontCache.DefaultFont; 392 } 393 #if SUBAXES 394 if(this.subAxes == null) 395 { 396 this.subAxes = new SubAxisCollection(this); 397 } 398 #endif // SUBAXES 399 400 #if Microsoft_CONTROL 401 402 // Initialize axis scroll bar class 403 this.ScrollBar.Initialize(); 404 405 #endif // Microsoft_CONTROL 406 407 // Create collection of scale segments 408 if (this.scaleSegments == null) 409 { 410 this.scaleSegments = new AxisScaleSegmentCollection(this); 411 } 412 413 // Create scale break style 414 if (this.axisScaleBreakStyle == null) 415 { 416 this.axisScaleBreakStyle = new AxisScaleBreakStyle(this); 417 } 418 } 419 420 /// <summary> 421 /// Initialize axis class 422 /// </summary> 423 /// <param name="chartArea">Chart area that the axis belongs.</param> 424 /// <param name="axisTypeName">Axis type.</param> Initialize(ChartArea chartArea, AxisName axisTypeName)425 internal void Initialize(ChartArea chartArea, AxisName axisTypeName) 426 { 427 this.Initialize(axisTypeName); 428 this.Parent = chartArea; 429 this.Name = GetName(axisTypeName); 430 } 431 432 /// <summary> 433 /// Set Axis Name 434 /// </summary> GetName(AxisName axisName)435 internal static string GetName(AxisName axisName) 436 { 437 // Set axis name. 438 // NOTE: Strings below should neber be localized. Name properties in the chart are never localized 439 // and represent consisten object name in all locales. 440 switch (axisName) 441 { 442 case (AxisName.X): 443 return "X axis"; 444 case (AxisName.Y): 445 return "Y (Value) axis"; 446 case (AxisName.X2): 447 return "Secondary X axis"; 448 case (AxisName.Y2): 449 return "Secondary Y (Value) axis"; 450 } 451 return null; 452 } 453 454 #endregion 455 456 #region Axis properies 457 458 // Internal 459 internal ChartArea ChartArea 460 { 461 get { return Parent as ChartArea; } 462 } 463 464 /// <summary> 465 /// Text orientation. 466 /// </summary> 467 [ 468 SRCategory("CategoryAttributeTitle"), 469 Bindable(true), 470 DefaultValue(TextOrientation.Auto), 471 SRDescription("DescriptionAttribute_TextOrientation"), 472 NotifyParentPropertyAttribute(true), 473 #if !Microsoft_CONTROL 474 PersistenceMode(PersistenceMode.Attribute) 475 #endif 476 ] 477 public TextOrientation TextOrientation 478 { 479 get 480 { 481 return this._textOrientation; 482 } 483 set 484 { 485 this._textOrientation = value; 486 this.Invalidate(); 487 } 488 } 489 490 /// <summary> 491 /// Returns sub-axis name. 492 /// </summary> 493 virtual internal string SubAxisName 494 { 495 get 496 { 497 return string.Empty; 498 } 499 } 500 501 #if SUBAXES 502 503 /// <summary> 504 /// Indicates if this axis object present the main or sub axis. 505 /// </summary> 506 virtual internal bool IsSubAxis 507 { 508 get 509 { 510 return false; 511 } 512 } 513 514 private SubAxisCollection subAxes = null; 515 516 /// <summary> 517 /// Sub-axes collection. 518 /// </summary> 519 [ 520 SRCategory("CategoryAttributeSubAxes"), 521 Bindable(true), 522 SRDescription("DescriptionAttributeSubAxes"), 523 #if Microsoft_CONTROL 524 DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 525 #else 526 PersistenceMode(PersistenceMode.InnerProperty), 527 #endif 528 Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base) 529 ] 530 virtual public SubAxisCollection SubAxes 531 { 532 get 533 { 534 return this.subAxes; 535 } 536 } 537 538 #endif // SUBAXES 539 540 /// <summary> 541 /// Gets or sets a flag which indicates whether interlaced strip lines will be displayed for the axis. 542 /// </summary> 543 [ 544 SRCategory("CategoryAttributeAppearance"), 545 Bindable(true), 546 DefaultValue(false), 547 SRDescription("DescriptionAttributeInterlaced"), 548 #if !Microsoft_CONTROL 549 PersistenceMode(PersistenceMode.Attribute), 550 #endif 551 NotifyParentPropertyAttribute(true), 552 ] 553 public bool IsInterlaced 554 { 555 get 556 { 557 return _isInterlaced; 558 } 559 set 560 { 561 _isInterlaced = value; 562 this.Invalidate(); 563 } 564 } 565 566 /// <summary> 567 /// Gets or sets the color used to draw interlaced strip lines for the axis. 568 /// </summary> 569 [ 570 SRCategory("CategoryAttributeAppearance"), 571 Bindable(true), 572 DefaultValue(typeof(Color), ""), 573 SRDescription("DescriptionAttributeInterlacedColor"), 574 NotifyParentPropertyAttribute(true), 575 TypeConverter(typeof(ColorConverter)), 576 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), 577 #if !Microsoft_CONTROL 578 PersistenceMode(PersistenceMode.Attribute) 579 #endif 580 ] 581 public Color InterlacedColor 582 { 583 get 584 { 585 return _interlacedColor; 586 } 587 set 588 { 589 _interlacedColor = value; 590 this.Invalidate(); 591 } 592 } 593 594 /// <summary> 595 /// Axis name. This field is reserved for internal use only. 596 /// </summary> 597 [ 598 SRCategory("CategoryAttributeAppearance"), 599 Bindable(true), 600 Browsable(false), 601 DefaultValue(""), 602 SRDescription("DescriptionAttributeAxis_Name"), 603 #if !Microsoft_CONTROL 604 PersistenceMode(PersistenceMode.Attribute), 605 #endif 606 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), 607 SerializationVisibilityAttribute(SerializationVisibility.Hidden) 608 ] 609 public override string Name 610 { 611 get 612 { 613 return base.Name; 614 } 615 set 616 { 617 base.Name = value; 618 } 619 } 620 621 /// <summary> 622 /// Axis name. This field is reserved for internal use only. 623 /// </summary> 624 [ 625 SRCategory("CategoryAttributeAppearance"), 626 Bindable(true), 627 Browsable(false), 628 DefaultValue(""), 629 SRDescription("DescriptionAttributeType"), 630 #if !Microsoft_CONTROL 631 PersistenceMode(PersistenceMode.Attribute), 632 #endif 633 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden), 634 SerializationVisibilityAttribute(SerializationVisibility.Hidden) 635 ] 636 virtual public AxisName AxisName 637 { 638 get 639 { 640 return axisType; 641 } 642 } 643 644 /// <summary> 645 /// Gets or sets the arrow style used for the axis. 646 /// </summary> 647 [ 648 SRCategory("CategoryAttributeAppearance"), 649 Bindable(true), 650 DefaultValue(AxisArrowStyle.None), 651 NotifyParentPropertyAttribute(true), 652 SRDescription("DescriptionAttributeArrows"), 653 #if !Microsoft_CONTROL 654 PersistenceMode(PersistenceMode.Attribute) 655 #endif 656 ] 657 public AxisArrowStyle ArrowStyle 658 { 659 get 660 { 661 return _arrowStyle; 662 } 663 set 664 { 665 _arrowStyle = value; 666 this.Invalidate(); 667 } 668 } 669 670 /// <summary> 671 /// Gets or sets the properties used for the major gridlines. 672 /// </summary> 673 [ 674 SRCategory("CategoryAttributeGridTickMarks"), 675 Bindable(true), 676 NotifyParentPropertyAttribute(true), 677 SRDescription("DescriptionAttributeMajorGrid"), 678 #if Microsoft_CONTROL 679 DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 680 #else 681 PersistenceMode(PersistenceMode.InnerProperty), 682 #endif 683 TypeConverter(typeof(NoNameExpandableObjectConverter)) 684 ] 685 public Grid MajorGrid 686 { 687 get 688 { 689 return majorGrid; 690 } 691 set 692 { 693 majorGrid = value; 694 majorGrid.Axis = this; 695 majorGrid.majorGridTick = true; 696 697 if (!majorGrid.intervalChanged) 698 majorGrid.Interval = double.NaN; 699 if (!majorGrid.intervalOffsetChanged) 700 majorGrid.IntervalOffset = double.NaN; 701 if (!majorGrid.intervalTypeChanged) 702 majorGrid.IntervalType = DateTimeIntervalType.NotSet; 703 if (!majorGrid.intervalOffsetTypeChanged) 704 majorGrid.IntervalOffsetType = DateTimeIntervalType.NotSet; 705 706 this.Invalidate(); 707 } 708 } 709 710 /// <summary> 711 /// Gets or sets the properties used for the minor gridlines. 712 /// </summary> 713 [ 714 SRCategory("CategoryAttributeGridTickMarks"), 715 Bindable(true), 716 NotifyParentPropertyAttribute(true), 717 SRDescription("DescriptionAttributeMinorGrid"), 718 #if Microsoft_CONTROL 719 DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 720 #else 721 PersistenceMode(PersistenceMode.InnerProperty), 722 #endif 723 TypeConverter(typeof(NoNameExpandableObjectConverter)) 724 ] 725 public Grid MinorGrid 726 { 727 get 728 { 729 return minorGrid; 730 } 731 set 732 { 733 minorGrid = value; 734 minorGrid.Initialize(this, false); 735 this.Invalidate(); 736 } 737 } 738 739 /// <summary> 740 /// Gets or sets the properties used for the major tick marks. 741 /// </summary> 742 [ 743 SRCategory("CategoryAttributeGridTickMarks"), 744 Bindable(true), 745 NotifyParentPropertyAttribute(true), 746 SRDescription("DescriptionAttributeMajorTickMark"), 747 #if Microsoft_CONTROL 748 DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 749 #else 750 PersistenceMode(PersistenceMode.InnerProperty), 751 #endif 752 TypeConverter(typeof(NoNameExpandableObjectConverter)) 753 ] 754 public TickMark MajorTickMark 755 { 756 get 757 { 758 return majorTickMark; 759 } 760 set 761 { 762 majorTickMark = value; 763 majorTickMark.Axis = this; 764 majorTickMark.majorGridTick = true; 765 766 if (!majorTickMark.intervalChanged) 767 majorTickMark.Interval = double.NaN; 768 if (!majorTickMark.intervalOffsetChanged) 769 majorTickMark.IntervalOffset = double.NaN; 770 if (!majorTickMark.intervalTypeChanged) 771 majorTickMark.IntervalType = DateTimeIntervalType.NotSet; 772 if (!majorTickMark.intervalOffsetTypeChanged) 773 majorTickMark.IntervalOffsetType = DateTimeIntervalType.NotSet; 774 775 this.Invalidate(); 776 } 777 } 778 779 /// <summary> 780 /// Gets or sets the properties used for the minor tick marks. 781 /// </summary> 782 [ 783 SRCategory("CategoryAttributeGridTickMarks"), 784 Bindable(true), 785 NotifyParentPropertyAttribute(true), 786 SRDescription("DescriptionAttributeMinorTickMark"), 787 #if Microsoft_CONTROL 788 DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 789 #else 790 PersistenceMode(PersistenceMode.InnerProperty), 791 #endif 792 TypeConverter(typeof(NoNameExpandableObjectConverter)) 793 ] 794 public TickMark MinorTickMark 795 { 796 get 797 { 798 return minorTickMark; 799 } 800 set 801 { 802 minorTickMark = value; 803 minorTickMark.Initialize(this, false); 804 this.Invalidate(); 805 } 806 } 807 808 /// <summary> 809 /// Gets or sets a flag which indicates whether auto-fitting of labels is enabled. 810 /// </summary> 811 [ 812 SRCategory("CategoryAttributeLabels"), 813 Bindable(true), 814 DefaultValue(true), 815 SRDescription("DescriptionAttributeLabelsAutoFit"), 816 NotifyParentPropertyAttribute(true), 817 #if !Microsoft_CONTROL 818 PersistenceMode(PersistenceMode.Attribute), 819 #endif 820 RefreshPropertiesAttribute(RefreshProperties.All) 821 ] 822 public bool IsLabelAutoFit 823 { 824 get 825 { 826 return _isLabelAutoFit; 827 } 828 set 829 { 830 _isLabelAutoFit = value; 831 this.Invalidate(); 832 } 833 } 834 835 836 837 /// <summary> 838 /// Gets or sets the minimum font size that can be used by 839 /// the label auto-fitting algorithm. 840 /// </summary> 841 [ 842 SRCategory("CategoryAttributeLabels"), 843 Bindable(true), 844 DefaultValue(6), 845 SRDescription("DescriptionAttributeLabelsAutoFitMinFontSize"), 846 NotifyParentPropertyAttribute(true), 847 #if !Microsoft_CONTROL 848 PersistenceMode(PersistenceMode.Attribute), 849 #endif 850 RefreshPropertiesAttribute(RefreshProperties.All) 851 ] 852 public int LabelAutoFitMinFontSize 853 { 854 get 855 { 856 return this.labelAutoFitMinFontSize; 857 } 858 set 859 { 860 // Font size cannot be less than 5 861 if(value < 5) 862 { 863 throw (new InvalidOperationException(SR.ExceptionAxisLabelsAutoFitMinFontSizeValueInvalid)); 864 } 865 866 this.labelAutoFitMinFontSize = value; 867 this.Invalidate(); 868 } 869 } 870 871 /// <summary> 872 /// Gets or sets the maximum font size that can be used by 873 /// the label auto-fitting algorithm. 874 /// </summary> 875 [ 876 SRCategory("CategoryAttributeLabels"), 877 Bindable(true), 878 DefaultValue(10), 879 SRDescription("DescriptionAttributeLabelsAutoFitMaxFontSize"), 880 NotifyParentPropertyAttribute(true), 881 #if !Microsoft_CONTROL 882 PersistenceMode(PersistenceMode.Attribute), 883 #endif 884 RefreshPropertiesAttribute(RefreshProperties.All) 885 ] 886 public int LabelAutoFitMaxFontSize 887 { 888 get 889 { 890 return this.labelAutoFitMaxFontSize; 891 } 892 set 893 { 894 // Font size cannot be less than 5 895 if(value < 5) 896 { 897 throw (new InvalidOperationException(SR.ExceptionAxisLabelsAutoFitMaxFontSizeInvalid)); 898 } 899 900 this.labelAutoFitMaxFontSize = value; 901 this.Invalidate(); 902 } 903 } 904 905 906 907 /// <summary> 908 /// Gets or sets the auto-fitting style used for the labels. 909 /// IsLabelAutoFit must be set to true. 910 /// </summary> 911 [ 912 SRCategory("CategoryAttributeLabels"), 913 Bindable(true), 914 DefaultValue(LabelAutoFitStyles.DecreaseFont | LabelAutoFitStyles.IncreaseFont | LabelAutoFitStyles.LabelsAngleStep30 | LabelAutoFitStyles.StaggeredLabels | LabelAutoFitStyles.WordWrap), 915 SRDescription("DescriptionAttributeLabelsAutoFitStyle"), 916 NotifyParentPropertyAttribute(true), 917 Editor(Editors.FlagsEnumUITypeEditor.Editor, Editors.FlagsEnumUITypeEditor.Base), 918 #if !Microsoft_CONTROL 919 PersistenceMode(PersistenceMode.Attribute), 920 #endif 921 ] 922 public LabelAutoFitStyles LabelAutoFitStyle 923 { 924 get 925 { 926 return this._labelAutoFitStyle; 927 } 928 set 929 { 930 this._labelAutoFitStyle = value; 931 this.Invalidate(); 932 } 933 } 934 935 /// <summary> 936 /// Gets or sets a flag which indicates whether 937 /// tick marks and labels move with the axis when 938 /// the crossing value changes. 939 /// </summary> 940 [ 941 SRCategory("CategoryAttributeAppearance"), 942 Bindable(true), 943 DefaultValue(true), 944 SRDescription("DescriptionAttributeMarksNextToAxis"), 945 NotifyParentPropertyAttribute(true), 946 #if !Microsoft_CONTROL 947 PersistenceMode(PersistenceMode.Attribute) 948 #endif 949 ] 950 virtual public bool IsMarksNextToAxis 951 { 952 get 953 { 954 return _isMarksNextToAxis; 955 } 956 set 957 { 958 _isMarksNextToAxis = value; 959 this.Invalidate(); 960 } 961 } 962 963 /// <summary> 964 /// Gets or sets the axis title. 965 /// </summary> 966 [ 967 SRCategory("CategoryAttributeTitle"), 968 Bindable(true), 969 DefaultValue(""), 970 SRDescription("DescriptionAttributeTitle6"), 971 NotifyParentPropertyAttribute(true), 972 #if !Microsoft_CONTROL 973 PersistenceMode(PersistenceMode.Attribute) 974 #endif 975 ] 976 public string Title 977 { 978 get 979 { 980 return _title; 981 } 982 set 983 { 984 _title = value; 985 this.Invalidate(); 986 } 987 } 988 989 /// <summary> 990 /// Gets or sets the color of the axis title. 991 /// </summary> 992 [ 993 SRCategory("CategoryAttributeTitle"), 994 Bindable(true), 995 DefaultValue(typeof(Color), "Black"), 996 SRDescription("DescriptionAttributeTitleColor"), 997 NotifyParentPropertyAttribute(true), 998 TypeConverter(typeof(ColorConverter)), 999 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), 1000 #if !Microsoft_CONTROL 1001 PersistenceMode(PersistenceMode.Attribute) 1002 #endif 1003 ] 1004 public Color TitleForeColor 1005 { 1006 get 1007 { 1008 return _titleForeColor; 1009 } 1010 set 1011 { 1012 _titleForeColor = value; 1013 this.Invalidate(); 1014 } 1015 } 1016 1017 /// <summary> 1018 /// Gets or sets the alignment of the axis title. 1019 /// </summary> 1020 [ 1021 SRCategory("CategoryAttributeTitle"), 1022 Bindable(true), 1023 DefaultValue(typeof(StringAlignment), "Center"), 1024 SRDescription("DescriptionAttributeTitleAlignment"), 1025 NotifyParentPropertyAttribute(true), 1026 #if !Microsoft_CONTROL 1027 PersistenceMode(PersistenceMode.Attribute) 1028 #endif 1029 ] 1030 public StringAlignment TitleAlignment 1031 { 1032 get 1033 { 1034 return _titleAlignment; 1035 } 1036 set 1037 { 1038 _titleAlignment = value; 1039 this.Invalidate(); 1040 } 1041 } 1042 1043 /// <summary> 1044 /// Gets or sets the font used for the axis title. 1045 /// </summary> 1046 [ 1047 SRCategory("CategoryAttributeTitle"), 1048 Bindable(true), 1049 DefaultValue(typeof(Font), "Microsoft Sans Serif, 8pt"), 1050 SRDescription("DescriptionAttributeTitleFont"), 1051 NotifyParentPropertyAttribute(true), 1052 #if !Microsoft_CONTROL 1053 PersistenceMode(PersistenceMode.Attribute) 1054 #endif 1055 ] 1056 public Font TitleFont 1057 { 1058 get 1059 { 1060 return _titleFont; 1061 } 1062 set 1063 { 1064 _titleFont = value; 1065 this.Invalidate(); 1066 } 1067 } 1068 1069 /// <summary> 1070 /// Gets or sets the line color of the axis. 1071 /// </summary> 1072 [ 1073 SRCategory("CategoryAttributeAppearance"), 1074 Bindable(true), 1075 DefaultValue(typeof(Color), "Black"), 1076 SRDescription("DescriptionAttributeLineColor"), 1077 NotifyParentPropertyAttribute(true), 1078 TypeConverter(typeof(ColorConverter)), 1079 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base), 1080 #if !Microsoft_CONTROL 1081 PersistenceMode(PersistenceMode.Attribute) 1082 #endif 1083 ] 1084 public Color LineColor 1085 { 1086 get 1087 { 1088 return _lineColor; 1089 } 1090 set 1091 { 1092 _lineColor = value; 1093 this.Invalidate(); 1094 } 1095 } 1096 1097 /// <summary> 1098 /// Gets or sets the line width of the axis. 1099 /// </summary> 1100 [ 1101 SRCategory("CategoryAttributeAppearance"), 1102 Bindable(true), 1103 DefaultValue(1), 1104 SRDescription("DescriptionAttributeLineWidth"), 1105 NotifyParentPropertyAttribute(true), 1106 #if !Microsoft_CONTROL 1107 PersistenceMode(PersistenceMode.Attribute) 1108 #endif 1109 ] 1110 public int LineWidth 1111 { 1112 get 1113 { 1114 return _lineWidth; 1115 } 1116 set 1117 { 1118 if (value < 0) 1119 { 1120 throw (new ArgumentOutOfRangeException("value", SR.ExceptionAxisWidthIsNegative)); 1121 } 1122 _lineWidth = value; 1123 this.Invalidate(); 1124 } 1125 } 1126 1127 /// <summary> 1128 /// Gets or sets the line style of the axis. 1129 /// </summary> 1130 [ 1131 SRCategory("CategoryAttributeAppearance"), 1132 Bindable(true), 1133 DefaultValue(ChartDashStyle.Solid), 1134 SRDescription("DescriptionAttributeLineDashStyle"), 1135 NotifyParentPropertyAttribute(true), 1136 #if !Microsoft_CONTROL 1137 PersistenceMode(PersistenceMode.Attribute) 1138 #endif 1139 ] 1140 public ChartDashStyle LineDashStyle 1141 { 1142 get 1143 { 1144 return _lineDashStyle; 1145 } 1146 set 1147 { 1148 _lineDashStyle = value; 1149 this.Invalidate(); 1150 } 1151 } 1152 1153 /// <summary> 1154 /// The collection of strip lines of the axis. 1155 /// </summary> 1156 [ 1157 SRCategory("CategoryAttributeAppearance"), 1158 Bindable(true), 1159 SRDescription("DescriptionAttributeStripLines"), 1160 #if Microsoft_CONTROL 1161 DesignerSerializationVisibility(DesignerSerializationVisibility.Content), 1162 #else 1163 PersistenceMode(PersistenceMode.InnerProperty), 1164 #endif 1165 Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base) 1166 ] 1167 public StripLinesCollection StripLines 1168 { 1169 get 1170 { 1171 return _stripLines; 1172 } 1173 } 1174 1175 1176 /// <summary> 1177 /// Gets or sets the maximum size (in percentage) of the axis used in the automatic layout algorithm. 1178 /// </summary> 1179 /// <remarks> 1180 /// This property determines the maximum size of the axis, measured as a percentage of the chart area. 1181 /// </remarks> 1182 [ 1183 SRCategory("CategoryAttributeLabels"), 1184 DefaultValue(75f), 1185 SRDescription("DescriptionAttributeAxis_MaxAutoSize"), 1186 ] 1187 public float MaximumAutoSize 1188 { 1189 get 1190 { 1191 return this._maximumAutoSize; 1192 } 1193 set 1194 { 1195 if (value < 0f || value > 100f) 1196 { 1197 throw (new ArgumentOutOfRangeException("value", SR.ExceptionValueMustBeInRange("MaximumAutoSize", "0", "100"))); 1198 } 1199 this._maximumAutoSize = value; 1200 this.Invalidate(); 1201 } 1202 } 1203 #endregion 1204 1205 #region IMapAreaAttributes Properties implementation 1206 1207 /// <summary> 1208 /// Tooltip of the axis. 1209 /// </summary> 1210 [ 1211 SRCategory("CategoryAttributeMapArea"), 1212 Bindable(true), 1213 SRDescription("DescriptionAttributeToolTip"), 1214 DefaultValue(""), 1215 ] 1216 public string ToolTip 1217 { 1218 set 1219 { 1220 this._toolTip = value; 1221 } 1222 get 1223 { 1224 return this._toolTip; 1225 } 1226 } 1227 1228 #if !Microsoft_CONTROL 1229 1230 /// <summary> 1231 /// URL target of the axis. 1232 /// </summary> 1233 [ 1234 SRCategory("CategoryAttributeMapArea"), 1235 Bindable(true), 1236 SRDescription("DescriptionAttributeUrl"), 1237 DefaultValue(""), 1238 PersistenceMode(PersistenceMode.Attribute), 1239 Editor(Editors.UrlValueEditor.Editor, Editors.UrlValueEditor.Base) 1240 ] 1241 public string Url 1242 { 1243 set 1244 { 1245 this._url = value; 1246 } 1247 get 1248 { 1249 return this._url; 1250 } 1251 } 1252 1253 1254 /// <summary> 1255 /// Gets or sets the map area attributes. 1256 /// </summary> 1257 [ 1258 SRCategory("CategoryAttributeMapArea"), 1259 Bindable(true), 1260 SRDescription("DescriptionAttributeMapAreaAttributes"), 1261 DefaultValue(""), 1262 PersistenceMode(PersistenceMode.Attribute) 1263 ] 1264 public string MapAreaAttributes 1265 { 1266 set 1267 { 1268 this._mapAreaAttributes = value; 1269 } 1270 get 1271 { 1272 return this._mapAreaAttributes; 1273 } 1274 } 1275 1276 /// <summary> 1277 /// Gets or sets the postback value which can be processed on click event. 1278 /// </summary> 1279 /// <value>The value which is passed to click event as argument.</value> 1280 [DefaultValue("")] 1281 [SRCategory(SR.Keys.CategoryAttributeMapArea)] 1282 [SRDescription(SR.Keys.DescriptionAttributePostBackValue)] 1283 public string PostBackValue 1284 { 1285 get 1286 { 1287 return this._postbackValue; 1288 } 1289 set 1290 { 1291 this._postbackValue = value; 1292 } 1293 } 1294 1295 1296 #endif // !Microsoft_CONTROL 1297 1298 1299 1300 #endregion 1301 1302 #region Axis Interavl properies 1303 1304 /// <summary> 1305 /// Axis interval size. 1306 /// </summary> 1307 [ 1308 SRCategory("CategoryAttributeInterval"), 1309 Bindable(true), 1310 DefaultValue(0.0), 1311 SRDescription("DescriptionAttributeInterval4"), 1312 RefreshPropertiesAttribute(RefreshProperties.All), 1313 TypeConverter(typeof(AxisIntervalValueConverter)), 1314 #if !Microsoft_CONTROL 1315 PersistenceMode(PersistenceMode.Attribute), 1316 #endif 1317 ] 1318 public double Interval 1319 { 1320 get 1321 { 1322 return interval; 1323 } 1324 set 1325 { 1326 // Axis interval properties must be set 1327 if (double.IsNaN(value)) 1328 { 1329 interval = 0; 1330 } 1331 else 1332 { 1333 interval = value; 1334 } 1335 1336 // Reset initial values 1337 majorGrid.interval = tempMajorGridInterval; 1338 majorTickMark.interval = tempMajorTickMarkInterval; 1339 minorGrid.interval = tempMinorGridInterval; 1340 minorTickMark.interval = tempMinorTickMarkInterval; 1341 labelStyle.interval = tempLabelInterval; 1342 1343 this.Invalidate(); 1344 } 1345 } 1346 1347 /// <summary> 1348 /// Axis interval offset. 1349 /// </summary> 1350 [ 1351 SRCategory("CategoryAttributeInterval"), 1352 Bindable(true), 1353 DefaultValue(0.0), 1354 SRDescription("DescriptionAttributeIntervalOffset6"), 1355 #if !Microsoft_CONTROL 1356 PersistenceMode(PersistenceMode.Attribute), 1357 #endif 1358 RefreshPropertiesAttribute(RefreshProperties.All), 1359 TypeConverter(typeof(AxisIntervalValueConverter)) 1360 ] 1361 public double IntervalOffset 1362 { 1363 get 1364 { 1365 return _intervalOffset; 1366 } 1367 set 1368 { 1369 // Axis interval properties must be set 1370 if (double.IsNaN(value)) 1371 { 1372 _intervalOffset = 0; 1373 } 1374 else 1375 { 1376 _intervalOffset = value; 1377 } 1378 1379 this.Invalidate(); 1380 } 1381 } 1382 1383 /// <summary> 1384 /// Axis interval type. 1385 /// </summary> 1386 [ 1387 SRCategory("CategoryAttributeInterval"), 1388 Bindable(true), 1389 DefaultValue(DateTimeIntervalType.Auto), 1390 SRDescription("DescriptionAttributeIntervalType4"), 1391 RefreshPropertiesAttribute(RefreshProperties.All), 1392 #if !Microsoft_CONTROL 1393 PersistenceMode(PersistenceMode.Attribute) 1394 #endif 1395 ] 1396 public DateTimeIntervalType IntervalType 1397 { 1398 get 1399 { 1400 return intervalType; 1401 } 1402 set 1403 { 1404 // Axis interval properties must be set 1405 if (value == DateTimeIntervalType.NotSet) 1406 { 1407 intervalType = DateTimeIntervalType.Auto; 1408 } 1409 else 1410 { 1411 intervalType = value; 1412 } 1413 1414 // Reset initial values 1415 majorGrid.intervalType = tempGridIntervalType; 1416 majorTickMark.intervalType = tempTickMarkIntervalType; 1417 labelStyle.intervalType = tempLabelIntervalType; 1418 1419 this.Invalidate(); 1420 } 1421 } 1422 1423 /// <summary> 1424 /// Axis interval offset type. 1425 /// </summary> 1426 [ 1427 SRCategory("CategoryAttributeInterval"), 1428 Bindable(true), 1429 DefaultValue(DateTimeIntervalType.Auto), 1430 SRDescription("DescriptionAttributeIntervalOffsetType4"), 1431 RefreshPropertiesAttribute(RefreshProperties.All), 1432 #if !Microsoft_CONTROL 1433 PersistenceMode(PersistenceMode.Attribute) 1434 #endif 1435 ] 1436 public DateTimeIntervalType IntervalOffsetType 1437 { 1438 get 1439 { 1440 return intervalOffsetType; 1441 } 1442 set 1443 { 1444 // Axis interval properties must be set 1445 if (value == DateTimeIntervalType.NotSet) 1446 { 1447 intervalOffsetType = DateTimeIntervalType.Auto; 1448 } 1449 else 1450 { 1451 intervalOffsetType = value; 1452 } 1453 1454 this.Invalidate(); 1455 } 1456 } 1457 1458 #endregion 1459 1460 #region Axis painting methods 1461 1462 /// <summary> 1463 /// Checks if Common.Chart axis title is drawn vertically. 1464 /// Note: From the drawing perspective stacked text orientation is not vertical. 1465 /// </summary> 1466 /// <returns>True if text is vertical.</returns> 1467 private bool IsTextVertical 1468 { 1469 get 1470 { 1471 TextOrientation currentTextOrientation = this.GetTextOrientation(); 1472 return currentTextOrientation == TextOrientation.Rotated90 || currentTextOrientation == TextOrientation.Rotated270; 1473 } 1474 } 1475 1476 /// <summary> 1477 /// Returns axis title text orientation. If set to Auto automatically determines the 1478 /// orientation based on the axis position. 1479 /// </summary> 1480 /// <returns>Current text orientation.</returns> GetTextOrientation()1481 private TextOrientation GetTextOrientation() 1482 { 1483 if (this.TextOrientation == TextOrientation.Auto) 1484 { 1485 if (this.AxisPosition == AxisPosition.Left) 1486 { 1487 return TextOrientation.Rotated270; 1488 } 1489 else if (this.AxisPosition == AxisPosition.Right) 1490 { 1491 return TextOrientation.Rotated90; 1492 } 1493 return TextOrientation.Horizontal; 1494 } 1495 return this.TextOrientation; 1496 } 1497 1498 /// <summary> 1499 /// Paint Axis elements on the back of the 3D scene. 1500 /// </summary> 1501 /// <param name="graph">Reference to the Chart Graphics object</param> PrePaint(ChartGraphics graph)1502 internal void PrePaint(ChartGraphics graph) 1503 { 1504 if (enabled != false) 1505 { 1506 // draw axis hot region 1507 DrawAxisLineHotRegion(graph, true); 1508 1509 // Paint Major Tick Marks 1510 majorTickMark.Paint(graph, true); 1511 1512 // Paint Minor Tick Marks 1513 minorTickMark.Paint(graph, true); 1514 1515 // Draw axis line 1516 DrawAxisLine(graph, true); 1517 1518 // Paint Labels 1519 labelStyle.Paint(graph, true); 1520 } 1521 1522 #if SUBAXES 1523 // Process all sub-axis 1524 if(!ChartArea.Area3DStyle.Enable3D && 1525 !ChartArea.chartAreaIsCurcular) 1526 { 1527 foreach(SubAxis subAxis in this.SubAxes) 1528 { 1529 subAxis.PrePaint( graph ); 1530 } 1531 } 1532 #endif // SUBAXES 1533 } 1534 1535 /// <summary> 1536 /// Paint Axis 1537 /// </summary> 1538 /// <param name="graph">Reference to the Chart Graphics object</param> Paint(ChartGraphics graph)1539 internal void Paint(ChartGraphics graph) 1540 { 1541 // Only Y axis is drawn in the circular Common.Chart area 1542 if (ChartArea != null && ChartArea.chartAreaIsCurcular) 1543 { 1544 // Y circular axes 1545 if (this.axisType == AxisName.Y && enabled != false) 1546 { 1547 ICircularChartType chartType = ChartArea.GetCircularChartType(); 1548 if (chartType != null) 1549 { 1550 Matrix oldMatrix = graph.Transform; 1551 float[] axesLocation = chartType.GetYAxisLocations(ChartArea); 1552 bool drawLabels = true; 1553 foreach (float curentSector in axesLocation) 1554 { 1555 // Set graphics rotation matrix 1556 Matrix newMatrix = oldMatrix.Clone(); 1557 newMatrix.RotateAt( 1558 curentSector, 1559 graph.GetAbsolutePoint(ChartArea.circularCenter)); 1560 graph.Transform = newMatrix; 1561 1562 // draw axis hot region 1563 DrawAxisLineHotRegion(graph, false); 1564 1565 // Paint Minor Tick Marks 1566 minorTickMark.Paint(graph, false); 1567 1568 // Paint Major Tick Marks 1569 majorTickMark.Paint(graph, false); 1570 1571 // Draw axis line 1572 DrawAxisLine(graph, false); 1573 1574 // Only first Y axis has labels 1575 if (drawLabels) 1576 { 1577 drawLabels = false; 1578 1579 // Save current font angle 1580 int currentAngle = labelStyle.Angle; 1581 1582 // Set labels text angle 1583 if (labelStyle.Angle == 0) 1584 { 1585 if (curentSector >= 45f && curentSector <= 180f) 1586 { 1587 labelStyle.angle = -90; 1588 } 1589 else if (curentSector > 180f && curentSector <= 315f) 1590 { 1591 labelStyle.angle = 90; 1592 } 1593 } 1594 1595 // Draw labels 1596 labelStyle.Paint(graph, false); 1597 1598 // Restore font angle 1599 labelStyle.angle = currentAngle; 1600 } 1601 } 1602 1603 graph.Transform = oldMatrix; 1604 } 1605 } 1606 1607 // X circular axes 1608 if (this.axisType == AxisName.X && enabled != false) 1609 { 1610 labelStyle.PaintCircular(graph); 1611 } 1612 1613 DrawAxisTitle(graph); 1614 1615 return; 1616 } 1617 1618 // If axis is disabled draw only Title 1619 if (enabled != false) 1620 { 1621 1622 // draw axis hot region 1623 DrawAxisLineHotRegion(graph, false); 1624 1625 // Paint Minor Tick Marks 1626 minorTickMark.Paint(graph, false); 1627 1628 // Paint Major Tick Marks 1629 majorTickMark.Paint(graph, false); 1630 1631 // Draw axis line 1632 DrawAxisLine(graph, false); 1633 1634 // Paint Labels 1635 labelStyle.Paint(graph, false); 1636 1637 #if Microsoft_CONTROL 1638 1639 // Scroll bar is supoorted only in 2D charts 1640 if (ChartArea != null && ChartArea.Area3DStyle.Enable3D == false) 1641 { 1642 // Draw axis scroll bar 1643 ScrollBar.Paint(graph); 1644 } 1645 #endif // Microsoft_CONTROL 1646 1647 } 1648 1649 // Draw axis title 1650 this.DrawAxisTitle(graph); 1651 1652 #if SUBAXES 1653 // Process all sub-axis 1654 if(ChartArea.IsSubAxesSupported) 1655 { 1656 foreach(SubAxis subAxis in this.SubAxes) 1657 { 1658 subAxis.Paint( graph ); 1659 } 1660 } 1661 #endif // SUBAXES 1662 1663 // Reset temp axis offset for side-by-side charts like column 1664 this.ResetTempAxisOffset(); 1665 } 1666 1667 1668 1669 /// <summary> 1670 /// Paint Axis element when segmented axis scale feature is used. 1671 /// </summary> 1672 /// <param name="graph">Reference to the Chart Graphics object</param> PaintOnSegmentedScalePassOne( ChartGraphics graph )1673 internal void PaintOnSegmentedScalePassOne( ChartGraphics graph ) 1674 { 1675 // If axis is disabled draw only Title 1676 if( enabled != false ) 1677 { 1678 // Paint Minor Tick Marks 1679 minorTickMark.Paint( graph, false ); 1680 1681 // Paint Major Tick Marks 1682 majorTickMark.Paint( graph, false ); 1683 } 1684 1685 #if SUBAXES 1686 // Process all sub-axis 1687 if(ChartArea.IsSubAxesSupported) 1688 { 1689 foreach(SubAxis subAxis in this.SubAxes) 1690 { 1691 subAxis.PaintOnSegmentedScalePassOne( graph ); 1692 } 1693 } 1694 #endif // SUBAXES 1695 1696 } 1697 1698 /// <summary> 1699 /// Paint Axis element when segmented axis scale feature is used. 1700 /// </summary> 1701 /// <param name="graph">Reference to the Chart Graphics object</param> PaintOnSegmentedScalePassTwo( ChartGraphics graph )1702 internal void PaintOnSegmentedScalePassTwo( ChartGraphics graph ) 1703 { 1704 // If axis is disabled draw only Title 1705 if( enabled != false ) 1706 { 1707 // Draw axis line 1708 DrawAxisLine( graph, false ); 1709 1710 // Paint Labels 1711 labelStyle.Paint( graph, false); 1712 } 1713 1714 // Draw axis title 1715 this.DrawAxisTitle( graph ); 1716 1717 // Reset temp axis offset for side-by-side charts like column 1718 this.ResetTempAxisOffset(); 1719 1720 #if SUBAXES 1721 // Process all sub-axis 1722 if(ChartArea.IsSubAxesSupported) 1723 { 1724 foreach(SubAxis subAxis in this.SubAxes) 1725 { 1726 subAxis.PaintOnSegmentedScalePassTwo( graph ); 1727 } 1728 } 1729 #endif // SUBAXES 1730 1731 } 1732 1733 /// <summary> 1734 /// Draw axis title 1735 /// </summary> 1736 /// <param name="graph">Reference to the Chart Graphics object</param> DrawAxisTitle(ChartGraphics graph)1737 private void DrawAxisTitle(ChartGraphics graph) 1738 { 1739 if (!this.enabled) 1740 return; 1741 1742 // Draw axis title 1743 if (this.Title.Length > 0) 1744 { 1745 Matrix oldTransform = null; 1746 1747 // Draw title in 3D 1748 if (ChartArea.Area3DStyle.Enable3D && !ChartArea.chartAreaIsCurcular) 1749 { 1750 DrawAxis3DTitle(graph); 1751 return; 1752 } 1753 1754 string axisTitle = this.Title; 1755 1756 //****************************************************** 1757 //** Check axis position 1758 //****************************************************** 1759 float axisPosition = (float)this.GetAxisPosition(); 1760 if (this.AxisPosition == AxisPosition.Bottom) 1761 { 1762 if (!this.GetIsMarksNextToAxis()) 1763 { 1764 axisPosition = ChartArea.PlotAreaPosition.Bottom; 1765 } 1766 axisPosition = ChartArea.PlotAreaPosition.Bottom - axisPosition; 1767 } 1768 else if (this.AxisPosition == AxisPosition.Top) 1769 { 1770 if (!this.GetIsMarksNextToAxis()) 1771 { 1772 axisPosition = ChartArea.PlotAreaPosition.Y; 1773 } 1774 axisPosition = axisPosition - ChartArea.PlotAreaPosition.Y; 1775 } 1776 else if (this.AxisPosition == AxisPosition.Right) 1777 { 1778 if (!this.GetIsMarksNextToAxis()) 1779 { 1780 axisPosition = ChartArea.PlotAreaPosition.Right; 1781 } 1782 axisPosition = ChartArea.PlotAreaPosition.Right - axisPosition; 1783 } 1784 else if (this.AxisPosition == AxisPosition.Left) 1785 { 1786 if (!this.GetIsMarksNextToAxis()) 1787 { 1788 axisPosition = ChartArea.PlotAreaPosition.X; 1789 } 1790 axisPosition = axisPosition - ChartArea.PlotAreaPosition.X; 1791 } 1792 1793 //****************************************************** 1794 //** Adjust axis elements size with axis position 1795 //****************************************************** 1796 // Calculate total size of axis elements 1797 float axisSize = this.markSize + this.labelSize; 1798 axisSize -= axisPosition; 1799 if (axisSize < 0) 1800 { 1801 axisSize = 0; 1802 } 1803 // Set title alignment 1804 using (StringFormat format = new StringFormat()) 1805 { 1806 format.Alignment = this.TitleAlignment; 1807 format.Trimming = StringTrimming.EllipsisCharacter; 1808 // VSTS #144398 1809 // We need to have the StringFormatFlags set to FitBlackBox as othwerwise axis titles using Fonts like 1810 // "Algerian" or "Forte" are completly clipped (= not drawn) due to the fact that MeasureString returns 1811 // a bounding rectangle that is too small. 1812 format.FormatFlags |= StringFormatFlags.FitBlackBox; 1813 1814 // Calculate title rectangle 1815 _titlePosition = ChartArea.PlotAreaPosition.ToRectangleF(); 1816 float titleSizeWithoutSpacing = this.titleSize - elementSpacing; 1817 if (this.AxisPosition == AxisPosition.Left) 1818 { 1819 _titlePosition.X = ChartArea.PlotAreaPosition.X - titleSizeWithoutSpacing - axisSize; 1820 _titlePosition.Y = ChartArea.PlotAreaPosition.Y; 1821 1822 if (!this.IsTextVertical) 1823 { 1824 SizeF axisTitleSize = new SizeF(titleSizeWithoutSpacing, ChartArea.PlotAreaPosition.Height); 1825 _titlePosition.Width = axisTitleSize.Width; 1826 _titlePosition.Height = axisTitleSize.Height; 1827 1828 format.Alignment = StringAlignment.Center; 1829 if (this.TitleAlignment == StringAlignment.Far) 1830 { 1831 format.LineAlignment = StringAlignment.Near; 1832 } 1833 else if (this.TitleAlignment == StringAlignment.Near) 1834 { 1835 format.LineAlignment = StringAlignment.Far; 1836 } 1837 else 1838 { 1839 format.LineAlignment = StringAlignment.Center; 1840 } 1841 } 1842 else 1843 { 1844 SizeF axisTitleSize = graph.GetAbsoluteSize(new SizeF(titleSizeWithoutSpacing, ChartArea.PlotAreaPosition.Height)); 1845 axisTitleSize = graph.GetRelativeSize(new SizeF(axisTitleSize.Height, axisTitleSize.Width)); 1846 1847 _titlePosition.Width = axisTitleSize.Width; 1848 _titlePosition.Height = axisTitleSize.Height; 1849 1850 _titlePosition.Y += ChartArea.PlotAreaPosition.Height / 2f - _titlePosition.Height / 2f; 1851 _titlePosition.X += titleSizeWithoutSpacing / 2f - _titlePosition.Width / 2f; 1852 1853 // Set graphics rotation transformation 1854 oldTransform = this.SetRotationTransformation(graph, _titlePosition); 1855 1856 // Set alignment 1857 format.LineAlignment = StringAlignment.Center; 1858 } 1859 } 1860 else if (this.AxisPosition == AxisPosition.Right) 1861 { 1862 _titlePosition.X = ChartArea.PlotAreaPosition.Right + axisSize; 1863 _titlePosition.Y = ChartArea.PlotAreaPosition.Y; 1864 1865 if (!this.IsTextVertical) 1866 { 1867 SizeF axisTitleSize = new SizeF(titleSizeWithoutSpacing, ChartArea.PlotAreaPosition.Height); 1868 _titlePosition.Width = axisTitleSize.Width; 1869 _titlePosition.Height = axisTitleSize.Height; 1870 1871 format.Alignment = StringAlignment.Center; 1872 if (this.TitleAlignment == StringAlignment.Far) 1873 { 1874 format.LineAlignment = StringAlignment.Near; 1875 } 1876 else if (this.TitleAlignment == StringAlignment.Near) 1877 { 1878 format.LineAlignment = StringAlignment.Far; 1879 } 1880 else 1881 { 1882 format.LineAlignment = StringAlignment.Center; 1883 } 1884 } 1885 else 1886 { 1887 SizeF axisTitleSize = graph.GetAbsoluteSize(new SizeF(titleSizeWithoutSpacing, ChartArea.PlotAreaPosition.Height)); 1888 axisTitleSize = graph.GetRelativeSize(new SizeF(axisTitleSize.Height, axisTitleSize.Width)); 1889 1890 _titlePosition.Width = axisTitleSize.Width; 1891 _titlePosition.Height = axisTitleSize.Height; 1892 1893 _titlePosition.Y += ChartArea.PlotAreaPosition.Height / 2f - _titlePosition.Height / 2f; 1894 _titlePosition.X += titleSizeWithoutSpacing / 2f - _titlePosition.Width / 2f; 1895 1896 // Set graphics rotation transformation 1897 oldTransform = this.SetRotationTransformation(graph, _titlePosition); 1898 1899 // Set alignment 1900 format.LineAlignment = StringAlignment.Center; 1901 } 1902 } 1903 else if (this.AxisPosition == AxisPosition.Top) 1904 { 1905 _titlePosition.Y = ChartArea.PlotAreaPosition.Y - titleSizeWithoutSpacing - axisSize; 1906 _titlePosition.Height = titleSizeWithoutSpacing; 1907 _titlePosition.X = ChartArea.PlotAreaPosition.X; 1908 _titlePosition.Width = ChartArea.PlotAreaPosition.Width; 1909 1910 if (this.IsTextVertical) 1911 { 1912 // Set graphics rotation transformation 1913 oldTransform = this.SetRotationTransformation(graph, _titlePosition); 1914 } 1915 1916 // Set alignment 1917 format.LineAlignment = StringAlignment.Center; 1918 } 1919 else if (this.AxisPosition == AxisPosition.Bottom) 1920 { 1921 _titlePosition.Y = ChartArea.PlotAreaPosition.Bottom + axisSize; 1922 _titlePosition.Height = titleSizeWithoutSpacing; 1923 _titlePosition.X = ChartArea.PlotAreaPosition.X; 1924 _titlePosition.Width = ChartArea.PlotAreaPosition.Width; 1925 1926 if (this.IsTextVertical) 1927 { 1928 // Set graphics rotation transformation 1929 oldTransform = this.SetRotationTransformation(graph, _titlePosition); 1930 } 1931 1932 // Set alignment 1933 format.LineAlignment = StringAlignment.Center; 1934 } 1935 1936 #if DEBUG 1937 // TESTING CODE: Shows labels rectangle position. 1938 // RectangleF rr = graph.GetAbsoluteRectangle(_titlePosition); 1939 // graph.DrawRectangle(Pens.Blue, rr.X, rr.Y, rr.Width, rr.Height); 1940 #endif // DEBUG 1941 1942 // Draw title 1943 using (Brush brush = new SolidBrush(this.TitleForeColor)) 1944 { 1945 graph.DrawStringRel( 1946 axisTitle.Replace("\\n", "\n"), 1947 this.TitleFont, 1948 brush, 1949 _titlePosition, 1950 format, 1951 this.GetTextOrientation()); 1952 } 1953 } 1954 1955 // Process selection regions 1956 if (this.Common.ProcessModeRegions) 1957 { 1958 // NOTE: Solves Issue #4423 1959 // Transform title position coordinates using curent Graphics matrix 1960 RectangleF transformedTitlePosition = graph.GetAbsoluteRectangle(_titlePosition); 1961 PointF[] rectPoints = new PointF[] { 1962 new PointF(transformedTitlePosition.X, transformedTitlePosition.Y), 1963 new PointF(transformedTitlePosition.Right, transformedTitlePosition.Bottom) }; 1964 graph.Transform.TransformPoints(rectPoints); 1965 transformedTitlePosition = new RectangleF( 1966 rectPoints[0].X, 1967 rectPoints[0].Y, 1968 rectPoints[1].X - rectPoints[0].X, 1969 rectPoints[1].Y - rectPoints[0].Y); 1970 if (transformedTitlePosition.Width < 0) 1971 { 1972 transformedTitlePosition.Width = Math.Abs(transformedTitlePosition.Width); 1973 transformedTitlePosition.X -= transformedTitlePosition.Width; 1974 } 1975 if (transformedTitlePosition.Height < 0) 1976 { 1977 transformedTitlePosition.Height = Math.Abs(transformedTitlePosition.Height); 1978 transformedTitlePosition.Y -= transformedTitlePosition.Height; 1979 } 1980 1981 // Add hot region 1982 this.Common.HotRegionsList.AddHotRegion( 1983 transformedTitlePosition, this, ChartElementType.AxisTitle, false, false); 1984 } 1985 1986 // Restore old transformation 1987 if (oldTransform != null) 1988 { 1989 graph.Transform = oldTransform; 1990 } 1991 } 1992 } 1993 1994 /// <summary> 1995 /// Helper method which sets 90 or -90 degrees transformation in the middle of the 1996 /// specified rectangle. It is used to draw title text rotated 90 or 270 degrees. 1997 /// </summary> 1998 /// <param name="graph">Chart graphics to apply transformation for.</param> 1999 /// <param name="titlePosition">Title position.</param> 2000 /// <returns>Old graphics transformation matrix.</returns> SetRotationTransformation(ChartGraphics graph, RectangleF titlePosition)2001 private Matrix SetRotationTransformation(ChartGraphics graph, RectangleF titlePosition) 2002 { 2003 // Save old graphics transformation 2004 Matrix oldTransform = graph.Transform.Clone(); 2005 2006 // Rotate left tile 90 degrees at center 2007 PointF center = PointF.Empty; 2008 center.X = titlePosition.X + titlePosition.Width / 2F; 2009 center.Y = titlePosition.Y + titlePosition.Height / 2F; 2010 2011 // Create and set new transformation matrix 2012 float angle = (this.GetTextOrientation() == TextOrientation.Rotated90) ? 90f : -90f; 2013 Matrix newMatrix = graph.Transform.Clone(); 2014 newMatrix.RotateAt(angle, graph.GetAbsolutePoint(center)); 2015 graph.Transform = newMatrix; 2016 2017 return oldTransform; 2018 } 2019 2020 2021 /// <summary> 2022 /// Draws a radial line in circular Common.Chart area. 2023 /// </summary> 2024 /// <param name="obj">Object requesting the painting.</param> 2025 /// <param name="graph">Graphics path.</param> 2026 /// <param name="color">Line color.</param> 2027 /// <param name="width">Line width.</param> 2028 /// <param name="style">Line style.</param> 2029 /// <param name="position">X axis circular position.</param> DrawRadialLine( object obj, ChartGraphics graph, Color color, int width, ChartDashStyle style, double position)2030 internal void DrawRadialLine( 2031 object obj, 2032 ChartGraphics graph, 2033 Color color, 2034 int width, 2035 ChartDashStyle style, 2036 double position) 2037 { 2038 // Create circle position rectangle 2039 RectangleF rect = ChartArea.PlotAreaPosition.ToRectangleF(); 2040 rect = graph.GetAbsoluteRectangle(rect); 2041 2042 // Make sure the rectangle width equals rectangle height for the circle 2043 if (rect.Width != rect.Height) 2044 { 2045 if (rect.Width > rect.Height) 2046 { 2047 rect.X += (rect.Width - rect.Height) / 2f; 2048 rect.Width = rect.Height; 2049 } 2050 else 2051 { 2052 rect.Y += (rect.Height - rect.Width) / 2f; 2053 rect.Height = rect.Width; 2054 } 2055 } 2056 2057 // Convert axis position to angle 2058 float angle = ChartArea.CircularPositionToAngle(position); 2059 2060 // Set clipping region to the polygon 2061 Region oldRegion = null; 2062 if (ChartArea.CircularUsePolygons) 2063 { 2064 oldRegion = graph.Clip; 2065 graph.Clip = new Region(graph.GetPolygonCirclePath(rect, ChartArea.CircularSectorsNumber)); 2066 } 2067 2068 // Get center point 2069 PointF centerPoint = graph.GetAbsolutePoint(ChartArea.circularCenter); 2070 2071 // Set graphics rotation matrix 2072 Matrix oldMatrix = graph.Transform; 2073 Matrix newMatrix = oldMatrix.Clone(); 2074 newMatrix.RotateAt( 2075 angle, 2076 centerPoint); 2077 graph.Transform = newMatrix; 2078 2079 // Draw Line 2080 PointF endPoint = new PointF(rect.X + rect.Width / 2f, rect.Y); 2081 graph.DrawLineAbs(color, width, style, centerPoint, endPoint); 2082 2083 // Process selection regions 2084 if (this.Common.ProcessModeRegions) 2085 { 2086 using (GraphicsPath path = new GraphicsPath()) 2087 { 2088 path.AddLine(centerPoint, endPoint); 2089 path.Transform(newMatrix); 2090 try 2091 { 2092 using (Pen pen = new Pen(Color.Black, width + 2)) 2093 { 2094 path.Widen(pen); 2095 this.Common.HotRegionsList.AddHotRegion(path, false, ChartElementType.Gridlines, obj); 2096 } 2097 } 2098 catch (OutOfMemoryException) 2099 { 2100 // GraphicsPath.Widen incorrectly throws OutOfMemoryException 2101 // catching here and reacting by not widening 2102 } 2103 catch (ArgumentException) 2104 { 2105 } 2106 } 2107 } 2108 2109 // Restore graphics 2110 graph.Transform = oldMatrix; 2111 newMatrix.Dispose(); 2112 2113 // Restore clip region 2114 if (ChartArea.CircularUsePolygons) 2115 { 2116 graph.Clip = oldRegion; 2117 } 2118 2119 } 2120 2121 /// <summary> 2122 /// Draws a circular line in circular Common.Chart area. 2123 /// </summary> 2124 /// <param name="obj">Object requesting the painting.</param> 2125 /// <param name="graph">Graphics path.</param> 2126 /// <param name="color">Line color.</param> 2127 /// <param name="width">Line width.</param> 2128 /// <param name="style">Line style.</param> 2129 /// <param name="position">Line position.</param> DrawCircularLine( object obj, ChartGraphics graph, Color color, int width, ChartDashStyle style, float position )2130 internal void DrawCircularLine( 2131 object obj, 2132 ChartGraphics graph, 2133 Color color, 2134 int width, 2135 ChartDashStyle style, 2136 float position 2137 ) 2138 { 2139 // Create circle position rectangle 2140 RectangleF rect = ChartArea.PlotAreaPosition.ToRectangleF(); 2141 rect = graph.GetAbsoluteRectangle(rect); 2142 2143 // Make sure the rectangle width equals rectangle height for the circle 2144 if (rect.Width != rect.Height) 2145 { 2146 if (rect.Width > rect.Height) 2147 { 2148 rect.X += (rect.Width - rect.Height) / 2f; 2149 rect.Width = rect.Height; 2150 } 2151 else 2152 { 2153 rect.Y += (rect.Height - rect.Width) / 2f; 2154 rect.Height = rect.Width; 2155 } 2156 } 2157 2158 // Inflate rectangle 2159 PointF absPoint = graph.GetAbsolutePoint(new PointF(position, position)); 2160 float rectInflate = absPoint.Y - rect.Top; 2161 rect.Inflate(-rectInflate, -rectInflate); 2162 2163 // Create circle pen 2164 Pen circlePen = new Pen(color, width); 2165 circlePen.DashStyle = graph.GetPenStyle(style); 2166 2167 // Draw circle 2168 if (ChartArea.CircularUsePolygons) 2169 { 2170 // Draw eaqula sides polygon 2171 graph.DrawCircleAbs(circlePen, null, rect, ChartArea.CircularSectorsNumber, false); 2172 } 2173 else 2174 { 2175 graph.DrawEllipse(circlePen, rect); 2176 } 2177 2178 // Process selection regions 2179 if (this.Common.ProcessModeRegions) 2180 { 2181 // Bounding rectangle must be more than 1 pixel by 1 pixel 2182 if (rect.Width >= 1f && rect.Height > 1) 2183 { 2184 GraphicsPath path = null; 2185 try 2186 { 2187 if (ChartArea.CircularUsePolygons) 2188 { 2189 path = graph.GetPolygonCirclePath(rect, ChartArea.CircularSectorsNumber); 2190 } 2191 else 2192 { 2193 path = new GraphicsPath(); 2194 path.AddEllipse(rect); 2195 } 2196 circlePen.Width += 2; 2197 path.Widen(circlePen); 2198 this.Common.HotRegionsList.AddHotRegion(path, false, ChartElementType.Gridlines, obj); 2199 } 2200 catch (OutOfMemoryException) 2201 { 2202 // GraphicsPath.Widen incorrectly throws OutOfMemoryException 2203 // catching here and reacting by not widening 2204 } 2205 catch (ArgumentException) 2206 { 2207 } 2208 finally 2209 { 2210 path.Dispose(); 2211 } 2212 } 2213 } 2214 2215 } 2216 2217 /// <summary> 2218 /// Draw axis title in 3D. 2219 /// </summary> 2220 /// <param name="graph">Reference to the Chart Graphics object</param> DrawAxis3DTitle(ChartGraphics graph)2221 private void DrawAxis3DTitle(ChartGraphics graph) 2222 { 2223 // Do not draw title if axis is not enabled 2224 if (!this.enabled) 2225 { 2226 return; 2227 } 2228 2229 string axisTitle = this.Title; 2230 2231 // Draw axis title 2232 PointF rotationCenter = PointF.Empty; 2233 int angle = 0; 2234 2235 // Set title alignment 2236 using (StringFormat format = new StringFormat()) 2237 { 2238 format.Alignment = this.TitleAlignment; 2239 format.Trimming = StringTrimming.EllipsisCharacter; 2240 format.FormatFlags |= StringFormatFlags.LineLimit; 2241 2242 // Measure title size for non-centered aligment 2243 SizeF realTitleSize = graph.MeasureString(axisTitle.Replace("\\n", "\n"), this.TitleFont, new SizeF(10000f, 10000f), format, this.GetTextOrientation()); 2244 SizeF axisTitleSize = SizeF.Empty; 2245 if (format.Alignment != StringAlignment.Center) 2246 { 2247 axisTitleSize = realTitleSize; 2248 if (this.IsTextVertical) 2249 { 2250 // Switch height and width for vertical axis 2251 float tempValue = axisTitleSize.Height; 2252 axisTitleSize.Height = axisTitleSize.Width; 2253 axisTitleSize.Width = tempValue; 2254 } 2255 2256 // Get relative size 2257 axisTitleSize = graph.GetRelativeSize(axisTitleSize); 2258 2259 // Change format aligment for the reversed mode 2260 if (ChartArea.ReverseSeriesOrder) 2261 { 2262 if (format.Alignment == StringAlignment.Near) 2263 { 2264 format.Alignment = StringAlignment.Far; 2265 } 2266 else 2267 { 2268 format.Alignment = StringAlignment.Near; 2269 } 2270 } 2271 } 2272 2273 // Set text rotation angle based on the text orientation 2274 if (this.GetTextOrientation() == TextOrientation.Rotated90) 2275 { 2276 angle = 90; 2277 } 2278 else if (this.GetTextOrientation() == TextOrientation.Rotated270) 2279 { 2280 angle = -90; 2281 } 2282 2283 // Calculate title center point on the axis 2284 if (this.AxisPosition == AxisPosition.Left) 2285 { 2286 rotationCenter = new PointF(ChartArea.PlotAreaPosition.X, ChartArea.PlotAreaPosition.Y + ChartArea.PlotAreaPosition.Height / 2f); 2287 if (format.Alignment == StringAlignment.Near) 2288 { 2289 rotationCenter.Y = ChartArea.PlotAreaPosition.Bottom - axisTitleSize.Height / 2f; 2290 } 2291 else if (format.Alignment == StringAlignment.Far) 2292 { 2293 rotationCenter.Y = ChartArea.PlotAreaPosition.Y + axisTitleSize.Height / 2f; 2294 } 2295 } 2296 else if (this.AxisPosition == AxisPosition.Right) 2297 { 2298 rotationCenter = new PointF(ChartArea.PlotAreaPosition.Right, ChartArea.PlotAreaPosition.Y + ChartArea.PlotAreaPosition.Height / 2f); 2299 if (format.Alignment == StringAlignment.Near) 2300 { 2301 rotationCenter.Y = ChartArea.PlotAreaPosition.Bottom - axisTitleSize.Height / 2f; 2302 } 2303 else if (format.Alignment == StringAlignment.Far) 2304 { 2305 rotationCenter.Y = ChartArea.PlotAreaPosition.Y + axisTitleSize.Height / 2f; 2306 } 2307 } 2308 else if (this.AxisPosition == AxisPosition.Top) 2309 { 2310 rotationCenter = new PointF(ChartArea.PlotAreaPosition.X + ChartArea.PlotAreaPosition.Width / 2f, ChartArea.PlotAreaPosition.Y); 2311 if (format.Alignment == StringAlignment.Near) 2312 { 2313 rotationCenter.X = ChartArea.PlotAreaPosition.X + axisTitleSize.Width / 2f; 2314 } 2315 else if (format.Alignment == StringAlignment.Far) 2316 { 2317 rotationCenter.X = ChartArea.PlotAreaPosition.Right - axisTitleSize.Width / 2f; 2318 } 2319 } 2320 else if (this.AxisPosition == AxisPosition.Bottom) 2321 { 2322 rotationCenter = new PointF(ChartArea.PlotAreaPosition.X + ChartArea.PlotAreaPosition.Width / 2f, ChartArea.PlotAreaPosition.Bottom); 2323 if (format.Alignment == StringAlignment.Near) 2324 { 2325 rotationCenter.X = ChartArea.PlotAreaPosition.X + axisTitleSize.Width / 2f; 2326 } 2327 else if (format.Alignment == StringAlignment.Far) 2328 { 2329 rotationCenter.X = ChartArea.PlotAreaPosition.Right - axisTitleSize.Width / 2f; 2330 } 2331 } 2332 2333 // Transform center of title coordinates and calculate axis angle 2334 bool isOnEdge = false; 2335 float zPosition = this.GetMarksZPosition(out isOnEdge); 2336 Point3D[] rotationCenterPoints = null; 2337 float angleAxis = 0; 2338 if (this.AxisPosition == AxisPosition.Top || this.AxisPosition == AxisPosition.Bottom) 2339 { 2340 rotationCenterPoints = new Point3D[] { 2341 new Point3D(rotationCenter.X, rotationCenter.Y, zPosition), 2342 new Point3D(rotationCenter.X - 20f, rotationCenter.Y, zPosition) }; 2343 2344 // Transform coordinates of text rotation point 2345 ChartArea.matrix3D.TransformPoints(rotationCenterPoints); 2346 rotationCenter = rotationCenterPoints[0].PointF; 2347 2348 // Get absolute coordinates 2349 rotationCenterPoints[0].PointF = graph.GetAbsolutePoint(rotationCenterPoints[0].PointF); 2350 rotationCenterPoints[1].PointF = graph.GetAbsolutePoint(rotationCenterPoints[1].PointF); 2351 2352 // Calculate X axis angle 2353 angleAxis = (float)Math.Atan( 2354 (rotationCenterPoints[1].Y - rotationCenterPoints[0].Y) / 2355 (rotationCenterPoints[1].X - rotationCenterPoints[0].X)); 2356 } 2357 else 2358 { 2359 rotationCenterPoints = new Point3D[] { 2360 new Point3D(rotationCenter.X, rotationCenter.Y, zPosition), 2361 new Point3D(rotationCenter.X, rotationCenter.Y - 20f, zPosition) }; 2362 2363 // Transform coordinates of text rotation point 2364 ChartArea.matrix3D.TransformPoints(rotationCenterPoints); 2365 rotationCenter = rotationCenterPoints[0].PointF; 2366 2367 // Get absolute coordinates 2368 rotationCenterPoints[0].PointF = graph.GetAbsolutePoint(rotationCenterPoints[0].PointF); 2369 rotationCenterPoints[1].PointF = graph.GetAbsolutePoint(rotationCenterPoints[1].PointF); 2370 2371 // Calculate Y axis angle 2372 if (rotationCenterPoints[1].Y != rotationCenterPoints[0].Y) 2373 { 2374 angleAxis = -(float)Math.Atan( 2375 (rotationCenterPoints[1].X - rotationCenterPoints[0].X) / 2376 (rotationCenterPoints[1].Y - rotationCenterPoints[0].Y)); 2377 } 2378 } 2379 angle += (int)Math.Round(angleAxis * 180f / (float)Math.PI); 2380 2381 2382 // Calculate title center offset from the axis line 2383 float offset = this.labelSize + this.markSize + this.titleSize / 2f; 2384 float dX = 0f, dY = 0f; 2385 2386 2387 // Adjust center of title with labels, marker and title size 2388 if (this.AxisPosition == AxisPosition.Left) 2389 { 2390 dX = (float)(offset * Math.Cos(angleAxis)); 2391 rotationCenter.X -= dX; 2392 } 2393 else if (this.AxisPosition == AxisPosition.Right) 2394 { 2395 dX = (float)(offset * Math.Cos(angleAxis)); 2396 rotationCenter.X += dX; 2397 } 2398 else if (this.AxisPosition == AxisPosition.Top) 2399 { 2400 dY = (float)(offset * Math.Cos(angleAxis)); 2401 dX = (float)(offset * Math.Sin(angleAxis)); 2402 rotationCenter.Y -= dY; 2403 if (dY > 0) 2404 { 2405 rotationCenter.X += dX; 2406 } 2407 else 2408 { 2409 rotationCenter.X -= dX; 2410 } 2411 } 2412 else if (this.AxisPosition == AxisPosition.Bottom) 2413 { 2414 dY = (float)(offset * Math.Cos(angleAxis)); 2415 dX = (float)(offset * Math.Sin(angleAxis)); 2416 rotationCenter.Y += dY; 2417 if (dY > 0) 2418 { 2419 rotationCenter.X -= dX; 2420 } 2421 else 2422 { 2423 rotationCenter.X += dX; 2424 } 2425 } 2426 2427 2428 // Always align text in the center 2429 format.LineAlignment = StringAlignment.Center; 2430 format.Alignment = StringAlignment.Center; 2431 // SQL VSTS Fix #259954, Dev10: 591135 Windows 7 crashes on empty transformation. 2432 if (rotationCenter.IsEmpty || float.IsNaN(rotationCenter.X) || float.IsNaN(rotationCenter.Y)) 2433 { 2434 return; 2435 } 2436 2437 // Draw 3D title 2438 using (Brush brush = new SolidBrush(this.TitleForeColor)) 2439 { 2440 graph.DrawStringRel( 2441 axisTitle.Replace("\\n", "\n"), 2442 this.TitleFont, 2443 brush, 2444 rotationCenter, 2445 format, 2446 angle, 2447 this.GetTextOrientation()); 2448 } 2449 2450 // Add hot region 2451 if (Common.ProcessModeRegions) 2452 { 2453 using (GraphicsPath hotPath = graph.GetTranformedTextRectPath(rotationCenter, realTitleSize, angle)) 2454 { 2455 this.Common.HotRegionsList.AddHotRegion(hotPath, false, ChartElementType.AxisTitle, this); 2456 } 2457 } 2458 } 2459 2460 } 2461 2462 /// <summary> 2463 /// Select Axis line 2464 /// </summary> 2465 /// <param name="graph">Reference to the Chart Graphics</param> 2466 /// <param name="backElements">Back elements of the axis should be drawn in 3D scene.</param> DrawAxisLine(ChartGraphics graph, bool backElements)2467 internal void DrawAxisLine(ChartGraphics graph, bool backElements) 2468 { 2469 Axis opositeAxis; 2470 ArrowOrientation arrowOrientation = ArrowOrientation.Top; 2471 PointF first = Point.Empty; 2472 PointF second = Point.Empty; 2473 2474 // Set the position of axis 2475 switch (AxisPosition) 2476 { 2477 2478 case AxisPosition.Left: 2479 2480 first.X = (float)GetAxisPosition(); 2481 first.Y = PlotAreaPosition.Bottom; 2482 second.X = (float)GetAxisPosition(); 2483 second.Y = PlotAreaPosition.Y; 2484 if (isReversed) 2485 arrowOrientation = ArrowOrientation.Bottom; 2486 else 2487 arrowOrientation = ArrowOrientation.Top; 2488 2489 break; 2490 2491 case AxisPosition.Right: 2492 2493 first.X = (float)GetAxisPosition(); 2494 first.Y = PlotAreaPosition.Bottom; 2495 second.X = (float)GetAxisPosition(); 2496 second.Y = PlotAreaPosition.Y; 2497 if (isReversed) 2498 arrowOrientation = ArrowOrientation.Bottom; 2499 else 2500 arrowOrientation = ArrowOrientation.Top; 2501 2502 break; 2503 2504 case AxisPosition.Bottom: 2505 2506 first.X = PlotAreaPosition.X; 2507 first.Y = (float)GetAxisPosition(); 2508 second.X = PlotAreaPosition.Right; 2509 second.Y = (float)GetAxisPosition(); 2510 if (isReversed) 2511 arrowOrientation = ArrowOrientation.Left; 2512 else 2513 arrowOrientation = ArrowOrientation.Right; 2514 2515 break; 2516 2517 case AxisPosition.Top: 2518 2519 first.X = PlotAreaPosition.X; 2520 first.Y = (float)GetAxisPosition(); 2521 second.X = PlotAreaPosition.Right; 2522 second.Y = (float)GetAxisPosition(); 2523 if (isReversed) 2524 arrowOrientation = ArrowOrientation.Left; 2525 else 2526 arrowOrientation = ArrowOrientation.Right; 2527 2528 break; 2529 2530 } 2531 2532 // Update axis line position for circular area 2533 if (ChartArea.chartAreaIsCurcular) 2534 { 2535 first.Y = PlotAreaPosition.Y + PlotAreaPosition.Height / 2f; 2536 } 2537 2538 2539 if (Common.ProcessModePaint) 2540 { 2541 if (!ChartArea.Area3DStyle.Enable3D || ChartArea.chartAreaIsCurcular) 2542 { 2543 2544 // Start Svg/Flash Selection mode 2545 graph.StartHotRegion( this._url, _toolTip ); 2546 2547 // Draw the line 2548 graph.DrawLineRel(_lineColor, _lineWidth, _lineDashStyle, first, second); 2549 2550 // End Svg/Flash Selection mode 2551 graph.EndHotRegion( ); 2552 2553 // Opposite axis. Arrow uses this axis to find 2554 // a shift from Common.Chart area border. This shift 2555 // depend on Tick mark size. 2556 switch (arrowOrientation) 2557 { 2558 case ArrowOrientation.Left: 2559 opositeAxis = ChartArea.AxisX; 2560 break; 2561 case ArrowOrientation.Right: 2562 opositeAxis = ChartArea.AxisX2; 2563 break; 2564 case ArrowOrientation.Top: 2565 opositeAxis = ChartArea.AxisY2; 2566 break; 2567 case ArrowOrientation.Bottom: 2568 opositeAxis = ChartArea.AxisY; 2569 break; 2570 default: 2571 opositeAxis = ChartArea.AxisX; 2572 break; 2573 } 2574 2575 // Draw arrow 2576 PointF arrowPosition; 2577 if (isReversed) 2578 arrowPosition = first; 2579 else 2580 arrowPosition = second; 2581 2582 // Draw Arrow 2583 graph.DrawArrowRel(arrowPosition, arrowOrientation, _arrowStyle, _lineColor, _lineWidth, _lineDashStyle, opositeAxis.majorTickMark.Size, _lineWidth); 2584 } 2585 else 2586 { 2587 Draw3DAxisLine(graph, first, second, (this.AxisPosition == AxisPosition.Top || this.AxisPosition == AxisPosition.Bottom), backElements); 2588 } 2589 } 2590 2591 } 2592 2593 2594 /// <summary> 2595 /// Draws the axis line hot region. 2596 /// </summary> 2597 /// <param name="graph">The graph.</param> 2598 /// <param name="backElements">set to <c>true</c> if we draw back elements.</param> DrawAxisLineHotRegion(ChartGraphics graph, bool backElements)2599 private void DrawAxisLineHotRegion(ChartGraphics graph, bool backElements) 2600 { 2601 if (Common.ProcessModeRegions) 2602 { 2603 //VSTS #229835: During the 3D rendering the axis is drawn twice: 2604 //1. In PrePaint() both axis and backelements (labels) are drawn. 2605 //2. In Paint() the axis is redrawn without labels and as a result it creates a second hot region which covered the labels' hotregions. 2606 //In order to avoid this we have to suppress the hotregion drawing in the Paint using the backElements flag (it's false during the Paint) 2607 //The circular charts and 2D charts are drawn only once in Paint() so we draw the hot regions. 2608 if (backElements || !ChartArea.Area3DStyle.Enable3D || ChartArea.chartAreaIsCurcular) 2609 { 2610 DrawAxisLineHotRegion(graph); 2611 } 2612 } 2613 2614 } 2615 2616 /// <summary> 2617 /// Adds the axis hot region 2618 /// </summary> 2619 /// <param name="graph">The chart graphics instance.</param> DrawAxisLineHotRegion(ChartGraphics graph)2620 private void DrawAxisLineHotRegion(ChartGraphics graph) 2621 { 2622 using (GraphicsPath path = new GraphicsPath()) 2623 { 2624 // Find the topLeft(first) and bottomRight(second) points of the hotregion rectangle 2625 PointF first = PointF.Empty; 2626 PointF second = PointF.Empty; 2627 float axisPosition = (float)GetAxisPosition(); 2628 2629 switch (this.AxisPosition) 2630 { 2631 case AxisPosition.Left: 2632 first.X = axisPosition - (labelSize + markSize); 2633 first.Y = PlotAreaPosition.Y; 2634 second.X = axisPosition; 2635 second.Y = PlotAreaPosition.Bottom; 2636 break; 2637 2638 case AxisPosition.Right: 2639 first.X = axisPosition; 2640 first.Y = PlotAreaPosition.Y; 2641 second.X = axisPosition + labelSize + markSize; 2642 second.Y = PlotAreaPosition.Bottom; 2643 break; 2644 2645 case AxisPosition.Bottom: 2646 first.X = PlotAreaPosition.X; 2647 first.Y = axisPosition; 2648 second.X = PlotAreaPosition.Right; 2649 second.Y = axisPosition + labelSize + markSize; 2650 break; 2651 2652 case AxisPosition.Top: 2653 first.X = PlotAreaPosition.X; 2654 first.Y = axisPosition - (labelSize + markSize); 2655 second.X = PlotAreaPosition.Right; 2656 second.Y = axisPosition; 2657 break; 2658 } 2659 2660 // Update axis line position for circular area 2661 if (ChartArea.chartAreaIsCurcular) 2662 { 2663 second.Y = PlotAreaPosition.Y + PlotAreaPosition.Height / 2f; 2664 } 2665 2666 // Create rectangle and inflate it 2667 RectangleF rect = new RectangleF(first.X, first.Y, second.X - first.X, second.Y - first.Y); 2668 SizeF size = graph.GetRelativeSize(new SizeF(3, 3)); 2669 2670 if (AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom) 2671 { 2672 rect.Inflate(2, size.Height); 2673 } 2674 else 2675 { 2676 rect.Inflate(size.Width, 2); 2677 } 2678 2679 // Get the rectangle points 2680 PointF[] points = new PointF[] { 2681 new PointF(rect.Left, rect.Top), 2682 new PointF(rect.Right, rect.Top), 2683 new PointF(rect.Right, rect.Bottom), 2684 new PointF(rect.Left, rect.Bottom)}; 2685 2686 // If we are dealing with the 3D - transform the rectangle 2687 if (ChartArea.Area3DStyle.Enable3D && !ChartArea.chartAreaIsCurcular) 2688 { 2689 Boolean axisOnEdge = false; 2690 float zPositon = GetMarksZPosition(out axisOnEdge); 2691 2692 // Convert points to 3D 2693 Point3D[] points3D = new Point3D[points.Length]; 2694 for (int i = 0; i < points.Length; i++) 2695 { 2696 points3D[i] = new Point3D(points[i].X, points[i].Y, zPositon); 2697 } 2698 2699 // Transform 2700 ChartArea.matrix3D.TransformPoints(points3D); 2701 2702 // Convert to 2D 2703 for (int i = 0; i < points3D.Length; i++) 2704 { 2705 points[i] = points3D[i].PointF; 2706 } 2707 } 2708 2709 // Transform points to absolute cooordinates 2710 for (int i = 0; i < points.Length; i++) 2711 { 2712 points[i] = graph.GetAbsolutePoint(points[i]); 2713 } 2714 2715 // Add the points to the path 2716 path.AddPolygon(points); 2717 2718 2719 #if Microsoft_CONTROL 2720 Common.HotRegionsList.AddHotRegion( 2721 graph, 2722 path, 2723 false, 2724 this._toolTip, 2725 string.Empty, 2726 string.Empty, 2727 string.Empty, 2728 this, 2729 ChartElementType.Axis); 2730 #else 2731 Common.HotRegionsList.AddHotRegion( 2732 graph, 2733 path, 2734 false, 2735 this._toolTip, 2736 this._url, 2737 this._mapAreaAttributes, 2738 this.PostBackValue, 2739 this, 2740 ChartElementType.Axis); 2741 #endif 2742 2743 } 2744 } 2745 2746 2747 /// <summary> 2748 /// Draws axis line in 3D space. 2749 /// </summary> 2750 /// <param name="graph">Reference to the Chart Graphics object.</param> 2751 /// <param name="point1">First line point.</param> 2752 /// <param name="point2">Second line point.</param> 2753 /// <param name="horizontal">Indicates that tick mark line is horizontal</param> 2754 /// <param name="backElements">Only back elements of axis should be drawn.</param> Draw3DAxisLine( ChartGraphics graph, PointF point1, PointF point2, bool horizontal, bool backElements )2755 private void Draw3DAxisLine( 2756 ChartGraphics graph, 2757 PointF point1, 2758 PointF point2, 2759 bool horizontal, 2760 bool backElements 2761 ) 2762 { 2763 // Check if axis is positioned on the plot area adge 2764 bool onEdge = this.IsAxisOnAreaEdge; 2765 2766 // Check if axis tick marks are drawn inside plotting area 2767 bool tickMarksOnEdge = onEdge; 2768 if (tickMarksOnEdge && 2769 this.MajorTickMark.TickMarkStyle == TickMarkStyle.AcrossAxis || 2770 this.MajorTickMark.TickMarkStyle == TickMarkStyle.InsideArea || 2771 this.MinorTickMark.TickMarkStyle == TickMarkStyle.AcrossAxis || 2772 this.MinorTickMark.TickMarkStyle == TickMarkStyle.InsideArea) 2773 { 2774 tickMarksOnEdge = false; 2775 } 2776 2777 // Make sure first point of axis coordinates has smaller values 2778 if ((horizontal && point1.X > point2.X) || 2779 (!horizontal && point1.Y > point2.Y)) 2780 { 2781 PointF tempPoint = new PointF(point1.X, point1.Y); 2782 point1.X = point2.X; 2783 point1.Y = point2.Y; 2784 point2 = tempPoint; 2785 } 2786 2787 // Check if the front/back wall is on the top drawing layer 2788 float zPositon = ChartArea.IsMainSceneWallOnFront() ? ChartArea.areaSceneDepth : 0f; 2789 SurfaceNames surfName = ChartArea.IsMainSceneWallOnFront() ? SurfaceNames.Front : SurfaceNames.Back; 2790 if (ChartArea.ShouldDrawOnSurface(SurfaceNames.Back, backElements, tickMarksOnEdge)) 2791 { 2792 2793 // Start Svg Selection mode 2794 graph.StartHotRegion( this._url, _toolTip ); 2795 2796 // Draw axis line on the back/front wall 2797 graph.Draw3DLine( 2798 ChartArea.matrix3D, 2799 _lineColor, _lineWidth, _lineDashStyle, 2800 new Point3D(point1.X, point1.Y, zPositon), 2801 new Point3D(point2.X, point2.Y, zPositon), 2802 Common, 2803 this, 2804 ChartElementType.Nothing 2805 ); 2806 2807 // End Svg Selection mode 2808 graph.EndHotRegion(); 2809 2810 } 2811 2812 // Check if the back wall is on the top drawing layer 2813 zPositon = ChartArea.IsMainSceneWallOnFront() ? 0f : ChartArea.areaSceneDepth; 2814 surfName = ChartArea.IsMainSceneWallOnFront() ? SurfaceNames.Back : SurfaceNames.Front; 2815 if (ChartArea.ShouldDrawOnSurface(surfName, backElements, tickMarksOnEdge)) 2816 { 2817 // Draw axis line on the front wall 2818 if (!onEdge || 2819 (this.AxisPosition == AxisPosition.Bottom && ChartArea.IsBottomSceneWallVisible()) || 2820 (this.AxisPosition == AxisPosition.Left && ChartArea.IsSideSceneWallOnLeft()) || 2821 (this.AxisPosition == AxisPosition.Right && !ChartArea.IsSideSceneWallOnLeft())) 2822 { 2823 2824 // Start Svg Selection mode 2825 graph.StartHotRegion( this._url, _toolTip ); 2826 2827 graph.Draw3DLine( 2828 ChartArea.matrix3D, 2829 _lineColor, _lineWidth, _lineDashStyle, 2830 new Point3D(point1.X, point1.Y, zPositon), 2831 new Point3D(point2.X, point2.Y, zPositon), 2832 Common, 2833 this, 2834 ChartElementType.Nothing 2835 ); 2836 2837 // End Svg Selection mode 2838 graph.EndHotRegion(); 2839 2840 } 2841 } 2842 2843 // Check if the left/top wall is on the top drawing layer 2844 SurfaceNames surfaceName = (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? SurfaceNames.Top : SurfaceNames.Left; 2845 if (ChartArea.ShouldDrawOnSurface(surfaceName, backElements, tickMarksOnEdge)) 2846 { 2847 // Draw axis line on the left/top side walls 2848 if (!onEdge || 2849 (this.AxisPosition == AxisPosition.Bottom && (ChartArea.IsBottomSceneWallVisible() || ChartArea.IsSideSceneWallOnLeft())) || 2850 (this.AxisPosition == AxisPosition.Left && ChartArea.IsSideSceneWallOnLeft()) || 2851 (this.AxisPosition == AxisPosition.Right && !ChartArea.IsSideSceneWallOnLeft()) || 2852 (this.AxisPosition == AxisPosition.Top && ChartArea.IsSideSceneWallOnLeft())) 2853 { 2854 2855 // Start Svg Selection mode 2856 graph.StartHotRegion( this._url, _toolTip ); 2857 2858 graph.Draw3DLine( 2859 ChartArea.matrix3D, 2860 _lineColor, _lineWidth, _lineDashStyle, 2861 new Point3D(point1.X, point1.Y, ChartArea.areaSceneDepth), 2862 new Point3D(point1.X, point1.Y, 0f), 2863 Common, 2864 this, 2865 ChartElementType.Nothing 2866 ); 2867 2868 // End Svg Selection mode 2869 graph.EndHotRegion( ); 2870 2871 } 2872 } 2873 2874 // Check if the right/bottom wall is on the top drawing layer 2875 surfaceName = (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? SurfaceNames.Bottom : SurfaceNames.Right; 2876 if (ChartArea.ShouldDrawOnSurface(surfaceName, backElements, tickMarksOnEdge)) 2877 { 2878 // Draw axis line on the bottom/right side walls 2879 if (!onEdge || 2880 (this.AxisPosition == AxisPosition.Bottom && (ChartArea.IsBottomSceneWallVisible() || !ChartArea.IsSideSceneWallOnLeft())) || 2881 (this.AxisPosition == AxisPosition.Left && (ChartArea.IsSideSceneWallOnLeft() || ChartArea.IsBottomSceneWallVisible())) || 2882 (this.AxisPosition == AxisPosition.Right && (!ChartArea.IsSideSceneWallOnLeft() || ChartArea.IsBottomSceneWallVisible())) || 2883 (this.AxisPosition == AxisPosition.Top && !ChartArea.IsSideSceneWallOnLeft()) 2884 ) 2885 { 2886 2887 // Start Svg Selection mode 2888 graph.StartHotRegion( this._url, _toolTip ); 2889 2890 graph.Draw3DLine( 2891 ChartArea.matrix3D, 2892 _lineColor, _lineWidth, _lineDashStyle, 2893 new Point3D(point2.X, point2.Y, ChartArea.areaSceneDepth), 2894 new Point3D(point2.X, point2.Y, 0f), 2895 Common, 2896 this, 2897 ChartElementType.Nothing 2898 ); 2899 2900 // End Svg Selection mode 2901 graph.EndHotRegion(); 2902 2903 } 2904 } 2905 2906 } 2907 2908 /// <summary> 2909 /// Gets Z position of axis tick marks and labels. 2910 /// </summary> 2911 /// <param name="axisOnEdge">Returns true if axis is on the edge.</param> 2912 /// <returns>Marks Z position.</returns> GetMarksZPosition(out bool axisOnEdge)2913 internal float GetMarksZPosition(out bool axisOnEdge) 2914 { 2915 axisOnEdge = this.IsAxisOnAreaEdge; 2916 if (!this.GetIsMarksNextToAxis()) 2917 { 2918 // Marks are forced to be on the area edge 2919 axisOnEdge = true; 2920 } 2921 float wallZPosition = 0f; 2922 if (this.AxisPosition == AxisPosition.Bottom && (ChartArea.IsBottomSceneWallVisible() || !axisOnEdge)) 2923 { 2924 wallZPosition = ChartArea.areaSceneDepth; 2925 } 2926 if (this.AxisPosition == AxisPosition.Left && (ChartArea.IsSideSceneWallOnLeft() || !axisOnEdge)) 2927 { 2928 wallZPosition = ChartArea.areaSceneDepth; 2929 } 2930 if (this.AxisPosition == AxisPosition.Right && (!ChartArea.IsSideSceneWallOnLeft() || !axisOnEdge)) 2931 { 2932 wallZPosition = ChartArea.areaSceneDepth; 2933 } 2934 if (this.AxisPosition == AxisPosition.Top && !axisOnEdge) 2935 { 2936 wallZPosition = ChartArea.areaSceneDepth; 2937 } 2938 2939 // Check if front wall is shown 2940 if (ChartArea.IsMainSceneWallOnFront()) 2941 { 2942 // Switch Z position of tick mark 2943 wallZPosition = (wallZPosition == 0f) ? ChartArea.areaSceneDepth : 0f; 2944 } 2945 2946 return wallZPosition; 2947 } 2948 2949 /// <summary> 2950 /// Paint Axis Grid lines 2951 /// </summary> 2952 /// <param name="graph">Reference to the Chart Graphics object</param> PaintGrids(ChartGraphics graph)2953 internal void PaintGrids(ChartGraphics graph) 2954 { 2955 object obj; 2956 2957 PaintGrids(graph, out obj); 2958 2959 } 2960 2961 /// <summary> 2962 /// Paint Axis Grid lines or 2963 /// hit test function for grid lines 2964 /// </summary> 2965 /// <param name="graph">Reference to the Chart Graphics object</param> 2966 /// <param name="obj">Returns selected grid object</param> PaintGrids(ChartGraphics graph, out object obj)2967 internal void PaintGrids(ChartGraphics graph, out object obj) 2968 { 2969 obj = null; 2970 2971 #if SUBAXES 2972 // Paint grids of sub-axis 2973 if(!ChartArea.Area3DStyle.Enable3D && 2974 !ChartArea.chartAreaIsCurcular) 2975 { 2976 foreach(SubAxis subAxis in this.SubAxes) 2977 { 2978 subAxis.PaintGrids( graph, out obj); 2979 } 2980 } 2981 #endif // SUBAXES 2982 2983 // Axis is disabled 2984 if (enabled == false) 2985 return; 2986 2987 // Paint Minor grid lines 2988 minorGrid.Paint(graph); 2989 2990 // Paint Major grid lines 2991 majorGrid.Paint(graph); 2992 } 2993 2994 /// <summary> 2995 /// Paint Axis Strip lines 2996 /// </summary> 2997 /// <param name="graph">Reference to the Chart Graphics object</param> 2998 /// <param name="drawLinesOnly">Indicates if Lines or Stripes should be drawn.</param> PaintStrips(ChartGraphics graph, bool drawLinesOnly)2999 internal void PaintStrips(ChartGraphics graph, bool drawLinesOnly) 3000 { 3001 object obj; 3002 PaintStrips(graph, false, 0, 0, out obj, drawLinesOnly); 3003 } 3004 3005 /// <summary> 3006 /// Paint Axis Strip lines or 3007 /// hit test function for Strip lines 3008 /// </summary> 3009 /// <param name="graph">Reference to the Chart Graphics object</param> 3010 /// <param name="selectionMode">The selection mode is active</param> 3011 /// <param name="x">X coordinate</param> 3012 /// <param name="y">Y coordinate</param> 3013 /// <param name="obj">Returns selected grid object</param> 3014 /// <param name="drawLinesOnly">Indicates if Lines or Stripes should be drawn.</param> 3015 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "y"), 3016 System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "x"), 3017 System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "selectionMode")] PaintStrips(ChartGraphics graph, bool selectionMode, int x, int y, out object obj, bool drawLinesOnly)3018 internal void PaintStrips(ChartGraphics graph, bool selectionMode, int x, int y, out object obj, bool drawLinesOnly) 3019 { 3020 obj = null; 3021 3022 #if SUBAXES 3023 // Paint strips of sub-axis 3024 if(ChartArea.IsSubAxesSupported) 3025 { 3026 foreach(SubAxis subAxis in this.SubAxes) 3027 { 3028 subAxis.PaintStrips( graph, selectionMode, x, y, out obj, drawLinesOnly); 3029 } 3030 } 3031 #endif // SUBAXES 3032 3033 // Axis is disabled 3034 if (enabled == false) 3035 return; 3036 3037 // Add axis isInterlaced strip lines into the collection 3038 bool interlacedStripAdded = AddInterlacedStrip(); 3039 3040 // Draw axis strips and lines 3041 foreach (StripLine strip in this.StripLines) 3042 { 3043 strip.Paint(graph, this.Common, drawLinesOnly); 3044 } 3045 3046 // Remove axis isInterlaced strip line from the collection after drawing 3047 if (interlacedStripAdded) 3048 { 3049 // Remove isInterlaced strips which always is the first strip line 3050 this.StripLines.RemoveAt(0); 3051 } 3052 3053 } 3054 3055 /// <summary> 3056 /// Helper function which adds temp. strip lines into the collection 3057 /// to display isInterlaced lines in axis. 3058 /// </summary> AddInterlacedStrip()3059 private bool AddInterlacedStrip() 3060 { 3061 bool addStrip = false; 3062 if (this.IsInterlaced) 3063 { 3064 StripLine stripLine = new StripLine(); 3065 stripLine.interlaced = true; 3066 // VSTS fix of 164115 IsInterlaced StripLines with no border are rendered with black border, regression of VSTS 136763 3067 stripLine.BorderColor = Color.Empty; 3068 3069 // Get interval from grid lines, tick marks or labels 3070 if (this.MajorGrid.Enabled && this.MajorGrid.GetInterval() != 0.0) 3071 { 3072 addStrip = true; 3073 stripLine.Interval = this.MajorGrid.GetInterval() * 2.0; 3074 stripLine.IntervalType = this.MajorGrid.GetIntervalType(); 3075 stripLine.IntervalOffset = this.MajorGrid.GetIntervalOffset(); 3076 stripLine.IntervalOffsetType = this.MajorGrid.GetIntervalOffsetType(); 3077 stripLine.StripWidth = this.MajorGrid.GetInterval(); 3078 stripLine.StripWidthType = this.MajorGrid.GetIntervalType(); 3079 } 3080 else if (this.MajorTickMark.Enabled && this.MajorTickMark.GetInterval() != 0.0) 3081 { 3082 addStrip = true; 3083 stripLine.Interval = this.MajorTickMark.GetInterval() * 2.0; 3084 stripLine.IntervalType = this.MajorTickMark.GetIntervalType(); 3085 stripLine.IntervalOffset = this.MajorTickMark.GetIntervalOffset(); 3086 stripLine.IntervalOffsetType = this.MajorTickMark.GetIntervalOffsetType(); 3087 stripLine.StripWidth = this.MajorTickMark.GetInterval(); 3088 stripLine.StripWidthType = this.MajorTickMark.GetIntervalType(); 3089 } 3090 else if (this.LabelStyle.Enabled && this.LabelStyle.GetInterval() != 0.0) 3091 { 3092 addStrip = true; 3093 stripLine.Interval = this.LabelStyle.GetInterval() * 2.0; 3094 stripLine.IntervalType = this.LabelStyle.GetIntervalType(); 3095 stripLine.IntervalOffset = this.LabelStyle.GetIntervalOffset(); 3096 stripLine.IntervalOffsetType = this.LabelStyle.GetIntervalOffsetType(); 3097 stripLine.StripWidth = this.LabelStyle.GetInterval(); 3098 stripLine.StripWidthType = this.LabelStyle.GetIntervalType(); 3099 } 3100 3101 // Insert item into the strips collection 3102 if (addStrip) 3103 { 3104 // Define stip color 3105 if (this.InterlacedColor != Color.Empty) 3106 { 3107 stripLine.BackColor = this.InterlacedColor; 3108 } 3109 else 3110 { 3111 // If isInterlaced strips color is not set - use darker color of the area 3112 if (ChartArea.BackColor == Color.Empty) 3113 { 3114 stripLine.BackColor = (ChartArea.Area3DStyle.Enable3D) ? Color.DarkGray : Color.LightGray; 3115 } 3116 else if (ChartArea.BackColor == Color.Transparent) 3117 { 3118 if (Common.Chart.BackColor != Color.Transparent && Common.Chart.BackColor != Color.Black) 3119 { 3120 stripLine.BackColor = ChartGraphics.GetGradientColor(Common.Chart.BackColor, Color.Black, 0.2); 3121 } 3122 else 3123 { 3124 stripLine.BackColor = Color.LightGray; 3125 } 3126 } 3127 else 3128 { 3129 stripLine.BackColor = ChartGraphics.GetGradientColor(ChartArea.BackColor, Color.Black, 0.2); 3130 } 3131 } 3132 3133 // Insert strip 3134 this.StripLines.Insert(0, stripLine); 3135 } 3136 } 3137 3138 return addStrip; 3139 } 3140 3141 #endregion 3142 3143 #region Axis parameters recalculation and resizing methods 3144 3145 /// <summary> 3146 /// This method will calculate the maximum and minimum values 3147 /// using interval on the X axis automatically. It will make a gap between 3148 /// data points and border of the Common.Chart area. 3149 /// Note that this method can only be called for primary or secondary X axes. 3150 /// </summary> RoundAxisValues()3151 public void RoundAxisValues() 3152 { 3153 this.roundedXValues = true; 3154 } 3155 3156 /// <summary> 3157 /// RecalculateAxesScale axis. 3158 /// </summary> 3159 /// <param name="position">Plotting area position.</param> ReCalc(ElementPosition position)3160 internal void ReCalc(ElementPosition position) 3161 { 3162 PlotAreaPosition = position; 3163 3164 #if SUBAXES 3165 3166 // Recalculate all sub-axis 3167 foreach(SubAxis subAxis in this.SubAxes) 3168 { 3169 subAxis.ReCalc( position ); 3170 } 3171 #endif // SUBAXES 3172 } 3173 3174 /// <summary> 3175 /// This method store Axis values as minimum, maximum, 3176 /// crossing, etc. Axis auto algorithm changes these 3177 /// values and they have to be set to default values 3178 /// after painting. 3179 /// </summary> StoreAxisValues()3180 internal void StoreAxisValues() 3181 { 3182 tempLabels = new CustomLabelsCollection(this); 3183 foreach (CustomLabel label in CustomLabels) 3184 { 3185 tempLabels.Add(label.Clone()); 3186 } 3187 3188 paintMode = true; 3189 3190 // This field synchronies the Storing and 3191 // resetting of temporary values 3192 if (_storeValuesEnabled) 3193 { 3194 3195 tempMaximum = maximum; 3196 tempMinimum = minimum; 3197 tempCrossing = crossing; 3198 tempAutoMinimum = _autoMinimum; 3199 tempAutoMaximum = _autoMaximum; 3200 3201 tempMajorGridInterval = majorGrid.interval; 3202 tempMajorTickMarkInterval = majorTickMark.interval; 3203 3204 tempMinorGridInterval = minorGrid.interval; 3205 tempMinorTickMarkInterval = minorTickMark.interval; 3206 3207 3208 tempGridIntervalType = majorGrid.intervalType; 3209 tempTickMarkIntervalType = majorTickMark.intervalType; 3210 3211 3212 tempLabelInterval = labelStyle.interval; 3213 tempLabelIntervalType = labelStyle.intervalType; 3214 3215 // Remember original ScaleView Position 3216 this._originalViewPosition = this.ScaleView.Position; 3217 3218 // This field synchronies the Storing and 3219 // resetting of temporary values 3220 _storeValuesEnabled = false; 3221 } 3222 3223 #if SUBAXES 3224 3225 // Store values of all sub-axis 3226 if(ChartArea.IsSubAxesSupported) 3227 { 3228 foreach(SubAxis subAxis in this.SubAxes) 3229 { 3230 subAxis.StoreAxisValues( ); 3231 } 3232 } 3233 #endif // SUBAXES 3234 3235 } 3236 3237 3238 /// <summary> 3239 /// This method reset Axis values as minimum, maximum, 3240 /// crossing, etc. Axis auto algorithm changes these 3241 /// values and they have to be set to default values 3242 /// after painting. 3243 /// </summary> ResetAxisValues()3244 internal void ResetAxisValues() 3245 { 3246 // Paint mode is finished 3247 paintMode = false; 3248 3249 #if Microsoft_CONTROL 3250 if(Common.Chart == null) 3251 { 3252 #if SUBAXES 3253 else if(this is SubAxis) 3254 { 3255 if( ((SubAxis)this).parentAxis != null) 3256 { 3257 this.Common = ((SubAxis)this).parentAxis.Common; 3258 Common.Chart = ((SubAxis)this).parentAxis.Common.Chart; 3259 } 3260 } 3261 #endif // SUBAXES 3262 } 3263 if(Common.Chart != null && Common.Chart.Site != null && Common.Chart.Site.DesignMode) 3264 { 3265 ResetAutoValues(); 3266 } 3267 #else 3268 ResetAutoValues(); 3269 #endif 3270 3271 // Reset back original custom labels 3272 if (tempLabels != null) 3273 { 3274 CustomLabels.Clear(); 3275 foreach (CustomLabel label in tempLabels) 3276 { 3277 CustomLabels.Add(label.Clone()); 3278 } 3279 3280 tempLabels = null; 3281 } 3282 3283 #if SUBAXES 3284 3285 // Reset values of all sub-axis 3286 if(ChartArea.IsSubAxesSupported) 3287 { 3288 foreach(SubAxis subAxis in this.SubAxes) 3289 { 3290 subAxis.ResetAxisValues( ); 3291 } 3292 } 3293 #endif // SUBAXES 3294 } 3295 3296 3297 /// <summary> 3298 /// Reset auto calculated axis values 3299 /// </summary> ResetAutoValues()3300 internal void ResetAutoValues() 3301 { 3302 refreshMinMaxFromData = true; 3303 maximum = tempMaximum; 3304 minimum = tempMinimum; 3305 crossing = tempCrossing; 3306 _autoMinimum = tempAutoMinimum; 3307 _autoMaximum = tempAutoMaximum; 3308 3309 majorGrid.interval = tempMajorGridInterval; 3310 majorTickMark.interval = tempMajorTickMarkInterval; 3311 3312 minorGrid.interval = tempMinorGridInterval; 3313 minorTickMark.interval = tempMinorTickMarkInterval; 3314 3315 3316 labelStyle.interval = tempLabelInterval; 3317 majorGrid.intervalType = tempGridIntervalType; 3318 majorTickMark.intervalType = tempTickMarkIntervalType; 3319 labelStyle.intervalType = tempLabelIntervalType; 3320 3321 // Restore original ScaleView Position 3322 if (Common.Chart != null) 3323 { 3324 if (!Common.Chart.serializing) 3325 { 3326 this.ScaleView.Position = this._originalViewPosition; 3327 } 3328 } 3329 3330 // This field synchronies the Storing and 3331 // resetting of temporary values 3332 _storeValuesEnabled = true; 3333 3334 #if SUBAXES 3335 3336 // Reset auto values of all sub-axis 3337 if(ChartArea.IsSubAxesSupported) 3338 { 3339 foreach(SubAxis subAxis in this.SubAxes) 3340 { 3341 subAxis.ResetAutoValues( ); 3342 } 3343 } 3344 #endif // SUBAXES 3345 3346 } 3347 3348 /// <summary> 3349 /// Calculate size of the axis elements like title, labels and marks. 3350 /// </summary> 3351 /// <param name="chartGraph">Chart graphics object.</param> 3352 /// <param name="chartAreaPosition">The Chart area position.</param> 3353 /// <param name="plotArea">Plotting area size.</param> 3354 /// <param name="axesNumber">Number of axis of the same orientation.</param> 3355 /// <param name="autoPlotPosition">Indicates that inner plot position is automatic.</param> Resize( ChartGraphics chartGraph, ElementPosition chartAreaPosition, RectangleF plotArea, float axesNumber, bool autoPlotPosition)3356 virtual internal void Resize( 3357 ChartGraphics chartGraph, 3358 ElementPosition chartAreaPosition, 3359 RectangleF plotArea, 3360 float axesNumber, 3361 bool autoPlotPosition) 3362 { 3363 #if SUBAXES 3364 // Resize all sub-axis 3365 if(ChartArea.IsSubAxesSupported) 3366 { 3367 foreach(SubAxis subAxis in this.SubAxes) 3368 { 3369 subAxis.Resize(chartGraph, chartAreaPosition, plotArea, axesNumber, autoPlotPosition); 3370 } 3371 } 3372 #endif // SUBAXES 3373 3374 3375 #if Microsoft_CONTROL 3376 // Disable Common.Chart invalidation 3377 bool oldDisableInvalidates = Common.Chart.disableInvalidates; 3378 Common.Chart.disableInvalidates = true; 3379 #endif //Microsoft_CONTROL 3380 3381 // Set Common.Chart area position 3382 PlotAreaPosition = chartAreaPosition; 3383 3384 // Initialize plot area size 3385 PlotAreaPosition.FromRectangleF(plotArea); 3386 3387 //****************************************************** 3388 //** Calculate axis title size 3389 //****************************************************** 3390 this.titleSize = 0F; 3391 if (this.Title.Length > 0) 3392 { 3393 // Measure axis title 3394 SizeF titleStringSize = chartGraph.MeasureStringRel(this.Title.Replace("\\n", "\n"), this.TitleFont, new SizeF(10000f, 10000f), StringFormat.GenericTypographic, this.GetTextOrientation()); 3395 3396 // Switch Width & Heigth for vertical axes 3397 // If axis is horizontal 3398 float maxTitlesize = 0; 3399 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3400 { 3401 maxTitlesize = (plotArea.Height / 100F) * (Axis.maxAxisTitleSize / axesNumber); 3402 if (this.IsTextVertical) 3403 { 3404 this.titleSize = Math.Min(titleStringSize.Width, maxTitlesize); 3405 } 3406 else 3407 { 3408 this.titleSize = Math.Min(titleStringSize.Height, maxTitlesize); 3409 } 3410 } 3411 // If axis is vertical 3412 else 3413 { 3414 titleStringSize = chartGraph.GetAbsoluteSize(titleStringSize); 3415 titleStringSize = chartGraph.GetRelativeSize(new SizeF(titleStringSize.Height, titleStringSize.Width)); 3416 maxTitlesize = (plotArea.Width / 100F) * (Axis.maxAxisTitleSize / axesNumber); 3417 if (this.IsTextVertical) 3418 { 3419 this.titleSize = Math.Min(titleStringSize.Width, maxTitlesize); 3420 } 3421 else 3422 { 3423 this.titleSize = Math.Min(titleStringSize.Height, maxTitlesize); 3424 } 3425 } 3426 } 3427 if (this.titleSize > 0) 3428 { 3429 this.titleSize += elementSpacing; 3430 } 3431 3432 //********************************************************* 3433 //** Get arrow size of the opposite axis 3434 //********************************************************* 3435 float arrowSize = 0F; 3436 SizeF arrowSizePrimary = SizeF.Empty; 3437 SizeF arrowSizeSecondary = SizeF.Empty; 3438 ArrowOrientation arrowOrientation = ArrowOrientation.Bottom; 3439 if (this.axisType == AxisName.X || this.axisType == AxisName.X2) 3440 { 3441 if (ChartArea.AxisY.ArrowStyle != AxisArrowStyle.None) 3442 { 3443 arrowSizePrimary = ChartArea.AxisY.GetArrowSize(out arrowOrientation); 3444 if (!IsArrowInAxis(arrowOrientation, this.AxisPosition)) 3445 { 3446 arrowSizePrimary = SizeF.Empty; 3447 } 3448 } 3449 3450 if (ChartArea.AxisY2.ArrowStyle != AxisArrowStyle.None) 3451 { 3452 arrowSizeSecondary = ChartArea.AxisY2.GetArrowSize(out arrowOrientation); 3453 if (!IsArrowInAxis(arrowOrientation, this.AxisPosition)) 3454 { 3455 arrowSizeSecondary = SizeF.Empty; 3456 } 3457 } 3458 } 3459 else 3460 { 3461 if (ChartArea.AxisX.ArrowStyle != AxisArrowStyle.None) 3462 { 3463 arrowSizePrimary = ChartArea.AxisX.GetArrowSize(out arrowOrientation); 3464 if (!IsArrowInAxis(arrowOrientation, this.AxisPosition)) 3465 { 3466 arrowSizePrimary = SizeF.Empty; 3467 } 3468 } 3469 3470 if (ChartArea.AxisX2.ArrowStyle != AxisArrowStyle.None) 3471 { 3472 arrowSizeSecondary = ChartArea.AxisX2.GetArrowSize(out arrowOrientation); 3473 if (!IsArrowInAxis(arrowOrientation, this.AxisPosition)) 3474 { 3475 arrowSizeSecondary = SizeF.Empty; 3476 } 3477 } 3478 } 3479 3480 // If axis is horizontal 3481 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3482 { 3483 arrowSize = Math.Max(arrowSizePrimary.Height, arrowSizeSecondary.Height); 3484 } 3485 // If axis is vertical 3486 else 3487 { 3488 arrowSize = Math.Max(arrowSizePrimary.Width, arrowSizeSecondary.Width); 3489 } 3490 3491 //********************************************************* 3492 //** Calculate axis tick marks, axis thickness, arrow size 3493 //** and scroll bar size 3494 //********************************************************* 3495 this.markSize = 0F; 3496 3497 // Get major and minor tick marks sizes 3498 float majorTickSize = 0; 3499 if (this.MajorTickMark.Enabled && this.MajorTickMark.TickMarkStyle != TickMarkStyle.None) 3500 { 3501 if (this.MajorTickMark.TickMarkStyle == TickMarkStyle.InsideArea) 3502 { 3503 majorTickSize = 0F; 3504 } 3505 else if (this.MajorTickMark.TickMarkStyle == TickMarkStyle.AcrossAxis) 3506 { 3507 majorTickSize = this.MajorTickMark.Size / 2F; 3508 } 3509 else if (this.MajorTickMark.TickMarkStyle == TickMarkStyle.OutsideArea) 3510 { 3511 majorTickSize = this.MajorTickMark.Size; 3512 } 3513 } 3514 3515 float minorTickSize = 0; 3516 if (this.MinorTickMark.Enabled && this.MinorTickMark.TickMarkStyle != TickMarkStyle.None && this.MinorTickMark.GetInterval() != 0) 3517 { 3518 if (this.MinorTickMark.TickMarkStyle == TickMarkStyle.InsideArea) 3519 { 3520 minorTickSize = 0F; 3521 } 3522 else if (this.MinorTickMark.TickMarkStyle == TickMarkStyle.AcrossAxis) 3523 { 3524 minorTickSize = this.MinorTickMark.Size / 2F; 3525 } 3526 else if (this.MinorTickMark.TickMarkStyle == TickMarkStyle.OutsideArea) 3527 { 3528 minorTickSize = this.MinorTickMark.Size; 3529 } 3530 } 3531 3532 this.markSize += (float)Math.Max(majorTickSize, minorTickSize); 3533 3534 3535 // Add axis line size 3536 SizeF borderSize = chartGraph.GetRelativeSize(new SizeF(this.LineWidth, this.LineWidth)); 3537 3538 // If axis is horizontal 3539 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3540 { 3541 this.markSize += borderSize.Height / 2f; 3542 this.markSize = Math.Min(this.markSize, (plotArea.Height / 100F) * (Axis.maxAxisMarkSize / axesNumber)); 3543 } 3544 // If axis is vertical 3545 else 3546 { 3547 this.markSize += borderSize.Width / 2f; 3548 this.markSize = Math.Min(this.markSize, (plotArea.Width / 100F) * (Axis.maxAxisMarkSize / axesNumber)); 3549 } 3550 3551 // Add axis scroll bar size (if it's visible) 3552 this.scrollBarSize = 0f; 3553 3554 #if Microsoft_CONTROL 3555 3556 if (this.ScrollBar.IsVisible && 3557 (this.IsAxisOnAreaEdge || !this.IsMarksNextToAxis)) 3558 { 3559 if (this.ScrollBar.IsPositionedInside) 3560 { 3561 this.markSize += (float)this.ScrollBar.GetScrollBarRelativeSize(); 3562 } 3563 else 3564 { 3565 this.scrollBarSize = (float)this.ScrollBar.GetScrollBarRelativeSize(); 3566 } 3567 } 3568 3569 #endif // Microsoft_CONTROL 3570 3571 3572 //********************************************************* 3573 //** Adjust mark size using area scene wall width 3574 //********************************************************* 3575 if (ChartArea.Area3DStyle.Enable3D && 3576 !ChartArea.chartAreaIsCurcular && 3577 ChartArea.BackColor != Color.Transparent && 3578 ChartArea.Area3DStyle.WallWidth > 0) 3579 { 3580 SizeF areaWallSize = chartGraph.GetRelativeSize(new SizeF(ChartArea.Area3DStyle.WallWidth, ChartArea.Area3DStyle.WallWidth)); 3581 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3582 { 3583 this.markSize += areaWallSize.Height; 3584 } 3585 else 3586 { 3587 this.markSize += areaWallSize.Width; 3588 } 3589 3590 // Ignore Max marks size for the 3D wall size. 3591 //this.markSize = Math.Min(this.markSize, (plotArea.Width / 100F) * (Axis.maxAxisMarkSize / axesNumber)); 3592 } 3593 3594 //********************************************************* 3595 //** Adjust title size and mark size using arrow size 3596 //********************************************************* 3597 if (arrowSize > (this.markSize + this.scrollBarSize + this.titleSize)) 3598 { 3599 this.markSize = Math.Max(this.markSize, arrowSize - (this.markSize + this.scrollBarSize + this.titleSize)); 3600 this.markSize = Math.Min(this.markSize, (plotArea.Width / 100F) * (Axis.maxAxisMarkSize / axesNumber)); 3601 } 3602 3603 //********************************************************* 3604 //** Calculate max label size 3605 //********************************************************* 3606 float maxLabelSize = 0; 3607 3608 if (!autoPlotPosition) 3609 { 3610 if (this.GetIsMarksNextToAxis()) 3611 { 3612 if (this.AxisPosition == AxisPosition.Top) 3613 maxLabelSize = (float)GetAxisPosition() - ChartArea.Position.Y; 3614 else if (this.AxisPosition == AxisPosition.Bottom) 3615 maxLabelSize = ChartArea.Position.Bottom - (float)GetAxisPosition(); 3616 if (this.AxisPosition == AxisPosition.Left) 3617 maxLabelSize = (float)GetAxisPosition() - ChartArea.Position.X; 3618 else if (this.AxisPosition == AxisPosition.Right) 3619 maxLabelSize = ChartArea.Position.Right - (float)GetAxisPosition(); 3620 } 3621 else 3622 { 3623 if (this.AxisPosition == AxisPosition.Top) 3624 maxLabelSize = plotArea.Y - ChartArea.Position.Y; 3625 else if (this.AxisPosition == AxisPosition.Bottom) 3626 maxLabelSize = ChartArea.Position.Bottom - plotArea.Bottom; 3627 if (this.AxisPosition == AxisPosition.Left) 3628 maxLabelSize = plotArea.X - ChartArea.Position.X; 3629 else if (this.AxisPosition == AxisPosition.Right) 3630 maxLabelSize = ChartArea.Position.Right - plotArea.Right; 3631 } 3632 3633 maxLabelSize *= 2F; 3634 } 3635 else 3636 { 3637 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3638 maxLabelSize = plotArea.Height * (_maximumAutoSize / 100f); 3639 else 3640 maxLabelSize = plotArea.Width * (_maximumAutoSize / 100f); 3641 } 3642 3643 3644 3645 //****************************************************** 3646 //** First try to select the interval that will 3647 //** generate best fit labels. 3648 //****************************************************** 3649 3650 3651 3652 // Make sure the variable interval mode is enabled and 3653 // no custom label interval used. 3654 if( this.Enabled != AxisEnabled.False && 3655 this.LabelStyle.Enabled && 3656 this.IsVariableLabelCountModeEnabled() ) 3657 { 3658 // Increase font by several points when height of the font is the most important 3659 // dimension. Use original size whenwidth is the most important size. 3660 float extraSize = 3f; 3661 if( (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) && 3662 (this.LabelStyle.Angle == 90 || this.LabelStyle.Angle == -90) ) 3663 { 3664 extraSize = 0f; 3665 } 3666 if( (this.AxisPosition == AxisPosition.Top || this.AxisPosition == AxisPosition.Bottom) && 3667 (this.LabelStyle.Angle == 180 || this.LabelStyle.Angle == 0) ) 3668 { 3669 extraSize = 0f; 3670 } 3671 3672 // If 3D Common.Chart is used make the measurements with font several point larger 3673 if(ChartArea.Area3DStyle.Enable3D) 3674 { 3675 extraSize += 1f; 3676 } 3677 3678 this.autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont(this.LabelStyle.Font.FontFamily, 3679 this.LabelStyle.Font.Size + extraSize, 3680 this.LabelStyle.Font.Style, 3681 GraphicsUnit.Point); 3682 3683 // Reset angle and stagged flag used in the auto-fitting algorithm 3684 this.autoLabelAngle = this.LabelStyle.Angle; 3685 this.autoLabelOffset = (this.LabelStyle.IsStaggered) ? 1 : 0; 3686 3687 // Adjust interval 3688 this.AdjustIntervalToFitLabels(chartGraph, autoPlotPosition, false); 3689 } 3690 3691 3692 3693 //****************************************************** 3694 //** Automatically calculate the best font size, angle 3695 //** and try to use offset labels. 3696 //****************************************************** 3697 // Reset all automatic label properties 3698 autoLabelFont = null; 3699 autoLabelAngle = -1000; 3700 autoLabelOffset = -1; 3701 3702 // For circular Common.Chart area process auto-fitting for Y Axis only 3703 if (this.IsLabelAutoFit && 3704 this.LabelAutoFitStyle != LabelAutoFitStyles.None && 3705 !ChartArea.chartAreaIsCurcular) 3706 { 3707 bool fitDone = false; 3708 bool noWordWrap = false; 3709 3710 // Set default font angle and labels offset flag 3711 autoLabelAngle = 0; 3712 autoLabelOffset = 0; 3713 3714 // Original labels collection 3715 CustomLabelsCollection originalLabels = null; 3716 3717 // Pick up maximum font size 3718 float size = 8f; 3719 size = (float)Math.Max(this.LabelAutoFitMaxFontSize, this.LabelAutoFitMinFontSize); 3720 _minLabelFontSize = Math.Min(this.LabelAutoFitMinFontSize, this.LabelAutoFitMaxFontSize); 3721 _aveLabelFontSize = _minLabelFontSize + Math.Abs(size - _minLabelFontSize)/2f; 3722 3723 3724 // Check if common font size should be used 3725 if (ChartArea.IsSameFontSizeForAllAxes) 3726 { 3727 size = (float)Math.Min(size, ChartArea.axesAutoFontSize); 3728 } 3729 3730 //Set new font 3731 autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont(this.LabelStyle.Font.FontFamily, 3732 size, 3733 this.LabelStyle.Font.Style, 3734 GraphicsUnit.Point 3735 ); 3736 3737 // Check if we allowed to increase font size while auto-fitting 3738 if ((this.LabelAutoFitStyle & LabelAutoFitStyles.IncreaseFont) != LabelAutoFitStyles.IncreaseFont) 3739 { 3740 // Use axis labels font as starting point 3741 autoLabelFont = this.LabelStyle.Font; 3742 } 3743 3744 // Loop while labels do not fit 3745 float spacer = 0f; 3746 while (!fitDone) 3747 { 3748 //****************************************************** 3749 //** Check if labels fit 3750 //****************************************************** 3751 3752 // Check if grouping labels fit should be checked 3753 bool checkLabelsFirstRowOnly = true; 3754 if ((this.LabelAutoFitStyle & LabelAutoFitStyles.DecreaseFont) == LabelAutoFitStyles.DecreaseFont) 3755 { 3756 // Only check grouping labels if we can reduce fonts size 3757 checkLabelsFirstRowOnly = false; 3758 } 3759 3760 // Check labels fit 3761 fitDone = CheckLabelsFit( 3762 chartGraph, 3763 this.markSize + this.scrollBarSize + this.titleSize + spacer, 3764 autoPlotPosition, 3765 checkLabelsFirstRowOnly, 3766 false); 3767 3768 //****************************************************** 3769 //** Adjust labels text properties to fit 3770 //****************************************************** 3771 if (!fitDone) 3772 { 3773 // If font is bigger than average try to make it smaller 3774 if (autoLabelFont.SizeInPoints >= _aveLabelFontSize && 3775 (this.LabelAutoFitStyle & LabelAutoFitStyles.DecreaseFont) == LabelAutoFitStyles.DecreaseFont) 3776 { 3777 //Clean up the old font 3778 autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 3779 autoLabelFont.FontFamily, 3780 autoLabelFont.SizeInPoints - 0.5f, 3781 autoLabelFont.Style, 3782 GraphicsUnit.Point); 3783 } 3784 3785 // Try to use offset labels (2D charts and non-circular arae only!!!) 3786 else if (!ChartArea.Area3DStyle.Enable3D && 3787 !ChartArea.chartAreaIsCurcular && 3788 originalLabels == null && 3789 autoLabelAngle == 0 && 3790 autoLabelOffset == 0 && 3791 (this.LabelAutoFitStyle & LabelAutoFitStyles.StaggeredLabels) == LabelAutoFitStyles.StaggeredLabels) 3792 { 3793 autoLabelOffset = 1; 3794 } 3795 3796 // Try to insert new line characters in labels text 3797 else if (!noWordWrap && 3798 (this.LabelAutoFitStyle & LabelAutoFitStyles.WordWrap) == LabelAutoFitStyles.WordWrap) 3799 { 3800 bool changed = false; 3801 autoLabelOffset = 0; 3802 3803 // Check if backup copy of the original lables was made 3804 if (originalLabels == null) 3805 { 3806 // Copy current labels collection 3807 originalLabels = new CustomLabelsCollection(this); 3808 foreach (CustomLabel label in this.CustomLabels) 3809 { 3810 originalLabels.Add(label.Clone()); 3811 } 3812 } 3813 3814 // Try to insert new line character into the longest label 3815 changed = WordWrapLongestLabel(this.CustomLabels); 3816 3817 // Word wrapping do not solve the labels overlapping issue 3818 if (!changed) 3819 { 3820 noWordWrap = true; 3821 3822 // Restore original labels 3823 if (originalLabels != null) 3824 { 3825 this.CustomLabels.Clear(); 3826 foreach (CustomLabel label in originalLabels) 3827 { 3828 this.CustomLabels.Add(label.Clone()); 3829 } 3830 3831 originalLabels = null; 3832 } 3833 3834 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3835 { 3836 if ((spacer == 0 || 3837 spacer == 30f || 3838 spacer == 20f) && 3839 ((this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep30) == LabelAutoFitStyles.LabelsAngleStep30 || 3840 (this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep45) == LabelAutoFitStyles.LabelsAngleStep45 || 3841 (this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep90) == LabelAutoFitStyles.LabelsAngleStep90)) 3842 { 3843 // Try to use 90 degrees angle 3844 autoLabelAngle = 90; 3845 noWordWrap = false; 3846 3847 // Usually 55% of Common.Chart area size is allowed for labels 3848 // Reduce that space. 3849 if (spacer == 0f) 3850 { 3851 // 30 3852 spacer = 30f; 3853 } 3854 else if (spacer == 30f) 3855 { 3856 // 20 3857 spacer = 20f; 3858 } 3859 else if (spacer == 20f) 3860 { 3861 // 5 3862 spacer = 5f; 3863 } 3864 else 3865 { 3866 autoLabelAngle = 0; 3867 noWordWrap = true; 3868 } 3869 3870 } 3871 else 3872 { 3873 spacer = 0f; 3874 } 3875 } 3876 } 3877 } 3878 3879 // Try to change font angle 3880 else if (autoLabelAngle != 90 && 3881 ((this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep30) == LabelAutoFitStyles.LabelsAngleStep30 || 3882 (this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep45) == LabelAutoFitStyles.LabelsAngleStep45 || 3883 (this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep90) == LabelAutoFitStyles.LabelsAngleStep90)) 3884 { 3885 spacer = 0f; 3886 autoLabelOffset = 0; 3887 3888 if ((this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep30) == LabelAutoFitStyles.LabelsAngleStep30) 3889 { 3890 // Increase angle by 45 degrees in 2D and 45 in 3D 3891 autoLabelAngle += (ChartArea.Area3DStyle.Enable3D) ? 45 : 30; 3892 } 3893 else if ((this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep45) == LabelAutoFitStyles.LabelsAngleStep45) 3894 { 3895 // Increase angle by 45 degrees 3896 autoLabelAngle += 45; 3897 } 3898 else if ((this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep90) == LabelAutoFitStyles.LabelsAngleStep90) 3899 { 3900 // Increase angle by 90 degrees 3901 autoLabelAngle += 90; 3902 } 3903 } 3904 3905 // Try to reduce font again 3906 else if (autoLabelFont.SizeInPoints > _minLabelFontSize && 3907 (this.LabelAutoFitStyle & LabelAutoFitStyles.DecreaseFont) == LabelAutoFitStyles.DecreaseFont) 3908 { 3909 //Clean up the old font 3910 autoLabelAngle = 0; 3911 autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 3912 autoLabelFont.FontFamily, 3913 autoLabelFont.SizeInPoints - 0.5f, 3914 autoLabelFont.Style, 3915 GraphicsUnit.Point); 3916 } 3917 3918 // Failed to fit 3919 else 3920 { 3921 // Use last font 3922 if ((this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep30) == LabelAutoFitStyles.LabelsAngleStep30 || 3923 (this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep45) == LabelAutoFitStyles.LabelsAngleStep45 || 3924 (this.LabelAutoFitStyle & LabelAutoFitStyles.LabelsAngleStep90) == LabelAutoFitStyles.LabelsAngleStep90) 3925 { 3926 // Reset angle 3927 if (this.AxisPosition == AxisPosition.Top || this.AxisPosition == AxisPosition.Bottom) 3928 { 3929 autoLabelAngle = 90; 3930 } 3931 else 3932 { 3933 autoLabelAngle = 0; 3934 } 3935 } 3936 if ((this.LabelAutoFitStyle & LabelAutoFitStyles.StaggeredLabels) == LabelAutoFitStyles.StaggeredLabels) 3937 { 3938 // Reset offset labels 3939 autoLabelOffset = 0; 3940 } 3941 fitDone = true; 3942 } 3943 } 3944 else if (ChartArea.Area3DStyle.Enable3D && 3945 !ChartArea.chartAreaIsCurcular && 3946 autoLabelFont.SizeInPoints > _minLabelFontSize) 3947 { 3948 // Reduce auto-fit font by 1 for the 3D charts 3949 autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 3950 autoLabelFont.FontFamily, 3951 autoLabelFont.SizeInPoints - 0.5f, 3952 autoLabelFont.Style, 3953 GraphicsUnit.Point); 3954 } 3955 } 3956 3957 // Change the auto-fit angle for top and bottom axes from 90 to -90 3958 if(this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3959 { 3960 if(autoLabelAngle == 90) 3961 { 3962 autoLabelAngle = -90; 3963 } 3964 } 3965 } 3966 3967 //********************************************************* 3968 //** Calculate overall labels size 3969 //********************************************************* 3970 this.labelSize = 0; 3971 3972 // if labels are not enabled their size needs to remain zero 3973 if (this.LabelStyle.Enabled) 3974 { 3975 //****************************************************** 3976 //** Calculate axis second labels row size 3977 //****************************************************** 3978 this.labelSize = (maxAxisElementsSize) - this.markSize - this.scrollBarSize - this.titleSize; 3979 if (this.labelSize > 0) 3980 { 3981 this.groupingLabelSizes = GetRequiredGroupLabelSize(chartGraph, (maxLabelSize / 100F) * maxAxisLabelRow2Size); 3982 this.totlaGroupingLabelsSize = GetGroupLablesToatalSize(); 3983 } 3984 3985 //****************************************************** 3986 //** Calculate axis labels size 3987 //****************************************************** 3988 this.labelSize -= this.totlaGroupingLabelsSize; 3989 if (this.labelSize > 0) 3990 { 3991 // If axis is horizontal 3992 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 3993 { 3994 this.labelSize = elementSpacing + GetRequiredLabelSize(chartGraph, 3995 (maxLabelSize / 100F) * (maxAxisElementsSize - this.markSize - this.scrollBarSize - this.titleSize), out this.unRotatedLabelSize); 3996 } 3997 // If axis is horizontal 3998 else 3999 { 4000 this.labelSize = elementSpacing + GetRequiredLabelSize(chartGraph, 4001 (maxLabelSize / 100F) * (maxAxisElementsSize - this.markSize - this.scrollBarSize - this.titleSize), out this.unRotatedLabelSize); 4002 } 4003 4004 if (!this.LabelStyle.Enabled) 4005 { 4006 this.labelSize -= elementSpacing; 4007 } 4008 } 4009 else 4010 { 4011 this.labelSize = 0; 4012 } 4013 4014 this.labelSize += this.totlaGroupingLabelsSize; 4015 } 4016 4017 #if SUBAXES 4018 // Calculate offsets for all sub axes 4019 if(!ChartArea.Area3DStyle.Enable3D && 4020 !ChartArea.chartAreaIsCurcular) 4021 { 4022 float currentOffset = this.markSize + this.labelSize + this.titleSize + this.scrollBarSize; 4023 foreach(SubAxis subAxis in this.SubAxes) 4024 { 4025 if(subAxis.Enabled != AxisEnabled.False) 4026 { 4027 currentOffset += (float)subAxis.LocationOffset; 4028 subAxis.offsetFromParent = currentOffset; 4029 currentOffset += subAxis.markSize + subAxis.labelSize + subAxis.titleSize; 4030 } 4031 } 4032 } 4033 #endif // SUBAXES 4034 4035 4036 #if Microsoft_CONTROL 4037 // Restore previous invalidation flag 4038 Common.Chart.disableInvalidates = oldDisableInvalidates; 4039 #endif //Microsoft_CONTROL 4040 } 4041 4042 /// <summary> 4043 /// Calculates axis interval so that labels will fit most efficiently. 4044 /// </summary> 4045 /// <param name="chartGraph">Chart graphics.</param> 4046 /// <param name="autoPlotPosition">True if plot position is auto calculated.</param> 4047 /// <param name="onlyIncreaseInterval">True if interval should only be increased.</param> AdjustIntervalToFitLabels(ChartGraphics chartGraph, bool autoPlotPosition, bool onlyIncreaseInterval)4048 private void AdjustIntervalToFitLabels(ChartGraphics chartGraph, bool autoPlotPosition, bool onlyIncreaseInterval) 4049 { 4050 // Calculates axis interval so that labels will fit most efficiently. 4051 if(this.ScaleSegments.Count == 0) 4052 { 4053 this.AdjustIntervalToFitLabels(chartGraph, autoPlotPosition, null, onlyIncreaseInterval); 4054 } 4055 else 4056 { 4057 // Allow values to go outside the segment boundary 4058 this.ScaleSegments.AllowOutOfScaleValues = true; 4059 4060 // Adjust interval of each segment first 4061 foreach(AxisScaleSegment axisScaleSegment in this.ScaleSegments) 4062 { 4063 this.AdjustIntervalToFitLabels(chartGraph, autoPlotPosition, axisScaleSegment, onlyIncreaseInterval); 4064 } 4065 4066 // Fill labels using new segment intervals 4067 bool removeLabels = true; 4068 int segmentIndex = 0; 4069 ArrayList removedLabels = new ArrayList(); 4070 ArrayList removedLabelsIndexes = new ArrayList(); 4071 foreach(AxisScaleSegment scaleSegment in this.ScaleSegments) 4072 { 4073 scaleSegment.SetTempAxisScaleAndInterval(); 4074 this.FillLabels(removeLabels); 4075 removeLabels = false; 4076 scaleSegment.RestoreAxisScaleAndInterval(); 4077 4078 // Remove last label of all segmenst except of the last 4079 if(segmentIndex < this.ScaleSegments.Count - 1 && 4080 this.CustomLabels.Count > 0) 4081 { 4082 // Remove label and save it in the list 4083 removedLabels.Add(this.CustomLabels[this.CustomLabels.Count - 1]); 4084 removedLabelsIndexes.Add(this.CustomLabels.Count - 1); 4085 this.CustomLabels.RemoveAt(this.CustomLabels.Count - 1); 4086 } 4087 4088 ++segmentIndex; 4089 } 4090 4091 // Check all previously removed last labels of each segment if there 4092 // is enough space to fit them 4093 int reInsertedLabelsCount = 0; 4094 int labelIndex = 0; 4095 foreach(CustomLabel label in removedLabels) 4096 { 4097 // Re-insert the label 4098 int labelInsertIndex = (int)removedLabelsIndexes[labelIndex] + reInsertedLabelsCount; 4099 if(labelIndex < this.CustomLabels.Count) 4100 { 4101 this.CustomLabels.Insert(labelInsertIndex, label); 4102 } 4103 else 4104 { 4105 this.CustomLabels.Add(label); 4106 } 4107 4108 // Check labels fit. Only horizontal or vertical fit is checked depending 4109 // on the axis orientation. 4110 ArrayList labelPositions = new ArrayList(); 4111 bool fitDone = CheckLabelsFit( 4112 chartGraph, 4113 this.markSize + this.scrollBarSize + this.titleSize, 4114 autoPlotPosition, 4115 true, 4116 false, 4117 (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? false : true, 4118 (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? true : false, 4119 labelPositions); 4120 4121 // If labels fit check if any of the label positions overlap 4122 if(fitDone) 4123 { 4124 for(int index = 0; fitDone && index < labelPositions.Count; index++) 4125 { 4126 RectangleF rect1 = (RectangleF)labelPositions[index]; 4127 for(int index2 = index + 1; fitDone && index2 < labelPositions.Count; index2++) 4128 { 4129 RectangleF rect2 = (RectangleF)labelPositions[index2]; 4130 if(rect1.IntersectsWith(rect2)) 4131 { 4132 fitDone = false; 4133 } 4134 } 4135 } 4136 } 4137 4138 // If labels do not fit or overlapp - remove completly 4139 if(!fitDone) 4140 { 4141 this.CustomLabels.RemoveAt(labelInsertIndex); 4142 } 4143 else 4144 { 4145 ++reInsertedLabelsCount; 4146 } 4147 4148 ++labelIndex; 4149 } 4150 4151 // Make sure now values are rounded on segment boundary 4152 this.ScaleSegments.AllowOutOfScaleValues = false; 4153 } 4154 } 4155 4156 /// <summary> 4157 /// Checks if variable count labels mode is enabled. 4158 /// </summary> 4159 /// <returns>True if variable count labels mode is enabled.</returns> IsVariableLabelCountModeEnabled()4160 private bool IsVariableLabelCountModeEnabled() 4161 { 4162 // Make sure the variable interval mode is enabled and 4163 // no custom label interval used. 4164 if( (this.IntervalAutoMode == IntervalAutoMode.VariableCount || this.ScaleSegments.Count > 0) && 4165 !this.IsLogarithmic && 4166 (this.tempLabelInterval <= 0.0 || (double.IsNaN(this.tempLabelInterval) && this.Interval <= 0.0)) ) 4167 { 4168 // This feature is not supported for charts that do not 4169 // require X and Y axes (Pie, Radar, ...) 4170 if(!ChartArea.requireAxes) 4171 { 4172 return false; 4173 } 4174 // This feature is not supported if the axis doesn't have data range 4175 if (Double.IsNaN(this.minimum) || Double.IsNaN(this.maximum)) 4176 { 4177 return false; 4178 } 4179 // Check if custom labels are used in the first row 4180 bool customLabels = false; 4181 foreach(CustomLabel label in this.CustomLabels) 4182 { 4183 if(label.customLabel && label.RowIndex == 0) 4184 { 4185 customLabels = true; 4186 break; 4187 } 4188 } 4189 4190 // Proceed only if no custom labels are used in the first row 4191 if(!customLabels) 4192 { 4193 return true; 4194 } 4195 } 4196 4197 return false; 4198 } 4199 4200 /// <summary> 4201 /// Calculates axis interval so that labels will fit most efficiently. 4202 /// </summary> 4203 /// <param name="chartGraph">Chart graphics.</param> 4204 /// <param name="autoPlotPosition">True if plot position is auto calculated.</param> 4205 /// <param name="axisScaleSegment">Axis scale segment to process.</param> 4206 /// <param name="onlyIncreaseInterval">True if interval should only be increased.</param> AdjustIntervalToFitLabels( ChartGraphics chartGraph, bool autoPlotPosition, AxisScaleSegment axisScaleSegment, bool onlyIncreaseInterval)4207 private void AdjustIntervalToFitLabels( 4208 ChartGraphics chartGraph, 4209 bool autoPlotPosition, 4210 AxisScaleSegment axisScaleSegment, 4211 bool onlyIncreaseInterval) 4212 { 4213 // Re-fill the labels just for the scale segment provided 4214 if(axisScaleSegment != null) 4215 { 4216 // Re-fill new axis labels 4217 if(this.tempLabels != null) 4218 { 4219 this.CustomLabels.Clear(); 4220 foreach( CustomLabel label in this.tempLabels ) 4221 { 4222 this.CustomLabels.Add(label.Clone()); 4223 } 4224 } 4225 4226 // Fill labels just for the segment 4227 axisScaleSegment.SetTempAxisScaleAndInterval(); 4228 this.FillLabels( true ); 4229 axisScaleSegment.RestoreAxisScaleAndInterval(); 4230 } 4231 4232 // Calculate minimum interval size 4233 double minIntervalSzie = double.NaN; 4234 ArrayList axisSeries = AxisScaleBreakStyle.GetAxisSeries(this); 4235 foreach(Series series in axisSeries) 4236 { 4237 if(this.axisType == AxisName.X || this.axisType == AxisName.X2) 4238 { 4239 if(ChartHelper.IndexedSeries(series)) 4240 { 4241 minIntervalSzie = 1.0; 4242 } 4243 else if(series.XValueType == ChartValueType.String || 4244 series.XValueType == ChartValueType.Int32 || 4245 series.XValueType == ChartValueType.UInt32 || 4246 series.XValueType == ChartValueType.UInt64 || 4247 series.XValueType == ChartValueType.Int64 ) 4248 { 4249 minIntervalSzie = 1.0; 4250 } 4251 } 4252 else 4253 { 4254 if(series.YValueType == ChartValueType.String || 4255 series.YValueType == ChartValueType.Int32 || 4256 series.YValueType == ChartValueType.UInt32 || 4257 series.YValueType == ChartValueType.UInt64 || 4258 series.YValueType == ChartValueType.Int64 ) 4259 { 4260 minIntervalSzie = 1.0; 4261 } 4262 } 4263 } 4264 4265 4266 // Iterate while interval is not found 4267 bool firstIteration = true; 4268 bool increaseNumberOfLabels = true; 4269 double currentInterval = (axisScaleSegment == null) ? this.labelStyle.GetInterval() : axisScaleSegment.Interval; 4270 DateTimeIntervalType currentIntervalType = (axisScaleSegment == null) ? this.labelStyle.GetIntervalType() : axisScaleSegment.IntervalType; 4271 DateTimeIntervalType lastFitIntervalType = currentIntervalType; 4272 double lastFitInterval = currentInterval; 4273 ArrayList lastFitLabels = new ArrayList(); 4274 bool intervalFound = false; 4275 int iterationNumber = 0; 4276 while(!intervalFound && iterationNumber <= 1000) 4277 { 4278 bool fillNewLabels = true; 4279 #if DEBUG 4280 if(iterationNumber >= 999) 4281 { 4282 throw (new InvalidOperationException(SR.ExceptionAxisDynamicIntervalCalculationFailed)); 4283 } 4284 #endif // DEBUG 4285 4286 // Check labels fit. Only horizontal or vertical fit is checked depending 4287 // on the axis orientation. 4288 bool fitDone = CheckLabelsFit( 4289 chartGraph, 4290 this.markSize + this.scrollBarSize + this.titleSize, 4291 autoPlotPosition, 4292 true, 4293 false, 4294 (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? false : true, 4295 (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? true : false, 4296 null); 4297 4298 // Check if we need to increase or reduce number of labels 4299 if(firstIteration) 4300 { 4301 firstIteration = false; 4302 increaseNumberOfLabels = (fitDone) ? true : false; 4303 4304 // Check if we can decrease the interva; 4305 if(onlyIncreaseInterval && increaseNumberOfLabels) 4306 { 4307 intervalFound = true; 4308 continue; 4309 } 4310 } 4311 4312 // Find new interval. Value 0.0 means that interval cannot be 4313 // reduced/increased any more and current interval should be used 4314 double newInterval = 0.0; 4315 DateTimeIntervalType newIntervalType = DateTimeIntervalType.Number; 4316 if(increaseNumberOfLabels) 4317 { 4318 if(fitDone) 4319 { 4320 // Make a copy of last interval and labels collection that previously fit 4321 lastFitInterval = currentInterval; 4322 lastFitIntervalType = currentIntervalType; 4323 lastFitLabels.Clear(); 4324 foreach(CustomLabel label in this.CustomLabels) 4325 { 4326 lastFitLabels.Add(label); 4327 } 4328 4329 newIntervalType = currentIntervalType; 4330 newInterval = this.ReduceLabelInterval( 4331 currentInterval, 4332 minIntervalSzie, 4333 ref newIntervalType); 4334 } 4335 else 4336 { 4337 newInterval = lastFitInterval; 4338 newIntervalType = lastFitIntervalType; 4339 intervalFound = true; 4340 4341 // Reuse previously saved labels 4342 fillNewLabels = false; 4343 this.CustomLabels.Clear(); 4344 foreach(CustomLabel label in lastFitLabels) 4345 { 4346 this.CustomLabels.Add(label); 4347 } 4348 4349 } 4350 } 4351 else 4352 { 4353 if(!fitDone && this.CustomLabels.Count > 1) 4354 { 4355 newIntervalType = currentIntervalType; 4356 newInterval = this.IncreaseLabelInterval( 4357 currentInterval, 4358 ref newIntervalType); 4359 } 4360 else 4361 { 4362 intervalFound = true; 4363 } 4364 } 4365 4366 // Set new interval 4367 if(newInterval != 0.0) 4368 { 4369 currentInterval = newInterval; 4370 currentIntervalType = newIntervalType; 4371 4372 if(axisScaleSegment == null) 4373 { 4374 this.SetIntervalAndType(newInterval, newIntervalType); 4375 } 4376 else 4377 { 4378 axisScaleSegment.Interval = newInterval; 4379 axisScaleSegment.IntervalType = newIntervalType; 4380 } 4381 4382 // Re-fill new axis labels 4383 if(fillNewLabels) 4384 { 4385 if(this.tempLabels != null) 4386 { 4387 this.CustomLabels.Clear(); 4388 foreach( CustomLabel label in this.tempLabels ) 4389 { 4390 CustomLabels.Add(label.Clone()); 4391 } 4392 } 4393 4394 if(axisScaleSegment == null) 4395 { 4396 this.FillLabels(true); 4397 } 4398 else 4399 { 4400 axisScaleSegment.SetTempAxisScaleAndInterval(); 4401 this.FillLabels( true ); 4402 axisScaleSegment.RestoreAxisScaleAndInterval(); 4403 } 4404 } 4405 } 4406 else 4407 { 4408 intervalFound = true; 4409 } 4410 4411 ++iterationNumber; 4412 } 4413 } 4414 4415 /// <summary> 4416 /// Reduces current label interval, so that more labels can fit. 4417 /// </summary> 4418 /// <param name="oldInterval">An interval to reduce.</param> 4419 /// <param name="minInterval">Minimum interval size.</param> 4420 /// <param name="axisIntervalType">Interval type.</param> 4421 /// <returns>New interval or 0.0 if interval cannot be reduced.</returns> ReduceLabelInterval( double oldInterval, double minInterval, ref DateTimeIntervalType axisIntervalType)4422 private double ReduceLabelInterval( 4423 double oldInterval, 4424 double minInterval, 4425 ref DateTimeIntervalType axisIntervalType) 4426 { 4427 double newInterval = oldInterval; 4428 4429 // Calculate rounded interval value 4430 double range = this.maximum - this.minimum; 4431 int iterationIndex = 0; 4432 if( axisIntervalType == DateTimeIntervalType.Auto || 4433 axisIntervalType == DateTimeIntervalType.NotSet || 4434 axisIntervalType == DateTimeIntervalType.Number) 4435 { 4436 // Process numeric scale 4437 double devider = 2.0; 4438 do 4439 { 4440 #if DEBUG 4441 if(iterationIndex >= 99) 4442 { 4443 throw (new InvalidOperationException(SR.ExceptionAxisIntervalDecreasingFailed)); 4444 } 4445 #endif // DEBUG 4446 4447 newInterval = CalcInterval( range / (range / (newInterval / devider)) ); 4448 if(newInterval == oldInterval) 4449 { 4450 devider *= 2.0; 4451 } 4452 4453 ++iterationIndex; 4454 } while(newInterval == oldInterval && iterationIndex <= 100); 4455 } 4456 else 4457 { 4458 // Process date scale 4459 if(oldInterval > 1.0 || oldInterval < 1.0) 4460 { 4461 if( axisIntervalType == DateTimeIntervalType.Minutes || 4462 axisIntervalType == DateTimeIntervalType.Seconds) 4463 { 4464 if(oldInterval >= 60) 4465 { 4466 newInterval = Math.Round(oldInterval / 2.0); 4467 } 4468 else if(oldInterval >= 30.0) 4469 { 4470 newInterval = 15.0; 4471 } 4472 else if(oldInterval >= 15.0) 4473 { 4474 newInterval = 5.0; 4475 } 4476 else if(oldInterval >= 5.0) 4477 { 4478 newInterval = 1.0; 4479 } 4480 } 4481 else 4482 { 4483 newInterval = Math.Round(oldInterval / 2.0); 4484 } 4485 if(newInterval < 1.0) 4486 { 4487 newInterval = 1.0; 4488 } 4489 } 4490 if(oldInterval == 1.0) 4491 { 4492 if(axisIntervalType == DateTimeIntervalType.Years) 4493 { 4494 newInterval = 6.0; 4495 axisIntervalType = DateTimeIntervalType.Months; 4496 } 4497 else if(axisIntervalType == DateTimeIntervalType.Months) 4498 { 4499 newInterval = 2.0; 4500 axisIntervalType = DateTimeIntervalType.Weeks; 4501 } 4502 else if(axisIntervalType == DateTimeIntervalType.Weeks) 4503 { 4504 newInterval = 2.0; 4505 axisIntervalType = DateTimeIntervalType.Days; 4506 } 4507 else if(axisIntervalType == DateTimeIntervalType.Days) 4508 { 4509 newInterval = 12.0; 4510 axisIntervalType = DateTimeIntervalType.Hours; 4511 } 4512 else if(axisIntervalType == DateTimeIntervalType.Hours) 4513 { 4514 newInterval = 30.0; 4515 axisIntervalType = DateTimeIntervalType.Minutes; 4516 } 4517 else if(axisIntervalType == DateTimeIntervalType.Minutes) 4518 { 4519 newInterval = 30.0; 4520 axisIntervalType = DateTimeIntervalType.Seconds; 4521 } 4522 else if(axisIntervalType == DateTimeIntervalType.Seconds) 4523 { 4524 newInterval = 100.0; 4525 axisIntervalType = DateTimeIntervalType.Milliseconds; 4526 } 4527 } 4528 } 4529 4530 4531 // Make sure interal is not less than min interval specified 4532 if(!double.IsNaN(minInterval) && newInterval < minInterval) 4533 { 4534 newInterval = 0.0; 4535 } 4536 4537 return newInterval; 4538 } 4539 4540 /// <summary> 4541 /// Increases current label interval, so that less labels fit. 4542 /// </summary> 4543 /// <param name="oldInterval">An interval to increase.</param> 4544 /// <param name="axisIntervalType">Interval type.</param> 4545 /// <returns>New interval or 0.0 if interval cannot be increased.</returns> IncreaseLabelInterval( double oldInterval, ref DateTimeIntervalType axisIntervalType)4546 private double IncreaseLabelInterval( 4547 double oldInterval, 4548 ref DateTimeIntervalType axisIntervalType) 4549 { 4550 double newInterval = oldInterval; 4551 4552 // Calculate rounded interval value 4553 double range = this.maximum - this.minimum; 4554 int iterationIndex = 0; 4555 if( axisIntervalType == DateTimeIntervalType.Auto || 4556 axisIntervalType == DateTimeIntervalType.NotSet || 4557 axisIntervalType == DateTimeIntervalType.Number) 4558 { 4559 // Process numeric scale 4560 double devider = 2.0; 4561 do 4562 { 4563 #if DEBUG 4564 if(iterationIndex >= 99) 4565 { 4566 throw (new InvalidOperationException(SR.ExceptionAxisIntervalIncreasingFailed)); 4567 } 4568 #endif // DEBUG 4569 4570 newInterval = CalcInterval( range / (range / (newInterval * devider)) ); 4571 if(newInterval == oldInterval) 4572 { 4573 devider *= 2.0; 4574 } 4575 ++iterationIndex; 4576 } while(newInterval == oldInterval && iterationIndex <= 100); 4577 } 4578 else 4579 { 4580 // Process date scale 4581 newInterval = oldInterval * 2.0; 4582 if(axisIntervalType == DateTimeIntervalType.Years) 4583 { 4584 // Do nothing for years 4585 } 4586 else if(axisIntervalType == DateTimeIntervalType.Months) 4587 { 4588 if(newInterval >= 12.0) 4589 { 4590 newInterval = 1.0; 4591 axisIntervalType = DateTimeIntervalType.Years; 4592 } 4593 } 4594 else if(axisIntervalType == DateTimeIntervalType.Weeks) 4595 { 4596 if(newInterval >= 4.0) 4597 { 4598 newInterval = 1.0; 4599 axisIntervalType = DateTimeIntervalType.Months; 4600 } 4601 } 4602 else if(axisIntervalType == DateTimeIntervalType.Days) 4603 { 4604 if(newInterval >= 7.0) 4605 { 4606 newInterval = 1.0; 4607 axisIntervalType = DateTimeIntervalType.Weeks; 4608 } 4609 } 4610 else if(axisIntervalType == DateTimeIntervalType.Hours) 4611 { 4612 if(newInterval >= 60.0) 4613 { 4614 newInterval = 1.0; 4615 axisIntervalType = DateTimeIntervalType.Days; 4616 } 4617 } 4618 else if(axisIntervalType == DateTimeIntervalType.Minutes) 4619 { 4620 if(newInterval >= 60.0) 4621 { 4622 newInterval = 1.0; 4623 axisIntervalType = DateTimeIntervalType.Hours; 4624 } 4625 } 4626 else if(axisIntervalType == DateTimeIntervalType.Seconds) 4627 { 4628 if(newInterval >= 60.0) 4629 { 4630 newInterval = 1.0; 4631 axisIntervalType = DateTimeIntervalType.Minutes; 4632 } 4633 } 4634 else if(axisIntervalType == DateTimeIntervalType.Milliseconds) 4635 { 4636 if(newInterval >= 1000.0) 4637 { 4638 newInterval = 1.0; 4639 axisIntervalType = DateTimeIntervalType.Seconds; 4640 } 4641 } 4642 } 4643 4644 return newInterval; 4645 } 4646 4647 /// <summary> 4648 /// Finds the longest labels with the space and inserts the new line character. 4649 /// </summary> 4650 /// <param name="labels">Labels collection.</param> 4651 /// <returns>True if collection was modified.</returns> WordWrapLongestLabel(CustomLabelsCollection labels)4652 private bool WordWrapLongestLabel(CustomLabelsCollection labels) 4653 { 4654 bool changed = false; 4655 4656 // Each label may contain several lines of text. 4657 // Create a list that contains an array of text for each label. 4658 ArrayList labelTextRows = new ArrayList(labels.Count); 4659 foreach (CustomLabel label in labels) 4660 { 4661 labelTextRows.Add(label.Text.Split('\n')); 4662 } 4663 4664 // Find the longest label with a space 4665 int longestLabelSize = 5; 4666 int longestLabelIndex = -1; 4667 int longestLabelRowIndex = -1; 4668 int index = 0; 4669 foreach (string[] textRows in labelTextRows) 4670 { 4671 for (int rowIndex = 0; rowIndex < textRows.Length; rowIndex++) 4672 { 4673 if (textRows[rowIndex].Length > longestLabelSize && textRows[rowIndex].Trim().IndexOf(' ') > 0) 4674 { 4675 longestLabelSize = textRows[rowIndex].Length; 4676 longestLabelIndex = index; 4677 longestLabelRowIndex = rowIndex; 4678 } 4679 } 4680 ++index; 4681 } 4682 4683 // Longest label with a space was found 4684 if (longestLabelIndex >= 0 && longestLabelRowIndex >= 0) 4685 { 4686 // Try to find a space and replace it with a new line 4687 string newText = ((string[])labelTextRows[longestLabelIndex])[longestLabelRowIndex]; 4688 for (index = 0; index < (newText.Length) / 2 - 1; index++) 4689 { 4690 if (newText[(newText.Length) / 2 - index] == ' ') 4691 { 4692 newText = 4693 newText.Substring(0, (newText.Length) / 2 - index) + 4694 "\n" + 4695 newText.Substring((newText.Length) / 2 - index + 1); 4696 changed = true; 4697 } 4698 else if (newText[(newText.Length) / 2 + index] == ' ') 4699 { 4700 newText = 4701 newText.Substring(0, (newText.Length) / 2 + index) + 4702 "\n" + 4703 newText.Substring((newText.Length) / 2 + index + 1); 4704 changed = true; 4705 } 4706 4707 if (changed) 4708 { 4709 ((string[])labelTextRows[longestLabelIndex])[longestLabelRowIndex] = newText; 4710 break; 4711 } 4712 } 4713 4714 // Update label text 4715 if (changed) 4716 { 4717 // Construct label text from multiple rows separated by "\n" 4718 CustomLabel label = labels[longestLabelIndex]; 4719 label.Text = string.Empty; 4720 for (int rowIndex = 0; rowIndex < ((string[])labelTextRows[longestLabelIndex]).Length; rowIndex++) 4721 { 4722 if (rowIndex > 0) 4723 { 4724 label.Text += "\n"; 4725 } 4726 label.Text += ((string[])labelTextRows[longestLabelIndex])[rowIndex]; 4727 } 4728 } 4729 } 4730 4731 return changed; 4732 } 4733 4734 /// <summary> 4735 /// Calculates the auto-fit font for the circular Common.Chart area axis labels. 4736 /// </summary> 4737 /// <param name="graph">Chart graphics object.</param> 4738 /// <param name="axisList">List of sector labels.</param> 4739 /// <param name="labelsStyle">Circular labels style.</param> 4740 /// <param name="plotAreaRectAbs">Plotting area position.</param> 4741 /// <param name="areaRectAbs">Chart area position.</param> 4742 /// <param name="labelsSizeEstimate">Estimated size of labels.</param> GetCircularAxisLabelsAutoFitFont( ChartGraphics graph, ArrayList axisList, CircularAxisLabelsStyle labelsStyle, RectangleF plotAreaRectAbs, RectangleF areaRectAbs, float labelsSizeEstimate)4743 internal void GetCircularAxisLabelsAutoFitFont( 4744 ChartGraphics graph, 4745 ArrayList axisList, 4746 CircularAxisLabelsStyle labelsStyle, 4747 RectangleF plotAreaRectAbs, 4748 RectangleF areaRectAbs, 4749 float labelsSizeEstimate) 4750 { 4751 // X axis settings defines if auto-fit font should be calculated 4752 if (!this.IsLabelAutoFit || 4753 this.LabelAutoFitStyle == LabelAutoFitStyles.None || 4754 !this.LabelStyle.Enabled) 4755 { 4756 return; 4757 } 4758 4759 // Set minimum font size 4760 _minLabelFontSize = Math.Min(this.LabelAutoFitMinFontSize, this.LabelAutoFitMaxFontSize); 4761 4762 // Create new auto-fit font 4763 this.autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 4764 this.LabelStyle.Font.FontFamily, 4765 Math.Max(this.LabelAutoFitMaxFontSize, this.LabelAutoFitMinFontSize), 4766 this.LabelStyle.Font.Style, 4767 GraphicsUnit.Point); 4768 4769 // Check if we allowed to increase font size while auto-fitting 4770 if ((this.LabelAutoFitStyle & LabelAutoFitStyles.IncreaseFont) != LabelAutoFitStyles.IncreaseFont) 4771 { 4772 // Use axis labels font as starting point 4773 this.autoLabelFont = this.LabelStyle.Font; 4774 } 4775 4776 // Loop while labels do not fit 4777 bool fitDone = false; 4778 while (!fitDone) 4779 { 4780 //****************************************************** 4781 //** Check if labels fit 4782 //****************************************************** 4783 fitDone = CheckCircularLabelsFit( 4784 graph, 4785 axisList, 4786 labelsStyle, 4787 plotAreaRectAbs, 4788 areaRectAbs, 4789 labelsSizeEstimate); 4790 4791 //****************************************************** 4792 //** Adjust labels text properties to fit 4793 //****************************************************** 4794 if (!fitDone) 4795 { 4796 // Try to reduce font size 4797 if (autoLabelFont.SizeInPoints > _minLabelFontSize && 4798 (this.LabelAutoFitStyle & LabelAutoFitStyles.DecreaseFont) == LabelAutoFitStyles.DecreaseFont) 4799 { 4800 autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 4801 autoLabelFont.FontFamily, 4802 autoLabelFont.SizeInPoints - 1, 4803 autoLabelFont.Style, 4804 GraphicsUnit.Point); 4805 4806 } 4807 4808 // Failed to fit 4809 else 4810 { 4811 // Use last font with no angles 4812 autoLabelAngle = 0; 4813 autoLabelOffset = 0; 4814 fitDone = true; 4815 } 4816 } 4817 } 4818 } 4819 4820 /// <summary> 4821 /// Checks id circular axis labels fits using current auto-fit font. 4822 /// </summary> 4823 /// <param name="graph">Chart graphics object.</param> 4824 /// <param name="axisList">List of sector labels.</param> 4825 /// <param name="labelsStyle">Circular labels style.</param> 4826 /// <param name="plotAreaRectAbs">Plotting area position.</param> 4827 /// <param name="areaRectAbs">Chart area position.</param> 4828 /// <param name="labelsSizeEstimate">Estimated size of labels.</param> 4829 /// <returns>True if labels fit.</returns> CheckCircularLabelsFit( ChartGraphics graph, ArrayList axisList, CircularAxisLabelsStyle labelsStyle, RectangleF plotAreaRectAbs, RectangleF areaRectAbs, float labelsSizeEstimate)4830 internal bool CheckCircularLabelsFit( 4831 ChartGraphics graph, 4832 ArrayList axisList, 4833 CircularAxisLabelsStyle labelsStyle, 4834 RectangleF plotAreaRectAbs, 4835 RectangleF areaRectAbs, 4836 float labelsSizeEstimate) 4837 { 4838 bool labelsFit = true; 4839 4840 // Get absolute center of the area 4841 PointF areaCenterAbs = graph.GetAbsolutePoint(ChartArea.circularCenter); 4842 4843 // Get absolute markers size and spacing 4844 float spacing = graph.GetAbsolutePoint(new PointF(0, this.markSize + Axis.elementSpacing)).Y; 4845 4846 //***************************************************************** 4847 //** Loop through all axis labels 4848 //***************************************************************** 4849 RectangleF prevLabelPosition = RectangleF.Empty; 4850 float prevLabelSideAngle = float.NaN; 4851 foreach (CircularChartAreaAxis axis in axisList) 4852 { 4853 //***************************************************************** 4854 //** Measure label text 4855 //***************************************************************** 4856 SizeF textSize = graph.MeasureString( 4857 axis.Title.Replace("\\n", "\n"), 4858 this.autoLabelFont); 4859 4860 //***************************************************************** 4861 //** Get circular style label position. 4862 //***************************************************************** 4863 if (labelsStyle == CircularAxisLabelsStyle.Circular || 4864 labelsStyle == CircularAxisLabelsStyle.Radial) 4865 { 4866 // Swith text size for the radial style 4867 if (labelsStyle == CircularAxisLabelsStyle.Radial) 4868 { 4869 float tempValue = textSize.Width; 4870 textSize.Width = textSize.Height; 4871 textSize.Height = tempValue; 4872 } 4873 4874 //***************************************************************** 4875 //** Check overlapping with previous label 4876 //***************************************************************** 4877 4878 // Get radius of plot area 4879 float plotAreaRadius = areaCenterAbs.Y - plotAreaRectAbs.Y; 4880 plotAreaRadius -= labelsSizeEstimate; 4881 plotAreaRadius += spacing; 4882 4883 // Calculate angle on the side of the label 4884 float leftSideAngle = (float)(Math.Atan((textSize.Width / 2f) / plotAreaRadius) * 180f / Math.PI); 4885 float rightSideAngle = axis.AxisPosition + leftSideAngle; 4886 leftSideAngle = axis.AxisPosition - leftSideAngle; 4887 4888 // Check if label overlap the previous label 4889 if (!float.IsNaN(prevLabelSideAngle)) 4890 { 4891 if (prevLabelSideAngle > leftSideAngle) 4892 { 4893 // Labels overlap 4894 labelsFit = false; 4895 break; 4896 } 4897 } 4898 4899 // Remember label side angle 4900 prevLabelSideAngle = rightSideAngle - 1; 4901 4902 4903 //***************************************************************** 4904 //** Check if label is inside the Common.Chart area 4905 //***************************************************************** 4906 4907 // Find the most outside point of the label 4908 PointF outsidePoint = new PointF(areaCenterAbs.X, plotAreaRectAbs.Y); 4909 outsidePoint.Y += labelsSizeEstimate; 4910 outsidePoint.Y -= textSize.Height; 4911 outsidePoint.Y -= spacing; 4912 4913 PointF[] rotatedPoint = new PointF[] { outsidePoint }; 4914 Matrix newMatrix = new Matrix(); 4915 newMatrix.RotateAt(axis.AxisPosition, areaCenterAbs); 4916 newMatrix.TransformPoints(rotatedPoint); 4917 4918 // Check if rotated point is inside Common.Chart area 4919 if (!areaRectAbs.Contains(rotatedPoint[0])) 4920 { 4921 // Label is not inside Common.Chart area 4922 labelsFit = false; 4923 break; 4924 } 4925 4926 } 4927 4928 //***************************************************************** 4929 //** Get horizontal style label position. 4930 //***************************************************************** 4931 else if (labelsStyle == CircularAxisLabelsStyle.Horizontal) 4932 { 4933 // Get text angle 4934 float textAngle = axis.AxisPosition; 4935 if (textAngle > 180f) 4936 { 4937 textAngle -= 180f; 4938 } 4939 4940 // Get label rotated position 4941 PointF[] labelPosition = new PointF[] { new PointF(areaCenterAbs.X, plotAreaRectAbs.Y) }; 4942 labelPosition[0].Y += labelsSizeEstimate; 4943 labelPosition[0].Y -= spacing; 4944 Matrix newMatrix = new Matrix(); 4945 newMatrix.RotateAt(textAngle, areaCenterAbs); 4946 newMatrix.TransformPoints(labelPosition); 4947 4948 // Calculate label position 4949 RectangleF curLabelPosition = new RectangleF( 4950 labelPosition[0].X, 4951 labelPosition[0].Y - textSize.Height / 2f, 4952 textSize.Width, 4953 textSize.Height); 4954 if (textAngle < 5f) 4955 { 4956 curLabelPosition.X = labelPosition[0].X - textSize.Width / 2f; 4957 curLabelPosition.Y = labelPosition[0].Y - textSize.Height; 4958 } 4959 if (textAngle > 175f) 4960 { 4961 curLabelPosition.X = labelPosition[0].X - textSize.Width / 2f; 4962 curLabelPosition.Y = labelPosition[0].Y; 4963 } 4964 4965 // Decrease label rectangle 4966 curLabelPosition.Inflate(0f, -curLabelPosition.Height * 0.15f); 4967 4968 // Check if label position goes outside of the Common.Chart area. 4969 if (!areaRectAbs.Contains(curLabelPosition)) 4970 { 4971 // Label is not inside Common.Chart area 4972 labelsFit = false; 4973 break; 4974 } 4975 4976 // Check if label position overlap previous label position. 4977 if (!prevLabelPosition.IsEmpty && curLabelPosition.IntersectsWith(prevLabelPosition)) 4978 { 4979 // Label intersects with previous label 4980 labelsFit = false; 4981 break; 4982 } 4983 4984 // Set previous point position 4985 prevLabelPosition = curLabelPosition; 4986 } 4987 } 4988 4989 return labelsFit; 4990 } 4991 4992 #endregion 4993 4994 #region Axis labels auto-fitting methods 4995 4996 /// <summary> 4997 /// Adjust labels font size at second pass of auto fitting. 4998 /// </summary> 4999 /// <param name="chartGraph">Chart graphics object.</param> 5000 /// <param name="autoPlotPosition">Indicates that inner plot position is automatic.</param> AdjustLabelFontAtSecondPass(ChartGraphics chartGraph, bool autoPlotPosition)5001 internal void AdjustLabelFontAtSecondPass(ChartGraphics chartGraph, bool autoPlotPosition) 5002 { 5003 #if SUBAXES 5004 // Process all sub-axis 5005 if(!ChartArea.Area3DStyle.Enable3D && 5006 !ChartArea.chartAreaIsCurcular) 5007 { 5008 foreach(SubAxis subAxis in this.SubAxes) 5009 { 5010 subAxis.AdjustLabelFontAtSecondPass(chartGraph, autoPlotPosition); 5011 } 5012 } 5013 #endif //SUBAXES 5014 5015 5016 //****************************************************** 5017 //** First try to select the interval that will 5018 //** generate best fit labels. 5019 //****************************************************** 5020 5021 5022 5023 // Make sure the variable interval mode is enabled 5024 if( this.Enabled != AxisEnabled.False && 5025 this.LabelStyle.Enabled && 5026 this.IsVariableLabelCountModeEnabled() ) 5027 { 5028 // Set font for labels fitting 5029 if(this.autoLabelFont == null) 5030 { 5031 this.autoLabelFont = this.LabelStyle.Font; 5032 } 5033 5034 // Reset angle and stagged flag used in the auto-fitting algorithm 5035 if(this.autoLabelAngle < 0) 5036 { 5037 this.autoLabelAngle = this.LabelStyle.Angle; 5038 } 5039 if(this.autoLabelOffset < 0) 5040 { 5041 this.autoLabelOffset = (this.LabelStyle.IsStaggered) ? 1 : 0; 5042 } 5043 5044 // Check labels fit 5045 bool fitDone = CheckLabelsFit( 5046 chartGraph, 5047 this.markSize + this.scrollBarSize + this.titleSize, 5048 autoPlotPosition, 5049 true, 5050 true, 5051 (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? false : true, 5052 (this.AxisPosition == AxisPosition.Left || this.AxisPosition == AxisPosition.Right) ? true : false, 5053 null); 5054 5055 // If there is a problem fitting labels try to reduce number of labels by 5056 // increasing of the interval. 5057 if(!fitDone) 5058 { 5059 // Adjust interval 5060 this.AdjustIntervalToFitLabels(chartGraph, autoPlotPosition, true); 5061 } 5062 } 5063 5064 5065 5066 5067 //****************************************************** 5068 //** If labels auto-fit is on try reducing font size. 5069 //****************************************************** 5070 5071 totlaGroupingLabelsSizeAdjustment = 0f; 5072 if (this.IsLabelAutoFit && 5073 this.LabelAutoFitStyle != LabelAutoFitStyles.None && 5074 this.Enabled != AxisEnabled.False) 5075 { 5076 bool fitDone = false; 5077 5078 if (autoLabelFont == null) 5079 { 5080 autoLabelFont = this.LabelStyle.Font; 5081 } 5082 5083 // Loop while labels do not fit 5084 float oldLabelSecondRowSize = totlaGroupingLabelsSize; 5085 while (!fitDone) 5086 { 5087 //****************************************************** 5088 //** Check if labels fit 5089 //****************************************************** 5090 fitDone = CheckLabelsFit( 5091 chartGraph, 5092 this.markSize + this.scrollBarSize + this.titleSize, 5093 autoPlotPosition, 5094 true, 5095 true); 5096 5097 //****************************************************** 5098 //** Adjust labels text properties to fit 5099 //****************************************************** 5100 if (!fitDone) 5101 { 5102 // Try to reduce font 5103 if (autoLabelFont.SizeInPoints > _minLabelFontSize) 5104 { 5105 // Reduce auto fit font 5106 if (ChartArea != null && ChartArea.IsSameFontSizeForAllAxes) 5107 { 5108 // Same font for all axes 5109 foreach (Axis currentAxis in ChartArea.Axes) 5110 { 5111 if (currentAxis.enabled && currentAxis.IsLabelAutoFit && currentAxis.autoLabelFont != null) 5112 { 5113 currentAxis.autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 5114 currentAxis.autoLabelFont.FontFamily, 5115 autoLabelFont.SizeInPoints - 1, 5116 currentAxis.autoLabelFont.Style, 5117 GraphicsUnit.Point); 5118 } 5119 } 5120 } 5121 else if ((this.LabelAutoFitStyle & LabelAutoFitStyles.DecreaseFont) == LabelAutoFitStyles.DecreaseFont) 5122 { 5123 autoLabelFont = Common.Chart.chartPicture.FontCache.GetFont( 5124 autoLabelFont.FontFamily, 5125 autoLabelFont.SizeInPoints - 1, 5126 autoLabelFont.Style, 5127 GraphicsUnit.Point); 5128 } 5129 else 5130 { 5131 // Failed to fit 5132 fitDone = true; 5133 } 5134 } 5135 else 5136 { 5137 // Failed to fit 5138 fitDone = true; 5139 } 5140 } 5141 } 5142 5143 this.totlaGroupingLabelsSizeAdjustment = oldLabelSecondRowSize - totlaGroupingLabelsSize; 5144 } 5145 } 5146 5147 /// <summary> 5148 /// Check if axis is logarithmic 5149 /// </summary> 5150 /// <param name="yValue">Y value from data</param> 5151 /// <returns>Corected Y value if axis is logarithmic</returns> GetLogValue(double yValue)5152 internal double GetLogValue(double yValue) 5153 { 5154 // Check if axis is logarithmic 5155 if (this.IsLogarithmic) 5156 { 5157 yValue = Math.Log(yValue, this.logarithmBase); 5158 } 5159 5160 return yValue; 5161 } 5162 5163 /// <summary> 5164 /// Checks if labels fit using current auto fit properties 5165 /// </summary> 5166 /// <param name="chartGraph">Chart graphics object.</param> 5167 /// <param name="otherElementsSize">Axis title and marks size.</param> 5168 /// <param name="autoPlotPosition">Indicates auto calculation of plotting area.</param> 5169 /// <param name="checkLabelsFirstRowOnly">Labels fit is checked during the second pass.</param> 5170 /// <param name="secondPass">Indicates second pass of labels fitting.</param> 5171 /// <returns>True if labels fit.</returns> CheckLabelsFit( ChartGraphics chartGraph, float otherElementsSize, bool autoPlotPosition, bool checkLabelsFirstRowOnly, bool secondPass)5172 private bool CheckLabelsFit( 5173 ChartGraphics chartGraph, 5174 float otherElementsSize, 5175 bool autoPlotPosition, 5176 bool checkLabelsFirstRowOnly, 5177 bool secondPass) 5178 { 5179 return this.CheckLabelsFit( 5180 chartGraph, 5181 otherElementsSize, 5182 autoPlotPosition, 5183 checkLabelsFirstRowOnly, 5184 secondPass, 5185 true, 5186 true, 5187 null); 5188 } 5189 5190 /// <summary> 5191 /// Checks if labels fit using current auto fit properties 5192 /// </summary> 5193 /// <param name="chartGraph">Chart graphics object.</param> 5194 /// <param name="otherElementsSize">Axis title and marks size.</param> 5195 /// <param name="autoPlotPosition">Indicates auto calculation of plotting area.</param> 5196 /// <param name="checkLabelsFirstRowOnly">Labels fit is checked during the second pass.</param> 5197 /// <param name="secondPass">Indicates second pass of labels fitting.</param> 5198 /// <param name="checkWidth">True if width should be checked.</param> 5199 /// <param name="checkHeight">True if height should be checked.</param> 5200 /// <param name="labelPositions">Returns an array of label positions.</param> 5201 /// <returns>True if labels fit.</returns> CheckLabelsFit( ChartGraphics chartGraph, float otherElementsSize, bool autoPlotPosition, bool checkLabelsFirstRowOnly, bool secondPass, bool checkWidth, bool checkHeight, ArrayList labelPositions)5202 private bool CheckLabelsFit( 5203 ChartGraphics chartGraph, 5204 float otherElementsSize, 5205 bool autoPlotPosition, 5206 bool checkLabelsFirstRowOnly, 5207 bool secondPass, 5208 bool checkWidth, 5209 bool checkHeight, 5210 ArrayList labelPositions) 5211 { 5212 // Reset list of label positions 5213 if (labelPositions != null) 5214 { 5215 labelPositions.Clear(); 5216 } 5217 5218 // Label string drawing format 5219 using (StringFormat format = new StringFormat()) 5220 { 5221 format.FormatFlags |= StringFormatFlags.LineLimit; 5222 format.Trimming = StringTrimming.EllipsisCharacter; 5223 5224 // Initialize all labels position rectangle 5225 RectangleF rect = RectangleF.Empty; 5226 5227 // Calculate max label size 5228 float maxLabelSize = 0; 5229 if (!autoPlotPosition) 5230 { 5231 if (this.GetIsMarksNextToAxis()) 5232 { 5233 if (this.AxisPosition == AxisPosition.Top) 5234 maxLabelSize = (float)GetAxisPosition() - ChartArea.Position.Y; 5235 else if (this.AxisPosition == AxisPosition.Bottom) 5236 maxLabelSize = ChartArea.Position.Bottom - (float)GetAxisPosition(); 5237 if (this.AxisPosition == AxisPosition.Left) 5238 maxLabelSize = (float)GetAxisPosition() - ChartArea.Position.X; 5239 else if (this.AxisPosition == AxisPosition.Right) 5240 maxLabelSize = ChartArea.Position.Right - (float)GetAxisPosition(); 5241 } 5242 else 5243 { 5244 if (this.AxisPosition == AxisPosition.Top) 5245 maxLabelSize = this.PlotAreaPosition.Y - ChartArea.Position.Y; 5246 else if (this.AxisPosition == AxisPosition.Bottom) 5247 maxLabelSize = ChartArea.Position.Bottom - this.PlotAreaPosition.Bottom; 5248 if (this.AxisPosition == AxisPosition.Left) 5249 maxLabelSize = this.PlotAreaPosition.X - ChartArea.Position.X; 5250 else if (this.AxisPosition == AxisPosition.Right) 5251 maxLabelSize = ChartArea.Position.Right - this.PlotAreaPosition.Right; 5252 } 5253 maxLabelSize *= 2F; 5254 } 5255 else 5256 { 5257 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5258 maxLabelSize = ChartArea.Position.Height; 5259 else 5260 maxLabelSize = ChartArea.Position.Width; 5261 } 5262 5263 // Loop through all grouping labels (all except first row) 5264 this.totlaGroupingLabelsSize = 0; 5265 5266 5267 // Get number of groups 5268 int groupLabelLevelCount = GetGroupLabelLevelCount(); 5269 5270 // Check ig grouping labels exist 5271 if (groupLabelLevelCount > 0) 5272 { 5273 groupingLabelSizes = new float[groupLabelLevelCount]; 5274 5275 // Loop through each level of grouping labels 5276 bool fitResult = true; 5277 for (int groupLevelIndex = 1; groupLevelIndex <= groupLabelLevelCount; groupLevelIndex++) 5278 { 5279 groupingLabelSizes[groupLevelIndex - 1] = 0f; 5280 5281 // Loop through all labels in the level 5282 foreach (CustomLabel label in this.CustomLabels) 5283 { 5284 // Skip if label middle point is outside current scaleView 5285 if (label.RowIndex == 0) 5286 { 5287 double middlePoint = (label.FromPosition + label.ToPosition) / 2.0; 5288 if (middlePoint < this.ViewMinimum || middlePoint > this.ViewMaximum) 5289 { 5290 continue; 5291 } 5292 } 5293 5294 if (label.RowIndex == groupLevelIndex) 5295 { 5296 // Calculate label rect 5297 double fromPosition = this.GetLinearPosition(label.FromPosition); 5298 double toPosition = this.GetLinearPosition(label.ToPosition); 5299 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5300 { 5301 rect.Height = (maxLabelSize / 100F) * maxAxisLabelRow2Size / groupLabelLevelCount; 5302 rect.X = (float)Math.Min(fromPosition, toPosition); 5303 rect.Width = (float)Math.Max(fromPosition, toPosition) - rect.X; 5304 } 5305 else 5306 { 5307 rect.Width = (maxLabelSize / 100F) * maxAxisLabelRow2Size / groupLabelLevelCount; 5308 rect.Y = (float)Math.Min(fromPosition, toPosition); 5309 rect.Height = (float)Math.Max(fromPosition, toPosition) - rect.Y; 5310 } 5311 5312 // Measure string 5313 SizeF axisLabelSize = chartGraph.MeasureStringRel(label.Text.Replace("\\n", "\n"), autoLabelFont); 5314 5315 // Add image size 5316 if (label.Image.Length > 0) 5317 { 5318 SizeF imageAbsSize = new SizeF(); 5319 5320 if (this.Common.ImageLoader.GetAdjustedImageSize(label.Image, chartGraph.Graphics, ref imageAbsSize)) 5321 { 5322 SizeF imageRelSize = chartGraph.GetRelativeSize(imageAbsSize); 5323 axisLabelSize.Width += imageRelSize.Width; 5324 axisLabelSize.Height = Math.Max(axisLabelSize.Height, imageRelSize.Height); 5325 } 5326 } 5327 5328 // Add extra spacing for the box marking of the label 5329 if (label.LabelMark == LabelMarkStyle.Box) 5330 { 5331 // Get relative size from pixels and add it to the label size 5332 SizeF spacerSize = chartGraph.GetRelativeSize(new SizeF(4, 4)); 5333 axisLabelSize.Width += spacerSize.Width; 5334 axisLabelSize.Height += spacerSize.Height; 5335 } 5336 5337 // Calculate max height of the second row of labels 5338 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5339 { 5340 groupingLabelSizes[groupLevelIndex - 1] = (float)Math.Max(groupingLabelSizes[groupLevelIndex - 1], axisLabelSize.Height); 5341 } 5342 else 5343 { 5344 axisLabelSize.Width = chartGraph.GetAbsoluteSize(new SizeF(axisLabelSize.Height, axisLabelSize.Height)).Height; 5345 axisLabelSize.Width = chartGraph.GetRelativeSize(new SizeF(axisLabelSize.Width, axisLabelSize.Width)).Width; 5346 groupingLabelSizes[groupLevelIndex - 1] = (float)Math.Max(groupingLabelSizes[groupLevelIndex - 1], axisLabelSize.Width); 5347 } 5348 5349 // Check if string fits 5350 if (Math.Round(axisLabelSize.Width) >= Math.Round(rect.Width) && 5351 checkWidth) 5352 { 5353 fitResult = false; 5354 } 5355 if (Math.Round(axisLabelSize.Height) >= Math.Round(rect.Height) && 5356 checkHeight) 5357 { 5358 fitResult = false; 5359 } 5360 } 5361 } 5362 } 5363 5364 this.totlaGroupingLabelsSize = this.GetGroupLablesToatalSize(); 5365 if (!fitResult && !checkLabelsFirstRowOnly) 5366 { 5367 return false; 5368 } 5369 5370 } 5371 5372 // Loop through all labels in the first row 5373 float angle = autoLabelAngle; 5374 int labelIndex = 0; 5375 foreach (CustomLabel label in this.CustomLabels) 5376 { 5377 // Skip if label middle point is outside current scaleView 5378 if (label.RowIndex == 0) 5379 { 5380 double middlePoint = (label.FromPosition + label.ToPosition) / 2.0; 5381 if (middlePoint < this.ViewMinimum || middlePoint > this.ViewMaximum) 5382 { 5383 continue; 5384 } 5385 } 5386 5387 if (label.RowIndex == 0) 5388 { 5389 5390 // Force which scale segment to use when calculating label position 5391 if (labelPositions != null) 5392 { 5393 this.ScaleSegments.EnforceSegment(this.ScaleSegments.FindScaleSegmentForAxisValue((label.FromPosition + label.ToPosition) / 2.0)); 5394 } 5395 5396 5397 // Set label From and To coordinates 5398 double fromPosition = this.GetLinearPosition(label.FromPosition); 5399 double toPosition = this.GetLinearPosition(label.ToPosition); 5400 5401 5402 // Reset scale segment to use when calculating label position 5403 if (labelPositions != null) 5404 { 5405 this.ScaleSegments.EnforceSegment(null); 5406 } 5407 5408 5409 // Calculate single label position 5410 rect.X = this.PlotAreaPosition.X; 5411 rect.Y = (float)Math.Min(fromPosition, toPosition); 5412 rect.Height = (float)Math.Max(fromPosition, toPosition) - rect.Y; 5413 5414 float maxElementSize = maxAxisElementsSize; 5415 if (maxAxisElementsSize - this.totlaGroupingLabelsSize > 55) 5416 { 5417 maxElementSize = 55 + this.totlaGroupingLabelsSize; 5418 } 5419 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5420 { 5421 rect.Width = (maxLabelSize / 100F) * 5422 (maxElementSize - this.totlaGroupingLabelsSize - otherElementsSize - elementSpacing); 5423 } 5424 else 5425 { 5426 rect.Width = (maxLabelSize / 100F) * 5427 (maxElementSize - this.totlaGroupingLabelsSize - otherElementsSize - elementSpacing); 5428 } 5429 5430 // Adjust label From/To position if labels are displayed with offset 5431 if (autoLabelOffset == 1) 5432 { 5433 rect.Y -= rect.Height / 2F; 5434 rect.Height *= 2F; 5435 rect.Width /= 2F; 5436 } 5437 5438 // If horizontal axis 5439 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5440 { 5441 // Switch rectangle sizes 5442 float val = rect.Height; 5443 rect.Height = rect.Width; 5444 rect.Width = val; 5445 5446 // Set vertical font for measuring 5447 if (angle != 0) 5448 { 5449 format.FormatFlags |= StringFormatFlags.DirectionVertical; 5450 } 5451 } 5452 else 5453 { 5454 // Set vertical font for measuring 5455 if (angle == 90 || angle == -90) 5456 { 5457 angle = 0; 5458 format.FormatFlags |= StringFormatFlags.DirectionVertical; 5459 } 5460 } 5461 5462 // Measure label text size. Add the 'I' character to allow a little bit of spacing between labels. 5463 SizeF axisLabelSize = chartGraph.MeasureStringRel( 5464 label.Text.Replace("\\n", "\n") + "W", 5465 autoLabelFont, 5466 (secondPass) ? rect.Size : ChartArea.Position.ToRectangleF().Size, 5467 format); 5468 5469 // Width and height maybe zeros if rect is too small to fit the text and 5470 // the LineLimit format flag is set. 5471 if (label.Text.Length > 0 && 5472 (axisLabelSize.Width == 0f || 5473 axisLabelSize.Height == 0f)) 5474 { 5475 // Measure string without the LineLimit flag 5476 format.FormatFlags ^= StringFormatFlags.LineLimit; 5477 axisLabelSize = chartGraph.MeasureStringRel( 5478 label.Text.Replace("\\n", "\n"), 5479 autoLabelFont, 5480 (secondPass) ? rect.Size : ChartArea.Position.ToRectangleF().Size, 5481 format); 5482 format.FormatFlags |= StringFormatFlags.LineLimit; 5483 } 5484 5485 5486 // Add image size 5487 if (label.Image.Length > 0) 5488 { 5489 SizeF imageAbsSize = new SizeF(); 5490 5491 if(this.Common.ImageLoader.GetAdjustedImageSize(label.Image, chartGraph.Graphics, ref imageAbsSize)) 5492 { 5493 SizeF imageRelSize = chartGraph.GetRelativeSize(imageAbsSize); 5494 if ((format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical) 5495 { 5496 axisLabelSize.Height += imageRelSize.Height; 5497 axisLabelSize.Width = Math.Max(axisLabelSize.Width, imageRelSize.Width); 5498 } 5499 else 5500 { 5501 axisLabelSize.Width += imageRelSize.Width; 5502 axisLabelSize.Height = Math.Max(axisLabelSize.Height, imageRelSize.Height); 5503 } 5504 } 5505 } 5506 5507 // Add extra spacing for the box marking of the label 5508 if (label.LabelMark == LabelMarkStyle.Box) 5509 { 5510 // Get relative size from pixels and add it to the label size 5511 SizeF spacerSize = chartGraph.GetRelativeSize(new SizeF(4, 4)); 5512 axisLabelSize.Width += spacerSize.Width; 5513 axisLabelSize.Height += spacerSize.Height; 5514 } 5515 5516 5517 // Calculate size using label angle 5518 float width = axisLabelSize.Width; 5519 float height = axisLabelSize.Height; 5520 if (angle != 0) 5521 { 5522 // Decrease label rectangle width by 3% 5523 rect.Width *= 0.97f; 5524 5525 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5526 { 5527 width = (float)Math.Cos((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Height; 5528 width += (float)Math.Sin((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Width; 5529 5530 height = (float)Math.Sin((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Height; 5531 height += (float)Math.Cos((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Width; 5532 } 5533 else 5534 { 5535 width = (float)Math.Cos((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Width; 5536 width += (float)Math.Sin((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Height; 5537 5538 height = (float)Math.Sin((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Width; 5539 height += (float)Math.Cos((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Height; 5540 } 5541 } 5542 5543 // Save label position 5544 if (labelPositions != null) 5545 { 5546 RectangleF labelPosition = rect; 5547 if (angle == 0F || angle == 90F || angle == -90F) 5548 { 5549 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5550 { 5551 labelPosition.X = labelPosition.X + labelPosition.Width / 2f - width / 2f; 5552 labelPosition.Width = width; 5553 } 5554 else 5555 { 5556 labelPosition.Y = labelPosition.Y + labelPosition.Height / 2f - height / 2f; 5557 labelPosition.Height = height; 5558 } 5559 } 5560 labelPositions.Add(labelPosition); 5561 } 5562 5563 // Check if string fits 5564 if (angle == 0F) 5565 { 5566 if (width >= rect.Width && checkWidth) 5567 { 5568 return false; 5569 } 5570 if (height >= rect.Height && checkHeight) 5571 { 5572 return false; 5573 } 5574 } 5575 if (angle == 90F || angle == -90F) 5576 { 5577 if (width >= rect.Width && checkWidth) 5578 { 5579 return false; 5580 } 5581 if (height >= rect.Height && checkHeight) 5582 { 5583 return false; 5584 } 5585 } 5586 else 5587 { 5588 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5589 { 5590 if (width >= rect.Width * 2F && checkWidth) 5591 { 5592 return false; 5593 } 5594 if (height >= rect.Height * 2F && checkHeight) 5595 { 5596 return false; 5597 } 5598 } 5599 else 5600 { 5601 if (width >= rect.Width * 2F && checkWidth) 5602 { 5603 return false; 5604 } 5605 if (height >= rect.Height * 2F && checkHeight) 5606 { 5607 return false; 5608 } 5609 } 5610 } 5611 5612 ++labelIndex; 5613 } 5614 } 5615 } 5616 5617 return true; 5618 } 5619 5620 /// <summary> 5621 /// Calculates the best size for labels area. 5622 /// </summary> 5623 /// <param name="chartGraph">Chart graphics object.</param> 5624 /// <param name="maxLabelSize">Maximum labels area size.</param> 5625 /// <param name="resultSize">Label size without angle = 0.</param> GetRequiredLabelSize(ChartGraphics chartGraph, float maxLabelSize, out float resultSize)5626 private float GetRequiredLabelSize(ChartGraphics chartGraph, float maxLabelSize, out float resultSize) 5627 { 5628 float resultRotatedSize = 0F; 5629 resultSize = 0F; 5630 float angle = (autoLabelAngle < -90) ? this.LabelStyle.Angle : autoLabelAngle; 5631 labelNearOffset = float.MaxValue; 5632 labelFarOffset = float.MinValue; 5633 5634 // Label string drawing format 5635 using (StringFormat format = new StringFormat()) 5636 { 5637 format.FormatFlags |= StringFormatFlags.LineLimit; 5638 format.Trimming = StringTrimming.EllipsisCharacter; 5639 5640 // Initialize all labels position rectangle 5641 RectangleF rectLabels = ChartArea.Position.ToRectangleF(); 5642 5643 // Loop through all labels in the first row 5644 foreach (CustomLabel label in this.CustomLabels) 5645 { 5646 // Skip if label middle point is outside current scaleView 5647 if (label.RowIndex == 0) 5648 { 5649 decimal middlePoint = (decimal)(label.FromPosition + label.ToPosition) / (decimal)2.0; 5650 if (middlePoint < (decimal)this.ViewMinimum || middlePoint > (decimal)this.ViewMaximum) 5651 { 5652 continue; 5653 } 5654 } 5655 if (label.RowIndex == 0) 5656 { 5657 // Calculate single label position 5658 RectangleF rect = rectLabels; 5659 rect.Width = maxLabelSize; 5660 5661 // Set label From and To coordinates 5662 double fromPosition = this.GetLinearPosition(label.FromPosition); 5663 double toPosition = this.GetLinearPosition(label.ToPosition); 5664 rect.Y = (float)Math.Min(fromPosition, toPosition); 5665 rect.Height = (float)Math.Max(fromPosition, toPosition) - rect.Y; 5666 5667 // Adjust label From/To position if labels are displayed with offset 5668 if ((autoLabelOffset == -1) ? this.LabelStyle.IsStaggered : (autoLabelOffset == 1)) 5669 { 5670 rect.Y -= rect.Height / 2F; 5671 rect.Height *= 2F; 5672 } 5673 5674 // If horizontal axis 5675 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5676 { 5677 // Switch rectangle sizes 5678 float val = rect.Height; 5679 rect.Height = rect.Width; 5680 rect.Width = val; 5681 5682 // Set vertical font for measuring 5683 if (angle != 0) 5684 { 5685 format.FormatFlags |= StringFormatFlags.DirectionVertical; 5686 } 5687 } 5688 else 5689 { 5690 // Set vertical font for measuring 5691 if (angle == 90 || angle == -90) 5692 { 5693 angle = 0; 5694 format.FormatFlags |= StringFormatFlags.DirectionVertical; 5695 } 5696 } 5697 5698 // Measure label text size 5699 rect.Width = (float)Math.Ceiling(rect.Width); 5700 rect.Height = (float)Math.Ceiling(rect.Height); 5701 SizeF axisLabelSize = chartGraph.MeasureStringRel(label.Text.Replace("\\n", "\n"), 5702 (autoLabelFont != null) ? autoLabelFont : this.LabelStyle.Font, 5703 rect.Size, 5704 format); 5705 5706 // Width and height maybe zeros if rect is too small to fit the text and 5707 // the LineLimit format flag is set. 5708 if (axisLabelSize.Width == 0f || axisLabelSize.Height == 0f) 5709 { 5710 // Measure string without the LineLimit flag 5711 format.FormatFlags ^= StringFormatFlags.LineLimit; 5712 axisLabelSize = chartGraph.MeasureStringRel(label.Text.Replace("\\n", "\n"), 5713 (autoLabelFont != null) ? autoLabelFont : this.LabelStyle.Font, 5714 rect.Size, 5715 format); 5716 format.FormatFlags |= StringFormatFlags.LineLimit; 5717 } 5718 5719 5720 // Add image size 5721 if (label.Image.Length > 0) 5722 { 5723 SizeF imageAbsSize = new SizeF(); 5724 5725 if (this.Common.ImageLoader.GetAdjustedImageSize(label.Image, chartGraph.Graphics, ref imageAbsSize)) 5726 { 5727 SizeF imageRelSize = chartGraph.GetRelativeSize(imageAbsSize); 5728 5729 if ((format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical) 5730 { 5731 axisLabelSize.Height += imageRelSize.Height; 5732 axisLabelSize.Width = Math.Max(axisLabelSize.Width, imageRelSize.Width); 5733 } 5734 else 5735 { 5736 axisLabelSize.Width += imageRelSize.Width; 5737 axisLabelSize.Height = Math.Max(axisLabelSize.Height, imageRelSize.Height); 5738 } 5739 } 5740 } 5741 5742 // Add extra spacing for the box marking of the label 5743 if (label.LabelMark == LabelMarkStyle.Box) 5744 { 5745 // Get relative size from pixels and add it to the label size 5746 SizeF spacerSize = chartGraph.GetRelativeSize(new SizeF(4, 4)); 5747 axisLabelSize.Width += spacerSize.Width; 5748 axisLabelSize.Height += spacerSize.Height; 5749 } 5750 5751 5752 // Calculate size using label angle 5753 float width = axisLabelSize.Width; 5754 float height = axisLabelSize.Height; 5755 if (angle != 0) 5756 { 5757 width = (float)Math.Cos((90 - Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Width; 5758 width += (float)Math.Cos((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Height; 5759 5760 height = (float)Math.Sin((Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Height; 5761 height += (float)Math.Sin((90 - Math.Abs(angle)) / 180F * Math.PI) * axisLabelSize.Width; 5762 } 5763 5764 width = (float)Math.Ceiling(width) * 1.05f; 5765 height = (float)Math.Ceiling(height) * 1.05f; 5766 axisLabelSize.Width = (float)Math.Ceiling(axisLabelSize.Width) * 1.05f; 5767 axisLabelSize.Height = (float)Math.Ceiling(axisLabelSize.Height) * 1.05f; 5768 5769 5770 // If axis is horizontal 5771 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5772 { 5773 if (angle == 90 || angle == -90 || angle == 0) 5774 { 5775 resultSize = Math.Max(resultSize, axisLabelSize.Height); 5776 resultRotatedSize = Math.Max(resultRotatedSize, axisLabelSize.Height); 5777 5778 // Calculate the over hang of labels on the side 5779 labelNearOffset = (float)Math.Min(labelNearOffset, (fromPosition + toPosition) / 2f - axisLabelSize.Width / 2f); 5780 labelFarOffset = (float)Math.Max(labelFarOffset, (fromPosition + toPosition) / 2f + axisLabelSize.Width / 2f); 5781 5782 } 5783 else 5784 { 5785 resultSize = Math.Max(resultSize, axisLabelSize.Height); 5786 resultRotatedSize = Math.Max(resultRotatedSize, height); 5787 5788 // Calculate the over hang of labels on the side 5789 if (angle > 0) 5790 { 5791 labelFarOffset = (float)Math.Max(labelFarOffset, (fromPosition + toPosition) / 2f + width * 1.1f); 5792 } 5793 else 5794 { 5795 labelNearOffset = (float)Math.Min(labelNearOffset, (fromPosition + toPosition) / 2f - width * 1.1f); 5796 } 5797 } 5798 } 5799 // If axis is vertical 5800 else 5801 { 5802 if (angle == 90 || angle == -90 || angle == 0) 5803 { 5804 resultSize = Math.Max(resultSize, axisLabelSize.Width); 5805 resultRotatedSize = Math.Max(resultRotatedSize, axisLabelSize.Width); 5806 5807 // Calculate the over hang of labels on the side 5808 labelNearOffset = (float)Math.Min(labelNearOffset, (fromPosition + toPosition) / 2f - axisLabelSize.Height / 2f); 5809 labelFarOffset = (float)Math.Max(labelFarOffset, (fromPosition + toPosition) / 2f + axisLabelSize.Height / 2f); 5810 } 5811 else 5812 { 5813 resultSize = Math.Max(resultSize, axisLabelSize.Width); 5814 resultRotatedSize = Math.Max(resultRotatedSize, width); 5815 5816 // Calculate the over hang of labels on the side 5817 if (angle > 0) 5818 { 5819 labelFarOffset = (float)Math.Max(labelFarOffset, (fromPosition + toPosition) / 2f + height * 1.1f); 5820 } 5821 else 5822 { 5823 labelNearOffset = (float)Math.Min(labelNearOffset, (fromPosition + toPosition) / 2f - height * 1.1f); 5824 } 5825 } 5826 } 5827 5828 // Check if we exceed the maximum value 5829 if (resultSize > maxLabelSize) 5830 { 5831 resultSize = maxLabelSize; 5832 } 5833 } 5834 } 5835 } 5836 5837 // Adjust results if labels are displayed with offset 5838 if ((autoLabelOffset == -1) ? this.LabelStyle.IsStaggered : (autoLabelOffset == 1)) 5839 { 5840 resultSize *= 2F; 5841 resultRotatedSize *= 2F; 5842 5843 // Check if we exceed the maximum value 5844 if (resultSize > maxLabelSize) 5845 { 5846 resultSize = maxLabelSize; 5847 resultRotatedSize = maxLabelSize; 5848 } 5849 } 5850 5851 // Adjust labels size for the 3D Common.Chart 5852 if (ChartArea.Area3DStyle.Enable3D && !ChartArea.chartAreaIsCurcular) 5853 { 5854 // Increase labels size 5855 resultSize *= 1.1f; 5856 resultRotatedSize *= 1.1f; 5857 } 5858 5859 return resultRotatedSize; 5860 } 5861 5862 /// <summary> 5863 /// Gets total size of all grouping labels. 5864 /// </summary> 5865 /// <returns>Total size of all grouping labels.</returns> GetGroupLablesToatalSize()5866 internal float GetGroupLablesToatalSize() 5867 { 5868 float size = 0f; 5869 if (this.groupingLabelSizes != null && this.groupingLabelSizes.Length > 0) 5870 { 5871 foreach (float val in this.groupingLabelSizes) 5872 { 5873 size += val; 5874 } 5875 } 5876 5877 return size; 5878 } 5879 5880 /// <summary> 5881 /// Gets number of levels of the grouping labels. 5882 /// </summary> 5883 /// <returns>Number of levels of the grouping labels.</returns> GetGroupLabelLevelCount()5884 internal int GetGroupLabelLevelCount() 5885 { 5886 int groupLabelLevel = 0; 5887 foreach (CustomLabel label in this.CustomLabels) 5888 { 5889 if (label.RowIndex > 0) 5890 { 5891 groupLabelLevel = Math.Max(groupLabelLevel, label.RowIndex); 5892 } 5893 } 5894 5895 return groupLabelLevel; 5896 } 5897 5898 /// <summary> 5899 /// Calculates the best size for axis labels for all rows except first one (grouping labels). 5900 /// </summary> 5901 /// <param name="chartGraph">Chart graphics object.</param> 5902 /// <param name="maxLabelSize">Maximum labels area size.</param> 5903 /// <returns>Array of grouping label sizes for each level.</returns> GetRequiredGroupLabelSize(ChartGraphics chartGraph, float maxLabelSize)5904 private float[] GetRequiredGroupLabelSize(ChartGraphics chartGraph, float maxLabelSize) 5905 { 5906 float[] resultSize = null; 5907 5908 // Get number of groups 5909 int groupLabelLevelCount = GetGroupLabelLevelCount(); 5910 5911 // Check ig grouping labels exist 5912 if (groupLabelLevelCount > 0) 5913 { 5914 // Create result array 5915 resultSize = new float[groupLabelLevelCount]; 5916 5917 // Loop through each level of grouping labels 5918 for (int groupLevelIndex = 1; groupLevelIndex <= groupLabelLevelCount; groupLevelIndex++) 5919 { 5920 resultSize[groupLevelIndex - 1] = 0f; 5921 5922 // Loop through all labels in the level 5923 foreach (CustomLabel label in this.CustomLabels) 5924 { 5925 // Skip if label middle point is outside current scaleView 5926 if (label.RowIndex == 0) 5927 { 5928 double middlePoint = (label.FromPosition + label.ToPosition) / 2.0; 5929 if (middlePoint < this.ViewMinimum || middlePoint > this.ViewMaximum) 5930 { 5931 continue; 5932 } 5933 } 5934 5935 if (label.RowIndex == groupLevelIndex) 5936 { 5937 // Measure label text size 5938 SizeF axisLabelSize = chartGraph.MeasureStringRel(label.Text.Replace("\\n", "\n"), (autoLabelFont != null) ? autoLabelFont : this.LabelStyle.Font); 5939 axisLabelSize.Width = (float)Math.Ceiling(axisLabelSize.Width); 5940 axisLabelSize.Height = (float)Math.Ceiling(axisLabelSize.Height); 5941 5942 5943 // Add image size 5944 if(label.Image.Length > 0) 5945 { 5946 SizeF imageAbsSize = new SizeF(); 5947 5948 if(this.Common.ImageLoader.GetAdjustedImageSize(label.Image, chartGraph.Graphics, ref imageAbsSize)) 5949 { 5950 SizeF imageRelSize = chartGraph.GetRelativeSize(imageAbsSize); 5951 axisLabelSize.Width += imageRelSize.Width; 5952 axisLabelSize.Height = Math.Max(axisLabelSize.Height, imageRelSize.Height); 5953 } 5954 } 5955 5956 // Add extra spacing for the box marking of the label 5957 if(label.LabelMark == LabelMarkStyle.Box) 5958 { 5959 // Get relative size from pixels and add it to the label size 5960 SizeF spacerSize = chartGraph.GetRelativeSize(new SizeF(4, 4)); 5961 axisLabelSize.Width += spacerSize.Width; 5962 axisLabelSize.Height += spacerSize.Height; 5963 } 5964 5965 5966 5967 // If axis is horizontal 5968 if (this.AxisPosition == AxisPosition.Bottom || this.AxisPosition == AxisPosition.Top) 5969 { 5970 resultSize[groupLevelIndex - 1] = Math.Max(resultSize[groupLevelIndex - 1], axisLabelSize.Height); 5971 } 5972 // If axis is vertical 5973 else 5974 { 5975 axisLabelSize.Width = chartGraph.GetAbsoluteSize(new SizeF(axisLabelSize.Height, axisLabelSize.Height)).Height; 5976 axisLabelSize.Width = chartGraph.GetRelativeSize(new SizeF(axisLabelSize.Width, axisLabelSize.Width)).Width; 5977 resultSize[groupLevelIndex - 1] = Math.Max(resultSize[groupLevelIndex - 1], axisLabelSize.Width); 5978 } 5979 5980 // Check if we exceed the maximum value 5981 if (resultSize[groupLevelIndex - 1] > maxLabelSize / groupLabelLevelCount) 5982 { 5983 // NOTE: Group Labels size limitations are removed !!! 5984 // resultSize[groupLevelIndex - 1] = maxLabelSize / groupLabelLevelCount; 5985 // break; 5986 } 5987 } 5988 } 5989 } 5990 } 5991 5992 return resultSize; 5993 } 5994 5995 #endregion 5996 5997 #region Axis helper methods 5998 5999 6000 /// <summary> 6001 /// Gets main or sub axis associated with this axis. 6002 /// </summary> 6003 /// <param name="subAxisName">Sub axis name or empty string to get the main axis.</param> 6004 /// <returns>Main or sub axis of the main axis.</returns> 6005 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "subAxisName")] GetSubAxis(string subAxisName)6006 internal Axis GetSubAxis(string subAxisName) 6007 { 6008 #if SUBAXES 6009 if(!this.IsSubAxis && subAxisName.Length > 0) 6010 { 6011 SubAxis subAxis = this.SubAxes.FindByName(subAxisName); 6012 if(subAxis == null) 6013 { 6014 throw(new InvalidOperationException( SR.ExceptionSubAxisNameNotFoundShort( subAxisName ))); 6015 } 6016 return subAxis; 6017 } 6018 #endif // SUBAXES 6019 return this; 6020 } 6021 6022 /// <summary> 6023 /// Checks if axis marks should be next to the axis 6024 /// </summary> 6025 /// <returns>true if marks are next to axis.</returns> GetIsMarksNextToAxis()6026 internal bool GetIsMarksNextToAxis() 6027 { 6028 if (ChartArea != null && ChartArea.chartAreaIsCurcular) 6029 { 6030 return true; 6031 } 6032 return this.IsMarksNextToAxis; 6033 } 6034 6035 /// <summary> 6036 /// Gets axis auto interval type. 6037 /// </summary> 6038 /// <returns>Axis interval type.</returns> GetAxisIntervalType()6039 internal DateTimeIntervalType GetAxisIntervalType() 6040 { 6041 if(InternalIntervalType == DateTimeIntervalType.Auto) 6042 { 6043 if(GetAxisValuesType() == ChartValueType.DateTime || 6044 GetAxisValuesType() == ChartValueType.Date || 6045 GetAxisValuesType() == ChartValueType.Time || 6046 GetAxisValuesType() == ChartValueType.DateTimeOffset) 6047 { 6048 return DateTimeIntervalType.Years; 6049 } 6050 6051 return DateTimeIntervalType.Number; 6052 } 6053 6054 return InternalIntervalType; 6055 } 6056 6057 /// <summary> 6058 /// Gets axis values type depending on the series attached 6059 /// </summary> 6060 /// <returns>Axis values type.</returns> GetAxisValuesType()6061 internal ChartValueType GetAxisValuesType() 6062 { 6063 ChartValueType type = ChartValueType.Double; 6064 6065 // Check all series in this Common.Chart area attached to this axis 6066 if (this.Common != null && this.Common.DataManager.Series != null && ChartArea != null) 6067 { 6068 foreach (Series series in this.Common.DataManager.Series) 6069 { 6070 bool seriesAttached = false; 6071 6072 // Check series name 6073 if (series.ChartArea == ChartArea.Name && series.IsVisible()) 6074 { 6075 // Check if axis type of series match 6076 if (this.axisType == AxisName.X && series.XAxisType == AxisType.Primary) 6077 { 6078 #if SUBAXES 6079 if(((Axis)this).SubAxisName == series.XSubAxisName) 6080 #endif // SUBAXES 6081 { 6082 seriesAttached = true; 6083 } 6084 } 6085 else if (this.axisType == AxisName.X2 && series.XAxisType == AxisType.Secondary) 6086 { 6087 #if SUBAXES 6088 if(((Axis)this).SubAxisName == series.XSubAxisName) 6089 #endif // SUBAXES 6090 { 6091 seriesAttached = true; 6092 } 6093 } 6094 else if (this.axisType == AxisName.Y && series.YAxisType == AxisType.Primary) 6095 { 6096 #if SUBAXES 6097 if(((Axis)this).SubAxisName == series.YSubAxisName) 6098 #endif // SUBAXES 6099 { 6100 seriesAttached = true; 6101 } 6102 } 6103 else if (this.axisType == AxisName.Y2 && series.YAxisType == AxisType.Secondary) 6104 { 6105 #if SUBAXES 6106 if(((Axis)this).SubAxisName == series.YSubAxisName) 6107 #endif // SUBAXES 6108 { 6109 seriesAttached = true; 6110 } 6111 } 6112 } 6113 6114 // If series attached to this axes 6115 if (seriesAttached) 6116 { 6117 if (this.axisType == AxisName.X || this.axisType == AxisName.X2) 6118 { 6119 type = series.XValueType; 6120 } 6121 else if (this.axisType == AxisName.Y || this.axisType == AxisName.Y2) 6122 { 6123 type = series.YValueType; 6124 } 6125 break; 6126 } 6127 } 6128 } 6129 return type; 6130 } 6131 6132 /// <summary> 6133 /// Returns Arrow size 6134 /// </summary> 6135 /// <param name="arrowOrientation">Return arrow orientation.</param> 6136 /// <returns>Size of arrow</returns> GetArrowSize(out ArrowOrientation arrowOrientation)6137 internal SizeF GetArrowSize(out ArrowOrientation arrowOrientation) 6138 { 6139 Axis opositeAxis; 6140 double size; 6141 double sizeOpposite; 6142 arrowOrientation = ArrowOrientation.Top; 6143 6144 // Set the position of axis 6145 switch (AxisPosition) 6146 { 6147 case AxisPosition.Left: 6148 6149 if (isReversed) 6150 arrowOrientation = ArrowOrientation.Bottom; 6151 else 6152 arrowOrientation = ArrowOrientation.Top; 6153 6154 break; 6155 case AxisPosition.Right: 6156 6157 if (isReversed) 6158 arrowOrientation = ArrowOrientation.Bottom; 6159 else 6160 arrowOrientation = ArrowOrientation.Top; 6161 6162 break; 6163 case AxisPosition.Bottom: 6164 6165 if (isReversed) 6166 arrowOrientation = ArrowOrientation.Left; 6167 else 6168 arrowOrientation = ArrowOrientation.Right; 6169 6170 break; 6171 case AxisPosition.Top: 6172 6173 if (isReversed) 6174 arrowOrientation = ArrowOrientation.Left; 6175 else 6176 arrowOrientation = ArrowOrientation.Right; 6177 6178 break; 6179 } 6180 6181 // Opposite axis. Arrow uses this axis to find 6182 // a shift from Common.Chart area border. This shift 6183 // depend on Tick mark size. 6184 switch (arrowOrientation) 6185 { 6186 case ArrowOrientation.Left: 6187 opositeAxis = ChartArea.AxisX; 6188 break; 6189 case ArrowOrientation.Right: 6190 opositeAxis = ChartArea.AxisX2; 6191 break; 6192 case ArrowOrientation.Top: 6193 opositeAxis = ChartArea.AxisY2; 6194 break; 6195 case ArrowOrientation.Bottom: 6196 opositeAxis = ChartArea.AxisY; 6197 break; 6198 default: 6199 opositeAxis = ChartArea.AxisX; 6200 break; 6201 } 6202 6203 // Arrow size has to have the same shape when width and height 6204 // are changed. When the picture is resized, width of the Common.Chart 6205 // picture is used only for arrow size. 6206 if (arrowOrientation == ArrowOrientation.Top || arrowOrientation == ArrowOrientation.Bottom) 6207 { 6208 size = _lineWidth; 6209 sizeOpposite = (double)(_lineWidth) * Common.Width / Common.Height; 6210 } 6211 else 6212 { 6213 size = (double)(_lineWidth) * Common.Width / Common.Height; 6214 sizeOpposite = _lineWidth; 6215 } 6216 6217 // Arrow is sharp triangle 6218 if (_arrowStyle == AxisArrowStyle.SharpTriangle) 6219 { 6220 // Arrow direction is vertical 6221 if (arrowOrientation == ArrowOrientation.Top || arrowOrientation == ArrowOrientation.Bottom) 6222 return new SizeF((float)(size * 2), (float)(opositeAxis.MajorTickMark.Size + sizeOpposite * 4)); 6223 else 6224 // Arrow direction is horizontal 6225 return new SizeF((float)(opositeAxis.MajorTickMark.Size + sizeOpposite * 4), (float)(size * 2)); 6226 } 6227 // There is no arrow 6228 else if (_arrowStyle == AxisArrowStyle.None) 6229 return new SizeF(0, 0); 6230 else// Arrow is triangle or line type 6231 { 6232 // Arrow direction is vertical 6233 if (arrowOrientation == ArrowOrientation.Top || arrowOrientation == ArrowOrientation.Bottom) 6234 return new SizeF((float)(size * 2), (float)(opositeAxis.MajorTickMark.Size + sizeOpposite * 2)); 6235 else 6236 // Arrow direction is horizontal 6237 return new SizeF((float)(opositeAxis.MajorTickMark.Size + sizeOpposite * 2), (float)(size * 2)); 6238 } 6239 } 6240 6241 6242 /// <summary> 6243 /// Checks if arrow with specified orientation will require space 6244 /// in axis with specified position 6245 /// </summary> 6246 /// <param name="arrowOrientation">Arrow orientation.</param> 6247 /// <param name="axisPosition">Axis position.</param> 6248 /// <returns>True if arrow will be drawn in axis space</returns> IsArrowInAxis(ArrowOrientation arrowOrientation, AxisPosition axisPosition)6249 private bool IsArrowInAxis(ArrowOrientation arrowOrientation, AxisPosition axisPosition) 6250 { 6251 if (axisPosition == AxisPosition.Top && arrowOrientation == ArrowOrientation.Top) 6252 return true; 6253 else if (axisPosition == AxisPosition.Bottom && arrowOrientation == ArrowOrientation.Bottom) 6254 return true; 6255 if (axisPosition == AxisPosition.Left && arrowOrientation == ArrowOrientation.Left) 6256 return true; 6257 else if (axisPosition == AxisPosition.Right && arrowOrientation == ArrowOrientation.Right) 6258 return true; 6259 6260 return false; 6261 } 6262 6263 6264 /// <summary> 6265 /// This function converts real Interval to 6266 /// absolute Interval 6267 /// </summary> 6268 /// <param name="realInterval">A interval represented as double value</param> 6269 /// <returns>A interval represented in pixels</returns> GetPixelInterval(double realInterval)6270 internal float GetPixelInterval(double realInterval) 6271 { 6272 double chartAreaSize; 6273 6274 // The Chart area pixel size as double 6275 if (AxisPosition == AxisPosition.Top || AxisPosition == AxisPosition.Bottom) 6276 { 6277 chartAreaSize = PlotAreaPosition.Right - PlotAreaPosition.X; 6278 } 6279 else 6280 { 6281 chartAreaSize = PlotAreaPosition.Bottom - PlotAreaPosition.Y; 6282 } 6283 6284 // Avoid division by zero. 6285 if (ViewMaximum - ViewMinimum == 0) 6286 { 6287 return (float)(chartAreaSize / realInterval); 6288 } 6289 6290 // The interval integer 6291 return (float)(chartAreaSize / (ViewMaximum - ViewMinimum) * realInterval); 6292 } 6293 6294 /// <summary> 6295 /// Find if axis is on the edge of the Common.Chart plot area 6296 /// </summary> 6297 internal bool IsAxisOnAreaEdge 6298 { 6299 get 6300 { 6301 double edgePosition = 0; 6302 if (this.AxisPosition == AxisPosition.Bottom) 6303 { 6304 edgePosition = PlotAreaPosition.Bottom; 6305 } 6306 else if (this.AxisPosition == AxisPosition.Left) 6307 { 6308 edgePosition = PlotAreaPosition.X; 6309 } 6310 else if (this.AxisPosition == AxisPosition.Right) 6311 { 6312 edgePosition = PlotAreaPosition.Right; 6313 } 6314 else if (this.AxisPosition == AxisPosition.Top) 6315 { 6316 edgePosition = PlotAreaPosition.Y; 6317 } 6318 6319 // DT Fix : problems with values on edge ~0.0005 6320 if (Math.Abs(GetAxisPosition() - edgePosition) < 0.0015) 6321 { 6322 return true; 6323 } 6324 6325 return false; 6326 } 6327 } 6328 6329 /// <summary> 6330 /// Find axis position using crossing value. 6331 /// </summary> 6332 /// <returns>Relative position</returns> GetAxisPosition()6333 internal double GetAxisPosition() 6334 { 6335 return GetAxisPosition(false); 6336 } 6337 6338 /// <summary> 6339 /// Find axis position using crossing value. 6340 /// </summary> 6341 /// <param name="ignoreCrossing">Axis crossing should be ignored.</param> 6342 /// <returns>Relative position</returns> GetAxisPosition(bool ignoreCrossing)6343 virtual internal double GetAxisPosition(bool ignoreCrossing) 6344 { 6345 Axis axisOpposite = GetOppositeAxis(); 6346 6347 // Get axis position for circular Common.Chart area 6348 if (ChartArea != null && ChartArea.chartAreaIsCurcular) 6349 { 6350 return PlotAreaPosition.X + PlotAreaPosition.Width / 2f; 6351 } 6352 6353 // Axis is not connected with any series. There is no maximum and minimum 6354 if (axisOpposite.maximum == axisOpposite.minimum || 6355 double.IsNaN(axisOpposite.maximum) || 6356 double.IsNaN(axisOpposite.minimum) || 6357 maximum == minimum || 6358 double.IsNaN(maximum) || 6359 double.IsNaN(minimum)) 6360 { 6361 switch (AxisPosition) 6362 { 6363 case AxisPosition.Top: 6364 return PlotAreaPosition.Y; 6365 case AxisPosition.Bottom: 6366 return PlotAreaPosition.Bottom; 6367 case AxisPosition.Right: 6368 return PlotAreaPosition.Right; 6369 case AxisPosition.Left: 6370 return PlotAreaPosition.X; 6371 } 6372 } 6373 6374 // Auto crossing enabled 6375 if (Double.IsNaN(axisOpposite.crossing) || ignoreCrossing) 6376 { 6377 // Primary 6378 if (axisType == AxisName.X || axisType == AxisName.Y) 6379 return axisOpposite.GetLinearPosition(axisOpposite.ViewMinimum); 6380 else // Secondary 6381 return axisOpposite.GetLinearPosition(axisOpposite.ViewMaximum); 6382 } 6383 else // Auto crossing disabled 6384 { 6385 axisOpposite.crossing = axisOpposite.tempCrossing; 6386 6387 if (axisOpposite.crossing < axisOpposite.ViewMinimum) 6388 { 6389 axisOpposite.crossing = axisOpposite.ViewMinimum; 6390 } 6391 else if (axisOpposite.crossing > axisOpposite.ViewMaximum) 6392 { 6393 axisOpposite.crossing = axisOpposite.ViewMaximum; 6394 } 6395 } 6396 6397 return axisOpposite.GetLinearPosition(axisOpposite.crossing); 6398 } 6399 6400 #endregion 6401 6402 #region Axis 3D helper methods 6403 6404 /// <summary> 6405 /// Returns angle between 2D axis line and it's 3D transformed projection. 6406 /// </summary> 6407 /// <returns>Axis projection angle.</returns> GetAxisProjectionAngle()6408 internal double GetAxisProjectionAngle() 6409 { 6410 // Get Z position 6411 bool axisOnEdge; 6412 float zPosition = GetMarksZPosition(out axisOnEdge); 6413 6414 // Get axis position 6415 float axisPosition = (float)GetAxisPosition(); 6416 6417 // Create two points on the sides of the axis 6418 Point3D[] axisPoints = new Point3D[2]; 6419 if (this.AxisPosition == AxisPosition.Top || this.AxisPosition == AxisPosition.Bottom) 6420 { 6421 axisPoints[0] = new Point3D(0f, axisPosition, zPosition); 6422 axisPoints[1] = new Point3D(100f, axisPosition, zPosition); 6423 } 6424 else 6425 { 6426 axisPoints[0] = new Point3D(axisPosition, 0f, zPosition); 6427 axisPoints[1] = new Point3D(axisPosition, 100f, zPosition); 6428 } 6429 6430 // Transform coordinates 6431 ChartArea.matrix3D.TransformPoints(axisPoints); 6432 6433 // Round result 6434 axisPoints[0].X = (float)Math.Round(axisPoints[0].X, 4); 6435 axisPoints[0].Y = (float)Math.Round(axisPoints[0].Y, 4); 6436 axisPoints[1].X = (float)Math.Round(axisPoints[1].X, 4); 6437 axisPoints[1].Y = (float)Math.Round(axisPoints[1].Y, 4); 6438 6439 // Calculate angle 6440 double angle = 0.0; 6441 if (this.AxisPosition == AxisPosition.Top || this.AxisPosition == AxisPosition.Bottom) 6442 { 6443 angle = Math.Atan((axisPoints[1].Y - axisPoints[0].Y) / (axisPoints[1].X - axisPoints[0].X)); 6444 } 6445 else 6446 { 6447 angle = Math.Atan((axisPoints[1].X - axisPoints[0].X) / (axisPoints[1].Y - axisPoints[0].Y)); 6448 } 6449 6450 // Conver to degrees 6451 return (angle * 180.0) / Math.PI; 6452 } 6453 6454 #endregion 6455 6456 #region IDisposable Members 6457 6458 /// <summary> 6459 /// Releases unmanaged and - optionally - managed resources 6460 /// </summary> 6461 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> Dispose(bool disposing)6462 protected override void Dispose(bool disposing) 6463 { 6464 if (disposing) 6465 { 6466 if (_fontCache != null) 6467 { 6468 _fontCache.Dispose(); 6469 _fontCache = null; 6470 } 6471 6472 if (labelStyle != null) 6473 { 6474 labelStyle.Dispose(); 6475 labelStyle = null; 6476 } 6477 6478 if (_stripLines != null) 6479 { 6480 _stripLines.Dispose(); 6481 _stripLines = null; 6482 } 6483 if (_customLabels != null) 6484 { 6485 _customLabels.Dispose(); 6486 _customLabels = null; 6487 } 6488 if (tempLabels != null) 6489 { 6490 tempLabels.Dispose(); 6491 tempLabels = null; 6492 } 6493 #if Microsoft_CONTROL 6494 if (this.scrollBar != null) 6495 { 6496 this.scrollBar.Dispose(); 6497 this.scrollBar = null; 6498 } 6499 #endif // Microsoft_CONTROL 6500 } 6501 base.Dispose(disposing); 6502 } 6503 6504 6505 #endregion 6506 6507 } 6508 } 6509