CalloutAnnotation.cs 60 KB

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