PieChart.cs

Description du code

PieChart.cs est un fichier du projet BrolDev.
Ce fichier est situé dans /var/www/bin/sniplets/bibliobrol/broldev/src/.

Projet BrolDev : Librairie de composants réutilisables pour les applications BrolDev en CSharp.

Code source ou contenu du fichier

  1. using System;
  2. using System.Collections;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5.  
  6. namespace be.gaudry.model.drawing.chart
  7. {
  8.  
  9. /// <summary>
  10. /// Object representing a pie chart.
  11. /// </summary>
  12. public class PieChart3D : IDisposable {
  13.  
  14. /// <summary>
  15. /// Initializes an empty instance of <c>PieChart3D</c>.
  16. /// </summary>
  17. protected PieChart3D() {
  18. }
  19.  
  20. /// <summary>
  21. /// Initializes an instance of a flat <c>PieChart3D</c> with
  22. /// specified bounds, values to chart and relative thickness.
  23. /// </summary>
  24. /// <param name="xBoundingRect">
  25. /// x-coordinate of the upper-left corner of the rectangle that
  26. /// bounds the chart.
  27. /// </param>
  28. /// <param name="yBoundingRect">
  29. /// y-coordinate of the upper-left corner of the rectangle that
  30. /// bounds the chart.
  31. /// </param>
  32. /// <param name="widthBoundingRect">
  33. /// Width of the rectangle that bounds the chart.
  34. /// </param>
  35. /// <param name="heightBoundingRect">
  36. /// Height of the rectangle that bounds the chart.
  37. /// </param>
  38. /// <param name="values">
  39. /// An array of <c>decimal</c> values to chart.
  40. /// </param>
  41. public PieChart3D(float xBoundingRect, float yBoundingRect, float widthBoundingRect, float heightBoundingRect, double[] values) : this() {
  42. m_xBoundingRect = xBoundingRect;
  43. m_yBoundingRect = yBoundingRect;
  44. m_widthBoundingRect = widthBoundingRect;
  45. m_heightBoundingRect = heightBoundingRect;
  46. Values = values;
  47. }
  48.  
  49. /// <summary>
  50. /// Initializes an instance of <c>PieChart3D</c> with specified
  51. /// bounds, values to chart and relative thickness.
  52. /// </summary>
  53. /// <param name="xBoundingRect">
  54. /// x-coordinate of the upper-left corner of the rectangle bounding
  55. /// the chart.
  56. /// </param>
  57. /// <param name="yBoundingRect">
  58. /// y-coordinate of the upper-left corner of the rectangle bounding
  59. /// the chart.
  60. /// </param>
  61. /// <param name="widthBoundingRect">
  62. /// Width of the rectangle bounding the chart.
  63. /// </param>
  64. /// <param name="heightBoundingRect">
  65. /// Height of the rectangle bounding the chart.
  66. /// </param>
  67. /// <param name="values">
  68. /// An array of <c>decimal</c> values to chart.
  69. /// </param>
  70. /// <param name="sliceRelativeHeight">
  71. /// Thickness of the pie slice to chart relative to the height of the
  72. /// bounding rectangle.
  73. /// </param>
  74. public PieChart3D(float xBoundingRect, float yBoundingRect, float widthBoundingRect, float heightBoundingRect, double[] values, float sliceRelativeHeight)
  75. : this(xBoundingRect, yBoundingRect, widthBoundingRect, heightBoundingRect, values) {
  76. m_sliceRelativeHeight = sliceRelativeHeight;
  77. }
  78.  
  79. /// <summary>
  80. /// Initializes a new instance of <c>PieChart3D</c> with given bounds,
  81. /// array of values and pie slice thickness.
  82. /// </summary>
  83. /// <param name="boundingRectangle">
  84. /// Bounding rectangle.
  85. /// </param>
  86. /// <param name="values">
  87. /// Array of values to initialize with.
  88. /// </param>
  89. /// <param name="sliceRelativeHeight"></param>
  90. public PieChart3D(RectangleF boundingRectangle, double[] values, float sliceRelativeHeight)
  91. : this(boundingRectangle.X, boundingRectangle.Y, boundingRectangle.Width, boundingRectangle.Height, values, sliceRelativeHeight) {
  92. }
  93.  
  94. /// <summary>
  95. /// Initializes a new instance of <c>PieChart3D</c> with given bounds,
  96. /// array of values and relative pie slice height.
  97. /// </summary>
  98. /// <param name="xBoundingRect">
  99. /// x-coordinate of the upper-left corner of the rectangle bounding
  100. /// the chart.
  101. /// </param>
  102. /// <param name="yBoundingRect">
  103. /// y-coordinate of the upper-left corner of the rectangle bounding
  104. /// the chart.
  105. /// </param>
  106. /// <param name="widthBoundingRect">
  107. /// Width of the rectangle bounding the chart.
  108. /// </param>
  109. /// <param name="heightBoundingRect">
  110. /// Height of the rectangle bounding the chart.
  111. /// </param>
  112. /// <param name="values">
  113. /// An array of <c>decimal</c> values to chart.
  114. /// </param>
  115. /// <param name="sliceColors">
  116. /// An array of colors used to render slices.
  117. /// </param>
  118. /// <param name="sliceRelativeHeight">
  119. /// Thickness of the slice to chart relative to the height of the
  120. /// bounding rectangle.
  121. /// </param>
  122. public PieChart3D(float xBoundingRect, float yBoundingRect, float widthBoundingRect, float heightBoundingRect, double[] values, Color[] sliceColors, float sliceRelativeHeight)
  123. : this(xBoundingRect, yBoundingRect, widthBoundingRect, heightBoundingRect, values, sliceRelativeHeight) {
  124. m_sliceColors = sliceColors;
  125. }
  126.  
  127. /// <summary>
  128. /// Initializes a new instance of <c>PieChart3D</c> with given bounds,
  129. /// array of values and corresponding colors.
  130. /// </summary>
  131. /// <param name="boundingRectangle">
  132. /// Bounding rectangle.
  133. /// </param>
  134. /// <param name="values">
  135. /// Array of values to chart.
  136. /// </param>
  137. /// <param name="sliceColors">
  138. /// Colors used for rendering individual slices.
  139. /// </param>
  140. /// <param name="sliceRelativeHeight">
  141. /// Pie slice relative height.
  142. /// </param>
  143. public PieChart3D(RectangleF boundingRectangle, double[] values, Color[] sliceColors, float sliceRelativeHeight)
  144. : this(boundingRectangle, values, sliceRelativeHeight) {
  145. m_sliceColors = sliceColors;
  146. }
  147.  
  148. /// <summary>
  149. /// Initializes a new instance of <c>PieChart3D</c> with given bounds,
  150. /// array of values and relative pie slice height.
  151. /// </summary>
  152. /// <param name="xBoundingRect">
  153. /// x-coordinate of the upper-left corner of the rectangle bounding
  154. /// the chart.
  155. /// </param>
  156. /// <param name="yBoundingRect">
  157. /// y-coordinate of the upper-left corner of the rectangle bounding
  158. /// the chart.
  159. /// </param>
  160. /// <param name="widthBoundingRect">
  161. /// Width of the rectangle bounding the chart.
  162. /// </param>
  163. /// <param name="heightBoundingRect">
  164. /// Height of the rectangle bounding the chart.
  165. /// </param>
  166. /// <param name="values">
  167. /// An array of <c>decimal</c> values to chart.
  168. /// </param>
  169. /// <param name="sliceColors">
  170. /// An array of colors used to render slices.
  171. /// </param>
  172. /// <param name="sliceRelativeHeight">
  173. /// Thickness of the slice to chart relative to the height of the
  174. /// bounding rectangle.
  175. /// </param>
  176. /// <param name="texts">
  177. /// An array of strings that are displayed on corresponding slice.
  178. /// </param>
  179. public PieChart3D(float xBoundingRect, float yBoundingRect, float widthBoundingRect, float heightBoundingRect, double[] values, Color[] sliceColors, float sliceRelativeHeight, string[] texts)
  180. : this(xBoundingRect, yBoundingRect, widthBoundingRect, heightBoundingRect, values, sliceColors, sliceRelativeHeight) {
  181. m_texts = texts;
  182. }
  183.  
  184. /// <summary>
  185. /// Initializes a new instance of <c>PieChart3D</c> with given bounds,
  186. /// array of values and relative pie slice height.
  187. /// </summary>
  188. /// <param name="xBoundingRect">
  189. /// x-coordinate of the upper-left corner of the rectangle bounding
  190. /// the chart.
  191. /// </param>
  192. /// <param name="yBoundingRect">
  193. /// y-coordinate of the upper-left corner of the rectangle bounding
  194. /// the chart.
  195. /// </param>
  196. /// <param name="widthBoundingRect">
  197. /// Width of the rectangle bounding the chart.
  198. /// </param>
  199. /// <param name="heightBoundingRect">
  200. /// Height of the rectangle bounding the chart.
  201. /// </param>
  202. /// <param name="values">
  203. /// An array of <c>decimal</c> values to chart.
  204. /// </param>
  205. /// <param name="sliceRelativeHeight">
  206. /// Thickness of the slice to chart relative to the height of the
  207. /// bounding rectangle.
  208. /// </param>
  209. /// <param name="texts">
  210. /// An array of strings that are displayed on corresponding slice.
  211. /// </param>
  212. public PieChart3D(float xBoundingRect, float yBoundingRect, float widthBoundingRect, float heightBoundingRect, double[] values, float sliceRelativeHeight, string[] texts)
  213. : this(xBoundingRect, yBoundingRect, widthBoundingRect, heightBoundingRect, values, sliceRelativeHeight) {
  214. m_texts = texts;
  215. }
  216.  
  217. /// <summary>
  218. /// <c>Finalize</c> method.
  219. /// </summary>
  220. ~PieChart3D() {
  221. Dispose(false);
  222. }
  223.  
  224. /// <summary>
  225. /// Implementation of <c>IDisposable</c> interface.
  226. /// </summary>
  227. public void Dispose() {
  228. Dispose(true);
  229. GC.SuppressFinalize(this);
  230. }
  231.  
  232. /// <summary>
  233. /// Disposes of all pie slices.
  234. /// </summary>
  235. protected virtual void Dispose(bool disposing) {
  236. if (!m_disposed) {
  237. if (disposing) {
  238. foreach (PieSlice slice in m_pieSlices) {
  239. slice.Dispose();
  240. }
  241. }
  242. m_disposed = true;
  243. }
  244. }
  245.  
  246. /// <summary>
  247. /// Sets values to be displayed on the chart.
  248. /// </summary>
  249. public double[] Values {
  250. set {
  251. Debug.Assert(value != null && value.Length > 0);
  252. m_values = value;
  253. }
  254. }
  255.  
  256. /// <summary>
  257. /// Sets colors used for individual pie slices.
  258. /// </summary>
  259. public Color[] Colors {
  260. set { m_sliceColors = value; }
  261. }
  262.  
  263. /// <summary>
  264. /// Sets text displayed by slices.
  265. /// </summary>
  266. public string[] Texts {
  267. set { m_texts = value; }
  268. }
  269.  
  270. /// <summary>
  271. /// Gets or sets the font of the text displayed by the control.
  272. /// </summary>
  273. public Font Font {
  274. get { return m_font; }
  275. set { m_font = value; }
  276. }
  277.  
  278. /// <summary>
  279. /// Gets or sets the foreground color of the control used to draw text.
  280. /// </summary>
  281. public Color ForeColor {
  282. get { return m_foreColor; }
  283. set { m_foreColor = value; }
  284. }
  285.  
  286. /// <summary>
  287. /// Sets slice edge color mode. If set to <c>PenColor</c> (default),
  288. /// then value set by <c>EdgeColor</c> property is used.
  289. /// </summary>
  290. public EdgeColorType EdgeColorType {
  291. set { m_edgeColorType = value; }
  292. }
  293.  
  294. /// <summary>
  295. /// Sets slice edge line width. If not set, default value is 1.
  296. /// </summary>
  297. public float EdgeLineWidth {
  298. set { m_edgeLineWidth = value; }
  299. }
  300.  
  301. /// <summary>
  302. /// Sets slice height, relative to the top ellipse semi-axis. Must be
  303. /// less than or equal to 0.5.
  304. /// </summary>
  305. public float SliceRelativeHeight {
  306. set {
  307. Debug.Assert(value <= 0.5F);
  308. m_sliceRelativeHeight = value;
  309. }
  310. }
  311.  
  312. /// <summary>
  313. /// Sets the slice displacement relative to the ellipse semi-axis.
  314. /// Must be less than 1.
  315. /// </summary>
  316. public float SliceRelativeDisplacement {
  317. set {
  318. Debug.Assert(IsDisplacementValid(value));
  319. m_sliceRelativeDisplacements = new float[] { value };
  320. }
  321. }
  322.  
  323. /// <summary>
  324. /// Sets the slice displacement relative to the ellipse semi-axis.
  325. /// Must be less than 1.
  326. /// </summary>
  327. public float[] SliceRelativeDisplacements {
  328. set {
  329. m_sliceRelativeDisplacements = value;
  330. Debug.Assert(AreDisplacementsValid(value));
  331. }
  332. }
  333.  
  334. /// <summary>
  335. /// Gets or sets the size of the entire pie chart.
  336. /// </summary>
  337. public SizeF ChartSize {
  338. set {
  339. m_widthBoundingRect = value.Width;
  340. m_heightBoundingRect = value.Height;
  341. }
  342. }
  343.  
  344. /// <summary>
  345. /// Gets or sets the width of the bounding rectangle.
  346. /// </summary>
  347. public float Width {
  348. get { return m_widthBoundingRect; }
  349. set { m_widthBoundingRect = value; }
  350. }
  351.  
  352. /// <summary>
  353. /// Gets or sets the height of the bounding rectangle.
  354. /// </summary>
  355. public float Height {
  356. get { return m_heightBoundingRect; }
  357. set { m_heightBoundingRect = value; }
  358. }
  359.  
  360. /// <summary>
  361. /// Gets the y-coordinate of the bounding rectangle top edge.
  362. /// </summary>
  363. public float Top {
  364. get { return m_yBoundingRect; }
  365. }
  366.  
  367. /// <summary>
  368. /// Gets the y-coordinate of the bounding rectangle bottom edge.
  369. /// </summary>
  370. public float Bottom {
  371. get { return m_yBoundingRect + m_heightBoundingRect; }
  372. }
  373.  
  374. /// <summary>
  375. /// Gets the x-coordinate of the bounding rectangle left edge.
  376. /// </summary>
  377. public float Left {
  378. get { return m_xBoundingRect; }
  379. }
  380.  
  381. /// <summary>
  382. /// Gets the x-coordinate of the bounding rectangle right edge.
  383. /// </summary>
  384. public float Right {
  385. get { return m_xBoundingRect + m_widthBoundingRect; }
  386. }
  387.  
  388. /// <summary>
  389. /// Gets or sets the x-coordinate of the upper-left corner of the
  390. /// bounding rectangle.
  391. /// </summary>
  392. public float X {
  393. get { return m_xBoundingRect; }
  394. set { m_xBoundingRect = value; }
  395. }
  396.  
  397. /// <summary>
  398. /// Gets or sets the y-coordinate of the upper-left corner of the
  399. /// bounding rectangle.
  400. /// </summary>
  401. public float Y {
  402. get { return m_yBoundingRect; }
  403. set { m_yBoundingRect = value; }
  404. }
  405.  
  406. /// <summary>
  407. /// Sets the shadowing style used.
  408. /// </summary>
  409. public ShadowStyle ShadowStyle {
  410. set { m_shadowStyle = value; }
  411. }
  412.  
  413. /// <summary>
  414. /// Sets the flag that stepControls if chart is fit to bounding rectangle
  415. /// exactly.
  416. /// </summary>
  417. public bool FitToBoundingRectangle {
  418. set { m_fitToBoundingRectangle = value; }
  419. }
  420.  
  421. /// <summary>
  422. /// Sets the initial angle from which pies are placed.
  423. /// </summary>
  424. public float InitialAngle {
  425. set {
  426. m_initialAngle = value % 360;
  427. if (m_initialAngle < 0)
  428. m_initialAngle += 360;
  429. }
  430. }
  431.  
  432. /// <summary>
  433. /// Sets the index of the highlighted pie.
  434. /// </summary>
  435. public int HighlightedIndex {
  436. set { m_highlightedIndex = value; }
  437. }
  438.  
  439. /// <summary>
  440. /// Draws the chart.
  441. /// </summary>
  442. /// <param name="graphics">
  443. /// <c>Graphics</c> object used for drawing.
  444. /// </param>
  445. public void Draw(Graphics graphics) {
  446. Debug.Assert(m_values != null && m_values.Length > 0);
  447. InitializePieSlices();
  448. if (m_fitToBoundingRectangle) {
  449. RectangleF newBoundingRectangle = GetFittingRectangle();
  450. ReadjustSlices(newBoundingRectangle);
  451. }
  452. DrawBottoms(graphics);
  453. if (m_sliceRelativeHeight > 0F) {
  454. DrawSliceSides(graphics);
  455. }
  456. DrawTops(graphics);
  457. }
  458.  
  459. /// <summary>
  460. /// Draws strings by individual slices. Position of the text is
  461. /// calculated by overridable <c>GetTextPosition</c> method of the
  462. /// <c>PieSlice</c> type.
  463. /// </summary>
  464. /// <param name="graphics">
  465. /// <c>Graphics</c> object.
  466. /// </param>
  467. public virtual void PlaceTexts(Graphics graphics) {
  468. Debug.Assert(graphics != null);
  469. Debug.Assert(m_font != null);
  470. Debug.Assert(m_foreColor != Color.Empty);
  471. StringFormat drawFormat = new StringFormat();
  472. drawFormat.Alignment = StringAlignment.Center;
  473. drawFormat.LineAlignment = StringAlignment.Center;
  474. using (Brush fontBrush = new SolidBrush(m_foreColor)) {
  475. foreach (PieSlice slice in m_pieSlices) {
  476. if (slice.Text != null && slice.Text.Length > 0) {
  477. PointF point = slice.GetTextPosition();
  478. graphics.DrawString(slice.Text, m_font, fontBrush, point, drawFormat);
  479. }
  480. }
  481. }
  482. }
  483.  
  484. /// <summary>
  485. /// Searches the chart to find the index of the pie slice which
  486. /// contains point given. Search order goes in the direction opposite
  487. /// to drawing order.
  488. /// </summary>
  489. /// <param name="point">
  490. /// <c>PointF</c> point for which pie slice is searched for.
  491. /// </param>
  492. /// <returns>
  493. /// Index of the corresponding pie slice, or -1 if none is found.
  494. /// </returns>
  495. public int FindPieSliceUnderPoint(PointF point) {
  496. // first check tops
  497. for (int i = 0; i < m_pieSlices.Length; ++i) {
  498. PieSlice slice = (PieSlice)m_pieSlices[i];
  499. if (slice.PieSliceContainsPoint(point))
  500. return (int)m_pieSlicesMapping[i];
  501. }
  502. // split the backmost (at 270 degrees) pie slice
  503. ArrayList pieSlicesList = new ArrayList(m_pieSlices);
  504. PieSlice[] splitSlices = m_pieSlices[0].Split(270F);
  505. if (splitSlices.Length > 1) {
  506. pieSlicesList[0] = splitSlices[1];
  507. if (splitSlices[0].SweepAngle > 0F) {
  508. pieSlicesList.Add(splitSlices[0]);
  509. }
  510. }
  511. PieSlice[] pieSlices = (PieSlice[])pieSlicesList.ToArray(typeof(PieSlice));
  512. int indexFound = -1;
  513. // if not found yet, then check for periferies
  514. int incrementIndex = 0;
  515. int decrementIndex = pieSlices.Length - 1;
  516. while (incrementIndex <= decrementIndex) {
  517. PieSlice sliceLeft = pieSlices[decrementIndex];
  518. float angle1 = 270 - sliceLeft.StartAngle;
  519. PieSlice sliceRight = pieSlices[incrementIndex];
  520. float angle2 = (sliceRight.EndAngle + 90) % 360;
  521. Debug.Assert(angle2 >= 0);
  522. if (angle2 < angle1) {
  523. if (sliceRight.PeripheryContainsPoint(point))
  524. indexFound = incrementIndex;
  525. ++incrementIndex;
  526. }
  527. else {
  528. if (sliceLeft.PeripheryContainsPoint(point))
  529. indexFound = decrementIndex;
  530. --decrementIndex;
  531. }
  532. }
  533. // check for start/stop sides, starting from the foremost
  534. if (indexFound < 0) {
  535. int foremostPieIndex = GetForemostPieSlice(pieSlices);
  536. // check for start sides from the foremost slice to the left
  537. // side
  538. int i = foremostPieIndex;
  539. while (i < pieSlices.Length) {
  540. PieSlice sliceLeft = (PieSlice)pieSlices[i];
  541. if (sliceLeft.StartSideContainsPoint(point)) {
  542. indexFound = i;
  543. break;
  544. }
  545. ++i;
  546. }
  547. // if not found yet, check end sides from the foremost to the right
  548. // side
  549. if (indexFound < 0) {
  550. i = foremostPieIndex;
  551. while (i >= 0) {
  552. PieSlice sliceLeft = (PieSlice)pieSlices[i];
  553. if (sliceLeft.EndSideContainsPoint(point)) {
  554. indexFound = i;
  555. break;
  556. }
  557. --i;
  558. }
  559. }
  560. }
  561. // finally search for bottom sides
  562. if (indexFound < 0) {
  563. for (int i = 0; i < m_pieSlices.Length; ++i) {
  564. PieSlice slice = (PieSlice)m_pieSlices[i];
  565. if (slice.BottomSurfaceSectionContainsPoint(point))
  566. return (int)m_pieSlicesMapping[i];
  567. }
  568. }
  569. if (indexFound > -1) {
  570. indexFound %= (m_pieSlicesMapping.Count);
  571. return (int)m_pieSlicesMapping[indexFound];
  572. }
  573. return -1;
  574. }
  575.  
  576. /// <summary>
  577. /// Return the index of the foremost pie slice i.e. the one crossing
  578. /// 90 degrees boundary.
  579. /// </summary>
  580. /// <param name="pieSlices">
  581. /// Array of <c>PieSlice</c> objects to examine.
  582. /// </param>
  583. /// <returns>
  584. /// Index of the foremost pie slice.
  585. /// </returns>
  586. private int GetForemostPieSlice(PieSlice[] pieSlices) {
  587. Debug.Assert(pieSlices != null && pieSlices.Length > 0);
  588. for (int i = 0; i < pieSlices.Length; ++i) {
  589. PieSlice pieSlice = pieSlices[i];
  590. if (((pieSlice.StartAngle <= 90) && ((pieSlice.StartAngle + pieSlice.SweepAngle) >= 90)) ||
  591. ((pieSlice.StartAngle + pieSlice.SweepAngle > 360) && ((pieSlice.StartAngle) <= 450) && (pieSlice.StartAngle + pieSlice.SweepAngle) >= 450)) {
  592. return i;
  593. }
  594. }
  595. Debug.Assert(false, "Foremost pie slice not found");
  596. return -1;
  597. }
  598.  
  599. /// <summary>
  600. /// Finds the smallest rectangle int which chart fits entirely.
  601. /// </summary>
  602. /// <returns>
  603. /// <c>RectangleF</c> into which all member slices fit.
  604. /// </returns>
  605. protected RectangleF GetFittingRectangle() {
  606. RectangleF boundingRectangle = m_pieSlices[0].GetFittingRectangle();
  607. for (int i = 1; i < m_pieSlices.Length; ++i) {
  608. boundingRectangle = RectangleF.Union(boundingRectangle, m_pieSlices[i].GetFittingRectangle());
  609. }
  610. return boundingRectangle;
  611. }
  612.  
  613. /// <summary>
  614. /// Readjusts each slice for new bounding rectangle.
  615. /// </summary>
  616. /// <param name="newBoundingRectangle">
  617. /// <c>RectangleF</c> representing new boundary.
  618. /// </param>
  619. protected void ReadjustSlices(RectangleF newBoundingRectangle) {
  620. float xResizeFactor = m_widthBoundingRect / newBoundingRectangle.Width;
  621. float yResizeFactor = m_heightBoundingRect / newBoundingRectangle.Height;
  622. float xOffset = newBoundingRectangle.X - m_xBoundingRect;
  623. float yOffset = newBoundingRectangle.Y - m_yBoundingRect;
  624. foreach (PieSlice slice in m_pieSlices) {
  625. float x = slice.BoundingRectangle.X - xOffset;
  626. float y = slice.BoundingRectangle.Y - yOffset;
  627. float width = slice.BoundingRectangle.Width * xResizeFactor;
  628. float height = slice.BoundingRectangle.Height * yResizeFactor;
  629. float sliceHeight = slice.SliceHeight * yResizeFactor;
  630. slice.Readjust(x, y, width, height, sliceHeight);
  631. }
  632. }
  633.  
  634. /// <summary>
  635. /// Finds the largest displacement.
  636. /// </summary>
  637. protected float LargestDisplacement {
  638. get {
  639. float value = 0F;
  640. for (int i = 0; i < m_sliceRelativeDisplacements.Length && i < m_values.Length; ++i) {
  641. if (m_sliceRelativeDisplacements[i] > value)
  642. value = m_sliceRelativeDisplacements[i];
  643. }
  644. return value;
  645. }
  646. }
  647.  
  648. /// <summary>
  649. /// Gets the top ellipse size.
  650. /// </summary>
  651. protected SizeF TopEllipseSize {
  652. get {
  653. float factor = 1 + LargestDisplacement;
  654. float widthTopEllipse = m_widthBoundingRect / factor;
  655. float heightTopEllipse = m_heightBoundingRect / factor * (1 - m_sliceRelativeHeight);
  656. return new SizeF(widthTopEllipse, heightTopEllipse);
  657. }
  658. }
  659.  
  660. /// <summary>
  661. /// Gets the ellipse defined by largest displacement.
  662. /// </summary>
  663. protected SizeF LargestDisplacementEllipseSize {
  664. get {
  665. float factor = LargestDisplacement;
  666. float widthDisplacementEllipse = TopEllipseSize.Width * factor;
  667. float heightDisplacementEllipse = TopEllipseSize.Height * factor;
  668. return new SizeF(widthDisplacementEllipse, heightDisplacementEllipse);
  669. }
  670. }
  671.  
  672. /// <summary>
  673. /// Calculates the pie height.
  674. /// </summary>
  675. protected float PieHeight {
  676. get {
  677. return m_heightBoundingRect / (1 + LargestDisplacement) * m_sliceRelativeHeight;
  678. }
  679. }
  680.  
  681. /// <summary>
  682. /// Initializes pies.
  683. /// </summary>
  684. /// Creates a list of pies, starting with the pie that is crossing the
  685. /// 270 degrees boundary, i.e. "backmost" pie that always has to be
  686. /// drawn first to ensure correct surface overlapping.
  687. protected virtual void InitializePieSlices() {
  688. // calculates the sum of values required to evaluate sweep angles
  689. // for individual pies
  690. double sum = 0;
  691. foreach (double itemValue in m_values)
  692. sum += itemValue;
  693. // some values and indices that will be used in the loop
  694. SizeF topEllipeSize = TopEllipseSize;
  695. SizeF largestDisplacementEllipseSize = LargestDisplacementEllipseSize;
  696. int maxDisplacementIndex = m_sliceRelativeDisplacements.Length - 1;
  697. float largestDisplacement = LargestDisplacement;
  698. ArrayList listPieSlices = new ArrayList();
  699. m_pieSlicesMapping.Clear();
  700. int colorIndex = 0;
  701. int backPieIndex = -1;
  702. int displacementIndex = 0;
  703. double startAngle = (double)m_initialAngle;
  704. for (int i = 0; i < m_values.Length; ++i) {
  705. double itemValue = m_values[i];
  706. double sweepAngle = (double)itemValue / sum * 360;
  707. // displacement from the center of the ellipse
  708. float xDisplacement = m_sliceRelativeDisplacements[displacementIndex];
  709. float yDisplacement = m_sliceRelativeDisplacements[displacementIndex];
  710. if (xDisplacement > 0F) {
  711. Debug.Assert(largestDisplacement > 0F);
  712. SizeF pieDisplacement = GetSliceDisplacement((float)(startAngle + sweepAngle / 2), m_sliceRelativeDisplacements[displacementIndex]);
  713. xDisplacement = pieDisplacement.Width;
  714. yDisplacement = pieDisplacement.Height;
  715. }
  716. PieSlice slice = null;
  717. if (i == m_highlightedIndex)
  718. slice = CreatePieSliceHighlighted(m_xBoundingRect + largestDisplacementEllipseSize.Width / 2 + xDisplacement, m_yBoundingRect + largestDisplacementEllipseSize.Height / 2 + yDisplacement, topEllipeSize.Width, topEllipeSize.Height, PieHeight, (float)(startAngle % 360), (float)(sweepAngle), m_sliceColors[colorIndex], m_shadowStyle, m_edgeColorType, m_edgeLineWidth);
  719. else
  720. slice = CreatePieSlice(m_xBoundingRect + largestDisplacementEllipseSize.Width / 2 + xDisplacement, m_yBoundingRect + largestDisplacementEllipseSize.Height / 2 + yDisplacement, topEllipeSize.Width, topEllipeSize.Height, PieHeight, (float)(startAngle % 360), (float)(sweepAngle), m_sliceColors[colorIndex], m_shadowStyle, m_edgeColorType, m_edgeLineWidth);
  721. slice.Text = m_texts[i];
  722. // the backmost pie is inserted to the front of the list for correct drawing
  723. if (backPieIndex > -1 || ((startAngle <= 270) && (startAngle + sweepAngle > 270)) || ((startAngle >= 270) && (startAngle + sweepAngle > 630))) {
  724. ++backPieIndex;
  725. listPieSlices.Insert(backPieIndex, slice);
  726. m_pieSlicesMapping.Insert(backPieIndex, i);
  727. }
  728. else {
  729. listPieSlices.Add(slice);
  730. m_pieSlicesMapping.Add(i);
  731. }
  732. // increment displacementIndex only if there are more displacements available
  733. if (displacementIndex < maxDisplacementIndex)
  734. ++displacementIndex;
  735. ++colorIndex;
  736. // if all colors have been exhausted, reset color index
  737. if (colorIndex >= m_sliceColors.Length)
  738. colorIndex = 0;
  739. // prepare for the next pie slice
  740. startAngle += sweepAngle;
  741. if (startAngle > 360)
  742. startAngle -= 360;
  743. }
  744. m_pieSlices = (PieSlice[])listPieSlices.ToArray(typeof(PieSlice));
  745. }
  746.  
  747. /// <summary>
  748. /// Creates a <c>PieSlice</c> object.
  749. /// </summary>
  750. /// <param name="boundingRectLeft">
  751. /// x-coordinate of the upper-left corner of the rectangle that is
  752. /// used to draw the top surface of the slice.
  753. /// </param>
  754. /// <param name="boundingRectTop">
  755. /// y-coordinate of the upper-left corner of the rectangle that is
  756. /// used to draw the top surface of the slice.
  757. /// </param>
  758. /// <param name="boundingRectWidth">
  759. /// Width of the rectangle that is used to draw the top surface of
  760. /// the slice.
  761. /// </param>
  762. /// <param name="boundingRectHeight">
  763. /// Height of the rectangle that is used to draw the top surface of
  764. /// the slice.
  765. /// </param>
  766. /// <param name="sliceHeight">
  767. /// Slice height.
  768. /// </param>
  769. /// <param name="startAngle">
  770. /// Starting angle.
  771. /// </param>
  772. /// <param name="sweepAngle">
  773. /// Sweep angle.
  774. /// </param>
  775. /// <param name="color">
  776. /// Color used for slice rendering.
  777. /// </param>
  778. /// <param name="shadowStyle">
  779. /// Shadow style used for slice rendering.
  780. /// </param>
  781. /// <param name="edgeColorType">
  782. /// Edge lines color type.
  783. /// </param>
  784. /// <param name="edgeLineWidth">
  785. /// Edge lines width.
  786. /// </param>
  787. /// <returns>
  788. /// <c>PieSlice</c> object with given values.
  789. /// </returns>
  790. protected virtual PieSlice CreatePieSlice(float boundingRectLeft, float boundingRectTop, float boundingRectWidth, float boundingRectHeight, float sliceHeight, float startAngle, float sweepAngle, Color color, ShadowStyle shadowStyle, EdgeColorType edgeColorType, float edgeLineWidth) {
  791. return new PieSlice(boundingRectLeft, boundingRectTop, boundingRectWidth, boundingRectHeight, sliceHeight, (float)(startAngle % 360), sweepAngle, color, shadowStyle, edgeColorType, edgeLineWidth);
  792. }
  793.  
  794. /// <summary>
  795. /// Creates highlighted <c>PieSlice</c> object.
  796. /// </summary>
  797. /// <param name="boundingRectLeft">
  798. /// x-coordinate of the upper-left corner of the rectangle that is
  799. /// used to draw the top surface of the slice.
  800. /// </param>
  801. /// <param name="boundingRectTop">
  802. /// y-coordinate of the upper-left corner of the rectangle that is
  803. /// used to draw the top surface of the slice.
  804. /// </param>
  805. /// <param name="boundingRectWidth">
  806. /// Width of the rectangle that is used to draw the top surface of
  807. /// the slice.
  808. /// </param>
  809. /// <param name="boundingRectHeight">
  810. /// Height of the rectangle that is used to draw the top surface of
  811. /// the slice.
  812. /// </param>
  813. /// <param name="sliceHeight">
  814. /// Slice height.
  815. /// </param>
  816. /// <param name="startAngle">
  817. /// Starting angle.
  818. /// </param>
  819. /// <param name="sweepAngle">
  820. /// Sweep angle.
  821. /// </param>
  822. /// <param name="color">
  823. /// Color used for slice rendering.
  824. /// </param>
  825. /// <param name="shadowStyle">
  826. /// Shadow style used for slice rendering.
  827. /// </param>
  828. /// <param name="edgeColorType">
  829. /// Edge lines color type.
  830. /// </param>
  831. /// <param name="edgeLineWidth">
  832. /// Edge lines width.
  833. /// </param>
  834. /// <returns>
  835. /// <c>PieSlice</c> object with given values.
  836. /// </returns>
  837. protected virtual PieSlice CreatePieSliceHighlighted(float boundingRectLeft, float boundingRectTop, float boundingRectWidth, float boundingRectHeight, float sliceHeight, float startAngle, float sweepAngle, Color color, ShadowStyle shadowStyle, EdgeColorType edgeColorType, float edgeLineWidth) {
  838. Color highLightedColor = ColorUtil.CreateColorWithCorrectedLightness(color, ColorUtil.BrightnessEnhancementFactor1);
  839. return new PieSlice(boundingRectLeft, boundingRectTop, boundingRectWidth, boundingRectHeight, sliceHeight, (float)(startAngle % 360), sweepAngle, highLightedColor, shadowStyle, edgeColorType, edgeLineWidth);
  840. }
  841.  
  842. /// <summary>
  843. /// Calculates the displacement for given angle.
  844. /// </summary>
  845. /// <param name="angle">
  846. /// Angle (in degrees).
  847. /// </param>
  848. /// <param name="displacementFactor">
  849. /// Displacement factor.
  850. /// </param>
  851. /// <returns>
  852. /// <c>SizeF</c> representing displacement.
  853. /// </returns>
  854. protected SizeF GetSliceDisplacement(float angle, float displacementFactor) {
  855. Debug.Assert(displacementFactor > 0F && displacementFactor <= 1F);
  856. if (displacementFactor == 0F)
  857. return SizeF.Empty;
  858. float xDisplacement = (float)(TopEllipseSize.Width * displacementFactor / 2 * Math.Cos(angle * Math.PI / 180));
  859. float yDisplacement = (float)(TopEllipseSize.Height * displacementFactor / 2 * Math.Sin(angle * Math.PI / 180));
  860. return new SizeF(xDisplacement, yDisplacement);
  861. }
  862.  
  863. /// <summary>
  864. /// Draws outer peripheries of all slices.
  865. /// </summary>
  866. /// <param name="graphics">
  867. /// <c>Graphics</c> used for drawing.
  868. /// </param>
  869. protected void DrawSliceSides(Graphics graphics) {
  870. ArrayList pieSlicesList = new ArrayList(m_pieSlices);
  871. PieSlice ps = null;
  872. // if the first pie slice (crossing 270 i.e. back) is crossing 90
  873. // (front) axis too, we have to split it
  874. if ((m_pieSlices[0].StartAngle > 90) && (m_pieSlices[0].StartAngle <= 270) && (m_pieSlices[0].StartAngle + m_pieSlices[0].SweepAngle > 450)) {
  875. ps = (PieSlice)pieSlicesList[0];
  876. // this one is split at 0 deg to avoid line of split to be
  877. // visible on the periphery
  878. PieSlice[] splitSlices = ps.Split(0F);
  879. pieSlicesList[0] = splitSlices[0];
  880. if (splitSlices[1].SweepAngle > 0F) {
  881. pieSlicesList.Insert(1, splitSlices[1]);
  882. }
  883. }
  884. else if (((m_pieSlices[0].StartAngle > 270) && (m_pieSlices[0].StartAngle + m_pieSlices[0].SweepAngle > 450)) || ((m_pieSlices[0].StartAngle < 90) && (m_pieSlices[0].StartAngle + m_pieSlices[0].SweepAngle > 270))) {
  885. ps = (PieSlice)pieSlicesList[0];
  886. // this one is split at 180 deg to avoid line of split to be
  887. // visible on the periphery
  888. PieSlice[] splitSlices = ps.Split(180F);
  889. pieSlicesList[0] = splitSlices[1];
  890. if (splitSlices[1].SweepAngle > 0F) {
  891. pieSlicesList.Add(splitSlices[0]);
  892. }
  893. }
  894. // first draw the backmost pie slice
  895. ps = (PieSlice)pieSlicesList[0];
  896. ps.DrawSides(graphics);
  897. // draw pie slices from the backmost to forward
  898. int incrementIndex = 1;
  899. int decrementIndex = pieSlicesList.Count - 1;
  900. while (incrementIndex < decrementIndex) {
  901. PieSlice sliceLeft = (PieSlice)pieSlicesList[decrementIndex];
  902. float angle1 = sliceLeft.StartAngle - 90;
  903. if (angle1 > 180 || angle1 < 0)
  904. angle1 = 0;
  905. PieSlice sliceRight = (PieSlice)pieSlicesList[incrementIndex];
  906. float angle2 = (450 - sliceRight.EndAngle) % 360;
  907. if (angle2 > 180 || angle2 < 0)
  908. angle2 = 0;
  909. Debug.Assert(angle1 >= 0);
  910. Debug.Assert(angle2 >= 0);
  911. if (angle2 >= angle1) {
  912. sliceRight.DrawSides(graphics);
  913. ++incrementIndex;
  914. }
  915. else if (angle2 < angle1) {
  916. sliceLeft.DrawSides(graphics);
  917. --decrementIndex;
  918. }
  919. }
  920. ps = (PieSlice)pieSlicesList[decrementIndex];
  921. ps.DrawSides(graphics);
  922. }
  923.  
  924. /// <summary>
  925. /// Draws bottom sides of all pie slices.
  926. /// </summary>
  927. /// <param name="graphics">
  928. /// <c>Graphics</c> used for drawing.
  929. /// </param>
  930. protected void DrawBottoms(Graphics graphics) {
  931. foreach (PieSlice slice in m_pieSlices) {
  932. slice.DrawBottom(graphics);
  933. }
  934. }
  935.  
  936. /// <summary>
  937. /// Draws top sides of all pie slices.
  938. /// </summary>
  939. /// <param name="graphics">
  940. /// <c>Graphics</c> used for drawing.
  941. /// </param>
  942. protected void DrawTops(Graphics graphics) {
  943. foreach (PieSlice slice in m_pieSlices) {
  944. slice.DrawTop(graphics);
  945. }
  946. }
  947.  
  948. /// <summary>
  949. /// Helper function used in assertions. Checks the validity of
  950. /// slice displacements.
  951. /// </summary>
  952. /// <param name="displacements">
  953. /// Array of displacements to check.
  954. /// </param>
  955. /// <returns>
  956. /// <c>true</c> if all displacements have a valid value; otherwise
  957. /// <c>false</c>.
  958. /// </returns>
  959. private bool AreDisplacementsValid(float[] displacements) {
  960. foreach (float value in displacements) {
  961. if (!IsDisplacementValid(value))
  962. return false;
  963. }
  964. return true;
  965. }
  966.  
  967. /// <summary>
  968. /// Helper function used in assertions. Checks the validity of
  969. /// a slice displacement.
  970. /// </summary>
  971. /// <param name="value">
  972. /// Displacement value to check.
  973. /// </param>
  974. /// <returns>
  975. /// <c>true</c> if displacement has a valid value; otherwise
  976. /// <c>false</c>.
  977. /// </returns>
  978. private bool IsDisplacementValid(float value) {
  979. return (value >= 0F && value <= 1F);
  980. }
  981.  
  982. /// <summary>
  983. /// x-coordinate of the top left corner of the bounding rectangle.
  984. /// </summary>
  985. protected float m_xBoundingRect;
  986. /// <summary>
  987. /// y-coordinate of the top left corner of the bounding rectangle.
  988. /// </summary>
  989. protected float m_yBoundingRect;
  990. /// <summary>
  991. /// Width of the bounding rectangle.
  992. /// </summary>
  993. protected float m_widthBoundingRect;
  994. /// <summary>
  995. /// Height of the bounding rectangle.
  996. /// </summary>
  997. protected float m_heightBoundingRect;
  998. /// <summary>
  999. /// Slice relative height.
  1000. /// </summary>
  1001. protected float m_sliceRelativeHeight = 0F;
  1002. /// <summary>
  1003. /// Initial angle from which chart is drawn.
  1004. /// </summary>
  1005. protected float m_initialAngle = 0F;
  1006. /// <summary>
  1007. /// Array of ordered pie slices constituting the chart, starting from
  1008. /// 270 degrees axis.
  1009. /// </summary>
  1010. protected PieSlice[] m_pieSlices;
  1011. /// <summary>
  1012. /// Collection of reordered pie slices mapped to original order.
  1013. /// </summary>
  1014. protected ArrayList m_pieSlicesMapping = new ArrayList();
  1015. /// <summary>
  1016. /// Array of values to be presented by the chart.
  1017. /// </summary>
  1018. protected double[] m_values = new double[] {};
  1019. /// <summary>
  1020. /// Array of colors used for rendering.
  1021. /// </summary>
  1022. protected Color[] m_sliceColors = new Color[] {
  1023. Color.Red,
  1024. Color.Green,
  1025. Color.Blue,
  1026. Color.Yellow,
  1027. Color.Purple,
  1028. Color.Olive,
  1029. Color.Navy,
  1030. Color.Aqua,
  1031. Color.Lime,
  1032. Color.Maroon,
  1033. Color.Teal,
  1034. Color.Fuchsia
  1035. };
  1036. /// <summary>
  1037. /// Array of description texts.
  1038. /// </summary>
  1039. protected string[] m_texts;
  1040. /// <summary>
  1041. /// Font used to display texts.
  1042. /// </summary>
  1043. protected Font m_font = System.Windows.Forms.Control.DefaultFont;
  1044. /// <summary>
  1045. /// Fore color used to display texts.
  1046. /// </summary>
  1047. protected Color m_foreColor = SystemColors.WindowText;
  1048. /// <summary>
  1049. /// Array of relative displacements from the common center.
  1050. /// </summary>
  1051. protected float[] m_sliceRelativeDisplacements = new float[] { 0F };
  1052. /// <summary>
  1053. /// Edge color type used for rendering.
  1054. /// </summary>
  1055. protected EdgeColorType m_edgeColorType = EdgeColorType.SystemColor;
  1056. /// <summary>
  1057. /// Edge line width.
  1058. /// </summary>
  1059. protected float m_edgeLineWidth = 1F;
  1060. /// <summary>
  1061. /// Shadow style.
  1062. /// </summary>
  1063. protected ShadowStyle m_shadowStyle = ShadowStyle.NoShadow;
  1064. /// <summary>
  1065. /// Should the chart fit the bounding rectangle exactly.
  1066. /// </summary>
  1067. protected bool m_fitToBoundingRectangle = true;
  1068. /// <summary>
  1069. /// Index of the currently highlighted pie slice.
  1070. /// </summary>
  1071. protected int m_highlightedIndex = -1;
  1072. /// <summary>
  1073. /// Flag indicating if object has been disposed.
  1074. /// </summary>
  1075. private bool m_disposed = false;
  1076. }
  1077. }

