Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / Annotation / CalloutAnnotation.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               CalloutAnnotation.cs
9 //
10 //  Namespace:  System.Web.UI.WebControls[Windows.Forms].Charting
11 //
12 //      Classes:        CalloutAnnotation
13 //
14 //  Purpose:    Callout annotation classes.
15 //
16 //      Reviewed:       
17 //
18 //===================================================================
19
20 #region Used namespace
21 using System;
22 using System.Collections;
23 using System.Collections.Specialized;
24 using System.ComponentModel;
25 using System.ComponentModel.Design;
26 using System.Data;
27 using System.Drawing;
28 using System.Drawing.Design;
29 using System.Drawing.Text;
30 using System.Drawing.Drawing2D;
31 #if Microsoft_CONTROL
32 using System.Windows.Forms.DataVisualization.Charting;
33 using System.Windows.Forms.DataVisualization.Charting.Data;
34 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
35 using System.Windows.Forms.DataVisualization.Charting.Utilities;
36 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
37
38 #else
39 using System.Web;
40 using System.Web.UI;
41 using System.Web.UI.DataVisualization.Charting;
42 using System.Web.UI.DataVisualization.Charting.Data;
43 using System.Web.UI.DataVisualization.Charting.Utilities;
44 using System.Web.UI.DataVisualization.Charting.Borders3D;
45 #endif
46
47
48 #endregion
49
50 #if Microsoft_CONTROL
51 namespace System.Windows.Forms.DataVisualization.Charting
52
53 #else
54 namespace System.Web.UI.DataVisualization.Charting
55
56 #endif
57 {
58         #region Enumerations
59
60         /// <summary>
61         /// Annotation callout style.
62         /// <seealso cref="CalloutAnnotation.CalloutStyle"/>
63         /// </summary>
64         [
65         SRDescription("DescriptionAttributeCalloutStyle_CalloutStyle"),
66         ]
67         public enum CalloutStyle
68         {
69                 /// <summary>
70                 /// Callout text is underlined and a line is pointing to the anchor point.
71                 /// </summary>
72                 SimpleLine,
73
74                 /// <summary>
75                 /// Border is drawn around text and a line is pointing to the anchor point.
76                 /// </summary>
77                 Borderline,
78
79                 /// <summary>
80                 /// Callout text is inside the cloud and smaller clouds are pointing to the anchor point.
81                 /// </summary>
82                 Cloud,
83
84                 /// <summary>
85                 /// Rectangle is drawn around the callout text, which is connected with the anchor point.
86                 /// </summary>
87                 Rectangle,
88
89                 /// <summary>
90                 /// Rounded rectangle is drawn around the callout text, which is connected with the anchor point.
91                 /// </summary>
92                 RoundedRectangle,
93
94                 /// <summary>
95                 /// Ellipse is drawn around the callout text, which is connected with the anchor point.
96                 /// </summary>
97                 Ellipse,
98
99                 /// <summary>
100                 /// Perspective rectangle is drawn around the callout text, which is connected with the anchor point.
101                 /// </summary>
102                 Perspective,
103         }
104
105         #endregion
106
107         /// <summary>
108         /// <b>CalloutAnnotation</b> is a class class that represents a callout annotation.
109         /// </summary>
110         /// <remarks>
111         /// Callout annotation is the only annotation that draws a connection between the
112         /// annotation position and anchor point. It can display text and automatically 
113         /// calculate the required size. Different <see cref="CalloutStyle"/> are supported.
114         /// </remarks>
115         [
116                 SRDescription("DescriptionAttributeCalloutAnnotation_CalloutAnnotation"),
117         ]
118 #if ASPPERM_35
119         [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
120     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
121 #endif
122         public class CalloutAnnotation : TextAnnotation
123         {
124                 #region Fields
125
126                 // Callout anchor type 
127                 private         LineAnchorCapStyle              _calloutAnchorCap = LineAnchorCapStyle.Arrow;
128
129                 // Callout drawing style
130                 private         CalloutStyle            _calloutStyle = CalloutStyle.Rectangle;
131
132                 // Cloud shape path
133                 private         static                          GraphicsPath    _cloudPath = null;
134
135                 // Cloud shape outline path
136                 private         static                          GraphicsPath    _cloudOutlinePath = null;
137
138                 // Cloud shape boundary rectangle
139                 private         static                          RectangleF      _cloudBounds = RectangleF.Empty;
140
141                 #endregion
142
143                 #region Construction and Initialization
144
145                 /// <summary>
146                 /// Default public constructor.
147                 /// </summary>
148                 public CalloutAnnotation() 
149             : base()
150                 {
151                         // Changing default values of properties
152                         this.anchorOffsetX = 3.0;
153                         this.anchorOffsetY = 3.0;
154                         this.anchorAlignment = ContentAlignment.BottomLeft;
155                 }
156
157                 #endregion
158
159                 #region Properties
160
161                 #region Callout properties
162
163         /// <summary>
164         /// Gets or sets the annotation callout style.
165         /// </summary>
166         /// <value>
167         /// <see cref="CalloutStyle"/> of the annotation.
168         /// </value>
169                 [
170                 SRCategory("CategoryAttributeAppearance"),
171                 Bindable(true),
172                 DefaultValue(CalloutStyle.Rectangle),
173                 SRDescription("DescriptionAttributeCalloutAnnotation_CalloutStyle"),
174                 ParenthesizePropertyNameAttribute(true),
175                 ]
176                 virtual public CalloutStyle CalloutStyle
177                 {
178                         get
179                         {
180                                 return _calloutStyle;
181                         }
182                         set
183                         {
184                                 _calloutStyle = value;
185                                 this.ResetCurrentRelativePosition();
186                                 
187                                 // Reset content size to empty
188                                 contentSize = SizeF.Empty;
189
190                                 Invalidate();
191                         }
192                 }
193
194         /// <summary>
195         /// Gets or sets the anchor cap style of a callout line.
196         /// </summary>
197         /// <value>
198         /// A <see cref="LineAnchorCapStyle"/> value used as the anchor cap of a callout line.
199         /// </value>
200         /// <remarks>
201         /// This property sets the anchor cap of the line connecting an annotation to 
202         /// its anchor point. It only applies when SimpleLine or BorderLine 
203         /// are used.
204         /// </remarks>
205                 [
206                 SRCategory("CategoryAttributeAppearance"),
207                 Bindable(true),
208                 DefaultValue(LineAnchorCapStyle.Arrow),
209                 SRDescription("DescriptionAttributeCalloutAnnotation_CalloutAnchorCap"),
210                 ]
211                 virtual public LineAnchorCapStyle CalloutAnchorCap
212                 {
213                         get
214                         {
215                                 return _calloutAnchorCap;
216                         }
217                         set
218                         {
219                                 _calloutAnchorCap = value;
220                                 Invalidate();
221                         }
222                 }
223                 #endregion // Callout properties
224
225                 #region Applicable Annotation Appearance Attributes (set as Browsable)
226
227                 /// <summary>
228                 /// Gets or sets the color of an annotation line.
229                 /// <seealso cref="LineWidth"/>
230                 /// <seealso cref="LineDashStyle"/>
231                 /// </summary>
232                 /// <value>
233                 /// A <see cref="Color"/> value used to draw an annotation line.
234                 /// </value>
235                 [
236                 SRCategory("CategoryAttributeAppearance"),
237                 Browsable(true),
238                 DefaultValue(typeof(Color), "Black"),
239         SRDescription("DescriptionAttributeLineColor"),
240         TypeConverter(typeof(ColorConverter)),
241         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
242                 ]
243                 override public Color LineColor
244                 {
245                         get
246                         {
247                                 return base.LineColor;
248                         }
249                         set
250                         {
251                                 base.LineColor = value;
252                         }
253                 }
254
255                 /// <summary>
256                 /// Gets or sets the width of an annotation line.
257                 /// <seealso cref="LineColor"/>
258                 /// <seealso cref="LineDashStyle"/>
259                 /// </summary>
260                 /// <value>
261                 /// An integer value defining the width of an annotation line in pixels.
262                 /// </value>
263                 [
264                 SRCategory("CategoryAttributeAppearance"),
265                 Browsable(true),
266                 DefaultValue(1),
267         SRDescription("DescriptionAttributeLineWidth"),
268                 ]
269                 override public int LineWidth
270                 {
271                         get
272                         {
273                                 return base.LineWidth;
274                         }
275                         set
276                         {
277                                 base.LineWidth = value;
278
279                         }
280                 }
281
282                 /// <summary>
283                 /// Gets or sets the style of an annotation line.
284                 /// <seealso cref="LineWidth"/>
285                 /// <seealso cref="LineColor"/>
286                 /// </summary>
287                 /// <value>
288                 /// A <see cref="ChartDashStyle"/> value used to draw an annotation line.
289                 /// </value>
290                 [
291                 SRCategory("CategoryAttributeAppearance"),
292                 Browsable(true),
293                 DefaultValue(ChartDashStyle.Solid),
294         SRDescription("DescriptionAttributeLineDashStyle"),
295                 ]
296                 override public ChartDashStyle LineDashStyle
297                 {
298                         get
299                         {
300                                 return base.LineDashStyle;
301                         }
302                         set
303                         {
304                                 base.LineDashStyle = value;
305                         }
306                 }
307
308                 /// <summary>
309                 /// Gets or sets the background color of an annotation.
310                 /// <seealso cref="BackSecondaryColor"/>
311                 /// <seealso cref="BackHatchStyle"/>
312                 /// <seealso cref="BackGradientStyle"/>
313                 /// </summary>
314                 /// <value>
315                 /// A <see cref="Color"/> value used for the background of an annotation.
316                 /// </value>
317                 [
318                 SRCategory("CategoryAttributeAppearance"),
319                 Browsable(true),
320                 DefaultValue(typeof(Color), ""),
321         SRDescription("DescriptionAttributeBackColor"),
322                 NotifyParentPropertyAttribute(true),
323         TypeConverter(typeof(ColorConverter)),
324         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
325                 ]
326                 override public Color BackColor
327                 {
328                         get
329                         {
330                                 return base.BackColor;
331                         }
332                         set
333                         {
334                                 base.BackColor = value;
335                         }
336                 }
337
338                 /// <summary>
339                 /// Gets or sets the background hatch style of an annotation.
340                 /// <seealso cref="BackSecondaryColor"/>
341                 /// <seealso cref="BackColor"/>
342                 /// <seealso cref="BackGradientStyle"/>
343                 /// </summary>
344                 /// <value>
345                 /// A <see cref="ChartHatchStyle"/> value used for the background of an annotation.
346                 /// </value>
347                 /// <remarks>
348                 /// Two colors are used to draw the hatching, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
349                 /// </remarks>
350                 [
351                 SRCategory("CategoryAttributeAppearance"),
352                 Browsable(true),
353                 DefaultValue(ChartHatchStyle.None),
354                 NotifyParentPropertyAttribute(true),
355         SRDescription("DescriptionAttributeBackHatchStyle"),
356                 Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
357                 ]
358                 override public ChartHatchStyle BackHatchStyle
359                 {
360                         get
361                         {
362                                 return base.BackHatchStyle;
363                         }
364                         set
365                         {
366                                 base.BackHatchStyle = value;
367                         }
368                 }
369
370                 /// <summary>
371                 /// Gets or sets the background gradient style of an annotation.
372                 /// <seealso cref="BackSecondaryColor"/>
373                 /// <seealso cref="BackColor"/>
374                 /// <seealso cref="BackHatchStyle"/>
375                 /// </summary>
376                 /// <value>
377                 /// A <see cref="GradientStyle"/> value used for the background of an annotation.
378                 /// </value>
379                 /// <remarks>
380                 /// Two colors are used to draw the gradient, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
381                 /// </remarks>
382                 [
383                 SRCategory("CategoryAttributeAppearance"),
384                 Browsable(true),
385                 DefaultValue(GradientStyle.None),
386                 NotifyParentPropertyAttribute(true),
387                 SRDescription("DescriptionAttributeBackGradientStyle"),
388                 Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
389                 ]               
390                 override public GradientStyle BackGradientStyle
391                 {
392                         get
393                         {
394                                 return base.BackGradientStyle;
395                         }
396                         set
397                         {
398                                 base.BackGradientStyle = value;
399                         }
400                 }
401
402                 /// <summary>
403                 /// Gets or sets the secondary background color of an annotation.
404                 /// <seealso cref="BackColor"/>
405                 /// <seealso cref="BackHatchStyle"/>
406                 /// <seealso cref="BackGradientStyle"/>
407                 /// </summary>
408                 /// <value>
409                 /// A <see cref="Color"/> value used for the secondary color of an annotation background with 
410                 /// hatching or gradient fill.
411                 /// </value>
412                 /// <remarks>
413                 /// This color is used with <see cref="BackColor"/> when <see cref="BackHatchStyle"/> or
414                 /// <see cref="BackGradientStyle"/> are used.
415                 /// </remarks>
416                 [
417                 SRCategory("CategoryAttributeAppearance"),
418                 Browsable(true),
419                 DefaultValue(typeof(Color), ""),
420                 NotifyParentPropertyAttribute(true),
421         SRDescription("DescriptionAttributeBackSecondaryColor"),
422         TypeConverter(typeof(ColorConverter)),
423         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
424                 ] 
425                 override public Color BackSecondaryColor
426                 {
427                         get
428                         {
429                                 return base.BackSecondaryColor;
430                         }
431                         set
432                         {
433                                 base.BackSecondaryColor = value;
434                         }
435                 }
436
437                 #endregion
438
439                 #region Anchor
440
441         /// <summary>
442         /// Gets or sets the x-coordinate offset between the positions of an annotation and its anchor point.
443         /// <seealso cref="AnchorOffsetY"/>
444         /// <seealso cref="Annotation.AnchorDataPoint"/>
445         /// <seealso cref="Annotation.AnchorX"/>
446         /// <seealso cref="AnchorAlignment"/>
447         /// </summary>
448         /// <value>
449         /// A double value that represents the x-coordinate offset between the positions of an annotation and its anchor point.
450         /// </value>
451         /// <remarks>
452         /// The annotation must be anchored using the <see cref="Annotation.AnchorDataPoint"/> or 
453         /// <see cref="Annotation.AnchorX"/> properties, and its <see cref="Annotation.X"/> property must be set 
454         /// to <b>Double.NaN</b>.
455         /// </remarks>
456                 [
457                 SRCategory("CategoryAttributeAnchor"),
458                 DefaultValue(3.0),
459                 SRDescription("DescriptionAttributeCalloutAnnotation_AnchorOffsetX"),
460                 RefreshPropertiesAttribute(RefreshProperties.All),
461                 ]
462                 override public double AnchorOffsetX
463                 {
464                         get
465                         {
466                                 return base.AnchorOffsetX;
467                         }
468                         set
469                         {
470                                 base.AnchorOffsetX = value;
471                         }
472                 }
473
474                 /// <summary>
475         /// Gets or sets the y-coordinate offset between the positions of an annotation and its anchor point.
476                 /// <seealso cref="Annotation.AnchorOffsetX"/>
477                 /// <seealso cref="Annotation.AnchorDataPoint"/>
478                 /// <seealso cref="Annotation.AnchorY"/>
479                 /// <seealso cref="Annotation.AnchorAlignment"/>
480                 /// </summary>
481                 /// <value>
482         /// A double value that represents the y-coordinate offset between the positions of an annotation and its anchor point.
483                 /// </value>
484                 /// <remarks>
485                 /// Annotation must be anchored using <see cref="Annotation.AnchorDataPoint"/> or 
486                 /// <see cref="Annotation.AnchorY"/> properties and its <see cref="Annotation.Y"/> property must be set
487                 /// to <b>Double.NaN</b>.
488                 /// </remarks>
489                 [
490                 SRCategory("CategoryAttributeAnchor"),
491                 DefaultValue(3.0),
492                 SRDescription("DescriptionAttributeCalloutAnnotation_AnchorOffsetY"),
493                 RefreshPropertiesAttribute(RefreshProperties.All),
494                 ]
495                 override public double AnchorOffsetY
496                 {
497                         get
498                         {
499                                 return base.AnchorOffsetY;
500                         }
501                         set
502                         {
503                                 base.AnchorOffsetY = value;
504                         }
505                 }
506
507         /// <summary>
508         /// Gets or sets an annotation position's alignment to the anchor point.
509         /// <seealso cref="Annotation.AnchorX"/>
510         /// <seealso cref="Annotation.AnchorY"/>
511         /// <seealso cref="Annotation.AnchorDataPoint"/>
512         /// <seealso cref="AnchorOffsetX"/>
513         /// <seealso cref="AnchorOffsetY"/>
514         /// </summary>
515         /// <value>
516         /// A <see cref="ContentAlignment"/> value that represents the annotation's alignment to 
517         /// the anchor point.
518         /// </value>
519         /// <remarks>
520         /// The annotation must be anchored using either <see cref="Annotation.AnchorDataPoint"/>, or the <see cref="Annotation.AnchorX"/> 
521         /// and <see cref="Annotation.AnchorY"/> properties. Its <see cref="Annotation.X"/> and <see cref="Annotation.Y"/> 
522         /// properties must be set to <b>Double.NaN</b>.
523         /// </remarks>
524                 [
525                 SRCategory("CategoryAttributeAnchor"),
526                 DefaultValue(typeof(ContentAlignment), "BottomLeft"),
527                 SRDescription("DescriptionAttributeAnchorAlignment"),
528                 ]
529                 override public ContentAlignment AnchorAlignment
530                 {
531                         get
532                         {
533                                 return base.AnchorAlignment;
534                         }
535                         set
536                         {
537                                 base.AnchorAlignment = value;
538                         }
539                 }
540
541                 #endregion      // Anchoring
542
543                 #region Other
544
545         /// <summary>
546         /// Gets or sets an annotation's type name.
547         /// </summary>
548         /// <remarks>
549         /// This property is used to get the name of each annotation type  
550         /// (e.g. Line, Rectangle, Ellipse). 
551         /// <para>
552         /// This property is for internal use and is hidden at design and run time.
553         /// </para>
554         /// </remarks>  
555                 [
556                 SRCategory("CategoryAttributeMisc"),
557                 Bindable(true),
558                 Browsable(false),
559                 EditorBrowsableAttribute(EditorBrowsableState.Never),
560                 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
561                 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
562                 SRDescription("DescriptionAttributeAnnotationType"),
563                 ]
564                 public override string AnnotationType
565                 {
566                         get
567                         {
568                                 return "Callout";
569                         }
570                 }
571
572                 /// <summary>
573                 /// Gets or sets annotation selection points style.
574                 /// </summary>
575                 /// <value>
576                 /// A <see cref="SelectionPointsStyle"/> value that represents annotation
577                 /// selection style.
578                 /// </value>
579                 /// <remarks>
580         /// This property is for internal use and is hidden at design and run time.
581                 /// </remarks>
582                 [
583                 SRCategory("CategoryAttributeAppearance"),
584                 DefaultValue(SelectionPointsStyle.Rectangle),
585                 ParenthesizePropertyNameAttribute(true),
586                 Browsable(false),
587                 EditorBrowsableAttribute(EditorBrowsableState.Never),
588                 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
589                 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
590                 SRDescription("DescriptionAttributeSelectionPointsStyle"),
591                 ]
592                 override internal SelectionPointsStyle SelectionPointsStyle
593                 {
594                         get
595                         {
596                                 return SelectionPointsStyle.Rectangle;
597                         }
598                 }
599
600                 #endregion
601
602                 #endregion
603
604                 #region Methods
605
606                 #region Text Spacing
607
608                 /// <summary>
609                 /// Gets text spacing on four different sides in relative coordinates.
610                 /// </summary>
611                 /// <param name="annotationRelative">Indicates that spacing is in annotation relative coordinates.</param>
612                 /// <returns>Rectangle with text spacing values.</returns>
613                 internal override RectangleF GetTextSpacing(out bool annotationRelative)
614                 {
615                         RectangleF spacing = base.GetTextSpacing(out annotationRelative);
616                         if(this._calloutStyle == CalloutStyle.Cloud ||
617                                 this._calloutStyle == CalloutStyle.Ellipse)
618                         {
619                                 spacing = new RectangleF(4f, 4f, 4f, 4f);
620                                 annotationRelative = true;
621                         }
622                         else if(this._calloutStyle == CalloutStyle.RoundedRectangle)
623                         {
624                                 spacing = new RectangleF(1f, 1f, 1f, 1f);
625                                 annotationRelative = true;
626                         }
627
628                         return spacing;
629                 }
630
631                 #endregion // Text Spacing
632
633                 #region Painting
634
635                 /// <summary>
636                 /// Paints annotation object on specified graphics.
637                 /// </summary>
638                 /// <param name="graphics">
639                 /// A <see cref="ChartGraphics"/> used to paint annotation object.
640                 /// </param>
641                 /// <param name="chart">
642                 /// Reference to the <see cref="Chart"/> control.
643                 /// </param>
644                 override internal void Paint(Chart chart, ChartGraphics graphics)
645                 {
646                         // Get annotation position in relative coordinates
647                         PointF firstPoint = PointF.Empty;
648                         PointF anchorPoint = PointF.Empty;
649                         SizeF size = SizeF.Empty;
650                         GetRelativePosition(out firstPoint, out size, out anchorPoint);
651                         PointF  secondPoint = new PointF(firstPoint.X + size.Width, firstPoint.Y + size.Height);
652
653                         // Create selection rectangle
654                         RectangleF selectionRect = new RectangleF(firstPoint, new SizeF(secondPoint.X - firstPoint.X, secondPoint.Y - firstPoint.Y));
655
656                         // Adjust negative rectangle width and height
657                         RectangleF      rectanglePosition = new RectangleF(selectionRect.Location, selectionRect.Size);
658                         if(rectanglePosition.Width < 0)
659                         {
660                                 rectanglePosition.X = rectanglePosition.Right;
661                                 rectanglePosition.Width = -rectanglePosition.Width;
662                         }
663                         if(rectanglePosition.Height < 0)
664                         {
665                                 rectanglePosition.Y = rectanglePosition.Bottom;
666                                 rectanglePosition.Height = -rectanglePosition.Height;
667                         }
668
669                         // Check if position is valid
670                         if( float.IsNaN(rectanglePosition.X) || 
671                                 float.IsNaN(rectanglePosition.Y) || 
672                                 float.IsNaN(rectanglePosition.Right) || 
673                                 float.IsNaN(rectanglePosition.Bottom) )
674                         {
675                                 return;
676                         }
677
678                         // Paint different style of callouts
679                         GraphicsPath hotRegionPathAbs = null;
680                         if(this.Common.ProcessModePaint)
681                         {
682                                 switch(this._calloutStyle)
683                                 {
684                                         case(CalloutStyle.SimpleLine):
685                                                 hotRegionPathAbs = DrawRectangleLineCallout(
686                                                         graphics,
687                                                         rectanglePosition,
688                                                         anchorPoint,
689                                                         false);
690                                                 break;
691                                         case(CalloutStyle.Borderline):
692                                                 hotRegionPathAbs = DrawRectangleLineCallout(
693                                                         graphics,
694                                                         rectanglePosition,
695                                                         anchorPoint,
696                                                         true);
697                                                 break;
698                                         case(CalloutStyle.Perspective):
699                                                 hotRegionPathAbs = DrawPerspectiveCallout(
700                                                         graphics,
701                                                         rectanglePosition,
702                                                         anchorPoint);
703                                                 break;
704                                         case(CalloutStyle.Cloud):
705                                                 hotRegionPathAbs = DrawCloudCallout(
706                                                         graphics,
707                                                         rectanglePosition,
708                                                         anchorPoint);
709                                                 break;
710                                         case(CalloutStyle.Rectangle):
711                                                 hotRegionPathAbs = DrawRectangleCallout(
712                                                         graphics,
713                                                         rectanglePosition,
714                                                         anchorPoint);
715                                                 break;
716                                         case(CalloutStyle.Ellipse):
717                                                 hotRegionPathAbs = DrawRoundedRectCallout(
718                                                         graphics,
719                                                         rectanglePosition,
720                                                         anchorPoint,
721                                                         true);
722                                                 break;
723                                         case(CalloutStyle.RoundedRectangle):
724                                                 hotRegionPathAbs = DrawRoundedRectCallout(
725                                                         graphics,
726                                                         rectanglePosition,
727                                                         anchorPoint,
728                                                         false);
729                                                 break;
730                                 }
731                         }
732
733                         if(this.Common.ProcessModeRegions)
734                         {
735                                 if(hotRegionPathAbs != null)
736                                 {
737                                         // If there is more then one graphical path split them and create 
738                                         // image maps for every graphical path separately.
739                                         GraphicsPathIterator iterator = new GraphicsPathIterator(hotRegionPathAbs);
740
741                                         // There is more then one path.
742                     using (GraphicsPath subPath = new GraphicsPath())
743                     {
744                         while (iterator.NextMarker(subPath) > 0)
745                         {
746                             // Use callout defined hot region
747                             this.Common.HotRegionsList.AddHotRegion(
748                                 graphics,
749                                 subPath,
750                                 false,
751                                 ReplaceKeywords(this.ToolTip),
752 #if Microsoft_CONTROL
753                                                         String.Empty,
754                                                         String.Empty,
755                                                         String.Empty,
756 #else // Microsoft_CONTROL
757  ReplaceKeywords(this.Url),
758                                             ReplaceKeywords(this.MapAreaAttributes),
759                             ReplaceKeywords(this.PostBackValue),
760 #endif // Microsoft_CONTROL
761  this,
762                                 ChartElementType.Annotation);
763
764                             // Reset current path
765                             subPath.Reset();
766                         }
767                     }
768                                 }
769                                 else
770                                 {
771                                         // Use rectangular hot region
772                                         this.Common.HotRegionsList.AddHotRegion(
773                                                 rectanglePosition,
774                                                 ReplaceKeywords(this.ToolTip),
775 #if Microsoft_CONTROL
776                                                 String.Empty,
777                                                 String.Empty,
778                                                 String.Empty,
779 #else // Microsoft_CONTROL
780                         ReplaceKeywords(this.Url),
781                                             ReplaceKeywords(this.MapAreaAttributes),
782                         ReplaceKeywords(this.PostBackValue),
783 #endif // Microsoft_CONTROL
784                                                 this,
785                                                 ChartElementType.Annotation,
786                                                 String.Empty);
787                                 }
788                         }
789
790             //Clean up
791             if (hotRegionPathAbs != null)
792                 hotRegionPathAbs.Dispose();
793
794                         // Paint selection handles
795                         PaintSelectionHandles(graphics, selectionRect, null);
796                 }
797
798                 /// <summary>
799                 /// Draws Rounded rectangle or Ellipse style callout.
800                 /// </summary>
801                 /// <param name="graphics">Chart graphics.</param>
802                 /// <param name="rectanglePosition">Position of annotation objet.</param>
803                 /// <param name="anchorPoint">Anchor location.</param>
804                 /// <param name="isEllipse">True if ellipse shape should be used.</param>
805                 /// <returns>Hot region of the callout.</returns>
806                 private GraphicsPath DrawRoundedRectCallout(
807                         ChartGraphics graphics,
808                         RectangleF rectanglePosition,
809                         PointF anchorPoint,
810                         bool isEllipse)
811                 {
812                         // Get absolute position
813                         RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
814
815             // NOTE: Fix for issue #6692.
816             // Do not draw the callout if size is not set. This may happen if callou text is set to empty string.
817             if (rectanglePositionAbs.Width <= 0 || rectanglePositionAbs.Height <= 0)
818             {
819                 return null;
820             }
821
822                         // Create ellipse path
823                         GraphicsPath ellipsePath = new GraphicsPath();
824                         if(isEllipse)
825                         {
826                                 // Add ellipse shape
827                                 ellipsePath.AddEllipse(rectanglePositionAbs);
828                         }
829                         else
830                         {
831                                 // Add rounded rectangle shape
832                                 float radius = Math.Min(rectanglePositionAbs.Width, rectanglePositionAbs.Height);
833                                 radius /= 5f;
834                                 ellipsePath = this.CreateRoundedRectPath(rectanglePositionAbs, radius);
835                         }
836
837                         // Draw perspective polygons from anchoring point
838                         if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
839                         {
840                                 // Check if point is inside annotation position
841                                 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
842                                 {
843                                         // Get absolute anchor point
844                                         PointF anchorPointAbs = graphics.GetAbsolutePoint(new PointF(anchorPoint.X, anchorPoint.Y));
845
846                                         // Flatten ellipse path
847                                         ellipsePath.Flatten();
848
849                                         // Find point in the path closest to the anchor point
850                                         PointF[] points = ellipsePath.PathPoints;
851                                         int closestPointIndex = 0;
852                                         int index = 0;
853                                         float currentDistance = float.MaxValue;
854                                         foreach(PointF point in points)
855                                         {
856                                                 float deltaX = point.X - anchorPointAbs.X;
857                                                 float deltaY = point.Y - anchorPointAbs.Y;
858                                                 float distance = deltaX * deltaX + deltaY * deltaY;
859                                                 if(distance < currentDistance)
860                                                 {
861                                                         currentDistance = distance;
862                                                         closestPointIndex = index;
863                                                 }
864                                                 ++ index;
865                                         }
866
867                                         // Change point to the anchor location
868                                         points[closestPointIndex] = anchorPointAbs;
869
870                                         // Recreate ellipse path
871                                         ellipsePath.Reset();
872                                         ellipsePath.AddLines(points);
873                                         ellipsePath.CloseAllFigures();
874                                 }
875                         }
876
877                         // Draw ellipse
878                         graphics.DrawPathAbs(
879                                 ellipsePath,
880                                 this.BackColor,
881                                 this.BackHatchStyle,
882                                 String.Empty,
883                                 ChartImageWrapMode.Scaled,
884                                 Color.Empty,
885                                 ChartImageAlignmentStyle.Center,
886                                 this.BackGradientStyle,
887                                 this.BackSecondaryColor,
888                                 this.LineColor,
889                                 this.LineWidth,
890                                 this.LineDashStyle,
891                                 PenAlignment.Center,
892                                 this.ShadowOffset,
893                                 this.ShadowColor);
894
895                         // Draw text 
896                         DrawText(graphics, rectanglePosition, true, false);
897
898                         return ellipsePath;
899                 }
900
901                 /// <summary>
902                 /// Draws Rectangle style callout.
903                 /// </summary>
904                 /// <param name="graphics">Chart graphics.</param>
905                 /// <param name="rectanglePosition">Position of annotation objet.</param>
906                 /// <param name="anchorPoint">Anchor location.</param>
907                 /// <returns>Hot region of the callout.</returns>
908                 private GraphicsPath DrawRectangleCallout(
909                         ChartGraphics graphics,
910                         RectangleF rectanglePosition,
911                         PointF anchorPoint)
912                 {
913                         // Create path for the rectangle connected with anchor point.
914                         GraphicsPath    hotRegion = null;
915                         bool anchorVisible = false;
916                         if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
917                         {
918                                 // Get relative size of a pixel
919                                 SizeF pixelSize = graphics.GetRelativeSize(new SizeF(1f, 1f));
920
921                                 // Increase annotation position rectangle by 1 pixel
922                                 RectangleF inflatedPosition = new RectangleF(rectanglePosition.Location, rectanglePosition.Size);
923                                 inflatedPosition.Inflate(pixelSize);
924
925                                 // Check if point is inside annotation position
926                                 if(!inflatedPosition.Contains(anchorPoint.X, anchorPoint.Y))
927                                 {
928                                         anchorVisible = true;
929
930                                         // Get absolute position
931                                         RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
932
933                                         // Get absolute anchor point
934                                         PointF anchorPointAbs = graphics.GetAbsolutePoint(new PointF(anchorPoint.X, anchorPoint.Y));
935
936                                         // Calculate anchor pointer thicness
937                                         float size = Math.Min(rectanglePositionAbs.Width, rectanglePositionAbs.Height);
938                                         size /= 4f;
939
940                                         // Create shape points
941                                         PointF[] points = new PointF[7];
942                                         if(anchorPoint.X < rectanglePosition.X && 
943                                                 anchorPoint.Y > rectanglePosition.Bottom)
944                                         {
945                                                 points[0] = rectanglePositionAbs.Location;
946                                                 points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
947                                                 points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
948                                                 points[3] = new PointF(rectanglePositionAbs.X + size, rectanglePositionAbs.Bottom);
949                                                 points[4] = anchorPointAbs;
950                                                 points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom - size);
951                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom - size);
952                                         }
953                                         else if(anchorPoint.X >= rectanglePosition.X && 
954                                                 anchorPoint.X <= rectanglePosition.Right &&
955                                                 anchorPoint.Y > rectanglePosition.Bottom)
956                                         {
957                                                 points[0] = rectanglePositionAbs.Location;
958                                                 points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
959                                                 points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
960                                                 points[3] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width / 2f + size, rectanglePositionAbs.Bottom);
961                                                 points[4] = anchorPointAbs;
962                                                 points[5] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width / 2f - size, rectanglePositionAbs.Bottom);
963                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
964                                         }
965                                         else if(anchorPoint.X > rectanglePosition.Right && 
966                                                 anchorPoint.Y > rectanglePosition.Bottom)
967                                         {
968                                                 points[0] = rectanglePositionAbs.Location;
969                                                 points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
970                                                 points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom - size);
971                                                 points[3] = anchorPointAbs;
972                                                 points[4] = new PointF(rectanglePositionAbs.Right - size, rectanglePositionAbs.Bottom);
973                                                 points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
974                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
975                                         }
976                                         else if(anchorPoint.X > rectanglePosition.Right && 
977                                                 anchorPoint.Y <= rectanglePosition.Bottom && 
978                                                 anchorPoint.Y >= rectanglePosition.Y)
979                                         {
980                                                 points[0] = rectanglePositionAbs.Location;
981                                                 points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
982                                                 points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y + rectanglePositionAbs.Height / 2f - size);
983                                                 points[3] = anchorPointAbs;
984                                                 points[4] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y + rectanglePositionAbs.Height / 2f + size);
985                                                 points[5] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
986                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
987                                         }
988                                         else if(anchorPoint.X > rectanglePosition.Right && 
989                                                 anchorPoint.Y < rectanglePosition.Y)
990                                         {
991                                                 points[0] = rectanglePositionAbs.Location;
992                                                 points[1] = new PointF(rectanglePositionAbs.Right - size, rectanglePositionAbs.Y);
993                                                 points[2] = anchorPointAbs;
994                                                 points[3] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y + size);
995                                                 points[4] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
996                                                 points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
997                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
998                                         }
999                                         else if(anchorPoint.X >= rectanglePosition.X && 
1000                                                 anchorPoint.X <= rectanglePosition.Right && 
1001                                                 anchorPoint.Y < rectanglePosition.Y)
1002                                         {
1003                                                 points[0] = rectanglePositionAbs.Location;
1004                                                 points[1] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width/2f - size, rectanglePositionAbs.Y);
1005                                                 points[2] = anchorPointAbs;
1006                                                 points[3] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width/2f + size, rectanglePositionAbs.Y);
1007                                                 points[4] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1008                                                 points[5] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1009                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1010                                         }
1011                                         else if(anchorPoint.X < rectanglePosition.X &&
1012                                                 anchorPoint.Y < rectanglePosition.Y)
1013                                         {
1014                                                 points[0] = anchorPointAbs;
1015                                                 points[1] = new PointF(rectanglePositionAbs.X + size, rectanglePositionAbs.Y);
1016                                                 points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1017                                                 points[3] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1018                                                 points[4] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1019                                                 points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + size);
1020                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + size);
1021                                         }
1022                                         else if(anchorPoint.X < rectanglePosition.X &&
1023                                                 anchorPoint.Y >= rectanglePosition.Y &&
1024                                                 anchorPoint.Y <= rectanglePosition.Bottom)
1025                                         {
1026                                                 points[0] = rectanglePositionAbs.Location;
1027                                                 points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1028                                                 points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1029                                                 points[3] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1030                                                 points[4] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + rectanglePositionAbs.Height/2f + size );
1031                                                 points[5] = anchorPointAbs;
1032                                                 points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + rectanglePositionAbs.Height/2f - size );
1033                                         }
1034
1035                                         // Create graphics path of the callout
1036                     hotRegion = new GraphicsPath();
1037                 
1038                     hotRegion.AddLines(points);
1039                     hotRegion.CloseAllFigures();
1040
1041                     // Draw callout
1042                     graphics.DrawPathAbs(
1043                         hotRegion,
1044                         this.BackColor,
1045                         this.BackHatchStyle,
1046                         String.Empty,
1047                         ChartImageWrapMode.Scaled,
1048                         Color.Empty,
1049                         ChartImageAlignmentStyle.Center,
1050                         this.BackGradientStyle,
1051                         this.BackSecondaryColor,
1052                         this.LineColor,
1053                         this.LineWidth,
1054                         this.LineDashStyle,
1055                         PenAlignment.Center,
1056                         this.ShadowOffset,
1057                         this.ShadowColor);
1058                 
1059                                 }
1060                         }
1061                 
1062                         // Draw rectangle if anchor is not visible
1063                         if(!anchorVisible)
1064                         {
1065                                 graphics.FillRectangleRel(
1066                                         rectanglePosition,
1067                                         this.BackColor,
1068                                         this.BackHatchStyle,
1069                                         String.Empty,
1070                                         ChartImageWrapMode.Scaled,
1071                                         Color.Empty,
1072                                         ChartImageAlignmentStyle.Center,
1073                                         this.BackGradientStyle,
1074                                         this.BackSecondaryColor,
1075                                         this.LineColor,
1076                                         this.LineWidth,
1077                                         this.LineDashStyle,
1078                                         this.ShadowColor,
1079                                         this.ShadowOffset,
1080                                         PenAlignment.Center);
1081
1082                                 // Get hot region
1083                                 hotRegion = new GraphicsPath();
1084                                 hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
1085                         }
1086
1087                         // Draw text 
1088                         DrawText(graphics, rectanglePosition, false, false);
1089
1090                         return hotRegion;
1091                 }
1092
1093                 /// <summary>
1094                 /// Draws Perspective style callout.
1095                 /// </summary>
1096                 /// <param name="graphics">Chart graphics.</param>
1097                 /// <param name="rectanglePosition">Position of annotation objet.</param>
1098                 /// <param name="anchorPoint">Anchor location.</param>
1099                 /// <returns>Hot region of the cloud.</returns>
1100                 private GraphicsPath DrawCloudCallout(
1101                         ChartGraphics graphics,
1102                         RectangleF rectanglePosition,
1103                         PointF anchorPoint)
1104                 {
1105                         // Get absolute position
1106                         RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
1107
1108                         // Draw perspective polygons from anchoring point
1109                         if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
1110                         {
1111                                 // Check if point is inside annotation position
1112                                 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
1113                                 {
1114                                         // Get center point of the cloud
1115                                         PointF cloudCenterAbs = graphics.GetAbsolutePoint(
1116                                                 new PointF(
1117                                                 rectanglePosition.X + rectanglePosition.Width / 2f, 
1118                                                 rectanglePosition.Y + rectanglePosition.Height / 2f) );
1119
1120                                         // Calculate absolute ellipse size and position
1121                                         SizeF ellipseSize = graphics.GetAbsoluteSize(
1122                                                 new SizeF(rectanglePosition.Width, rectanglePosition.Height));
1123                                         ellipseSize.Width /= 10f;
1124                                         ellipseSize.Height /= 10f;
1125                                         PointF anchorPointAbs = graphics.GetAbsolutePoint(
1126                                                 new PointF(anchorPoint.X, anchorPoint.Y));
1127                                         PointF ellipseLocation = anchorPointAbs;
1128
1129                                         // Get distance between anchor point and center of the cloud
1130                                         float dxAbs = anchorPointAbs.X - cloudCenterAbs.X;
1131                                         float dyAbs = anchorPointAbs.Y - cloudCenterAbs.Y;
1132
1133                                         PointF point = PointF.Empty;
1134                                         if(anchorPoint.Y < rectanglePosition.Y)
1135                                         {
1136                                                 point = GetIntersectionY(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Y);
1137                                                 if(point.X < rectanglePositionAbs.X)
1138                                                 {
1139                                                         point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
1140                                                 }
1141                                                 else if(point.X > rectanglePositionAbs.Right)
1142                                                 {
1143                                                         point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
1144                                                 }
1145                                         }
1146                                         else if(anchorPoint.Y > rectanglePosition.Bottom)
1147                                         {
1148                                                 point = GetIntersectionY(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Bottom);
1149                                                 if(point.X < rectanglePositionAbs.X)
1150                                                 {
1151                                                         point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
1152                                                 }
1153                                                 else if(point.X > rectanglePositionAbs.Right)
1154                                                 {
1155                                                         point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
1156                                                 }
1157                                         }
1158                                         else
1159                                         {
1160                                                 if(anchorPoint.X < rectanglePosition.X)
1161                                                 {
1162                                                         point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
1163                                                 }
1164                                                 else
1165                                                 {
1166                                                         point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
1167                                                 }
1168                                         }
1169                                                 
1170                                         SizeF size = new SizeF(Math.Abs(cloudCenterAbs.X - point.X), Math.Abs(cloudCenterAbs.Y - point.Y));
1171                                         if(dxAbs > 0)
1172                                                 dxAbs -= size.Width;
1173                                         else
1174                                                 dxAbs += size.Width;
1175
1176                                         if(dyAbs > 0)
1177                                                 dyAbs -= size.Height;
1178                                         else
1179                                                 dyAbs += size.Height;
1180
1181
1182                                         // Draw 3 smaller ellipses from anchor point to the cloud
1183                                         for(int index = 0; index < 3; index++)
1184                                         {
1185                                                 using( GraphicsPath path = new GraphicsPath() )
1186                                                 {
1187                                                         // Create ellipse path
1188                                                         path.AddEllipse(
1189                                                                 ellipseLocation.X - ellipseSize.Width / 2f,
1190                                                                 ellipseLocation.Y - ellipseSize.Height / 2f,
1191                                                                 ellipseSize.Width,
1192                                                                 ellipseSize.Height);
1193
1194                                                         // Draw ellipse
1195                                                         graphics.DrawPathAbs(
1196                                                                 path,
1197                                                                 this.BackColor,
1198                                                                 this.BackHatchStyle,
1199                                                                 String.Empty,
1200                                                                 ChartImageWrapMode.Scaled,
1201                                                                 Color.Empty,
1202                                                                 ChartImageAlignmentStyle.Center,
1203                                                                 this.BackGradientStyle,
1204                                                                 this.BackSecondaryColor,
1205                                                                 this.LineColor,
1206                                                                 1, // this.LineWidth,   NOTE: Cloud supports only 1 pixel border
1207                                                                 this.LineDashStyle,
1208                                                                 PenAlignment.Center,
1209                                                                 this.ShadowOffset,
1210                                                                 this.ShadowColor);
1211
1212                                                         // Adjust ellipse size
1213                                                         ellipseSize.Width *= 1.5f;
1214                                                         ellipseSize.Height *= 1.5f;
1215
1216                                                         // Adjust next ellipse position
1217                                                         ellipseLocation.X -= dxAbs / 3f + (index * (dxAbs / 10f) );
1218                                                         ellipseLocation.Y -= dyAbs / 3f + (index * (dyAbs / 10f) );
1219                                                 }
1220                                         }
1221                                 }
1222                         }
1223
1224                         // Draw cloud
1225                         GraphicsPath pathCloud = GetCloudPath(rectanglePositionAbs);
1226                         graphics.DrawPathAbs(
1227                                 pathCloud,
1228                                 this.BackColor,
1229                                 this.BackHatchStyle,
1230                                 String.Empty,
1231                                 ChartImageWrapMode.Scaled,
1232                                 Color.Empty,
1233                                 ChartImageAlignmentStyle.Center,
1234                                 this.BackGradientStyle,
1235                                 this.BackSecondaryColor,
1236                                 this.LineColor,
1237                                 1, // this.LineWidth,   NOTE: Cloud supports only 1 pixel border
1238                                 this.LineDashStyle,
1239                                 PenAlignment.Center,
1240                                 this.ShadowOffset,
1241                                 this.ShadowColor);
1242
1243                         // Draw cloud outline (Do not draw in SVG or Flash Animation)
1244                         {
1245                                 using(GraphicsPath pathCloudOutline = GetCloudOutlinePath(rectanglePositionAbs))
1246                                 {
1247                                         graphics.DrawPathAbs(
1248                                                 pathCloudOutline,
1249                                                 this.BackColor,
1250                                                 this.BackHatchStyle,
1251                                                 String.Empty,
1252                                                 ChartImageWrapMode.Scaled,
1253                                                 Color.Empty,
1254                                                 ChartImageAlignmentStyle.Center,
1255                                                 this.BackGradientStyle,
1256                                                 this.BackSecondaryColor,
1257                                                 this.LineColor,
1258                                                 1, // this.LineWidth,   NOTE: Cloud supports only 1 pixel border
1259                                                 this.LineDashStyle,
1260                                                 PenAlignment.Center);
1261                                 }
1262                         }
1263                         
1264                         // Draw text 
1265                         DrawText(graphics, rectanglePosition, true, false);
1266
1267                         return pathCloud;
1268                 }
1269
1270                 /// <summary>
1271                 /// Draws Perspective style callout.
1272                 /// </summary>
1273                 /// <param name="graphics">Chart graphics.</param>
1274                 /// <param name="rectanglePosition">Position of annotation objet.</param>
1275                 /// <param name="anchorPoint">Anchor location.</param>
1276                 /// <returns>Hot region of the cloud.</returns>
1277                 private GraphicsPath DrawPerspectiveCallout(
1278                         ChartGraphics graphics,
1279                         RectangleF rectanglePosition,
1280                         PointF anchorPoint)
1281                 {
1282                         // Draw rectangle
1283                         graphics.FillRectangleRel(
1284                                 rectanglePosition,
1285                                 this.BackColor,
1286                                 this.BackHatchStyle,
1287                                 String.Empty,
1288                                 ChartImageWrapMode.Scaled,
1289                                 Color.Empty,
1290                                 ChartImageAlignmentStyle.Center,
1291                                 this.BackGradientStyle,
1292                                 this.BackSecondaryColor,
1293                                 this.LineColor,
1294                                 this.LineWidth,
1295                                 this.LineDashStyle,
1296                                 this.ShadowColor,
1297                                 0,      // Shadow is never drawn
1298                                 PenAlignment.Center);
1299
1300                         // Create hot region path
1301                         GraphicsPath hotRegion = new GraphicsPath();
1302                         hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
1303
1304                         // Draw text 
1305                         DrawText(graphics, rectanglePosition, false, false);
1306
1307                         // Draw perspective polygons from anchoring point
1308                         if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
1309                         {
1310                                 // Check if point is inside annotation position
1311                                 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
1312                                 {
1313                                         Color[] perspectivePathColors = new Color[2];
1314                                         Color color = (this.BackColor.IsEmpty) ? Color.White : this.BackColor;
1315                                         perspectivePathColors[0] = graphics.GetBrightGradientColor(color, 0.6);
1316                                         perspectivePathColors[1] = graphics.GetBrightGradientColor(color, 0.8);
1317                                         GraphicsPath[] perspectivePaths = new GraphicsPath[2];
1318                                         using(perspectivePaths[0] = new GraphicsPath()) 
1319                                         {
1320                                                 using(perspectivePaths[1] = new GraphicsPath()) 
1321                                                 {
1322                                                         // Convert coordinates to absolute
1323                                                         RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
1324                                                         PointF anchorPointAbs = graphics.GetAbsolutePoint(anchorPoint);
1325
1326                                                         // Create paths of perspective
1327                                                         if(anchorPoint.Y < rectanglePosition.Y)
1328                                                         {
1329                                                                 PointF[] points1 = new PointF[3];
1330                                                                 points1[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
1331                                                                 points1[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1332                                                                 points1[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1333                                                                 perspectivePaths[0].AddLines(points1);
1334                                                                 if(anchorPoint.X < rectanglePosition.X)
1335                                                                 {
1336                                                                         PointF[] points2 = new PointF[3];
1337                                                                         points2[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1338                                                                         points2[1] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
1339                                                                         points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1340                                                                         perspectivePaths[1].AddLines(points2);
1341                                                                 }
1342                                                                 else if(anchorPoint.X > rectanglePosition.Right)
1343                                                                 {
1344                                                                         PointF[] points2 = new PointF[3];
1345                                                                         points2[0] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1346                                                                         points2[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1347                                                                         points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1348                                                                         perspectivePaths[1].AddLines(points2);
1349                                                                 }
1350                                                         }
1351                                                         else if(anchorPoint.Y > rectanglePosition.Bottom)
1352                                                         {
1353                                                                 PointF[] points1 = new PointF[3];
1354                                                                 points1[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1355                                                                 points1[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1356                                                                 points1[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1357                                                                 perspectivePaths[0].AddLines(points1);
1358                                                                 if(anchorPoint.X < rectanglePosition.X)
1359                                                                 {
1360                                                                         PointF[] points2 = new PointF[3];
1361                                                                         points2[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1362                                                                         points2[1] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
1363                                                                         points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1364                                                                         perspectivePaths[1].AddLines(points2);
1365                                                                 }
1366                                                                 else if(anchorPoint.X > rectanglePosition.Right)
1367                                                                 {
1368                                                                         PointF[] points2 = new PointF[3];
1369                                                                         points2[0] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1370                                                                         points2[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1371                                                                         points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1372                                                                         perspectivePaths[1].AddLines(points2);
1373                                                                 }
1374                                                         }
1375                                                         else
1376                                                         {
1377                                                                 if(anchorPoint.X < rectanglePosition.X)
1378                                                                 {
1379                                                                         PointF[] points2 = new PointF[3];
1380                                                                         points2[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
1381                                                                         points2[1] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
1382                                                                         points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1383                                                                         perspectivePaths[1].AddLines(points2);
1384                                                                 }
1385                                                                 else if(anchorPoint.X > rectanglePosition.Right)
1386                                                                 {
1387                                                                         PointF[] points2 = new PointF[3];
1388                                                                         points2[0] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
1389                                                                         points2[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
1390                                                                         points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
1391                                                                         perspectivePaths[1].AddLines(points2);
1392                                                                 }
1393                                                         }
1394
1395                                                         // Draw paths if non-empty
1396                                                         int index = 0;
1397                                                         foreach(GraphicsPath path in perspectivePaths)
1398                                                         {
1399                                                                 if(path.PointCount > 0)
1400                                                                 {
1401                                                                         path.CloseAllFigures();
1402                                                                         graphics.DrawPathAbs(
1403                                                                                 path,
1404                                                                                 perspectivePathColors[index],
1405                                                                                 this.BackHatchStyle,
1406                                                                                 String.Empty,
1407                                                                                 ChartImageWrapMode.Scaled,
1408                                                                                 Color.Empty,
1409                                                                                 ChartImageAlignmentStyle.Center,
1410                                                                                 this.BackGradientStyle,
1411                                                                                 this.BackSecondaryColor,
1412                                                                                 this.LineColor,
1413                                                                                 this.LineWidth,
1414                                                                                 this.LineDashStyle,
1415                                                                                 PenAlignment.Center);
1416
1417                                                                         // Add area to hot region path
1418                                                                         hotRegion.SetMarkers();
1419                                                                         hotRegion.AddPath( path, false );
1420                                                                 }
1421                                                                 ++index;
1422                                                         }
1423                                                 }
1424                                         }
1425                                 }
1426                         }
1427
1428                         return hotRegion;
1429                 }
1430
1431                 /// <summary>
1432                 /// Draws SimpleLine or BorderLine style callout.
1433                 /// </summary>
1434                 /// <param name="graphics">Chart graphics.</param>
1435                 /// <param name="rectanglePosition">Position of annotation objet.</param>
1436                 /// <param name="anchorPoint">Anchor location.</param>
1437                 /// <param name="drawRectangle">If true draws BorderLine style, otherwise SimpleLine.</param>
1438                 /// <returns>Hot region of the cloud.</returns>
1439                 private GraphicsPath DrawRectangleLineCallout(
1440                         ChartGraphics graphics,
1441                         RectangleF rectanglePosition,
1442                         PointF anchorPoint,
1443                         bool drawRectangle)
1444                 {
1445                         // Rectangle mode
1446                         if(drawRectangle)
1447                         {
1448                                 // Draw rectangle
1449                                 graphics.FillRectangleRel(
1450                                         rectanglePosition,
1451                                         this.BackColor,
1452                                         this.BackHatchStyle,
1453                                         String.Empty,
1454                                         ChartImageWrapMode.Scaled,
1455                                         Color.Empty,
1456                                         ChartImageAlignmentStyle.Center,
1457                                         this.BackGradientStyle,
1458                                         this.BackSecondaryColor,
1459                                         this.LineColor,
1460                                         this.LineWidth,
1461                                         this.LineDashStyle,
1462                                         this.ShadowColor,
1463                                         this.ShadowOffset,
1464                                         PenAlignment.Center);
1465
1466                                 // Draw text 
1467                                 DrawText(graphics, rectanglePosition, false, false);
1468                         }
1469                         else
1470                         {
1471                                 // Draw text 
1472                                 rectanglePosition = DrawText(graphics, rectanglePosition, false, true);
1473                                 SizeF pixelSize = graphics.GetRelativeSize(new SizeF(2f, 2f));
1474                                 rectanglePosition.Inflate(pixelSize);
1475                         }
1476
1477                         // Create hot region path
1478                         GraphicsPath hotRegion = new GraphicsPath();
1479                         hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
1480
1481                         // Define position of text underlying line
1482                         PointF  textLinePoint1 = new PointF(rectanglePosition.X, rectanglePosition.Bottom);
1483                         PointF  textLinePoint2 = new PointF(rectanglePosition.Right, rectanglePosition.Bottom);
1484
1485                         // Draw line to the anchor point
1486                         if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
1487                         {
1488                                 // Check if point is inside annotation position
1489                                 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
1490                                 {
1491                                         PointF  lineSecondPoint = PointF.Empty;
1492                                         if(anchorPoint.X < rectanglePosition.X)
1493                                         {
1494                                                 lineSecondPoint.X = rectanglePosition.X;
1495                                         }
1496                                         else if(anchorPoint.X > rectanglePosition.Right)
1497                                         {
1498                                                 lineSecondPoint.X = rectanglePosition.Right;
1499                                         }
1500                                         else
1501                                         {
1502                                                 lineSecondPoint.X = rectanglePosition.X + rectanglePosition.Width / 2f;
1503                                         }
1504
1505                                         if(anchorPoint.Y < rectanglePosition.Y)
1506                                         {
1507                                                 lineSecondPoint.Y = rectanglePosition.Y;
1508                                         }
1509                                         else if(anchorPoint.Y > rectanglePosition.Bottom)
1510                                         {
1511                                                 lineSecondPoint.Y = rectanglePosition.Bottom;
1512                                         }
1513                                         else
1514                                         {
1515                                                 lineSecondPoint.Y = rectanglePosition.Y + rectanglePosition.Height / 2f;
1516                                         }
1517
1518                                         // Set line caps
1519                                         bool capChanged = false;
1520                                         LineCap oldStartCap = LineCap.Flat;
1521                                         if(this.CalloutAnchorCap != LineAnchorCapStyle.None)
1522                                         {
1523                                                 // Save old pen
1524                                                 capChanged = true;
1525                                                 oldStartCap = graphics.Pen.StartCap;
1526
1527                                                 // Apply anchor cap settings
1528                                                 if(this.CalloutAnchorCap == LineAnchorCapStyle.Arrow)
1529                                                 {
1530                                                         // Adjust arrow size for small line width
1531                                                         if(this.LineWidth < 4)
1532                                                         {
1533                                                                 int adjustment = 3 - this.LineWidth;
1534                                 graphics.Pen.StartCap = LineCap.Custom;
1535                                 graphics.Pen.CustomStartCap = new AdjustableArrowCap(
1536                                                                         this.LineWidth + adjustment, 
1537                                                                         this.LineWidth + adjustment, 
1538                                                                         true);
1539                                                         }
1540                                                         else
1541                                                         {
1542                                 graphics.Pen.StartCap = LineCap.ArrowAnchor;
1543                                                         }
1544                                                 }
1545                                                 else if(this.CalloutAnchorCap == LineAnchorCapStyle.Diamond)
1546                                                 {
1547                             graphics.Pen.StartCap = LineCap.DiamondAnchor;
1548                                                 }
1549                                                 else if(this.CalloutAnchorCap == LineAnchorCapStyle.Round)
1550                                                 {
1551                             graphics.Pen.StartCap = LineCap.RoundAnchor;
1552                                                 }
1553                                                 else if(this.CalloutAnchorCap == LineAnchorCapStyle.Square)
1554                                                 {
1555                             graphics.Pen.StartCap = LineCap.SquareAnchor;
1556                                                 }
1557                                         }
1558
1559                                         // Draw callout line
1560                                         graphics.DrawLineAbs(
1561                                                 this.LineColor,
1562                                                 this.LineWidth,
1563                                                 this.LineDashStyle,
1564                                                 graphics.GetAbsolutePoint(anchorPoint),
1565                                                 graphics.GetAbsolutePoint(lineSecondPoint),
1566                                                 this.ShadowColor,
1567                                                 this.ShadowOffset);
1568
1569                                         // Create hot region path
1570                                         using( GraphicsPath linePath = new GraphicsPath() )
1571                                         {
1572                                                 linePath.AddLine(                                               
1573                                                         graphics.GetAbsolutePoint(anchorPoint),
1574                                                         graphics.GetAbsolutePoint(lineSecondPoint) );
1575
1576                                                 linePath.Widen(new Pen(Color.Black, this.LineWidth + 2));
1577                                                 hotRegion.SetMarkers();
1578                                                 hotRegion.AddPath( linePath, false );
1579                                         }
1580
1581                                         // Restore line caps
1582                                         if(capChanged)
1583                                         {
1584                         graphics.Pen.StartCap = oldStartCap;
1585                                         }
1586
1587                                         // Adjust text underlying line position
1588                                         if(anchorPoint.Y < rectanglePosition.Y)
1589                                         {
1590                                                 textLinePoint1.Y = rectanglePosition.Y;
1591                                                 textLinePoint2.Y = rectanglePosition.Y;
1592                                         }
1593                                         else if(anchorPoint.Y > rectanglePosition.Y && 
1594                                                 anchorPoint.Y < rectanglePosition.Bottom)
1595                                         {
1596                                                 textLinePoint1.Y = rectanglePosition.Y;
1597                                                 textLinePoint2.Y = rectanglePosition.Bottom;
1598                                                 if(anchorPoint.X < rectanglePosition.X)
1599                                                 {
1600                                                         textLinePoint1.X = rectanglePosition.X;
1601                                                         textLinePoint2.X = rectanglePosition.X;
1602                                                 }
1603                                                 else
1604                                                 {
1605                                                         textLinePoint1.X = rectanglePosition.Right;
1606                                                         textLinePoint2.X = rectanglePosition.Right;
1607                                                 }
1608                                         }
1609                                 }
1610
1611                                 // Draw text underlying line
1612                                 if(!drawRectangle)
1613                                 {
1614                                         graphics.DrawLineAbs(
1615                                                 this.LineColor,
1616                                                 this.LineWidth,
1617                                                 this.LineDashStyle,
1618                                                 graphics.GetAbsolutePoint(textLinePoint1),
1619                                                 graphics.GetAbsolutePoint(textLinePoint2),
1620                                                 this.ShadowColor,
1621                                                 this.ShadowOffset);
1622
1623                                         // Create hot region path
1624                                         using( GraphicsPath linePath = new GraphicsPath() )
1625                                         {
1626                                                 linePath.AddLine(                                               
1627                                                         graphics.GetAbsolutePoint(textLinePoint1),
1628                                                         graphics.GetAbsolutePoint(textLinePoint2) );
1629
1630                                                 linePath.Widen(new Pen(Color.Black, this.LineWidth + 2));
1631                                                 hotRegion.SetMarkers();
1632                                                 hotRegion.AddPath( linePath, false );
1633                                         }
1634
1635                                 }
1636                         }
1637
1638                         return hotRegion;
1639                 }
1640
1641                 #endregion // Painting
1642
1643                 #region Anchor Methods
1644
1645                 /// <summary>
1646                 /// Checks if annotation draw anything in the anchor position (except selection handle)
1647                 /// </summary>
1648                 /// <returns>True if annotation "connects" itself and anchor point visually.</returns>
1649                 override internal bool IsAnchorDrawn()
1650                 {
1651                         return true;
1652                 }
1653
1654                 #endregion // Anchor Methods
1655
1656                 #region Helper methods
1657
1658                 /// <summary>
1659                 /// Gets cloud callout outline graphics path.
1660                 /// </summary>
1661                 /// <param name="position">Absolute position of the callout cloud.</param>
1662                 /// <returns>Cloud outline path.</returns>
1663                 private static GraphicsPath GetCloudOutlinePath(RectangleF position)
1664                 {
1665                         if(_cloudOutlinePath == null)
1666                         {
1667                                 GetCloudPath(position);
1668                         }
1669
1670                         // Translate and sacle original path to fit specified position
1671                         GraphicsPath resultPath = (GraphicsPath)_cloudOutlinePath.Clone();
1672                         Matrix matrix = new Matrix();
1673                         matrix.Translate(-_cloudBounds.X, -_cloudBounds.Y);
1674                         resultPath.Transform(matrix);
1675                         matrix = new Matrix();
1676                         matrix.Translate(position.X, position.Y);
1677                         matrix.Scale(position.Width / _cloudBounds.Width, position.Height / _cloudBounds.Height);
1678                         resultPath.Transform(matrix);
1679
1680                         return resultPath;
1681                 }
1682         
1683                 /// <summary>
1684                 /// Gets cloud callout graphics path.
1685                 /// </summary>
1686                 /// <param name="position">Absolute position of the callout cloud.</param>
1687                 /// <returns>Cloud path.</returns>
1688                 private static GraphicsPath GetCloudPath(RectangleF position)
1689                 {
1690                         // Check if cloud path was already created
1691                         if(_cloudPath == null)
1692                         {
1693                                 // Create cloud path
1694                                 _cloudPath = new GraphicsPath();
1695
1696                                 _cloudPath.AddBezier(1689.5f, 1998.6f, 1581.8f, 2009.4f, 1500f, 2098.1f, 1500f, 2204f);
1697
1698                                 _cloudPath.AddBezier(1500f, 2204f, 1499.9f, 2277.2f, 1539.8f, 2345.1f, 1604.4f, 2382.1f);
1699
1700                                 _cloudPath.AddBezier(1603.3f, 2379.7f, 1566.6f, 2417.8f, 1546.2f, 2468.1f, 1546.2f, 2520.1f);
1701                                 _cloudPath.AddBezier(1546.2f, 2520.1f, 1546.2f, 2633.7f, 1641.1f, 2725.7f, 1758.1f, 2725.7f);
1702                                 _cloudPath.AddBezier(1758.1f, 2725.7f, 1766.3f, 2725.6f, 1774.6f, 2725.2f, 1782.8f, 2724.2f);
1703
1704                                 _cloudPath.AddBezier(1781.7f, 2725.6f, 1848.5f, 2839.4f, 1972.8f, 2909.7f, 2107.3f, 2909.7f);
1705                                 _cloudPath.AddBezier(2107.3f, 2909.7f, 2175.4f, 2909.7f, 2242.3f, 2891.6f, 2300.6f, 2857.4f);
1706
1707                                 _cloudPath.AddBezier(2300f, 2857.6f, 2360.9f, 2946.5f, 2463.3f, 2999.7f, 2572.9f, 2999.7f);
1708                                 _cloudPath.AddBezier(2572.9f, 2999.7f, 2717.5f, 2999.7f, 2845.2f, 2907.4f, 2887.1f, 2772.5f);
1709
1710                                 _cloudPath.AddBezier(2887.4f, 2774.3f, 2932.1f, 2801.4f, 2983.6f, 2815.7f, 3036.3f, 2815.7f);
1711                                 _cloudPath.AddBezier(3036.3f, 2815.7f, 3190.7f, 2815.7f, 3316.3f, 2694.8f, 3317.5f, 2544.8f);
1712
1713                                 _cloudPath.AddBezier(3317f, 2544.1f, 3479.2f, 2521.5f, 3599.7f, 2386.5f, 3599.7f, 2227.2f);
1714                                 _cloudPath.AddBezier(3599.7f, 2227.2f, 3599.7f, 2156.7f, 3575.7f, 2088.1f, 3531.6f, 2032.2f);
1715
1716                                 _cloudPath.AddBezier(3530.9f, 2032f, 3544.7f, 2000.6f, 3551.9f, 1966.7f, 3551.9f, 1932.5f);
1717                                 _cloudPath.AddBezier(3551.9f, 1932.5f, 3551.9f, 1818.6f, 3473.5f, 1718.8f, 3360.7f, 1688.8f);
1718
1719                                 _cloudPath.AddBezier(3361.6f, 1688.3f, 3341.4f, 1579.3f, 3243.5f, 1500f, 3129.3f, 1500f);
1720                                 _cloudPath.AddBezier(3129.3f, 1500f, 3059.8f, 1499.9f, 2994f, 1529.6f, 2949.1f, 1580.9f);
1721
1722                                 _cloudPath.AddBezier(2949.5f, 1581.3f, 2909.4f, 1530f, 2847f, 1500f, 2780.8f, 1500f);
1723                                 _cloudPath.AddBezier(2780.8f, 1500f, 2700.4f, 1499.9f, 2626.8f, 1544.2f, 2590.9f, 1614.2f);
1724
1725                                 _cloudPath.AddBezier(2591.7f, 1617.6f, 2543.2f, 1571.1f, 2477.9f, 1545.1f, 2409.8f, 1545.1f);
1726                                 _cloudPath.AddBezier(2409.8f, 1545.1f, 2313.9f, 1545.1f, 2225.9f, 1596.6f, 2180.8f, 1679f);
1727
1728                                 _cloudPath.AddBezier(2180.1f, 1680.7f, 2129.7f, 1652f, 2072.4f, 1636.9f, 2014.1f, 1636.9f);
1729                                 _cloudPath.AddBezier(2014.1f, 1636.9f, 1832.8f, 1636.9f, 1685.9f, 1779.8f, 1685.9f, 1956f);
1730                                 _cloudPath.AddBezier(1685.9f, 1956f, 1685.8f, 1970.4f, 1686.9f, 1984.8f, 1688.8f, 1999f);
1731
1732                                 _cloudPath.CloseAllFigures();
1733
1734
1735                                 // Create cloud outline path
1736                                 _cloudOutlinePath = new GraphicsPath();
1737
1738                                 _cloudOutlinePath.AddBezier(1604.4f, 2382.1f, 1636.8f, 2400.6f, 1673.6f, 2410.3f, 1711.2f, 2410.3f);
1739                                 _cloudOutlinePath.AddBezier(1711.2f, 2410.3f, 1716.6f, 2410.3f, 1722.2f, 2410.2f, 1727.6f, 2409.8f);
1740                         
1741                                 _cloudOutlinePath.StartFigure();
1742                                 _cloudOutlinePath.AddBezier(1782.8f, 2724.2f, 1801.3f, 2722.2f, 1819.4f, 2717.7f, 1836.7f, 2711f);
1743
1744                                 _cloudOutlinePath.StartFigure();
1745                                 _cloudOutlinePath.AddBezier(2267.6f, 2797.2f, 2276.1f, 2818.4f, 2287f, 2838.7f, 2300f, 2857.6f);
1746
1747                                 _cloudOutlinePath.StartFigure();
1748                                 _cloudOutlinePath.AddBezier(2887.1f, 2772.5f, 2893.8f, 2750.9f, 2898.1f, 2728.7f, 2900f, 2706.3f);
1749
1750                                 // NOTE: This cloud segment overlaps text too much. Removed for now!
1751                                 //cloudOutlinePath.StartFigure();
1752                                 //cloudOutlinePath.AddBezier(3317.5f, 2544.8f, 3317.5f, 2544f, 3317.6f, 2543.3f, 3317.6f, 2542.6f);
1753                                 //cloudOutlinePath.AddBezier(3317.6f, 2542.6f, 3317.6f, 2438.1f, 3256.1f, 2342.8f, 3159.5f, 2297f);
1754
1755                                 _cloudOutlinePath.StartFigure();
1756                                 _cloudOutlinePath.AddBezier(3460.5f, 2124.9f, 3491f, 2099.7f, 3515f, 2067.8f, 3530.9f, 2032f);
1757
1758                                 _cloudOutlinePath.StartFigure();
1759                                 _cloudOutlinePath.AddBezier(3365.3f, 1732.2f, 3365.3f, 1731.1f, 3365.4f, 1730.1f, 3365.4f, 1729f);
1760                                 _cloudOutlinePath.AddBezier(3365.4f, 1729f, 3365.4f, 1715.3f, 3364.1f, 1701.7f, 3361.6f, 1688.3f);
1761
1762                                 _cloudOutlinePath.StartFigure();
1763                                 _cloudOutlinePath.AddBezier(2949.1f, 1580.9f, 2934.4f, 1597.8f, 2922.3f, 1616.6f, 2913.1f, 1636.9f);
1764                                 _cloudOutlinePath.CloseFigure();
1765
1766                                 _cloudOutlinePath.StartFigure();
1767                                 _cloudOutlinePath.AddBezier(2590.9f, 1614.2f, 2583.1f, 1629.6f, 2577.2f, 1645.8f, 2573.4f, 1662.5f);
1768
1769                                 _cloudOutlinePath.StartFigure();
1770                                 _cloudOutlinePath.AddBezier(2243.3f, 1727.5f, 2224.2f, 1709.4f, 2203f, 1693.8f, 2180.1f, 1680.7f);
1771
1772                                 _cloudOutlinePath.StartFigure();
1773                                 _cloudOutlinePath.AddBezier(1688.8f, 1999f, 1691.1f, 2015.7f, 1694.8f, 2032.2f, 1699.9f, 2048.3f);
1774
1775                                 _cloudOutlinePath.CloseAllFigures();
1776
1777                                 // Get cloud path bounds
1778                                 _cloudBounds = _cloudPath.GetBounds();
1779                         }
1780
1781                         // Translate and sacle original path to fit specified position
1782                         GraphicsPath resultPath = (GraphicsPath)_cloudPath.Clone();
1783                         Matrix matrix = new Matrix();
1784                         matrix.Translate(-_cloudBounds.X, -_cloudBounds.Y);
1785                         resultPath.Transform(matrix);
1786                         matrix = new Matrix();
1787                         matrix.Translate(position.X, position.Y);
1788                         matrix.Scale(position.Width / _cloudBounds.Width, position.Height / _cloudBounds.Height);
1789                         resultPath.Transform(matrix);
1790
1791                         return resultPath;
1792                 }
1793
1794                 /// <summary>
1795                 /// Gets intersection point coordinates between point line and and horizontal 
1796                 /// line specified by Y coordinate.
1797                 /// </summary>
1798                 /// <param name="firstPoint">First data point.</param>
1799                 /// <param name="secondPoint">Second data point.</param>
1800                 /// <param name="pointY">Y coordinate.</param>
1801                 /// <returns>Intersection point coordinates.</returns>
1802                 internal static PointF GetIntersectionY(PointF firstPoint, PointF secondPoint, float pointY)
1803                 {
1804                         PointF  intersectionPoint = new PointF();
1805                         intersectionPoint.Y = pointY;
1806                         intersectionPoint.X = (pointY - firstPoint.Y) *
1807                                 (secondPoint.X - firstPoint.X) / 
1808                                 (secondPoint.Y - firstPoint.Y) + 
1809                                 firstPoint.X;
1810                         return intersectionPoint;
1811                 }
1812
1813                 /// <summary>
1814                 /// Gets intersection point coordinates between point line and and vertical 
1815                 /// line specified by X coordinate.
1816                 /// </summary>
1817                 /// <param name="firstPoint">First data point.</param>
1818                 /// <param name="secondPoint">Second data point.</param>
1819                 /// <param name="pointX">X coordinate.</param>
1820                 /// <returns>Intersection point coordinates.</returns>
1821                 internal static PointF GetIntersectionX(PointF firstPoint, PointF secondPoint, float pointX)
1822                 {
1823                         PointF  intersectionPoint = new PointF();
1824                         intersectionPoint.X = pointX;
1825                         intersectionPoint.Y = (pointX - firstPoint.X) *
1826                                 (secondPoint.Y - firstPoint.Y) / 
1827                                 (secondPoint.X - firstPoint.X) + 
1828                                 firstPoint.Y;
1829                         return intersectionPoint;
1830                 }
1831
1832                 /// <summary>
1833                 /// Adds a horizontal or vertical line into the path as multiple segments.
1834                 /// </summary>
1835                 /// <param name="path">Graphics path.</param>
1836                 /// <param name="x1">First point X coordinate.</param>
1837                 /// <param name="y1">First point Y coordinate.</param>
1838                 /// <param name="x2">Second point X coordinate.</param>
1839                 /// <param name="y2">Second point Y coordinate.</param>
1840                 /// <param name="segments">Number of segments to add.</param>
1841                 private void PathAddLineAsSegments(GraphicsPath path, float x1, float y1, float x2, float y2, int segments)
1842                 {
1843                         if(x1 == x2)
1844                         {
1845                                 float distance = (y2 - y1) / segments;
1846                                 for(int index = 0; index < segments; index++)
1847                                 {
1848                                         path.AddLine(x1, y1, x1, y1 + distance);
1849                                         y1 += distance;
1850                                 }
1851                         }
1852                         else if(y1 == y2)
1853                         {
1854                                 float distance = (x2 - x1) / segments;
1855                                 for(int index = 0; index < segments; index++)
1856                                 {
1857                                         path.AddLine(x1, y1, x1 + distance, y1);
1858                                         x1 += distance;
1859                                 }
1860                         }
1861                         else
1862                         {
1863                 throw (new InvalidOperationException(SR.ExceptionAnnotationPathAddLineAsSegmentsInvalid));
1864                         }
1865                 }
1866                 /// <summary>
1867                 /// Helper function which creates a rounded rectangle path.
1868                 /// Extra points are added on the sides to allow anchor connection.
1869                 /// </summary>
1870                 /// <param name="rect">Rectangle coordinates.</param>
1871                 /// <param name="cornerRadius">Corner radius.</param>
1872                 /// <returns>Graphics path object.</returns>
1873                 private GraphicsPath CreateRoundedRectPath(RectangleF rect, float cornerRadius)
1874                 {
1875                         // Create rounded rectangle path
1876                         GraphicsPath path = new GraphicsPath();
1877                         int segments = 10;
1878                         PathAddLineAsSegments(path, rect.X+cornerRadius, rect.Y, rect.Right-cornerRadius, rect.Y, segments);
1879
1880                         path.AddArc(rect.Right-2f*cornerRadius, rect.Y, 2f*cornerRadius, 2f*cornerRadius, 270, 90);
1881
1882                         PathAddLineAsSegments(path, rect.Right, rect.Y + cornerRadius, rect.Right, rect.Bottom - cornerRadius, segments);
1883
1884                         path.AddArc(rect.Right-2f*cornerRadius, rect.Bottom-2f*cornerRadius, 2f*cornerRadius, 2f*cornerRadius, 0, 90);
1885
1886                         PathAddLineAsSegments(path, rect.Right-cornerRadius, rect.Bottom, rect.X + cornerRadius, rect.Bottom, segments);
1887
1888                         path.AddArc(rect.X, rect.Bottom-2f*cornerRadius, 2f*cornerRadius, 2f*cornerRadius, 90, 90);
1889
1890                         PathAddLineAsSegments(path, rect.X, rect.Bottom-cornerRadius, rect.X, rect.Y+cornerRadius, segments);
1891
1892                         path.AddArc(rect.X, rect.Y, 2f*cornerRadius, 2f*cornerRadius, 180, 90);
1893
1894                         return path;
1895                 }
1896
1897                 #endregion // Helper methods
1898
1899                 #endregion
1900         }
1901 }