1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: CalloutAnnotation.cs
10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting
12 // Classes: CalloutAnnotation
14 // Purpose: Callout annotation classes.
18 //===================================================================
20 #region Used namespace
22 using System.Collections;
23 using System.Collections.Specialized;
24 using System.ComponentModel;
25 using System.ComponentModel.Design;
28 using System.Drawing.Design;
29 using System.Drawing.Text;
30 using System.Drawing.Drawing2D;
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;
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;
51 namespace System.Windows.Forms.DataVisualization.Charting
54 namespace System.Web.UI.DataVisualization.Charting
61 /// Annotation callout style.
62 /// <seealso cref="CalloutAnnotation.CalloutStyle"/>
65 SRDescription("DescriptionAttributeCalloutStyle_CalloutStyle"),
67 public enum CalloutStyle
70 /// Callout text is underlined and a line is pointing to the anchor point.
75 /// Border is drawn around text and a line is pointing to the anchor point.
80 /// Callout text is inside the cloud and smaller clouds are pointing to the anchor point.
85 /// Rectangle is drawn around the callout text, which is connected with the anchor point.
90 /// Rounded rectangle is drawn around the callout text, which is connected with the anchor point.
95 /// Ellipse is drawn around the callout text, which is connected with the anchor point.
100 /// Perspective rectangle is drawn around the callout text, which is connected with the anchor point.
108 /// <b>CalloutAnnotation</b> is a class class that represents a callout annotation.
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.
116 SRDescription("DescriptionAttributeCalloutAnnotation_CalloutAnnotation"),
119 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
120 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
122 public class CalloutAnnotation : TextAnnotation
126 // Callout anchor type
127 private LineAnchorCapStyle _calloutAnchorCap = LineAnchorCapStyle.Arrow;
129 // Callout drawing style
130 private CalloutStyle _calloutStyle = CalloutStyle.Rectangle;
133 private static GraphicsPath _cloudPath = null;
135 // Cloud shape outline path
136 private static GraphicsPath _cloudOutlinePath = null;
138 // Cloud shape boundary rectangle
139 private static RectangleF _cloudBounds = RectangleF.Empty;
143 #region Construction and Initialization
146 /// Default public constructor.
148 public CalloutAnnotation()
151 // Changing default values of properties
152 this.anchorOffsetX = 3.0;
153 this.anchorOffsetY = 3.0;
154 this.anchorAlignment = ContentAlignment.BottomLeft;
161 #region Callout properties
164 /// Gets or sets the annotation callout style.
167 /// <see cref="CalloutStyle"/> of the annotation.
170 SRCategory("CategoryAttributeAppearance"),
172 DefaultValue(CalloutStyle.Rectangle),
173 SRDescription("DescriptionAttributeCalloutAnnotation_CalloutStyle"),
174 ParenthesizePropertyNameAttribute(true),
176 virtual public CalloutStyle CalloutStyle
180 return _calloutStyle;
184 _calloutStyle = value;
185 this.ResetCurrentRelativePosition();
187 // Reset content size to empty
188 contentSize = SizeF.Empty;
195 /// Gets or sets the anchor cap style of a callout line.
198 /// A <see cref="LineAnchorCapStyle"/> value used as the anchor cap of a callout line.
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
206 SRCategory("CategoryAttributeAppearance"),
208 DefaultValue(LineAnchorCapStyle.Arrow),
209 SRDescription("DescriptionAttributeCalloutAnnotation_CalloutAnchorCap"),
211 virtual public LineAnchorCapStyle CalloutAnchorCap
215 return _calloutAnchorCap;
219 _calloutAnchorCap = value;
223 #endregion // Callout properties
225 #region Applicable Annotation Appearance Attributes (set as Browsable)
228 /// Gets or sets the color of an annotation line.
229 /// <seealso cref="LineWidth"/>
230 /// <seealso cref="LineDashStyle"/>
233 /// A <see cref="Color"/> value used to draw an annotation line.
236 SRCategory("CategoryAttributeAppearance"),
238 DefaultValue(typeof(Color), "Black"),
239 SRDescription("DescriptionAttributeLineColor"),
240 TypeConverter(typeof(ColorConverter)),
241 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
243 override public Color LineColor
247 return base.LineColor;
251 base.LineColor = value;
256 /// Gets or sets the width of an annotation line.
257 /// <seealso cref="LineColor"/>
258 /// <seealso cref="LineDashStyle"/>
261 /// An integer value defining the width of an annotation line in pixels.
264 SRCategory("CategoryAttributeAppearance"),
267 SRDescription("DescriptionAttributeLineWidth"),
269 override public int LineWidth
273 return base.LineWidth;
277 base.LineWidth = value;
283 /// Gets or sets the style of an annotation line.
284 /// <seealso cref="LineWidth"/>
285 /// <seealso cref="LineColor"/>
288 /// A <see cref="ChartDashStyle"/> value used to draw an annotation line.
291 SRCategory("CategoryAttributeAppearance"),
293 DefaultValue(ChartDashStyle.Solid),
294 SRDescription("DescriptionAttributeLineDashStyle"),
296 override public ChartDashStyle LineDashStyle
300 return base.LineDashStyle;
304 base.LineDashStyle = value;
309 /// Gets or sets the background color of an annotation.
310 /// <seealso cref="BackSecondaryColor"/>
311 /// <seealso cref="BackHatchStyle"/>
312 /// <seealso cref="BackGradientStyle"/>
315 /// A <see cref="Color"/> value used for the background of an annotation.
318 SRCategory("CategoryAttributeAppearance"),
320 DefaultValue(typeof(Color), ""),
321 SRDescription("DescriptionAttributeBackColor"),
322 NotifyParentPropertyAttribute(true),
323 TypeConverter(typeof(ColorConverter)),
324 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
326 override public Color BackColor
330 return base.BackColor;
334 base.BackColor = value;
339 /// Gets or sets the background hatch style of an annotation.
340 /// <seealso cref="BackSecondaryColor"/>
341 /// <seealso cref="BackColor"/>
342 /// <seealso cref="BackGradientStyle"/>
345 /// A <see cref="ChartHatchStyle"/> value used for the background of an annotation.
348 /// Two colors are used to draw the hatching, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
351 SRCategory("CategoryAttributeAppearance"),
353 DefaultValue(ChartHatchStyle.None),
354 NotifyParentPropertyAttribute(true),
355 SRDescription("DescriptionAttributeBackHatchStyle"),
356 Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
358 override public ChartHatchStyle BackHatchStyle
362 return base.BackHatchStyle;
366 base.BackHatchStyle = value;
371 /// Gets or sets the background gradient style of an annotation.
372 /// <seealso cref="BackSecondaryColor"/>
373 /// <seealso cref="BackColor"/>
374 /// <seealso cref="BackHatchStyle"/>
377 /// A <see cref="GradientStyle"/> value used for the background of an annotation.
380 /// Two colors are used to draw the gradient, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
383 SRCategory("CategoryAttributeAppearance"),
385 DefaultValue(GradientStyle.None),
386 NotifyParentPropertyAttribute(true),
387 SRDescription("DescriptionAttributeBackGradientStyle"),
388 Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
390 override public GradientStyle BackGradientStyle
394 return base.BackGradientStyle;
398 base.BackGradientStyle = value;
403 /// Gets or sets the secondary background color of an annotation.
404 /// <seealso cref="BackColor"/>
405 /// <seealso cref="BackHatchStyle"/>
406 /// <seealso cref="BackGradientStyle"/>
409 /// A <see cref="Color"/> value used for the secondary color of an annotation background with
410 /// hatching or gradient fill.
413 /// This color is used with <see cref="BackColor"/> when <see cref="BackHatchStyle"/> or
414 /// <see cref="BackGradientStyle"/> are used.
417 SRCategory("CategoryAttributeAppearance"),
419 DefaultValue(typeof(Color), ""),
420 NotifyParentPropertyAttribute(true),
421 SRDescription("DescriptionAttributeBackSecondaryColor"),
422 TypeConverter(typeof(ColorConverter)),
423 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base)
425 override public Color BackSecondaryColor
429 return base.BackSecondaryColor;
433 base.BackSecondaryColor = value;
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"/>
449 /// A double value that represents the x-coordinate offset between the positions of an annotation and its anchor point.
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>.
457 SRCategory("CategoryAttributeAnchor"),
459 SRDescription("DescriptionAttributeCalloutAnnotation_AnchorOffsetX"),
460 RefreshPropertiesAttribute(RefreshProperties.All),
462 override public double AnchorOffsetX
466 return base.AnchorOffsetX;
470 base.AnchorOffsetX = value;
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"/>
482 /// A double value that represents the y-coordinate offset between the positions of an annotation and its anchor point.
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>.
490 SRCategory("CategoryAttributeAnchor"),
492 SRDescription("DescriptionAttributeCalloutAnnotation_AnchorOffsetY"),
493 RefreshPropertiesAttribute(RefreshProperties.All),
495 override public double AnchorOffsetY
499 return base.AnchorOffsetY;
503 base.AnchorOffsetY = value;
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"/>
516 /// A <see cref="ContentAlignment"/> value that represents the annotation's alignment to
517 /// the anchor point.
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>.
525 SRCategory("CategoryAttributeAnchor"),
526 DefaultValue(typeof(ContentAlignment), "BottomLeft"),
527 SRDescription("DescriptionAttributeAnchorAlignment"),
529 override public ContentAlignment AnchorAlignment
533 return base.AnchorAlignment;
537 base.AnchorAlignment = value;
541 #endregion // Anchoring
546 /// Gets or sets an annotation's type name.
549 /// This property is used to get the name of each annotation type
550 /// (e.g. Line, Rectangle, Ellipse).
552 /// This property is for internal use and is hidden at design and run time.
556 SRCategory("CategoryAttributeMisc"),
559 EditorBrowsableAttribute(EditorBrowsableState.Never),
560 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
561 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
562 SRDescription("DescriptionAttributeAnnotationType"),
564 public override string AnnotationType
573 /// Gets or sets annotation selection points style.
576 /// A <see cref="SelectionPointsStyle"/> value that represents annotation
580 /// This property is for internal use and is hidden at design and run time.
583 SRCategory("CategoryAttributeAppearance"),
584 DefaultValue(SelectionPointsStyle.Rectangle),
585 ParenthesizePropertyNameAttribute(true),
587 EditorBrowsableAttribute(EditorBrowsableState.Never),
588 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
589 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
590 SRDescription("DescriptionAttributeSelectionPointsStyle"),
592 override internal SelectionPointsStyle SelectionPointsStyle
596 return SelectionPointsStyle.Rectangle;
609 /// Gets text spacing on four different sides in relative coordinates.
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)
615 RectangleF spacing = base.GetTextSpacing(out annotationRelative);
616 if(this._calloutStyle == CalloutStyle.Cloud ||
617 this._calloutStyle == CalloutStyle.Ellipse)
619 spacing = new RectangleF(4f, 4f, 4f, 4f);
620 annotationRelative = true;
622 else if(this._calloutStyle == CalloutStyle.RoundedRectangle)
624 spacing = new RectangleF(1f, 1f, 1f, 1f);
625 annotationRelative = true;
631 #endregion // Text Spacing
636 /// Paints annotation object on specified graphics.
638 /// <param name="graphics">
639 /// A <see cref="ChartGraphics"/> used to paint annotation object.
641 /// <param name="chart">
642 /// Reference to the <see cref="Chart"/> control.
644 override internal void Paint(Chart chart, ChartGraphics graphics)
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);
653 // Create selection rectangle
654 RectangleF selectionRect = new RectangleF(firstPoint, new SizeF(secondPoint.X - firstPoint.X, secondPoint.Y - firstPoint.Y));
656 // Adjust negative rectangle width and height
657 RectangleF rectanglePosition = new RectangleF(selectionRect.Location, selectionRect.Size);
658 if(rectanglePosition.Width < 0)
660 rectanglePosition.X = rectanglePosition.Right;
661 rectanglePosition.Width = -rectanglePosition.Width;
663 if(rectanglePosition.Height < 0)
665 rectanglePosition.Y = rectanglePosition.Bottom;
666 rectanglePosition.Height = -rectanglePosition.Height;
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) )
678 // Paint different style of callouts
679 GraphicsPath hotRegionPathAbs = null;
680 if(this.Common.ProcessModePaint)
682 switch(this._calloutStyle)
684 case(CalloutStyle.SimpleLine):
685 hotRegionPathAbs = DrawRectangleLineCallout(
691 case(CalloutStyle.Borderline):
692 hotRegionPathAbs = DrawRectangleLineCallout(
698 case(CalloutStyle.Perspective):
699 hotRegionPathAbs = DrawPerspectiveCallout(
704 case(CalloutStyle.Cloud):
705 hotRegionPathAbs = DrawCloudCallout(
710 case(CalloutStyle.Rectangle):
711 hotRegionPathAbs = DrawRectangleCallout(
716 case(CalloutStyle.Ellipse):
717 hotRegionPathAbs = DrawRoundedRectCallout(
723 case(CalloutStyle.RoundedRectangle):
724 hotRegionPathAbs = DrawRoundedRectCallout(
733 if(this.Common.ProcessModeRegions)
735 if(hotRegionPathAbs != null)
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);
741 // There is more then one path.
742 using (GraphicsPath subPath = new GraphicsPath())
744 while (iterator.NextMarker(subPath) > 0)
746 // Use callout defined hot region
747 this.Common.HotRegionsList.AddHotRegion(
751 ReplaceKeywords(this.ToolTip),
752 #if Microsoft_CONTROL
756 #else // Microsoft_CONTROL
757 ReplaceKeywords(this.Url),
758 ReplaceKeywords(this.MapAreaAttributes),
759 ReplaceKeywords(this.PostBackValue),
760 #endif // Microsoft_CONTROL
762 ChartElementType.Annotation);
764 // Reset current path
771 // Use rectangular hot region
772 this.Common.HotRegionsList.AddHotRegion(
774 ReplaceKeywords(this.ToolTip),
775 #if Microsoft_CONTROL
779 #else // Microsoft_CONTROL
780 ReplaceKeywords(this.Url),
781 ReplaceKeywords(this.MapAreaAttributes),
782 ReplaceKeywords(this.PostBackValue),
783 #endif // Microsoft_CONTROL
785 ChartElementType.Annotation,
791 if (hotRegionPathAbs != null)
792 hotRegionPathAbs.Dispose();
794 // Paint selection handles
795 PaintSelectionHandles(graphics, selectionRect, null);
799 /// Draws Rounded rectangle or Ellipse style callout.
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,
812 // Get absolute position
813 RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
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)
822 // Create ellipse path
823 GraphicsPath ellipsePath = new GraphicsPath();
827 ellipsePath.AddEllipse(rectanglePositionAbs);
831 // Add rounded rectangle shape
832 float radius = Math.Min(rectanglePositionAbs.Width, rectanglePositionAbs.Height);
834 ellipsePath = this.CreateRoundedRectPath(rectanglePositionAbs, radius);
837 // Draw perspective polygons from anchoring point
838 if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
840 // Check if point is inside annotation position
841 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
843 // Get absolute anchor point
844 PointF anchorPointAbs = graphics.GetAbsolutePoint(new PointF(anchorPoint.X, anchorPoint.Y));
846 // Flatten ellipse path
847 ellipsePath.Flatten();
849 // Find point in the path closest to the anchor point
850 PointF[] points = ellipsePath.PathPoints;
851 int closestPointIndex = 0;
853 float currentDistance = float.MaxValue;
854 foreach(PointF point in points)
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)
861 currentDistance = distance;
862 closestPointIndex = index;
867 // Change point to the anchor location
868 points[closestPointIndex] = anchorPointAbs;
870 // Recreate ellipse path
872 ellipsePath.AddLines(points);
873 ellipsePath.CloseAllFigures();
878 graphics.DrawPathAbs(
883 ChartImageWrapMode.Scaled,
885 ChartImageAlignmentStyle.Center,
886 this.BackGradientStyle,
887 this.BackSecondaryColor,
896 DrawText(graphics, rectanglePosition, true, false);
902 /// Draws Rectangle style callout.
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,
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))
918 // Get relative size of a pixel
919 SizeF pixelSize = graphics.GetRelativeSize(new SizeF(1f, 1f));
921 // Increase annotation position rectangle by 1 pixel
922 RectangleF inflatedPosition = new RectangleF(rectanglePosition.Location, rectanglePosition.Size);
923 inflatedPosition.Inflate(pixelSize);
925 // Check if point is inside annotation position
926 if(!inflatedPosition.Contains(anchorPoint.X, anchorPoint.Y))
928 anchorVisible = true;
930 // Get absolute position
931 RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
933 // Get absolute anchor point
934 PointF anchorPointAbs = graphics.GetAbsolutePoint(new PointF(anchorPoint.X, anchorPoint.Y));
936 // Calculate anchor pointer thicness
937 float size = Math.Min(rectanglePositionAbs.Width, rectanglePositionAbs.Height);
940 // Create shape points
941 PointF[] points = new PointF[7];
942 if(anchorPoint.X < rectanglePosition.X &&
943 anchorPoint.Y > rectanglePosition.Bottom)
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);
953 else if(anchorPoint.X >= rectanglePosition.X &&
954 anchorPoint.X <= rectanglePosition.Right &&
955 anchorPoint.Y > rectanglePosition.Bottom)
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);
965 else if(anchorPoint.X > rectanglePosition.Right &&
966 anchorPoint.Y > rectanglePosition.Bottom)
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);
976 else if(anchorPoint.X > rectanglePosition.Right &&
977 anchorPoint.Y <= rectanglePosition.Bottom &&
978 anchorPoint.Y >= rectanglePosition.Y)
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);
988 else if(anchorPoint.X > rectanglePosition.Right &&
989 anchorPoint.Y < rectanglePosition.Y)
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);
999 else if(anchorPoint.X >= rectanglePosition.X &&
1000 anchorPoint.X <= rectanglePosition.Right &&
1001 anchorPoint.Y < rectanglePosition.Y)
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);
1011 else if(anchorPoint.X < rectanglePosition.X &&
1012 anchorPoint.Y < rectanglePosition.Y)
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);
1022 else if(anchorPoint.X < rectanglePosition.X &&
1023 anchorPoint.Y >= rectanglePosition.Y &&
1024 anchorPoint.Y <= rectanglePosition.Bottom)
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 );
1035 // Create graphics path of the callout
1036 hotRegion = new GraphicsPath();
1038 hotRegion.AddLines(points);
1039 hotRegion.CloseAllFigures();
1042 graphics.DrawPathAbs(
1045 this.BackHatchStyle,
1047 ChartImageWrapMode.Scaled,
1049 ChartImageAlignmentStyle.Center,
1050 this.BackGradientStyle,
1051 this.BackSecondaryColor,
1055 PenAlignment.Center,
1062 // Draw rectangle if anchor is not visible
1065 graphics.FillRectangleRel(
1068 this.BackHatchStyle,
1070 ChartImageWrapMode.Scaled,
1072 ChartImageAlignmentStyle.Center,
1073 this.BackGradientStyle,
1074 this.BackSecondaryColor,
1080 PenAlignment.Center);
1083 hotRegion = new GraphicsPath();
1084 hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
1088 DrawText(graphics, rectanglePosition, false, false);
1094 /// Draws Perspective style callout.
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,
1105 // Get absolute position
1106 RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
1108 // Draw perspective polygons from anchoring point
1109 if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
1111 // Check if point is inside annotation position
1112 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
1114 // Get center point of the cloud
1115 PointF cloudCenterAbs = graphics.GetAbsolutePoint(
1117 rectanglePosition.X + rectanglePosition.Width / 2f,
1118 rectanglePosition.Y + rectanglePosition.Height / 2f) );
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;
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;
1133 PointF point = PointF.Empty;
1134 if(anchorPoint.Y < rectanglePosition.Y)
1136 point = GetIntersectionY(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Y);
1137 if(point.X < rectanglePositionAbs.X)
1139 point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
1141 else if(point.X > rectanglePositionAbs.Right)
1143 point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
1146 else if(anchorPoint.Y > rectanglePosition.Bottom)
1148 point = GetIntersectionY(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Bottom);
1149 if(point.X < rectanglePositionAbs.X)
1151 point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
1153 else if(point.X > rectanglePositionAbs.Right)
1155 point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
1160 if(anchorPoint.X < rectanglePosition.X)
1162 point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
1166 point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
1170 SizeF size = new SizeF(Math.Abs(cloudCenterAbs.X - point.X), Math.Abs(cloudCenterAbs.Y - point.Y));
1172 dxAbs -= size.Width;
1174 dxAbs += size.Width;
1177 dyAbs -= size.Height;
1179 dyAbs += size.Height;
1182 // Draw 3 smaller ellipses from anchor point to the cloud
1183 for(int index = 0; index < 3; index++)
1185 using( GraphicsPath path = new GraphicsPath() )
1187 // Create ellipse path
1189 ellipseLocation.X - ellipseSize.Width / 2f,
1190 ellipseLocation.Y - ellipseSize.Height / 2f,
1192 ellipseSize.Height);
1195 graphics.DrawPathAbs(
1198 this.BackHatchStyle,
1200 ChartImageWrapMode.Scaled,
1202 ChartImageAlignmentStyle.Center,
1203 this.BackGradientStyle,
1204 this.BackSecondaryColor,
1206 1, // this.LineWidth, NOTE: Cloud supports only 1 pixel border
1208 PenAlignment.Center,
1212 // Adjust ellipse size
1213 ellipseSize.Width *= 1.5f;
1214 ellipseSize.Height *= 1.5f;
1216 // Adjust next ellipse position
1217 ellipseLocation.X -= dxAbs / 3f + (index * (dxAbs / 10f) );
1218 ellipseLocation.Y -= dyAbs / 3f + (index * (dyAbs / 10f) );
1225 GraphicsPath pathCloud = GetCloudPath(rectanglePositionAbs);
1226 graphics.DrawPathAbs(
1229 this.BackHatchStyle,
1231 ChartImageWrapMode.Scaled,
1233 ChartImageAlignmentStyle.Center,
1234 this.BackGradientStyle,
1235 this.BackSecondaryColor,
1237 1, // this.LineWidth, NOTE: Cloud supports only 1 pixel border
1239 PenAlignment.Center,
1243 // Draw cloud outline (Do not draw in SVG or Flash Animation)
1245 using(GraphicsPath pathCloudOutline = GetCloudOutlinePath(rectanglePositionAbs))
1247 graphics.DrawPathAbs(
1250 this.BackHatchStyle,
1252 ChartImageWrapMode.Scaled,
1254 ChartImageAlignmentStyle.Center,
1255 this.BackGradientStyle,
1256 this.BackSecondaryColor,
1258 1, // this.LineWidth, NOTE: Cloud supports only 1 pixel border
1260 PenAlignment.Center);
1265 DrawText(graphics, rectanglePosition, true, false);
1271 /// Draws Perspective style callout.
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,
1283 graphics.FillRectangleRel(
1286 this.BackHatchStyle,
1288 ChartImageWrapMode.Scaled,
1290 ChartImageAlignmentStyle.Center,
1291 this.BackGradientStyle,
1292 this.BackSecondaryColor,
1297 0, // Shadow is never drawn
1298 PenAlignment.Center);
1300 // Create hot region path
1301 GraphicsPath hotRegion = new GraphicsPath();
1302 hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
1305 DrawText(graphics, rectanglePosition, false, false);
1307 // Draw perspective polygons from anchoring point
1308 if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
1310 // Check if point is inside annotation position
1311 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
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())
1320 using(perspectivePaths[1] = new GraphicsPath())
1322 // Convert coordinates to absolute
1323 RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
1324 PointF anchorPointAbs = graphics.GetAbsolutePoint(anchorPoint);
1326 // Create paths of perspective
1327 if(anchorPoint.Y < rectanglePosition.Y)
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)
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);
1342 else if(anchorPoint.X > rectanglePosition.Right)
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);
1351 else if(anchorPoint.Y > rectanglePosition.Bottom)
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)
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);
1366 else if(anchorPoint.X > rectanglePosition.Right)
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);
1377 if(anchorPoint.X < rectanglePosition.X)
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);
1385 else if(anchorPoint.X > rectanglePosition.Right)
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);
1395 // Draw paths if non-empty
1397 foreach(GraphicsPath path in perspectivePaths)
1399 if(path.PointCount > 0)
1401 path.CloseAllFigures();
1402 graphics.DrawPathAbs(
1404 perspectivePathColors[index],
1405 this.BackHatchStyle,
1407 ChartImageWrapMode.Scaled,
1409 ChartImageAlignmentStyle.Center,
1410 this.BackGradientStyle,
1411 this.BackSecondaryColor,
1415 PenAlignment.Center);
1417 // Add area to hot region path
1418 hotRegion.SetMarkers();
1419 hotRegion.AddPath( path, false );
1432 /// Draws SimpleLine or BorderLine style callout.
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,
1449 graphics.FillRectangleRel(
1452 this.BackHatchStyle,
1454 ChartImageWrapMode.Scaled,
1456 ChartImageAlignmentStyle.Center,
1457 this.BackGradientStyle,
1458 this.BackSecondaryColor,
1464 PenAlignment.Center);
1467 DrawText(graphics, rectanglePosition, false, false);
1472 rectanglePosition = DrawText(graphics, rectanglePosition, false, true);
1473 SizeF pixelSize = graphics.GetRelativeSize(new SizeF(2f, 2f));
1474 rectanglePosition.Inflate(pixelSize);
1477 // Create hot region path
1478 GraphicsPath hotRegion = new GraphicsPath();
1479 hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
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);
1485 // Draw line to the anchor point
1486 if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
1488 // Check if point is inside annotation position
1489 if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
1491 PointF lineSecondPoint = PointF.Empty;
1492 if(anchorPoint.X < rectanglePosition.X)
1494 lineSecondPoint.X = rectanglePosition.X;
1496 else if(anchorPoint.X > rectanglePosition.Right)
1498 lineSecondPoint.X = rectanglePosition.Right;
1502 lineSecondPoint.X = rectanglePosition.X + rectanglePosition.Width / 2f;
1505 if(anchorPoint.Y < rectanglePosition.Y)
1507 lineSecondPoint.Y = rectanglePosition.Y;
1509 else if(anchorPoint.Y > rectanglePosition.Bottom)
1511 lineSecondPoint.Y = rectanglePosition.Bottom;
1515 lineSecondPoint.Y = rectanglePosition.Y + rectanglePosition.Height / 2f;
1519 bool capChanged = false;
1520 LineCap oldStartCap = LineCap.Flat;
1521 if(this.CalloutAnchorCap != LineAnchorCapStyle.None)
1525 oldStartCap = graphics.Pen.StartCap;
1527 // Apply anchor cap settings
1528 if(this.CalloutAnchorCap == LineAnchorCapStyle.Arrow)
1530 // Adjust arrow size for small line width
1531 if(this.LineWidth < 4)
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,
1542 graphics.Pen.StartCap = LineCap.ArrowAnchor;
1545 else if(this.CalloutAnchorCap == LineAnchorCapStyle.Diamond)
1547 graphics.Pen.StartCap = LineCap.DiamondAnchor;
1549 else if(this.CalloutAnchorCap == LineAnchorCapStyle.Round)
1551 graphics.Pen.StartCap = LineCap.RoundAnchor;
1553 else if(this.CalloutAnchorCap == LineAnchorCapStyle.Square)
1555 graphics.Pen.StartCap = LineCap.SquareAnchor;
1559 // Draw callout line
1560 graphics.DrawLineAbs(
1564 graphics.GetAbsolutePoint(anchorPoint),
1565 graphics.GetAbsolutePoint(lineSecondPoint),
1569 // Create hot region path
1570 using( GraphicsPath linePath = new GraphicsPath() )
1573 graphics.GetAbsolutePoint(anchorPoint),
1574 graphics.GetAbsolutePoint(lineSecondPoint) );
1576 linePath.Widen(new Pen(Color.Black, this.LineWidth + 2));
1577 hotRegion.SetMarkers();
1578 hotRegion.AddPath( linePath, false );
1581 // Restore line caps
1584 graphics.Pen.StartCap = oldStartCap;
1587 // Adjust text underlying line position
1588 if(anchorPoint.Y < rectanglePosition.Y)
1590 textLinePoint1.Y = rectanglePosition.Y;
1591 textLinePoint2.Y = rectanglePosition.Y;
1593 else if(anchorPoint.Y > rectanglePosition.Y &&
1594 anchorPoint.Y < rectanglePosition.Bottom)
1596 textLinePoint1.Y = rectanglePosition.Y;
1597 textLinePoint2.Y = rectanglePosition.Bottom;
1598 if(anchorPoint.X < rectanglePosition.X)
1600 textLinePoint1.X = rectanglePosition.X;
1601 textLinePoint2.X = rectanglePosition.X;
1605 textLinePoint1.X = rectanglePosition.Right;
1606 textLinePoint2.X = rectanglePosition.Right;
1611 // Draw text underlying line
1614 graphics.DrawLineAbs(
1618 graphics.GetAbsolutePoint(textLinePoint1),
1619 graphics.GetAbsolutePoint(textLinePoint2),
1623 // Create hot region path
1624 using( GraphicsPath linePath = new GraphicsPath() )
1627 graphics.GetAbsolutePoint(textLinePoint1),
1628 graphics.GetAbsolutePoint(textLinePoint2) );
1630 linePath.Widen(new Pen(Color.Black, this.LineWidth + 2));
1631 hotRegion.SetMarkers();
1632 hotRegion.AddPath( linePath, false );
1641 #endregion // Painting
1643 #region Anchor Methods
1646 /// Checks if annotation draw anything in the anchor position (except selection handle)
1648 /// <returns>True if annotation "connects" itself and anchor point visually.</returns>
1649 override internal bool IsAnchorDrawn()
1654 #endregion // Anchor Methods
1656 #region Helper methods
1659 /// Gets cloud callout outline graphics path.
1661 /// <param name="position">Absolute position of the callout cloud.</param>
1662 /// <returns>Cloud outline path.</returns>
1663 private static GraphicsPath GetCloudOutlinePath(RectangleF position)
1665 if(_cloudOutlinePath == null)
1667 GetCloudPath(position);
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);
1684 /// Gets cloud callout graphics path.
1686 /// <param name="position">Absolute position of the callout cloud.</param>
1687 /// <returns>Cloud path.</returns>
1688 private static GraphicsPath GetCloudPath(RectangleF position)
1690 // Check if cloud path was already created
1691 if(_cloudPath == null)
1693 // Create cloud path
1694 _cloudPath = new GraphicsPath();
1696 _cloudPath.AddBezier(1689.5f, 1998.6f, 1581.8f, 2009.4f, 1500f, 2098.1f, 1500f, 2204f);
1698 _cloudPath.AddBezier(1500f, 2204f, 1499.9f, 2277.2f, 1539.8f, 2345.1f, 1604.4f, 2382.1f);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
1732 _cloudPath.CloseAllFigures();
1735 // Create cloud outline path
1736 _cloudOutlinePath = new GraphicsPath();
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);
1741 _cloudOutlinePath.StartFigure();
1742 _cloudOutlinePath.AddBezier(1782.8f, 2724.2f, 1801.3f, 2722.2f, 1819.4f, 2717.7f, 1836.7f, 2711f);
1744 _cloudOutlinePath.StartFigure();
1745 _cloudOutlinePath.AddBezier(2267.6f, 2797.2f, 2276.1f, 2818.4f, 2287f, 2838.7f, 2300f, 2857.6f);
1747 _cloudOutlinePath.StartFigure();
1748 _cloudOutlinePath.AddBezier(2887.1f, 2772.5f, 2893.8f, 2750.9f, 2898.1f, 2728.7f, 2900f, 2706.3f);
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);
1755 _cloudOutlinePath.StartFigure();
1756 _cloudOutlinePath.AddBezier(3460.5f, 2124.9f, 3491f, 2099.7f, 3515f, 2067.8f, 3530.9f, 2032f);
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);
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();
1766 _cloudOutlinePath.StartFigure();
1767 _cloudOutlinePath.AddBezier(2590.9f, 1614.2f, 2583.1f, 1629.6f, 2577.2f, 1645.8f, 2573.4f, 1662.5f);
1769 _cloudOutlinePath.StartFigure();
1770 _cloudOutlinePath.AddBezier(2243.3f, 1727.5f, 2224.2f, 1709.4f, 2203f, 1693.8f, 2180.1f, 1680.7f);
1772 _cloudOutlinePath.StartFigure();
1773 _cloudOutlinePath.AddBezier(1688.8f, 1999f, 1691.1f, 2015.7f, 1694.8f, 2032.2f, 1699.9f, 2048.3f);
1775 _cloudOutlinePath.CloseAllFigures();
1777 // Get cloud path bounds
1778 _cloudBounds = _cloudPath.GetBounds();
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);
1795 /// Gets intersection point coordinates between point line and and horizontal
1796 /// line specified by Y coordinate.
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)
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) +
1810 return intersectionPoint;
1814 /// Gets intersection point coordinates between point line and and vertical
1815 /// line specified by X coordinate.
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)
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) +
1829 return intersectionPoint;
1833 /// Adds a horizontal or vertical line into the path as multiple segments.
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)
1845 float distance = (y2 - y1) / segments;
1846 for(int index = 0; index < segments; index++)
1848 path.AddLine(x1, y1, x1, y1 + distance);
1854 float distance = (x2 - x1) / segments;
1855 for(int index = 0; index < segments; index++)
1857 path.AddLine(x1, y1, x1 + distance, y1);
1863 throw (new InvalidOperationException(SR.ExceptionAnnotationPathAddLineAsSegmentsInvalid));
1867 /// Helper function which creates a rounded rectangle path.
1868 /// Extra points are added on the sides to allow anchor connection.
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)
1875 // Create rounded rectangle path
1876 GraphicsPath path = new GraphicsPath();
1878 PathAddLineAsSegments(path, rect.X+cornerRadius, rect.Y, rect.Right-cornerRadius, rect.Y, segments);
1880 path.AddArc(rect.Right-2f*cornerRadius, rect.Y, 2f*cornerRadius, 2f*cornerRadius, 270, 90);
1882 PathAddLineAsSegments(path, rect.Right, rect.Y + cornerRadius, rect.Right, rect.Bottom - cornerRadius, segments);
1884 path.AddArc(rect.Right-2f*cornerRadius, rect.Bottom-2f*cornerRadius, 2f*cornerRadius, 2f*cornerRadius, 0, 90);
1886 PathAddLineAsSegments(path, rect.Right-cornerRadius, rect.Bottom, rect.X + cornerRadius, rect.Bottom, segments);
1888 path.AddArc(rect.X, rect.Bottom-2f*cornerRadius, 2f*cornerRadius, 2f*cornerRadius, 90, 90);
1890 PathAddLineAsSegments(path, rect.X, rect.Bottom-cornerRadius, rect.X, rect.Y+cornerRadius, segments);
1892 path.AddArc(rect.X, rect.Y, 2f*cornerRadius, 2f*cornerRadius, 180, 90);
1897 #endregion // Helper methods