Structure et Fichiers du projet

Afficher/masquer...


Répertoires contenus dans /var/www/bin/sniplets/bibliobrol/broldev/src/model/drawing/chart/ 
IcôneNomTailleModification
Pas de sous-répertoires.
IcôneNomTailleModification
| _ Répertoire parent0 octets1734970201 23/12/2024 17:10:01
Fichiers contenus dans /var/www/bin/sniplets/bibliobrol/broldev/src/model/drawing/chart/ 
IcôneNomTailleModificationAction
IcôneNomTailleModificationAction
Afficher le fichier .cs|.csShadowStyle.cs620 octets31/10/2018 18:33:22-refusé-
Afficher le fichier .cs|.csEdgeColor.cs2.92 Ko31/10/2018 18:33:21-refusé-
Afficher le fichier .cs|.csPieChart.cs44.97 Ko31/10/2018 18:33:22-refusé-
Afficher le fichier .cs|.csColorUtil.cs2.05 Ko31/10/2018 18:33:21-refusé-
Afficher le fichier .resx|.resxPieChartControl.resx1.69 Ko31/10/2018 18:33:22-refusé-
Afficher le fichier .cs|.csPieChartControl.cs14.04 Ko31/10/2018 18:33:22-refusé-
Afficher le fichier .cs|.csPieSlice.cs52.86 Ko31/10/2018 18:33:22-refusé-
Afficher le fichier .cs|.csGraphicsUtil.cs2.33 Ko31/10/2018 18:33:21-refusé-
Afficher le fichier .cs|.csQuadrilateral.cs6.42 Ko31/10/2018 18:33:22-refusé-
Afficher le fichier .cs|.csEdgeColorType.cs1.64 Ko31/10/2018 18:33:21-refusé-

