12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- //
- // Purpose: Callout annotation classes.
- //
- using System;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Design;
- using System.Drawing.Drawing2D;
- using FastReport.DataVisualization.Charting.Utilities;
- namespace FastReport.DataVisualization.Charting
- {
- #region Enumerations
- /// <summary>
- /// Annotation callout style.
- /// <seealso cref="CalloutAnnotation.CalloutStyle"/>
- /// </summary>
- [
- SRDescription("DescriptionAttributeCalloutStyle_CalloutStyle"),
- ]
- public enum CalloutStyle
- {
- /// <summary>
- /// Callout text is underlined and a line is pointing to the anchor point.
- /// </summary>
- SimpleLine,
- /// <summary>
- /// Border is drawn around text and a line is pointing to the anchor point.
- /// </summary>
- Borderline,
- /// <summary>
- /// Callout text is inside the cloud and smaller clouds are pointing to the anchor point.
- /// </summary>
- Cloud,
- /// <summary>
- /// Rectangle is drawn around the callout text, which is connected with the anchor point.
- /// </summary>
- Rectangle,
- /// <summary>
- /// Rounded rectangle is drawn around the callout text, which is connected with the anchor point.
- /// </summary>
- RoundedRectangle,
- /// <summary>
- /// Ellipse is drawn around the callout text, which is connected with the anchor point.
- /// </summary>
- Ellipse,
- /// <summary>
- /// Perspective rectangle is drawn around the callout text, which is connected with the anchor point.
- /// </summary>
- Perspective,
- }
- #endregion
- /// <summary>
- /// <b>CalloutAnnotation</b> is a class class that represents a callout annotation.
- /// </summary>
- /// <remarks>
- /// Callout annotation is the only annotation that draws a connection between the
- /// annotation position and anchor point. It can display text and automatically
- /// calculate the required size. Different <see cref="CalloutStyle"/> are supported.
- /// </remarks>
- [
- SRDescription("DescriptionAttributeCalloutAnnotation_CalloutAnnotation"),
- ]
- public class CalloutAnnotation : TextAnnotation
- {
- #region Fields
- // Callout anchor type
- private LineAnchorCapStyle _calloutAnchorCap = LineAnchorCapStyle.Arrow;
- // Callout drawing style
- private CalloutStyle _calloutStyle = CalloutStyle.Rectangle;
- // Cloud shape path
- private static GraphicsPath _cloudPath = null;
- // Cloud shape outline path
- private static GraphicsPath _cloudOutlinePath = null;
- // Cloud shape boundary rectangle
- private static RectangleF _cloudBounds = RectangleF.Empty;
- #endregion
- #region Construction and Initialization
- /// <summary>
- /// Default public constructor.
- /// </summary>
- public CalloutAnnotation()
- : base()
- {
- // Changing default values of properties
- this.anchorOffsetX = 3.0;
- this.anchorOffsetY = 3.0;
- this.anchorAlignment = ContentAlignment.BottomLeft;
- }
- #endregion
- #region Properties
- #region Callout properties
- /// <summary>
- /// Gets or sets the annotation callout style.
- /// </summary>
- /// <value>
- /// <see cref="CalloutStyle"/> of the annotation.
- /// </value>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(CalloutStyle.Rectangle),
- SRDescription("DescriptionAttributeCalloutAnnotation_CalloutStyle"),
- ParenthesizePropertyNameAttribute(true),
- ]
- virtual public CalloutStyle CalloutStyle
- {
- get
- {
- return _calloutStyle;
- }
- set
- {
- _calloutStyle = value;
- this.ResetCurrentRelativePosition();
-
- // Reset content size to empty
- contentSize = SizeF.Empty;
- Invalidate();
- }
- }
- /// <summary>
- /// Gets or sets the anchor cap style of a callout line.
- /// </summary>
- /// <value>
- /// A <see cref="LineAnchorCapStyle"/> value used as the anchor cap of a callout line.
- /// </value>
- /// <remarks>
- /// This property sets the anchor cap of the line connecting an annotation to
- /// its anchor point. It only applies when SimpleLine or BorderLine
- /// are used.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(LineAnchorCapStyle.Arrow),
- SRDescription("DescriptionAttributeCalloutAnnotation_CalloutAnchorCap"),
- ]
- virtual public LineAnchorCapStyle CalloutAnchorCap
- {
- get
- {
- return _calloutAnchorCap;
- }
- set
- {
- _calloutAnchorCap = value;
- Invalidate();
- }
- }
- #endregion // Callout properties
- #region Applicable Annotation Appearance Attributes (set as Browsable)
- /// <summary>
- /// Gets or sets the color of an annotation line.
- /// <seealso cref="LineWidth"/>
- /// <seealso cref="LineDashStyle"/>
- /// </summary>
- /// <value>
- /// A <see cref="Color"/> value used to draw an annotation line.
- /// </value>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(typeof(Color), "Black"),
- SRDescription("DescriptionAttributeLineColor"),
- TypeConverter(typeof(ColorConverter)),
- #if DESIGNER
- Editor(typeof(ChartColorEditor), typeof(UITypeEditor))
- #endif
- ]
- override public Color LineColor
- {
- get
- {
- return base.LineColor;
- }
- set
- {
- base.LineColor = value;
- }
- }
- /// <summary>
- /// Gets or sets the width of an annotation line.
- /// <seealso cref="LineColor"/>
- /// <seealso cref="LineDashStyle"/>
- /// </summary>
- /// <value>
- /// An integer value defining the width of an annotation line in pixels.
- /// </value>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(1),
- SRDescription("DescriptionAttributeLineWidth"),
- ]
- override public int LineWidth
- {
- get
- {
- return base.LineWidth;
- }
- set
- {
- base.LineWidth = value;
- }
- }
- /// <summary>
- /// Gets or sets the style of an annotation line.
- /// <seealso cref="LineWidth"/>
- /// <seealso cref="LineColor"/>
- /// </summary>
- /// <value>
- /// A <see cref="ChartDashStyle"/> value used to draw an annotation line.
- /// </value>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(ChartDashStyle.Solid),
- SRDescription("DescriptionAttributeLineDashStyle"),
- ]
- override public ChartDashStyle LineDashStyle
- {
- get
- {
- return base.LineDashStyle;
- }
- set
- {
- base.LineDashStyle = value;
- }
- }
- /// <summary>
- /// Gets or sets the background color of an annotation.
- /// <seealso cref="BackSecondaryColor"/>
- /// <seealso cref="BackHatchStyle"/>
- /// <seealso cref="BackGradientStyle"/>
- /// </summary>
- /// <value>
- /// A <see cref="Color"/> value used for the background of an annotation.
- /// </value>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(typeof(Color), ""),
- SRDescription("DescriptionAttributeBackColor"),
- NotifyParentPropertyAttribute(true),
- TypeConverter(typeof(ColorConverter)),
- #if DESIGNER
- Editor(typeof(ChartColorEditor), typeof(UITypeEditor))
- #endif
- ]
- override public Color BackColor
- {
- get
- {
- return base.BackColor;
- }
- set
- {
- base.BackColor = value;
- }
- }
- /// <summary>
- /// Gets or sets the background hatch style of an annotation.
- /// <seealso cref="BackSecondaryColor"/>
- /// <seealso cref="BackColor"/>
- /// <seealso cref="BackGradientStyle"/>
- /// </summary>
- /// <value>
- /// A <see cref="ChartHatchStyle"/> value used for the background of an annotation.
- /// </value>
- /// <remarks>
- /// Two colors are used to draw the hatching, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(ChartHatchStyle.None),
- NotifyParentPropertyAttribute(true),
- SRDescription("DescriptionAttributeBackHatchStyle"),
- #if DESIGNER
- Editor(typeof(HatchStyleEditor), typeof(UITypeEditor))
- #endif
- ]
- override public ChartHatchStyle BackHatchStyle
- {
- get
- {
- return base.BackHatchStyle;
- }
- set
- {
- base.BackHatchStyle = value;
- }
- }
- /// <summary>
- /// Gets or sets the background gradient style of an annotation.
- /// <seealso cref="BackSecondaryColor"/>
- /// <seealso cref="BackColor"/>
- /// <seealso cref="BackHatchStyle"/>
- /// </summary>
- /// <value>
- /// A <see cref="GradientStyle"/> value used for the background of an annotation.
- /// </value>
- /// <remarks>
- /// Two colors are used to draw the gradient, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(GradientStyle.None),
- NotifyParentPropertyAttribute(true),
- SRDescription("DescriptionAttributeBackGradientStyle"),
- #if DESIGNER
- Editor(typeof(GradientEditor), typeof(UITypeEditor))
- #endif
- ]
- override public GradientStyle BackGradientStyle
- {
- get
- {
- return base.BackGradientStyle;
- }
- set
- {
- base.BackGradientStyle = value;
- }
- }
- /// <summary>
- /// Gets or sets the secondary background color of an annotation.
- /// <seealso cref="BackColor"/>
- /// <seealso cref="BackHatchStyle"/>
- /// <seealso cref="BackGradientStyle"/>
- /// </summary>
- /// <value>
- /// A <see cref="Color"/> value used for the secondary color of an annotation background with
- /// hatching or gradient fill.
- /// </value>
- /// <remarks>
- /// This color is used with <see cref="BackColor"/> when <see cref="BackHatchStyle"/> or
- /// <see cref="BackGradientStyle"/> are used.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAppearance"),
- Browsable(true),
- DefaultValue(typeof(Color), ""),
- NotifyParentPropertyAttribute(true),
- SRDescription("DescriptionAttributeBackSecondaryColor"),
- TypeConverter(typeof(ColorConverter)),
- #if DESIGNER
- Editor(typeof(ChartColorEditor), typeof(UITypeEditor))
- #endif
- ]
- override public Color BackSecondaryColor
- {
- get
- {
- return base.BackSecondaryColor;
- }
- set
- {
- base.BackSecondaryColor = value;
- }
- }
- #endregion
- #region Anchor
- /// <summary>
- /// Gets or sets the x-coordinate offset between the positions of an annotation and its anchor point.
- /// <seealso cref="AnchorOffsetY"/>
- /// <seealso cref="Annotation.AnchorDataPoint"/>
- /// <seealso cref="Annotation.AnchorX"/>
- /// <seealso cref="AnchorAlignment"/>
- /// </summary>
- /// <value>
- /// A double value that represents the x-coordinate offset between the positions of an annotation and its anchor point.
- /// </value>
- /// <remarks>
- /// The annotation must be anchored using the <see cref="Annotation.AnchorDataPoint"/> or
- /// <see cref="Annotation.AnchorX"/> properties, and its <see cref="Annotation.X"/> property must be set
- /// to <b>Double.NaN</b>.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAnchor"),
- DefaultValue(3.0),
- SRDescription("DescriptionAttributeCalloutAnnotation_AnchorOffsetX"),
- RefreshPropertiesAttribute(RefreshProperties.All),
- ]
- override public double AnchorOffsetX
- {
- get
- {
- return base.AnchorOffsetX;
- }
- set
- {
- base.AnchorOffsetX = value;
- }
- }
- /// <summary>
- /// Gets or sets the y-coordinate offset between the positions of an annotation and its anchor point.
- /// <seealso cref="Annotation.AnchorOffsetX"/>
- /// <seealso cref="Annotation.AnchorDataPoint"/>
- /// <seealso cref="Annotation.AnchorY"/>
- /// <seealso cref="Annotation.AnchorAlignment"/>
- /// </summary>
- /// <value>
- /// A double value that represents the y-coordinate offset between the positions of an annotation and its anchor point.
- /// </value>
- /// <remarks>
- /// Annotation must be anchored using <see cref="Annotation.AnchorDataPoint"/> or
- /// <see cref="Annotation.AnchorY"/> properties and its <see cref="Annotation.Y"/> property must be set
- /// to <b>Double.NaN</b>.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAnchor"),
- DefaultValue(3.0),
- SRDescription("DescriptionAttributeCalloutAnnotation_AnchorOffsetY"),
- RefreshPropertiesAttribute(RefreshProperties.All),
- ]
- override public double AnchorOffsetY
- {
- get
- {
- return base.AnchorOffsetY;
- }
- set
- {
- base.AnchorOffsetY = value;
- }
- }
- /// <summary>
- /// Gets or sets an annotation position's alignment to the anchor point.
- /// <seealso cref="Annotation.AnchorX"/>
- /// <seealso cref="Annotation.AnchorY"/>
- /// <seealso cref="Annotation.AnchorDataPoint"/>
- /// <seealso cref="AnchorOffsetX"/>
- /// <seealso cref="AnchorOffsetY"/>
- /// </summary>
- /// <value>
- /// A <see cref="ContentAlignment"/> value that represents the annotation's alignment to
- /// the anchor point.
- /// </value>
- /// <remarks>
- /// The annotation must be anchored using either <see cref="Annotation.AnchorDataPoint"/>, or the <see cref="Annotation.AnchorX"/>
- /// and <see cref="Annotation.AnchorY"/> properties. Its <see cref="Annotation.X"/> and <see cref="Annotation.Y"/>
- /// properties must be set to <b>Double.NaN</b>.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAnchor"),
- DefaultValue(typeof(ContentAlignment), "BottomLeft"),
- SRDescription("DescriptionAttributeAnchorAlignment"),
- ]
- override public ContentAlignment AnchorAlignment
- {
- get
- {
- return base.AnchorAlignment;
- }
- set
- {
- base.AnchorAlignment = value;
- }
- }
- #endregion // Anchoring
- #region Other
- /// <summary>
- /// Gets or sets an annotation's type name.
- /// </summary>
- /// <remarks>
- /// This property is used to get the name of each annotation type
- /// (e.g. Line, Rectangle, Ellipse).
- /// <para>
- /// This property is for internal use and is hidden at design and run time.
- /// </para>
- /// </remarks>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- Browsable(false),
- EditorBrowsableAttribute(EditorBrowsableState.Never),
- DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
- SerializationVisibilityAttribute(SerializationVisibility.Hidden),
- SRDescription("DescriptionAttributeAnnotationType"),
- ]
- public override string AnnotationType
- {
- get
- {
- return "Callout";
- }
- }
- /// <summary>
- /// Gets or sets annotation selection points style.
- /// </summary>
- /// <value>
- /// A <see cref="SelectionPointsStyle"/> value that represents annotation
- /// selection style.
- /// </value>
- /// <remarks>
- /// This property is for internal use and is hidden at design and run time.
- /// </remarks>
- [
- SRCategory("CategoryAttributeAppearance"),
- DefaultValue(SelectionPointsStyle.Rectangle),
- ParenthesizePropertyNameAttribute(true),
- Browsable(false),
- EditorBrowsableAttribute(EditorBrowsableState.Never),
- DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
- SerializationVisibilityAttribute(SerializationVisibility.Hidden),
- SRDescription("DescriptionAttributeSelectionPointsStyle"),
- ]
- override internal SelectionPointsStyle SelectionPointsStyle
- {
- get
- {
- return SelectionPointsStyle.Rectangle;
- }
- }
- #endregion
- #endregion
- #region Methods
- #region Text Spacing
- /// <summary>
- /// Gets text spacing on four different sides in relative coordinates.
- /// </summary>
- /// <param name="annotationRelative">Indicates that spacing is in annotation relative coordinates.</param>
- /// <returns>Rectangle with text spacing values.</returns>
- internal override RectangleF GetTextSpacing(out bool annotationRelative)
- {
- RectangleF spacing = base.GetTextSpacing(out annotationRelative);
- if(this._calloutStyle == CalloutStyle.Cloud ||
- this._calloutStyle == CalloutStyle.Ellipse)
- {
- spacing = new RectangleF(4f, 4f, 4f, 4f);
- annotationRelative = true;
- }
- else if(this._calloutStyle == CalloutStyle.RoundedRectangle)
- {
- spacing = new RectangleF(1f, 1f, 1f, 1f);
- annotationRelative = true;
- }
- return spacing;
- }
- #endregion // Text Spacing
- #region Painting
- /// <summary>
- /// Paints annotation object on specified graphics.
- /// </summary>
- /// <param name="graphics">
- /// A <see cref="ChartGraphics"/> used to paint annotation object.
- /// </param>
- /// <param name="chart">
- /// Reference to the <see cref="Chart"/> control.
- /// </param>
- override internal void Paint(Chart chart, ChartGraphics graphics)
- {
- // Get annotation position in relative coordinates
- PointF firstPoint = PointF.Empty;
- PointF anchorPoint = PointF.Empty;
- SizeF size = SizeF.Empty;
- GetRelativePosition(out firstPoint, out size, out anchorPoint);
- PointF secondPoint = new PointF(firstPoint.X + size.Width, firstPoint.Y + size.Height);
- // Create selection rectangle
- RectangleF selectionRect = new RectangleF(firstPoint, new SizeF(secondPoint.X - firstPoint.X, secondPoint.Y - firstPoint.Y));
- // Adjust negative rectangle width and height
- RectangleF rectanglePosition = new RectangleF(selectionRect.Location, selectionRect.Size);
- if(rectanglePosition.Width < 0)
- {
- rectanglePosition.X = rectanglePosition.Right;
- rectanglePosition.Width = -rectanglePosition.Width;
- }
- if(rectanglePosition.Height < 0)
- {
- rectanglePosition.Y = rectanglePosition.Bottom;
- rectanglePosition.Height = -rectanglePosition.Height;
- }
- // Check if position is valid
- if( float.IsNaN(rectanglePosition.X) ||
- float.IsNaN(rectanglePosition.Y) ||
- float.IsNaN(rectanglePosition.Right) ||
- float.IsNaN(rectanglePosition.Bottom) )
- {
- return;
- }
- // Paint different style of callouts
- GraphicsPath hotRegionPathAbs = null;
- if(this.Common.ProcessModePaint)
- {
- switch(this._calloutStyle)
- {
- case(CalloutStyle.SimpleLine):
- hotRegionPathAbs = DrawRectangleLineCallout(
- graphics,
- rectanglePosition,
- anchorPoint,
- false);
- break;
- case(CalloutStyle.Borderline):
- hotRegionPathAbs = DrawRectangleLineCallout(
- graphics,
- rectanglePosition,
- anchorPoint,
- true);
- break;
- case(CalloutStyle.Perspective):
- hotRegionPathAbs = DrawPerspectiveCallout(
- graphics,
- rectanglePosition,
- anchorPoint);
- break;
- case(CalloutStyle.Cloud):
- hotRegionPathAbs = DrawCloudCallout(
- graphics,
- rectanglePosition,
- anchorPoint);
- break;
- case(CalloutStyle.Rectangle):
- hotRegionPathAbs = DrawRectangleCallout(
- graphics,
- rectanglePosition,
- anchorPoint);
- break;
- case(CalloutStyle.Ellipse):
- hotRegionPathAbs = DrawRoundedRectCallout(
- graphics,
- rectanglePosition,
- anchorPoint,
- true);
- break;
- case(CalloutStyle.RoundedRectangle):
- hotRegionPathAbs = DrawRoundedRectCallout(
- graphics,
- rectanglePosition,
- anchorPoint,
- false);
- break;
- }
- }
- if(this.Common.ProcessModeRegions)
- {
- if(hotRegionPathAbs != null)
- {
- // If there is more then one graphical path split them and create
- // image maps for every graphical path separately.
- GraphicsPathIterator iterator = new GraphicsPathIterator(hotRegionPathAbs);
- // There is more then one path.
- using (GraphicsPath subPath = new GraphicsPath())
- {
- while (iterator.NextMarker(subPath) > 0)
- {
- // Use callout defined hot region
- this.Common.HotRegionsList.AddHotRegion(
- graphics,
- subPath,
- false,
- ReplaceKeywords(this.ToolTip),
- String.Empty,
- String.Empty,
- String.Empty,
- this,
- ChartElementType.Annotation);
- // Reset current path
- subPath.Reset();
- }
- }
- }
- else
- {
- // Use rectangular hot region
- this.Common.HotRegionsList.AddHotRegion(
- rectanglePosition,
- ReplaceKeywords(this.ToolTip),
- String.Empty,
- String.Empty,
- String.Empty,
- this,
- ChartElementType.Annotation,
- String.Empty);
- }
- }
- //Clean up
- if (hotRegionPathAbs != null)
- hotRegionPathAbs.Dispose();
- // Paint selection handles
- PaintSelectionHandles(graphics, selectionRect, null);
- }
- /// <summary>
- /// Draws Rounded rectangle or Ellipse style callout.
- /// </summary>
- /// <param name="graphics">Chart graphics.</param>
- /// <param name="rectanglePosition">Position of annotation objet.</param>
- /// <param name="anchorPoint">Anchor location.</param>
- /// <param name="isEllipse">True if ellipse shape should be used.</param>
- /// <returns>Hot region of the callout.</returns>
- private GraphicsPath DrawRoundedRectCallout(
- ChartGraphics graphics,
- RectangleF rectanglePosition,
- PointF anchorPoint,
- bool isEllipse)
- {
- // Get absolute position
- RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
- // NOTE: Fix for issue #6692.
- // Do not draw the callout if size is not set. This may happen if callou text is set to empty string.
- if (rectanglePositionAbs.Width <= 0 || rectanglePositionAbs.Height <= 0)
- {
- return null;
- }
- // Create ellipse path
- GraphicsPath ellipsePath = new GraphicsPath();
- if(isEllipse)
- {
- // Add ellipse shape
- ellipsePath.AddEllipse(rectanglePositionAbs);
- }
- else
- {
- // Add rounded rectangle shape
- float radius = Math.Min(rectanglePositionAbs.Width, rectanglePositionAbs.Height);
- radius /= 5f;
- ellipsePath = this.CreateRoundedRectPath(rectanglePositionAbs, radius);
- }
- // Draw perspective polygons from anchoring point
- if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
- {
- // Check if point is inside annotation position
- if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
- {
- // Get absolute anchor point
- PointF anchorPointAbs = graphics.GetAbsolutePoint(new PointF(anchorPoint.X, anchorPoint.Y));
- // Flatten ellipse path
- ellipsePath.Flatten();
- // Find point in the path closest to the anchor point
- PointF[] points = ellipsePath.PathPoints;
- int closestPointIndex = 0;
- int index = 0;
- float currentDistance = float.MaxValue;
- foreach(PointF point in points)
- {
- float deltaX = point.X - anchorPointAbs.X;
- float deltaY = point.Y - anchorPointAbs.Y;
- float distance = deltaX * deltaX + deltaY * deltaY;
- if(distance < currentDistance)
- {
- currentDistance = distance;
- closestPointIndex = index;
- }
- ++ index;
- }
- // Change point to the anchor location
- points[closestPointIndex] = anchorPointAbs;
- // Recreate ellipse path
- ellipsePath.Reset();
- ellipsePath.AddLines(points);
- ellipsePath.CloseAllFigures();
- }
- }
- // Draw ellipse
- graphics.DrawPathAbs(
- ellipsePath,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- PenAlignment.Center,
- this.ShadowOffset,
- this.ShadowColor);
- // Draw text
- DrawText(graphics, rectanglePosition, true, false);
- return ellipsePath;
- }
- /// <summary>
- /// Draws Rectangle style callout.
- /// </summary>
- /// <param name="graphics">Chart graphics.</param>
- /// <param name="rectanglePosition">Position of annotation objet.</param>
- /// <param name="anchorPoint">Anchor location.</param>
- /// <returns>Hot region of the callout.</returns>
- private GraphicsPath DrawRectangleCallout(
- ChartGraphics graphics,
- RectangleF rectanglePosition,
- PointF anchorPoint)
- {
- // Create path for the rectangle connected with anchor point.
- GraphicsPath hotRegion = null;
- bool anchorVisible = false;
- if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
- {
- // Get relative size of a pixel
- SizeF pixelSize = graphics.GetRelativeSize(new SizeF(1f, 1f));
- // Increase annotation position rectangle by 1 pixel
- RectangleF inflatedPosition = new RectangleF(rectanglePosition.Location, rectanglePosition.Size);
- inflatedPosition.Inflate(pixelSize);
- // Check if point is inside annotation position
- if(!inflatedPosition.Contains(anchorPoint.X, anchorPoint.Y))
- {
- anchorVisible = true;
- // Get absolute position
- RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
- // Get absolute anchor point
- PointF anchorPointAbs = graphics.GetAbsolutePoint(new PointF(anchorPoint.X, anchorPoint.Y));
- // Calculate anchor pointer thicness
- float size = Math.Min(rectanglePositionAbs.Width, rectanglePositionAbs.Height);
- size /= 4f;
- // Create shape points
- PointF[] points = new PointF[7];
- if(anchorPoint.X < rectanglePosition.X &&
- anchorPoint.Y > rectanglePosition.Bottom)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[3] = new PointF(rectanglePositionAbs.X + size, rectanglePositionAbs.Bottom);
- points[4] = anchorPointAbs;
- points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom - size);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom - size);
- }
- else if(anchorPoint.X >= rectanglePosition.X &&
- anchorPoint.X <= rectanglePosition.Right &&
- anchorPoint.Y > rectanglePosition.Bottom)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[3] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width / 2f + size, rectanglePositionAbs.Bottom);
- points[4] = anchorPointAbs;
- points[5] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width / 2f - size, rectanglePositionAbs.Bottom);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- }
- else if(anchorPoint.X > rectanglePosition.Right &&
- anchorPoint.Y > rectanglePosition.Bottom)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom - size);
- points[3] = anchorPointAbs;
- points[4] = new PointF(rectanglePositionAbs.Right - size, rectanglePositionAbs.Bottom);
- points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- }
- else if(anchorPoint.X > rectanglePosition.Right &&
- anchorPoint.Y <= rectanglePosition.Bottom &&
- anchorPoint.Y >= rectanglePosition.Y)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y + rectanglePositionAbs.Height / 2f - size);
- points[3] = anchorPointAbs;
- points[4] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y + rectanglePositionAbs.Height / 2f + size);
- points[5] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- }
- else if(anchorPoint.X > rectanglePosition.Right &&
- anchorPoint.Y < rectanglePosition.Y)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.Right - size, rectanglePositionAbs.Y);
- points[2] = anchorPointAbs;
- points[3] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y + size);
- points[4] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- }
- else if(anchorPoint.X >= rectanglePosition.X &&
- anchorPoint.X <= rectanglePosition.Right &&
- anchorPoint.Y < rectanglePosition.Y)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width/2f - size, rectanglePositionAbs.Y);
- points[2] = anchorPointAbs;
- points[3] = new PointF(rectanglePositionAbs.X + rectanglePositionAbs.Width/2f + size, rectanglePositionAbs.Y);
- points[4] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[5] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- }
- else if(anchorPoint.X < rectanglePosition.X &&
- anchorPoint.Y < rectanglePosition.Y)
- {
- points[0] = anchorPointAbs;
- points[1] = new PointF(rectanglePositionAbs.X + size, rectanglePositionAbs.Y);
- points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[3] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[4] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points[5] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + size);
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + size);
- }
- else if(anchorPoint.X < rectanglePosition.X &&
- anchorPoint.Y >= rectanglePosition.Y &&
- anchorPoint.Y <= rectanglePosition.Bottom)
- {
- points[0] = rectanglePositionAbs.Location;
- points[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points[2] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points[3] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points[4] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + rectanglePositionAbs.Height/2f + size );
- points[5] = anchorPointAbs;
- points[6] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y + rectanglePositionAbs.Height/2f - size );
- }
- // Create graphics path of the callout
- hotRegion = new GraphicsPath();
-
- hotRegion.AddLines(points);
- hotRegion.CloseAllFigures();
- // Draw callout
- graphics.DrawPathAbs(
- hotRegion,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- PenAlignment.Center,
- this.ShadowOffset,
- this.ShadowColor);
-
- }
- }
-
- // Draw rectangle if anchor is not visible
- if(!anchorVisible)
- {
- graphics.FillRectangleRel(
- rectanglePosition,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- this.ShadowColor,
- this.ShadowOffset,
- PenAlignment.Center);
- // Get hot region
- hotRegion = new GraphicsPath();
- hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
- }
- // Draw text
- DrawText(graphics, rectanglePosition, false, false);
- return hotRegion;
- }
- /// <summary>
- /// Draws Perspective style callout.
- /// </summary>
- /// <param name="graphics">Chart graphics.</param>
- /// <param name="rectanglePosition">Position of annotation objet.</param>
- /// <param name="anchorPoint">Anchor location.</param>
- /// <returns>Hot region of the cloud.</returns>
- private GraphicsPath DrawCloudCallout(
- ChartGraphics graphics,
- RectangleF rectanglePosition,
- PointF anchorPoint)
- {
- // Get absolute position
- RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
- // Draw perspective polygons from anchoring point
- if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
- {
- // Check if point is inside annotation position
- if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
- {
- // Get center point of the cloud
- PointF cloudCenterAbs = graphics.GetAbsolutePoint(
- new PointF(
- rectanglePosition.X + rectanglePosition.Width / 2f,
- rectanglePosition.Y + rectanglePosition.Height / 2f) );
- // Calculate absolute ellipse size and position
- SizeF ellipseSize = graphics.GetAbsoluteSize(
- new SizeF(rectanglePosition.Width, rectanglePosition.Height));
- ellipseSize.Width /= 10f;
- ellipseSize.Height /= 10f;
- PointF anchorPointAbs = graphics.GetAbsolutePoint(
- new PointF(anchorPoint.X, anchorPoint.Y));
- PointF ellipseLocation = anchorPointAbs;
- // Get distance between anchor point and center of the cloud
- float dxAbs = anchorPointAbs.X - cloudCenterAbs.X;
- float dyAbs = anchorPointAbs.Y - cloudCenterAbs.Y;
- PointF point = PointF.Empty;
- if(anchorPoint.Y < rectanglePosition.Y)
- {
- point = GetIntersectionY(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Y);
- if(point.X < rectanglePositionAbs.X)
- {
- point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
- }
- else if(point.X > rectanglePositionAbs.Right)
- {
- point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
- }
- }
- else if(anchorPoint.Y > rectanglePosition.Bottom)
- {
- point = GetIntersectionY(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Bottom);
- if(point.X < rectanglePositionAbs.X)
- {
- point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
- }
- else if(point.X > rectanglePositionAbs.Right)
- {
- point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
- }
- }
- else
- {
- if(anchorPoint.X < rectanglePosition.X)
- {
- point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.X);
- }
- else
- {
- point = GetIntersectionX(cloudCenterAbs, anchorPointAbs, rectanglePositionAbs.Right);
- }
- }
-
- SizeF size = new SizeF(Math.Abs(cloudCenterAbs.X - point.X), Math.Abs(cloudCenterAbs.Y - point.Y));
- if(dxAbs > 0)
- dxAbs -= size.Width;
- else
- dxAbs += size.Width;
- if(dyAbs > 0)
- dyAbs -= size.Height;
- else
- dyAbs += size.Height;
- // Draw 3 smaller ellipses from anchor point to the cloud
- for(int index = 0; index < 3; index++)
- {
- using( GraphicsPath path = new GraphicsPath() )
- {
- // Create ellipse path
- path.AddEllipse(
- ellipseLocation.X - ellipseSize.Width / 2f,
- ellipseLocation.Y - ellipseSize.Height / 2f,
- ellipseSize.Width,
- ellipseSize.Height);
- // Draw ellipse
- graphics.DrawPathAbs(
- path,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- 1, // this.LineWidth, NOTE: Cloud supports only 1 pixel border
- this.LineDashStyle,
- PenAlignment.Center,
- this.ShadowOffset,
- this.ShadowColor);
- // Adjust ellipse size
- ellipseSize.Width *= 1.5f;
- ellipseSize.Height *= 1.5f;
- // Adjust next ellipse position
- ellipseLocation.X -= dxAbs / 3f + (index * (dxAbs / 10f) );
- ellipseLocation.Y -= dyAbs / 3f + (index * (dyAbs / 10f) );
- }
- }
- }
- }
- // Draw cloud
- GraphicsPath pathCloud = GetCloudPath(rectanglePositionAbs);
- graphics.DrawPathAbs(
- pathCloud,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- 1, // this.LineWidth, NOTE: Cloud supports only 1 pixel border
- this.LineDashStyle,
- PenAlignment.Center,
- this.ShadowOffset,
- this.ShadowColor);
- // Draw cloud outline (Do not draw in SVG or Flash Animation)
- {
- using(GraphicsPath pathCloudOutline = GetCloudOutlinePath(rectanglePositionAbs))
- {
- graphics.DrawPathAbs(
- pathCloudOutline,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- 1, // this.LineWidth, NOTE: Cloud supports only 1 pixel border
- this.LineDashStyle,
- PenAlignment.Center);
- }
- }
-
- // Draw text
- DrawText(graphics, rectanglePosition, true, false);
- return pathCloud;
- }
- /// <summary>
- /// Draws Perspective style callout.
- /// </summary>
- /// <param name="graphics">Chart graphics.</param>
- /// <param name="rectanglePosition">Position of annotation objet.</param>
- /// <param name="anchorPoint">Anchor location.</param>
- /// <returns>Hot region of the cloud.</returns>
- private GraphicsPath DrawPerspectiveCallout(
- ChartGraphics graphics,
- RectangleF rectanglePosition,
- PointF anchorPoint)
- {
- // Draw rectangle
- graphics.FillRectangleRel(
- rectanglePosition,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- this.ShadowColor,
- 0, // Shadow is never drawn
- PenAlignment.Center);
- // Create hot region path
- GraphicsPath hotRegion = new GraphicsPath();
- hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
- // Draw text
- DrawText(graphics, rectanglePosition, false, false);
- // Draw perspective polygons from anchoring point
- if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
- {
- // Check if point is inside annotation position
- if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
- {
- Color[] perspectivePathColors = new Color[2];
- Color color = (this.BackColor.IsEmpty) ? Color.White : this.BackColor;
- perspectivePathColors[0] = graphics.GetBrightGradientColor(color, 0.6);
- perspectivePathColors[1] = graphics.GetBrightGradientColor(color, 0.8);
- GraphicsPath[] perspectivePaths = new GraphicsPath[2];
- using(perspectivePaths[0] = new GraphicsPath())
- {
- using(perspectivePaths[1] = new GraphicsPath())
- {
- // Convert coordinates to absolute
- RectangleF rectanglePositionAbs = graphics.GetAbsoluteRectangle(rectanglePosition);
- PointF anchorPointAbs = graphics.GetAbsolutePoint(anchorPoint);
- // Create paths of perspective
- if(anchorPoint.Y < rectanglePosition.Y)
- {
- PointF[] points1 = new PointF[3];
- points1[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
- points1[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points1[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[0].AddLines(points1);
- if(anchorPoint.X < rectanglePosition.X)
- {
- PointF[] points2 = new PointF[3];
- points2[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points2[1] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
- points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[1].AddLines(points2);
- }
- else if(anchorPoint.X > rectanglePosition.Right)
- {
- PointF[] points2 = new PointF[3];
- points2[0] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points2[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[1].AddLines(points2);
- }
- }
- else if(anchorPoint.Y > rectanglePosition.Bottom)
- {
- PointF[] points1 = new PointF[3];
- points1[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points1[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points1[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[0].AddLines(points1);
- if(anchorPoint.X < rectanglePosition.X)
- {
- PointF[] points2 = new PointF[3];
- points2[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points2[1] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
- points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[1].AddLines(points2);
- }
- else if(anchorPoint.X > rectanglePosition.Right)
- {
- PointF[] points2 = new PointF[3];
- points2[0] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points2[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[1].AddLines(points2);
- }
- }
- else
- {
- if(anchorPoint.X < rectanglePosition.X)
- {
- PointF[] points2 = new PointF[3];
- points2[0] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Bottom);
- points2[1] = new PointF(rectanglePositionAbs.X, rectanglePositionAbs.Y);
- points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[1].AddLines(points2);
- }
- else if(anchorPoint.X > rectanglePosition.Right)
- {
- PointF[] points2 = new PointF[3];
- points2[0] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Bottom);
- points2[1] = new PointF(rectanglePositionAbs.Right, rectanglePositionAbs.Y);
- points2[2] = new PointF(anchorPointAbs.X, anchorPointAbs.Y);
- perspectivePaths[1].AddLines(points2);
- }
- }
- // Draw paths if non-empty
- int index = 0;
- foreach(GraphicsPath path in perspectivePaths)
- {
- if(path.PointCount > 0)
- {
- path.CloseAllFigures();
- graphics.DrawPathAbs(
- path,
- perspectivePathColors[index],
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- PenAlignment.Center);
- // Add area to hot region path
- hotRegion.SetMarkers();
- hotRegion.AddPath( path, false );
- }
- ++index;
- }
- }
- }
- }
- }
- return hotRegion;
- }
- /// <summary>
- /// Draws SimpleLine or BorderLine style callout.
- /// </summary>
- /// <param name="graphics">Chart graphics.</param>
- /// <param name="rectanglePosition">Position of annotation objet.</param>
- /// <param name="anchorPoint">Anchor location.</param>
- /// <param name="drawRectangle">If true draws BorderLine style, otherwise SimpleLine.</param>
- /// <returns>Hot region of the cloud.</returns>
- private GraphicsPath DrawRectangleLineCallout(
- ChartGraphics graphics,
- RectangleF rectanglePosition,
- PointF anchorPoint,
- bool drawRectangle)
- {
- // Rectangle mode
- if(drawRectangle)
- {
- // Draw rectangle
- graphics.FillRectangleRel(
- rectanglePosition,
- this.BackColor,
- this.BackHatchStyle,
- String.Empty,
- ChartImageWrapMode.Scaled,
- Color.Empty,
- ChartImageAlignmentStyle.Center,
- this.BackGradientStyle,
- this.BackSecondaryColor,
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- this.ShadowColor,
- this.ShadowOffset,
- PenAlignment.Center);
- // Draw text
- DrawText(graphics, rectanglePosition, false, false);
- }
- else
- {
- // Draw text
- rectanglePosition = DrawText(graphics, rectanglePosition, false, true);
- SizeF pixelSize = graphics.GetRelativeSize(new SizeF(2f, 2f));
- rectanglePosition.Inflate(pixelSize);
- }
- // Create hot region path
- GraphicsPath hotRegion = new GraphicsPath();
- hotRegion.AddRectangle( graphics.GetAbsoluteRectangle(rectanglePosition) );
- // Define position of text underlying line
- PointF textLinePoint1 = new PointF(rectanglePosition.X, rectanglePosition.Bottom);
- PointF textLinePoint2 = new PointF(rectanglePosition.Right, rectanglePosition.Bottom);
- // Draw line to the anchor point
- if(!float.IsNaN(anchorPoint.X) && !float.IsNaN(anchorPoint.Y))
- {
- // Check if point is inside annotation position
- if(!rectanglePosition.Contains(anchorPoint.X, anchorPoint.Y))
- {
- PointF lineSecondPoint = PointF.Empty;
- if(anchorPoint.X < rectanglePosition.X)
- {
- lineSecondPoint.X = rectanglePosition.X;
- }
- else if(anchorPoint.X > rectanglePosition.Right)
- {
- lineSecondPoint.X = rectanglePosition.Right;
- }
- else
- {
- lineSecondPoint.X = rectanglePosition.X + rectanglePosition.Width / 2f;
- }
- if(anchorPoint.Y < rectanglePosition.Y)
- {
- lineSecondPoint.Y = rectanglePosition.Y;
- }
- else if(anchorPoint.Y > rectanglePosition.Bottom)
- {
- lineSecondPoint.Y = rectanglePosition.Bottom;
- }
- else
- {
- lineSecondPoint.Y = rectanglePosition.Y + rectanglePosition.Height / 2f;
- }
- // Set line caps
- bool capChanged = false;
- LineCap oldStartCap = LineCap.Flat;
- if(this.CalloutAnchorCap != LineAnchorCapStyle.None)
- {
- // Save old pen
- capChanged = true;
- oldStartCap = graphics.Pen.StartCap;
- // Apply anchor cap settings
- if(this.CalloutAnchorCap == LineAnchorCapStyle.Arrow)
- {
- // Adjust arrow size for small line width
- if(this.LineWidth < 4)
- {
- int adjustment = 3 - this.LineWidth;
- graphics.Pen.StartCap = LineCap.Custom;
- graphics.Pen.CustomStartCap = new AdjustableArrowCap(
- this.LineWidth + adjustment,
- this.LineWidth + adjustment,
- true);
- }
- else
- {
- graphics.Pen.StartCap = LineCap.ArrowAnchor;
- }
- }
- else if(this.CalloutAnchorCap == LineAnchorCapStyle.Diamond)
- {
- graphics.Pen.StartCap = LineCap.DiamondAnchor;
- }
- else if(this.CalloutAnchorCap == LineAnchorCapStyle.Round)
- {
- graphics.Pen.StartCap = LineCap.RoundAnchor;
- }
- else if(this.CalloutAnchorCap == LineAnchorCapStyle.Square)
- {
- graphics.Pen.StartCap = LineCap.SquareAnchor;
- }
- }
- // Draw callout line
- graphics.DrawLineAbs(
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- graphics.GetAbsolutePoint(anchorPoint),
- graphics.GetAbsolutePoint(lineSecondPoint),
- this.ShadowColor,
- this.ShadowOffset);
- // Create hot region path
- using( GraphicsPath linePath = new GraphicsPath() )
- {
- linePath.AddLine(
- graphics.GetAbsolutePoint(anchorPoint),
- graphics.GetAbsolutePoint(lineSecondPoint) );
- linePath.Widen(new Pen(Color.Black, this.LineWidth + 2));
- hotRegion.SetMarkers();
- hotRegion.AddPath( linePath, false );
- }
- // Restore line caps
- if(capChanged)
- {
- graphics.Pen.StartCap = oldStartCap;
- }
- // Adjust text underlying line position
- if(anchorPoint.Y < rectanglePosition.Y)
- {
- textLinePoint1.Y = rectanglePosition.Y;
- textLinePoint2.Y = rectanglePosition.Y;
- }
- else if(anchorPoint.Y > rectanglePosition.Y &&
- anchorPoint.Y < rectanglePosition.Bottom)
- {
- textLinePoint1.Y = rectanglePosition.Y;
- textLinePoint2.Y = rectanglePosition.Bottom;
- if(anchorPoint.X < rectanglePosition.X)
- {
- textLinePoint1.X = rectanglePosition.X;
- textLinePoint2.X = rectanglePosition.X;
- }
- else
- {
- textLinePoint1.X = rectanglePosition.Right;
- textLinePoint2.X = rectanglePosition.Right;
- }
- }
- }
- // Draw text underlying line
- if(!drawRectangle)
- {
- graphics.DrawLineAbs(
- this.LineColor,
- this.LineWidth,
- this.LineDashStyle,
- graphics.GetAbsolutePoint(textLinePoint1),
- graphics.GetAbsolutePoint(textLinePoint2),
- this.ShadowColor,
- this.ShadowOffset);
- // Create hot region path
- using( GraphicsPath linePath = new GraphicsPath() )
- {
- linePath.AddLine(
- graphics.GetAbsolutePoint(textLinePoint1),
- graphics.GetAbsolutePoint(textLinePoint2) );
- linePath.Widen(new Pen(Color.Black, this.LineWidth + 2));
- hotRegion.SetMarkers();
- hotRegion.AddPath( linePath, false );
- }
- }
- }
- return hotRegion;
- }
- #endregion // Painting
- #region Anchor Methods
- /// <summary>
- /// Checks if annotation draw anything in the anchor position (except selection handle)
- /// </summary>
- /// <returns>True if annotation "connects" itself and anchor point visually.</returns>
- override internal bool IsAnchorDrawn()
- {
- return true;
- }
- #endregion // Anchor Methods
- #region Helper methods
- /// <summary>
- /// Gets cloud callout outline graphics path.
- /// </summary>
- /// <param name="position">Absolute position of the callout cloud.</param>
- /// <returns>Cloud outline path.</returns>
- private static GraphicsPath GetCloudOutlinePath(RectangleF position)
- {
- if(_cloudOutlinePath == null)
- {
- GetCloudPath(position);
- }
- // Translate and sacle original path to fit specified position
- GraphicsPath resultPath = (GraphicsPath)_cloudOutlinePath.Clone();
- Matrix matrix = new Matrix();
- matrix.Translate(-_cloudBounds.X, -_cloudBounds.Y);
- resultPath.Transform(matrix);
- matrix = new Matrix();
- matrix.Translate(position.X, position.Y);
- matrix.Scale(position.Width / _cloudBounds.Width, position.Height / _cloudBounds.Height);
- resultPath.Transform(matrix);
- return resultPath;
- }
-
- /// <summary>
- /// Gets cloud callout graphics path.
- /// </summary>
- /// <param name="position">Absolute position of the callout cloud.</param>
- /// <returns>Cloud path.</returns>
- private static GraphicsPath GetCloudPath(RectangleF position)
- {
- // Check if cloud path was already created
- if(_cloudPath == null)
- {
- // Create cloud path
- _cloudPath = new GraphicsPath();
- _cloudPath.AddBezier(1689.5f, 1998.6f, 1581.8f, 2009.4f, 1500f, 2098.1f, 1500f, 2204f);
- _cloudPath.AddBezier(1500f, 2204f, 1499.9f, 2277.2f, 1539.8f, 2345.1f, 1604.4f, 2382.1f);
- _cloudPath.AddBezier(1603.3f, 2379.7f, 1566.6f, 2417.8f, 1546.2f, 2468.1f, 1546.2f, 2520.1f);
- _cloudPath.AddBezier(1546.2f, 2520.1f, 1546.2f, 2633.7f, 1641.1f, 2725.7f, 1758.1f, 2725.7f);
- _cloudPath.AddBezier(1758.1f, 2725.7f, 1766.3f, 2725.6f, 1774.6f, 2725.2f, 1782.8f, 2724.2f);
- _cloudPath.AddBezier(1781.7f, 2725.6f, 1848.5f, 2839.4f, 1972.8f, 2909.7f, 2107.3f, 2909.7f);
- _cloudPath.AddBezier(2107.3f, 2909.7f, 2175.4f, 2909.7f, 2242.3f, 2891.6f, 2300.6f, 2857.4f);
- _cloudPath.AddBezier(2300f, 2857.6f, 2360.9f, 2946.5f, 2463.3f, 2999.7f, 2572.9f, 2999.7f);
- _cloudPath.AddBezier(2572.9f, 2999.7f, 2717.5f, 2999.7f, 2845.2f, 2907.4f, 2887.1f, 2772.5f);
- _cloudPath.AddBezier(2887.4f, 2774.3f, 2932.1f, 2801.4f, 2983.6f, 2815.7f, 3036.3f, 2815.7f);
- _cloudPath.AddBezier(3036.3f, 2815.7f, 3190.7f, 2815.7f, 3316.3f, 2694.8f, 3317.5f, 2544.8f);
- _cloudPath.AddBezier(3317f, 2544.1f, 3479.2f, 2521.5f, 3599.7f, 2386.5f, 3599.7f, 2227.2f);
- _cloudPath.AddBezier(3599.7f, 2227.2f, 3599.7f, 2156.7f, 3575.7f, 2088.1f, 3531.6f, 2032.2f);
- _cloudPath.AddBezier(3530.9f, 2032f, 3544.7f, 2000.6f, 3551.9f, 1966.7f, 3551.9f, 1932.5f);
- _cloudPath.AddBezier(3551.9f, 1932.5f, 3551.9f, 1818.6f, 3473.5f, 1718.8f, 3360.7f, 1688.8f);
- _cloudPath.AddBezier(3361.6f, 1688.3f, 3341.4f, 1579.3f, 3243.5f, 1500f, 3129.3f, 1500f);
- _cloudPath.AddBezier(3129.3f, 1500f, 3059.8f, 1499.9f, 2994f, 1529.6f, 2949.1f, 1580.9f);
- _cloudPath.AddBezier(2949.5f, 1581.3f, 2909.4f, 1530f, 2847f, 1500f, 2780.8f, 1500f);
- _cloudPath.AddBezier(2780.8f, 1500f, 2700.4f, 1499.9f, 2626.8f, 1544.2f, 2590.9f, 1614.2f);
- _cloudPath.AddBezier(2591.7f, 1617.6f, 2543.2f, 1571.1f, 2477.9f, 1545.1f, 2409.8f, 1545.1f);
- _cloudPath.AddBezier(2409.8f, 1545.1f, 2313.9f, 1545.1f, 2225.9f, 1596.6f, 2180.8f, 1679f);
- _cloudPath.AddBezier(2180.1f, 1680.7f, 2129.7f, 1652f, 2072.4f, 1636.9f, 2014.1f, 1636.9f);
- _cloudPath.AddBezier(2014.1f, 1636.9f, 1832.8f, 1636.9f, 1685.9f, 1779.8f, 1685.9f, 1956f);
- _cloudPath.AddBezier(1685.9f, 1956f, 1685.8f, 1970.4f, 1686.9f, 1984.8f, 1688.8f, 1999f);
- _cloudPath.CloseAllFigures();
- // Create cloud outline path
- _cloudOutlinePath = new GraphicsPath();
- _cloudOutlinePath.AddBezier(1604.4f, 2382.1f, 1636.8f, 2400.6f, 1673.6f, 2410.3f, 1711.2f, 2410.3f);
- _cloudOutlinePath.AddBezier(1711.2f, 2410.3f, 1716.6f, 2410.3f, 1722.2f, 2410.2f, 1727.6f, 2409.8f);
-
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(1782.8f, 2724.2f, 1801.3f, 2722.2f, 1819.4f, 2717.7f, 1836.7f, 2711f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(2267.6f, 2797.2f, 2276.1f, 2818.4f, 2287f, 2838.7f, 2300f, 2857.6f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(2887.1f, 2772.5f, 2893.8f, 2750.9f, 2898.1f, 2728.7f, 2900f, 2706.3f);
- // NOTE: This cloud segment overlaps text too much. Removed for now!
- //cloudOutlinePath.StartFigure();
- //cloudOutlinePath.AddBezier(3317.5f, 2544.8f, 3317.5f, 2544f, 3317.6f, 2543.3f, 3317.6f, 2542.6f);
- //cloudOutlinePath.AddBezier(3317.6f, 2542.6f, 3317.6f, 2438.1f, 3256.1f, 2342.8f, 3159.5f, 2297f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(3460.5f, 2124.9f, 3491f, 2099.7f, 3515f, 2067.8f, 3530.9f, 2032f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(3365.3f, 1732.2f, 3365.3f, 1731.1f, 3365.4f, 1730.1f, 3365.4f, 1729f);
- _cloudOutlinePath.AddBezier(3365.4f, 1729f, 3365.4f, 1715.3f, 3364.1f, 1701.7f, 3361.6f, 1688.3f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(2949.1f, 1580.9f, 2934.4f, 1597.8f, 2922.3f, 1616.6f, 2913.1f, 1636.9f);
- _cloudOutlinePath.CloseFigure();
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(2590.9f, 1614.2f, 2583.1f, 1629.6f, 2577.2f, 1645.8f, 2573.4f, 1662.5f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(2243.3f, 1727.5f, 2224.2f, 1709.4f, 2203f, 1693.8f, 2180.1f, 1680.7f);
- _cloudOutlinePath.StartFigure();
- _cloudOutlinePath.AddBezier(1688.8f, 1999f, 1691.1f, 2015.7f, 1694.8f, 2032.2f, 1699.9f, 2048.3f);
- _cloudOutlinePath.CloseAllFigures();
- // Get cloud path bounds
- _cloudBounds = _cloudPath.GetBounds();
- }
- // Translate and sacle original path to fit specified position
- GraphicsPath resultPath = (GraphicsPath)_cloudPath.Clone();
- Matrix matrix = new Matrix();
- matrix.Translate(-_cloudBounds.X, -_cloudBounds.Y);
- resultPath.Transform(matrix);
- matrix = new Matrix();
- matrix.Translate(position.X, position.Y);
- matrix.Scale(position.Width / _cloudBounds.Width, position.Height / _cloudBounds.Height);
- resultPath.Transform(matrix);
- return resultPath;
- }
- /// <summary>
- /// Gets intersection point coordinates between point line and and horizontal
- /// line specified by Y coordinate.
- /// </summary>
- /// <param name="firstPoint">First data point.</param>
- /// <param name="secondPoint">Second data point.</param>
- /// <param name="pointY">Y coordinate.</param>
- /// <returns>Intersection point coordinates.</returns>
- internal static PointF GetIntersectionY(PointF firstPoint, PointF secondPoint, float pointY)
- {
- PointF intersectionPoint = new PointF();
- intersectionPoint.Y = pointY;
- intersectionPoint.X = (pointY - firstPoint.Y) *
- (secondPoint.X - firstPoint.X) /
- (secondPoint.Y - firstPoint.Y) +
- firstPoint.X;
- return intersectionPoint;
- }
- /// <summary>
- /// Gets intersection point coordinates between point line and and vertical
- /// line specified by X coordinate.
- /// </summary>
- /// <param name="firstPoint">First data point.</param>
- /// <param name="secondPoint">Second data point.</param>
- /// <param name="pointX">X coordinate.</param>
- /// <returns>Intersection point coordinates.</returns>
- internal static PointF GetIntersectionX(PointF firstPoint, PointF secondPoint, float pointX)
- {
- PointF intersectionPoint = new PointF();
- intersectionPoint.X = pointX;
- intersectionPoint.Y = (pointX - firstPoint.X) *
- (secondPoint.Y - firstPoint.Y) /
- (secondPoint.X - firstPoint.X) +
- firstPoint.Y;
- return intersectionPoint;
- }
- /// <summary>
- /// Adds a horizontal or vertical line into the path as multiple segments.
- /// </summary>
- /// <param name="path">Graphics path.</param>
- /// <param name="x1">First point X coordinate.</param>
- /// <param name="y1">First point Y coordinate.</param>
- /// <param name="x2">Second point X coordinate.</param>
- /// <param name="y2">Second point Y coordinate.</param>
- /// <param name="segments">Number of segments to add.</param>
- private void PathAddLineAsSegments(GraphicsPath path, float x1, float y1, float x2, float y2, int segments)
- {
- if(x1 == x2)
- {
- float distance = (y2 - y1) / segments;
- for(int index = 0; index < segments; index++)
- {
- path.AddLine(x1, y1, x1, y1 + distance);
- y1 += distance;
- }
- }
- else if(y1 == y2)
- {
- float distance = (x2 - x1) / segments;
- for(int index = 0; index < segments; index++)
- {
- path.AddLine(x1, y1, x1 + distance, y1);
- x1 += distance;
- }
- }
- else
- {
- throw (new InvalidOperationException(SR.ExceptionAnnotationPathAddLineAsSegmentsInvalid));
- }
- }
- /// <summary>
- /// Helper function which creates a rounded rectangle path.
- /// Extra points are added on the sides to allow anchor connection.
- /// </summary>
- /// <param name="rect">Rectangle coordinates.</param>
- /// <param name="cornerRadius">Corner radius.</param>
- /// <returns>Graphics path object.</returns>
- private GraphicsPath CreateRoundedRectPath(RectangleF rect, float cornerRadius)
- {
- // Create rounded rectangle path
- GraphicsPath path = new GraphicsPath();
- int segments = 10;
- PathAddLineAsSegments(path, rect.X+cornerRadius, rect.Y, rect.Right-cornerRadius, rect.Y, segments);
- path.AddArc(rect.Right-2f*cornerRadius, rect.Y, 2f*cornerRadius, 2f*cornerRadius, 270, 90);
- PathAddLineAsSegments(path, rect.Right, rect.Y + cornerRadius, rect.Right, rect.Bottom - cornerRadius, segments);
- path.AddArc(rect.Right-2f*cornerRadius, rect.Bottom-2f*cornerRadius, 2f*cornerRadius, 2f*cornerRadius, 0, 90);
- PathAddLineAsSegments(path, rect.Right-cornerRadius, rect.Bottom, rect.X + cornerRadius, rect.Bottom, segments);
- path.AddArc(rect.X, rect.Bottom-2f*cornerRadius, 2f*cornerRadius, 2f*cornerRadius, 90, 90);
- PathAddLineAsSegments(path, rect.X, rect.Bottom-cornerRadius, rect.X, rect.Y+cornerRadius, segments);
- path.AddArc(rect.X, rect.Y, 2f*cornerRadius, 2f*cornerRadius, 180, 90);
- return path;
- }
- #endregion // Helper methods
- #endregion
- }
- }
|