Utilisation de l'explorateur de code

  • Navigation :
    • Un clic sur une icône de répertoire ouvre ce répertoire pour en afficher les fichiers.
    • Lorsque le répertoire en cours ne contient pas de sous-répertoires il est possible de remonter vers le répertoire parent.
    • La structure de répertoires en treetable (tableau en forme d'arborescence) n'est plus possibledans cette version.
    • Un clic sur une icône de fichier ouvre ce fichier pour en afficher le code avec la coloration syntaxique adaptée en fonction du langage principal utilisé dans le fichier.
  • Affichage :
    • Il est possible de trier les répertoires ou les fichiers selon certains critères (nom, taille, date).
  • Actions :
    • Les actions possible sur les fichiers dépendent de vos droits d'utilisateur sur le site. Veuillez activer le mode utilisateur pour activer les actions.

Version en cache

23/12/2024 17:10:01 Cette version de la page est en cache (à la date du 23/12/2024 17:10:01) afin d'accélérer le traitement. Vous pouvez activer le mode utilisateur dans le menu en haut pour afficher la dernère version de la page.

Document créé le 16/10/2009, dernière modification le 26/10/2018
Source du document imprimé : https://www.gaudry.be/cs-broldev-source-rf-model/drawing/chart/PieChart.cs.html

L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.