DataManager.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  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: Series storage and manipulation class.
  6. //
  7. using System;
  8. using System.ComponentModel;
  9. using System.ComponentModel.Design;
  10. using System.Drawing;
  11. using System.Drawing.Design;
  12. using FastReport.DataVisualization.Charting.ChartTypes;
  13. using FastReport.DataVisualization.Charting.Utilities;
  14. namespace FastReport.DataVisualization.Charting.Data
  15. {
  16. /// <summary>
  17. /// Data Manager.
  18. /// </summary>
  19. internal class DataManager : ChartElement, IServiceProvider
  20. {
  21. #region Fields
  22. // Series collection
  23. private SeriesCollection _series = null;
  24. // Servise container reference
  25. internal IServiceContainer serviceContainer = null;
  26. // Chart color palette
  27. private ChartColorPalette _colorPalette = ChartColorPalette.BrightPastel;
  28. #endregion
  29. #region Constructors and initialization
  30. /// <summary>
  31. /// Data manager public constructor
  32. /// </summary>
  33. /// <param name="container">Service container object.</param>
  34. public DataManager(IServiceContainer container)
  35. {
  36. if(container == null)
  37. {
  38. throw(new ArgumentNullException(SR.ExceptionInvalidServiceContainer));
  39. }
  40. serviceContainer = container;
  41. Common = new CommonElements(container);
  42. _series = new SeriesCollection(this);
  43. }
  44. /// <summary>
  45. /// Returns Data Manager service object.
  46. /// </summary>
  47. /// <param name="serviceType">Service type requested.</param>
  48. /// <returns>Data Manager service object.</returns>
  49. [EditorBrowsableAttribute(EditorBrowsableState.Never)]
  50. object IServiceProvider.GetService(Type serviceType)
  51. {
  52. if(serviceType == typeof(DataManager))
  53. {
  54. return this;
  55. }
  56. throw (new ArgumentException( SR.ExceptionDataManagerUnsupportedType(serviceType.ToString())));
  57. }
  58. /// <summary>
  59. /// Initialize data manger object
  60. /// </summary>
  61. internal void Initialize()
  62. {
  63. // Attach to the Chart Picture painting events
  64. ChartImage chartPicture = (ChartImage)serviceContainer.GetService(typeof(ChartImage));
  65. chartPicture.BeforePaint += new EventHandler<ChartPaintEventArgs>(this.ChartPicture_BeforePaint);
  66. chartPicture.AfterPaint += new EventHandler<ChartPaintEventArgs>(this.ChartPicture_AfterPaint);
  67. }
  68. #endregion
  69. #region Chart picture painting events hanlers
  70. internal override void Invalidate()
  71. {
  72. base.Invalidate();
  73. if (Chart!=null)
  74. Chart.Invalidate();
  75. }
  76. /// <summary>
  77. /// Event fired when chart picture is going to be painted.
  78. /// </summary>
  79. /// <param name="sender">Sender object.</param>
  80. /// <param name="e">Event arguments.</param>
  81. private void ChartPicture_BeforePaint(object sender, ChartPaintEventArgs e)
  82. {
  83. // Prepare series for drawing
  84. int markerIndex = 1;
  85. for(int index = 0; index < this.Series.Count; index++)
  86. {
  87. Series series = this.Series[index];
  88. // Reset series "X values are zeros" flag
  89. series.xValuesZerosChecked = false;
  90. series.xValuesZeros = false;
  91. // Set series colors from palette
  92. IChartType chartType = e.CommonElements.ChartTypeRegistry.GetChartType(series.ChartTypeName);
  93. bool paletteColorsInPoints = chartType.ApplyPaletteColorsToPoints;
  94. // if the series palette is set the we can color all data points, even on column chart.
  95. if (series.Palette != ChartColorPalette.None)
  96. {
  97. paletteColorsInPoints = true;
  98. }
  99. this.PrepareData(
  100. paletteColorsInPoints,
  101. series.Name);
  102. // Clear temp. marker style
  103. if(series.tempMarkerStyleIsSet)
  104. {
  105. series.MarkerStyle = MarkerStyle.None;
  106. series.tempMarkerStyleIsSet = false;
  107. }
  108. // Set marker style for chart types based on markes
  109. if(chartType.GetLegendImageStyle(series) == LegendImageStyle.Marker && series.MarkerStyle == MarkerStyle.None)
  110. {
  111. series.MarkerStyle = (MarkerStyle)markerIndex++;
  112. series.tempMarkerStyleIsSet = true;
  113. if(markerIndex > 9)
  114. {
  115. markerIndex = 1;
  116. }
  117. }
  118. }
  119. }
  120. /// <summary>
  121. /// Event fired after chart picture was painted.
  122. /// </summary>
  123. /// <param name="sender">Sender object.</param>
  124. /// <param name="e">Event arguments.</param>
  125. private void ChartPicture_AfterPaint(object sender, ChartPaintEventArgs e)
  126. {
  127. Chart control = (Chart)serviceContainer.GetService(typeof(Chart));
  128. if(control != null)
  129. {
  130. // Clean up series after drawing
  131. for(int index = 0; index < this.Series.Count; index++)
  132. {
  133. Series series = this.Series[index];
  134. if(series.UnPrepareData(control.Site))
  135. {
  136. --index;
  137. }
  138. }
  139. }
  140. }
  141. #endregion
  142. #region Series data preparation methods
  143. /// <summary>
  144. /// Apply palette colors to the data series if UsePaletteColors property is set.
  145. /// </summary>
  146. internal void ApplyPaletteColors()
  147. {
  148. ChartColorPalette palette = this.Palette;
  149. // switch to default pallette if is none and custom collors array is empty.
  150. if (palette == ChartColorPalette.None && this.PaletteCustomColors.Length == 0)
  151. {
  152. palette = ChartColorPalette.BrightPastel;
  153. }
  154. // Get palette colors
  155. int colorIndex = 0;
  156. Color[] paletteColors = (palette == ChartColorPalette.None) ?
  157. this.PaletteCustomColors : ChartPaletteColors.GetPaletteColors(palette);
  158. foreach (Series dataSeries in _series)
  159. {
  160. // Check if chart area name is valid
  161. bool validAreaName = false;
  162. if (Chart!=null)
  163. {
  164. validAreaName = Chart.ChartAreas.IsNameReferenceValid(dataSeries.ChartArea);
  165. }
  166. // Change color of the series only if valid chart area name is specified
  167. if(validAreaName)
  168. {
  169. // Change color of the series only if default color is set
  170. if(dataSeries.Color == Color.Empty || dataSeries.tempColorIsSet)
  171. {
  172. dataSeries.color = paletteColors[colorIndex++];
  173. dataSeries.tempColorIsSet = true;
  174. if(colorIndex >= paletteColors.Length)
  175. {
  176. colorIndex = 0;
  177. }
  178. }
  179. }
  180. }
  181. }
  182. /// <summary>
  183. /// Called just before the data from the series to be used to perform these operations:
  184. /// - apply palette colors to the data series
  185. /// - prepare data in series
  186. /// </summary>
  187. /// <param name="pointsApplyPaletteColors">If true each data point will be assigned a color from the palette (if it's set)</param>
  188. /// <param name="series">List of series indexes, which requires data preparation</param>
  189. internal void PrepareData(bool pointsApplyPaletteColors, params string[] series)
  190. {
  191. this.ApplyPaletteColors();
  192. // Prepare data in series
  193. Chart control = (Chart)serviceContainer.GetService(typeof(Chart));
  194. if(control != null)
  195. {
  196. foreach(string seriesName in series)
  197. {
  198. this.Series[seriesName].PrepareData(pointsApplyPaletteColors);
  199. }
  200. }
  201. }
  202. #endregion
  203. #region Series Min/Max values methods
  204. /// <summary>
  205. /// This method checks if data point should be skipped. This
  206. /// method will return true if data point is empty.
  207. /// </summary>
  208. /// <param name="point">Data point</param>
  209. /// <returns>This method returns true if data point is empty.</returns>
  210. private bool IsPointSkipped( DataPoint point )
  211. {
  212. if( point.IsEmpty )
  213. {
  214. return true;
  215. }
  216. return false;
  217. }
  218. /// <summary>
  219. /// Gets max number of data points in specified series.
  220. /// </summary>
  221. /// <param name="series">Series IDs</param>
  222. /// <returns>Maximum number of data points</returns>
  223. internal int GetNumberOfPoints(params string[] series)
  224. {
  225. int numberOfPoints = 0;
  226. foreach(string seriesName in series)
  227. {
  228. numberOfPoints = Math.Max(numberOfPoints, this._series[seriesName].Points.Count);
  229. }
  230. return numberOfPoints;
  231. }
  232. /// <summary>
  233. /// Gets maximum Y value from many series
  234. /// </summary>
  235. /// <param name="valueIndex">Index of Y value to use</param>
  236. /// <param name="series">Series IDs</param>
  237. /// <returns>Maximum Y value</returns>
  238. internal double GetMaxYValue(int valueIndex, params string[] series)
  239. {
  240. double returnValue = Double.MinValue;
  241. foreach(string seriesName in series)
  242. {
  243. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  244. {
  245. // The empty point
  246. if( IsPointSkipped( seriesPoint ) )
  247. {
  248. continue;
  249. }
  250. if(!double.IsNaN(seriesPoint.YValues[valueIndex]))
  251. {
  252. returnValue = Math.Max(returnValue, seriesPoint.YValues[valueIndex]);
  253. }
  254. }
  255. }
  256. return returnValue;
  257. }
  258. /// <summary>
  259. /// Get Maximum value for Y and and Radius (Y2) ( used for bubble chart )
  260. /// </summary>
  261. /// <param name="area">Chart Area</param>
  262. /// <param name="series">Series IDs</param>
  263. /// <returns>Maximum Y value</returns>
  264. internal double GetMaxYWithRadiusValue( ChartArea area, params string[] series )
  265. {
  266. double returnValue = Double.MinValue;
  267. foreach(string seriesName in series)
  268. {
  269. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  270. {
  271. // The empty point
  272. if( IsPointSkipped( seriesPoint ) )
  273. {
  274. continue;
  275. }
  276. if(!double.IsNaN(seriesPoint.YValues[0]))
  277. {
  278. if (seriesPoint.YValues.Length > 1)
  279. {
  280. returnValue = Math.Max(returnValue, seriesPoint.YValues[0] + BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.YValues[1], true));
  281. }
  282. else
  283. {
  284. returnValue = Math.Max(returnValue, seriesPoint.YValues[0]);
  285. }
  286. }
  287. }
  288. }
  289. return returnValue;
  290. }
  291. /// <summary>
  292. /// Get Maximum value for X and Radius (Y2) ( used for bubble chart )
  293. /// </summary>
  294. /// <param name="area">Chart Area</param>
  295. /// <param name="series">Series IDs</param>
  296. /// <returns>Maximum X value</returns>
  297. internal double GetMaxXWithRadiusValue( ChartArea area, params string[] series )
  298. {
  299. double returnValue = Double.MinValue;
  300. foreach(string seriesName in series)
  301. {
  302. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  303. {
  304. // The empty point
  305. if( IsPointSkipped( seriesPoint ) )
  306. {
  307. continue;
  308. }
  309. if(!double.IsNaN(seriesPoint.XValue))
  310. {
  311. if (seriesPoint.YValues.Length > 1)
  312. {
  313. returnValue = Math.Max(returnValue, seriesPoint.XValue + BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.XValue, false));
  314. }
  315. else
  316. {
  317. returnValue = Math.Max(returnValue, seriesPoint.XValue);
  318. }
  319. }
  320. }
  321. }
  322. return returnValue;
  323. }
  324. /// <summary>
  325. /// Get Minimum value for X and Radius Y2 ( used for bubble chart )
  326. /// </summary>
  327. /// <param name="area">Chart Area</param>
  328. /// <param name="series">Series IDs</param>
  329. /// <returns>Minimum X value</returns>
  330. internal double GetMinXWithRadiusValue( ChartArea area, params string[] series )
  331. {
  332. double returnValue = Double.MaxValue;
  333. foreach(string seriesName in series)
  334. {
  335. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  336. {
  337. // The empty point
  338. if( IsPointSkipped( seriesPoint ) )
  339. {
  340. continue;
  341. }
  342. if(!double.IsNaN(seriesPoint.XValue))
  343. {
  344. if (seriesPoint.YValues.Length > 1)
  345. {
  346. returnValue = Math.Min(returnValue, seriesPoint.XValue - BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.YValues[1], false));
  347. }
  348. else
  349. {
  350. returnValue = Math.Min(returnValue, seriesPoint.XValue);
  351. }
  352. }
  353. }
  354. }
  355. return returnValue;
  356. }
  357. /// <summary>
  358. /// Gets maximum Y value from many series
  359. /// </summary>
  360. /// <param name="series">Series IDs</param>
  361. /// <returns>Maximum Y value</returns>
  362. internal double GetMaxYValue(params string[] series)
  363. {
  364. double returnValue = Double.MinValue;
  365. foreach(string seriesName in series)
  366. {
  367. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  368. {
  369. // The empty point
  370. if( IsPointSkipped( seriesPoint ) )
  371. {
  372. continue;
  373. }
  374. foreach( double y in seriesPoint.YValues )
  375. {
  376. if(!double.IsNaN(y))
  377. {
  378. returnValue = Math.Max(returnValue, y);
  379. }
  380. }
  381. }
  382. }
  383. return returnValue;
  384. }
  385. /// <summary>
  386. /// Gets maximum X value from many series
  387. /// </summary>
  388. /// <param name="series">Series IDs</param>
  389. /// <returns>Maximum X value</returns>
  390. internal double GetMaxXValue(params string[] series)
  391. {
  392. double returnValue = Double.MinValue;
  393. foreach(string seriesName in series)
  394. {
  395. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  396. {
  397. returnValue = Math.Max(returnValue, seriesPoint.XValue);
  398. }
  399. }
  400. return returnValue;
  401. }
  402. /// <summary>
  403. /// Gets minimum and maximum X value from many series.
  404. /// </summary>
  405. /// <param name="min">Returns maximum X value.</param>
  406. /// <param name="max">Returns minimum X value.</param>
  407. /// <param name="series">Series IDs</param>
  408. internal void GetMinMaxXValue(out double min, out double max, params string[] series)
  409. {
  410. max = Double.MinValue;
  411. min = Double.MaxValue;
  412. foreach(string seriesName in series)
  413. {
  414. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  415. {
  416. max = Math.Max(max, seriesPoint.XValue);
  417. min = Math.Min(min, seriesPoint.XValue);
  418. }
  419. }
  420. }
  421. /// <summary>
  422. /// Gets minimum and maximum Y value from many series.
  423. /// </summary>
  424. /// <param name="valueIndex">Index of Y value to use.</param>
  425. /// <param name="min">Returns maximum Y value.</param>
  426. /// <param name="max">Returns minimum Y value.</param>
  427. /// <param name="series">Series IDs</param>
  428. internal void GetMinMaxYValue(int valueIndex, out double min, out double max, params string[] series)
  429. {
  430. max = Double.MinValue;
  431. min = Double.MaxValue;
  432. foreach(string seriesName in series)
  433. {
  434. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  435. {
  436. // Skip empty point
  437. if( IsPointSkipped( seriesPoint ) )
  438. {
  439. continue;
  440. }
  441. double yValue = seriesPoint.YValues[valueIndex];
  442. if(!double.IsNaN(yValue))
  443. {
  444. max = Math.Max(max, yValue);
  445. min = Math.Min(min, yValue);
  446. }
  447. }
  448. }
  449. }
  450. /// <summary>
  451. /// Gets minimum and maximum Y value from many series.
  452. /// </summary>
  453. /// <param name="min">Returns maximum Y value.</param>
  454. /// <param name="max">Returns minimum Y value.</param>
  455. /// <param name="series">Series IDs</param>
  456. internal void GetMinMaxYValue(out double min, out double max, params string[] series)
  457. {
  458. max = Double.MinValue;
  459. min = Double.MaxValue;
  460. foreach(string seriesName in series)
  461. {
  462. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  463. {
  464. // Skip empty point
  465. if( IsPointSkipped( seriesPoint ) )
  466. {
  467. continue;
  468. }
  469. // Iterate through all Y values
  470. foreach( double y in seriesPoint.YValues )
  471. {
  472. if(!double.IsNaN(y))
  473. {
  474. max = Math.Max(max, y);
  475. min = Math.Min(min, y);
  476. }
  477. }
  478. }
  479. }
  480. }
  481. /// <summary>
  482. /// Gets minimum and maximum Y value from many series.
  483. /// </summary>
  484. /// <param name="seriesList">Series objects list.</param>
  485. /// <param name="min">Returns maximum Y value.</param>
  486. /// <param name="max">Returns minimum Y value.</param>
  487. internal void GetMinMaxYValue(System.Collections.ArrayList seriesList, out double min, out double max)
  488. {
  489. max = Double.MinValue;
  490. min = Double.MaxValue;
  491. foreach(Series series in seriesList)
  492. {
  493. foreach(DataPoint seriesPoint in series.Points)
  494. {
  495. // Skip empty point
  496. if( IsPointSkipped( seriesPoint ) )
  497. {
  498. continue;
  499. }
  500. // Iterate through all Y values
  501. foreach( double y in seriesPoint.YValues )
  502. {
  503. if(!double.IsNaN(y))
  504. {
  505. max = Math.Max(max, y);
  506. min = Math.Min(min, y);
  507. }
  508. }
  509. }
  510. }
  511. }
  512. /// <summary>
  513. /// Gets maximum stacked Y value from many series
  514. /// </summary>
  515. /// <param name="valueIndex">Index of Y value to use</param>
  516. /// <param name="series">Series IDs</param>
  517. /// <returns>Maximum stacked Y value</returns>
  518. internal double GetMaxStackedYValue(int valueIndex, params string[] series)
  519. {
  520. double returnValue = 0;
  521. double numberOfPoints = GetNumberOfPoints(series);
  522. for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
  523. {
  524. double stackedMax = 0;
  525. double noStackedMax = 0;
  526. foreach(string seriesName in series)
  527. {
  528. if(this._series[seriesName].Points.Count > pointIndex)
  529. {
  530. // Take chart type from the series
  531. ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry));
  532. IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName);
  533. // If stacked area
  534. if( !chartType.StackSign )
  535. continue;
  536. if( chartType.Stacked )
  537. {
  538. if(this._series[seriesName].Points[pointIndex].YValues[valueIndex] > 0)
  539. {
  540. stackedMax += this._series[seriesName].Points[pointIndex].YValues[valueIndex];
  541. }
  542. }
  543. else
  544. {
  545. noStackedMax = Math.Max(noStackedMax,this._series[seriesName].Points[pointIndex].YValues[valueIndex]);
  546. }
  547. }
  548. }
  549. stackedMax = Math.Max(stackedMax, noStackedMax);
  550. returnValue = Math.Max(returnValue, stackedMax);
  551. }
  552. return returnValue;
  553. }
  554. /// <summary>
  555. /// Gets maximum Unsigned stacked Y value from many series ( Stacked Area chart )
  556. /// </summary>
  557. /// <param name="valueIndex">Index of Y value to use</param>
  558. /// <param name="series">Series IDs</param>
  559. /// <returns>Maximum stacked Y value</returns>
  560. internal double GetMaxUnsignedStackedYValue(int valueIndex, params string[] series)
  561. {
  562. double returnValue = 0;
  563. double maxValue = Double.MinValue;
  564. double numberOfPoints = GetNumberOfPoints(series);
  565. for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
  566. {
  567. double stackedMax = 0;
  568. double noStackedMax = 0;
  569. foreach(string seriesName in series)
  570. {
  571. if (this._series[seriesName].Points.Count > pointIndex)
  572. {
  573. // Take chart type from the series
  574. ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry));
  575. IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName);
  576. // If stacked column and bar
  577. if (chartType.StackSign || double.IsNaN(this._series[seriesName].Points[pointIndex].YValues[valueIndex]))
  578. {
  579. continue;
  580. }
  581. if (chartType.Stacked)
  582. {
  583. maxValue = Double.MinValue;
  584. stackedMax += this._series[seriesName].Points[pointIndex].YValues[valueIndex];
  585. if (stackedMax > maxValue)
  586. maxValue = stackedMax;
  587. }
  588. else
  589. {
  590. noStackedMax = Math.Max(noStackedMax, this._series[seriesName].Points[pointIndex].YValues[valueIndex]);
  591. }
  592. }
  593. }
  594. maxValue = Math.Max(maxValue, noStackedMax);
  595. returnValue = Math.Max(returnValue, maxValue);
  596. }
  597. return returnValue;
  598. }
  599. /// <summary>
  600. /// Gets maximum stacked X value from many series
  601. /// </summary>
  602. /// <param name="series">Series IDs</param>
  603. /// <returns>Maximum stacked X value</returns>
  604. internal double GetMaxStackedXValue(params string[] series)
  605. {
  606. double returnValue = 0;
  607. double numberOfPoints = GetNumberOfPoints(series);
  608. for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
  609. {
  610. double doubleIndexValue = 0;
  611. foreach(string seriesName in series)
  612. {
  613. if (this._series[seriesName].Points.Count > pointIndex)
  614. {
  615. if (this._series[seriesName].Points[pointIndex].XValue > 0)
  616. {
  617. doubleIndexValue += this._series[seriesName].Points[pointIndex].XValue;
  618. }
  619. }
  620. }
  621. returnValue = Math.Max(returnValue, doubleIndexValue);
  622. }
  623. return returnValue;
  624. }
  625. /// <summary>
  626. /// Gets minimum Y value from many series
  627. /// </summary>
  628. /// <param name="valueIndex">Index of Y value to use</param>
  629. /// <param name="series">Series IDs</param>
  630. /// <returns>Minimum Y value</returns>
  631. internal double GetMinYValue(int valueIndex, params string[] series)
  632. {
  633. double returnValue = Double.MaxValue;
  634. foreach(string seriesName in series)
  635. {
  636. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  637. {
  638. // The empty point
  639. if( IsPointSkipped( seriesPoint ) )
  640. {
  641. continue;
  642. }
  643. if(!double.IsNaN(seriesPoint.YValues[valueIndex]))
  644. {
  645. returnValue = Math.Min(returnValue, seriesPoint.YValues[valueIndex]);
  646. }
  647. }
  648. }
  649. return returnValue;
  650. }
  651. /// <summary>
  652. /// Get Minimum value for Y and and Radius (Y2) ( used for bubble chart )
  653. /// </summary>
  654. /// <param name="area">Chart Area</param>
  655. /// <param name="series">Series IDs</param>
  656. /// <returns>Minimum Y value</returns>
  657. internal double GetMinYWithRadiusValue( ChartArea area, params string[] series )
  658. {
  659. double returnValue = Double.MaxValue;
  660. foreach(string seriesName in series)
  661. {
  662. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  663. {
  664. // The empty point
  665. if( IsPointSkipped( seriesPoint ) )
  666. {
  667. continue;
  668. }
  669. if(!double.IsNaN(seriesPoint.YValues[0]))
  670. {
  671. if (seriesPoint.YValues.Length > 1)
  672. {
  673. returnValue = Math.Min(returnValue, seriesPoint.YValues[0] - BubbleChart.AxisScaleBubbleSize(area.Common, area, seriesPoint.YValues[1], true));
  674. }
  675. else
  676. {
  677. returnValue = Math.Min(returnValue, seriesPoint.YValues[0]);
  678. }
  679. }
  680. }
  681. }
  682. return returnValue;
  683. }
  684. /// <summary>
  685. /// Gets minimum Y value from many series
  686. /// </summary>
  687. /// <param name="series">Series IDs</param>
  688. /// <returns>Minimum Y value</returns>
  689. internal double GetMinYValue(params string[] series)
  690. {
  691. double returnValue = Double.MaxValue;
  692. foreach(string seriesName in series)
  693. {
  694. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  695. {
  696. // The empty point
  697. if( IsPointSkipped( seriesPoint ) )
  698. {
  699. continue;
  700. }
  701. foreach(double y in seriesPoint.YValues)
  702. {
  703. if(!double.IsNaN(y))
  704. {
  705. returnValue = Math.Min(returnValue, y);
  706. }
  707. }
  708. }
  709. }
  710. return returnValue;
  711. }
  712. /// <summary>
  713. /// Gets minimum X value from many series
  714. /// </summary>
  715. /// <param name="series">Series IDs</param>
  716. /// <returns>Minimum X value</returns>
  717. internal double GetMinXValue(params string[] series)
  718. {
  719. double returnValue = Double.MaxValue;
  720. foreach(string seriesName in series)
  721. {
  722. foreach(DataPoint seriesPoint in this._series[seriesName].Points)
  723. {
  724. returnValue = Math.Min(returnValue, seriesPoint.XValue);
  725. }
  726. }
  727. return returnValue;
  728. }
  729. /// <summary>
  730. /// Gets minimum stacked Y value from many series
  731. /// </summary>
  732. /// <param name="valueIndex">Index of Y value to use</param>
  733. /// <param name="series">Series IDs</param>
  734. /// <returns>Minimum stacked Y value</returns>
  735. internal double GetMinStackedYValue(int valueIndex, params string[] series)
  736. {
  737. double returnValue = Double.MaxValue;
  738. double numberOfPoints = GetNumberOfPoints(series);
  739. for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
  740. {
  741. double stackedMin = 0;
  742. double noStackedMin = 0;
  743. foreach(string seriesName in series)
  744. {
  745. if(this._series[seriesName].Points.Count > pointIndex)
  746. {
  747. // Take chart type from the series
  748. ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry));
  749. IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName);
  750. // If stacked area
  751. if( !chartType.StackSign || double.IsNaN(this._series[seriesName].Points[pointIndex].YValues[valueIndex]))
  752. continue;
  753. if( chartType.Stacked )
  754. {
  755. if(this._series[seriesName].Points[pointIndex].YValues[valueIndex] < 0)
  756. {
  757. stackedMin += this._series[seriesName].Points[pointIndex].YValues[valueIndex];
  758. }
  759. }
  760. else
  761. {
  762. noStackedMin = Math.Min(noStackedMin,this._series[seriesName].Points[pointIndex].YValues[valueIndex]);
  763. }
  764. }
  765. }
  766. stackedMin = Math.Min(stackedMin, noStackedMin);
  767. if( stackedMin == 0 )
  768. {
  769. stackedMin = this._series[series[0]].Points[this._series[series[0]].Points.Count - 1].YValues[valueIndex];
  770. }
  771. returnValue = Math.Min(returnValue, stackedMin);
  772. }
  773. return returnValue;
  774. }
  775. /// <summary>
  776. /// Gets minimum Unsigned stacked Y value from many series
  777. /// </summary>
  778. /// <param name="valueIndex">Index of Y value to use</param>
  779. /// <param name="series">Series IDs</param>
  780. /// <returns>Minimum stacked Y value</returns>
  781. internal double GetMinUnsignedStackedYValue(int valueIndex, params string[] series)
  782. {
  783. double returnValue = Double.MaxValue;
  784. double minValue = Double.MaxValue;
  785. double numberOfPoints = GetNumberOfPoints(series);
  786. for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
  787. {
  788. double stackedMin = 0;
  789. double noStackedMin = 0;
  790. minValue = Double.MaxValue;
  791. foreach(string seriesName in series)
  792. {
  793. if (this._series[seriesName].Points.Count > pointIndex)
  794. {
  795. // Take chart type from the series
  796. ChartTypeRegistry chartTypeRegistry = (ChartTypeRegistry)serviceContainer.GetService(typeof(ChartTypeRegistry));
  797. IChartType chartType = chartTypeRegistry.GetChartType(this._series[seriesName].ChartTypeName);
  798. // If stacked column and bar
  799. if (chartType.StackSign || double.IsNaN(this._series[seriesName].Points[pointIndex].YValues[valueIndex]))
  800. {
  801. continue;
  802. }
  803. if (chartType.Stacked)
  804. {
  805. if (this._series[seriesName].Points[pointIndex].YValues[valueIndex] < 0)
  806. {
  807. stackedMin += this._series[seriesName].Points[pointIndex].YValues[valueIndex];
  808. if (stackedMin < minValue)
  809. minValue = stackedMin;
  810. }
  811. }
  812. else
  813. {
  814. noStackedMin = Math.Min(noStackedMin, this._series[seriesName].Points[pointIndex].YValues[valueIndex]);
  815. }
  816. }
  817. }
  818. minValue = Math.Min(noStackedMin, minValue);
  819. returnValue = Math.Min(returnValue, minValue);
  820. }
  821. return returnValue;
  822. }
  823. /// <summary>
  824. /// Gets minimum stacked X value from many series
  825. /// </summary>
  826. /// <param name="series">Series IDs</param>
  827. /// <returns>Minimum stacked X value</returns>
  828. internal double GetMinStackedXValue(params string[] series)
  829. {
  830. double returnValue = 0;
  831. double numberOfPoints = GetNumberOfPoints(series);
  832. for(int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
  833. {
  834. double doubleIndexValue = 0;
  835. foreach(string seriesName in series)
  836. {
  837. if(this._series[seriesName].Points[pointIndex].XValue < 0)
  838. {
  839. doubleIndexValue += this._series[seriesName].Points[pointIndex].XValue;
  840. }
  841. }
  842. returnValue = Math.Min(returnValue, doubleIndexValue);
  843. }
  844. return returnValue;
  845. }
  846. /// <summary>
  847. /// Gets maximum hundred percent stacked Y value
  848. /// </summary>
  849. /// <param name="supportNegative">Indicates that negative values are shown on the other side of the axis.</param>
  850. /// <param name="series">Series names</param>
  851. /// <returns>Maximum 100% stacked Y value</returns>
  852. internal double GetMaxHundredPercentStackedYValue(bool supportNegative, params string[] series)
  853. {
  854. double returnValue = 0;
  855. // Convert array of series names into array of series
  856. Series[] seriesArray = new Series[series.Length];
  857. int seriesIndex = 0;
  858. foreach(string seriesName in series)
  859. {
  860. seriesArray[seriesIndex++] = this._series[seriesName];
  861. }
  862. // Loop through all dat points
  863. try
  864. {
  865. for(int pointIndex = 0; pointIndex < this._series[series[0]].Points.Count; pointIndex++)
  866. {
  867. // Calculate the total for all series
  868. double totalPerPoint = 0;
  869. double positiveTotalPerPoint = 0;
  870. foreach(Series ser in seriesArray)
  871. {
  872. if(supportNegative)
  873. {
  874. totalPerPoint += Math.Abs(ser.Points[pointIndex].YValues[0]);
  875. }
  876. else
  877. {
  878. totalPerPoint += ser.Points[pointIndex].YValues[0];
  879. }
  880. if(ser.Points[pointIndex].YValues[0] > 0 || supportNegative == false)
  881. {
  882. positiveTotalPerPoint += ser.Points[pointIndex].YValues[0];
  883. }
  884. }
  885. totalPerPoint = Math.Abs(totalPerPoint);
  886. // Calculate percentage of total
  887. if(totalPerPoint != 0)
  888. {
  889. returnValue = Math.Max(returnValue,
  890. (positiveTotalPerPoint / totalPerPoint) * 100.0);
  891. }
  892. }
  893. }
  894. catch(System.Exception)
  895. {
  896. throw (new InvalidOperationException(SR.ExceptionDataManager100StackedSeriesPointsNumeberMismatch));
  897. }
  898. return returnValue;
  899. }
  900. /// <summary>
  901. /// Gets minimum hundred percent stacked Y value
  902. /// </summary>
  903. /// <param name="supportNegative">Indicates that negative values are shown on the other side of the axis.</param>
  904. /// <param name="series">Series names</param>
  905. /// <returns>Minimum 100% stacked Y value</returns>
  906. internal double GetMinHundredPercentStackedYValue(bool supportNegative, params string[] series)
  907. {
  908. double returnValue = 0.0;
  909. // Convert array of series names into array of series
  910. Series[] seriesArray = new Series[series.Length];
  911. int seriesIndex = 0;
  912. foreach(string seriesName in series)
  913. {
  914. seriesArray[seriesIndex++] = this._series[seriesName];
  915. }
  916. // Loop through all dat points
  917. try
  918. {
  919. for(int pointIndex = 0; pointIndex < this._series[series[0]].Points.Count; pointIndex++)
  920. {
  921. // Calculate the total for all series
  922. double totalPerPoint = 0;
  923. double negativeTotalPerPoint = 0;
  924. foreach(Series ser in seriesArray)
  925. {
  926. if(supportNegative)
  927. {
  928. totalPerPoint += Math.Abs(ser.Points[pointIndex].YValues[0]);
  929. }
  930. else
  931. {
  932. totalPerPoint += ser.Points[pointIndex].YValues[0];
  933. }
  934. if(ser.Points[pointIndex].YValues[0] < 0 || supportNegative == false)
  935. {
  936. negativeTotalPerPoint += ser.Points[pointIndex].YValues[0];
  937. }
  938. }
  939. totalPerPoint = Math.Abs(totalPerPoint);
  940. // Calculate percentage of total
  941. if(totalPerPoint != 0)
  942. {
  943. returnValue = Math.Min(returnValue,
  944. (negativeTotalPerPoint / totalPerPoint) * 100.0);
  945. }
  946. }
  947. }
  948. catch(System.Exception)
  949. {
  950. throw (new InvalidOperationException(SR.ExceptionDataManager100StackedSeriesPointsNumeberMismatch));
  951. }
  952. return returnValue;
  953. }
  954. #endregion
  955. #region DataManager Properties
  956. /// <summary>
  957. /// Chart series collection.
  958. /// </summary>
  959. [
  960. SRCategory("CategoryAttributeData"),
  961. #if DESIGNER
  962. Editor(typeof(SeriesCollectionEditor), typeof(UITypeEditor)),
  963. #endif
  964. Bindable(true)
  965. ]
  966. public SeriesCollection Series
  967. {
  968. get
  969. {
  970. return _series;
  971. }
  972. }
  973. /// <summary>
  974. /// Color palette to use
  975. /// </summary>
  976. [
  977. SRCategory("CategoryAttributeAppearance"),
  978. Bindable(true),
  979. SRDescription("DescriptionAttributePalette"),
  980. DefaultValue(ChartColorPalette.BrightPastel),
  981. #if DESIGNER
  982. Editor(typeof(ColorPaletteEditor), typeof(UITypeEditor))
  983. #endif
  984. ]
  985. public ChartColorPalette Palette
  986. {
  987. get
  988. {
  989. return _colorPalette;
  990. }
  991. set
  992. {
  993. _colorPalette = value;
  994. }
  995. }
  996. // Array of custom palette colors.
  997. private Color[] _paletteCustomColors = new Color[0];
  998. /// <summary>
  999. /// Array of custom palette colors.
  1000. /// </summary>
  1001. /// <remarks>
  1002. /// When this custom colors array is non-empty the <b>Palette</b> property is ignored.
  1003. /// </remarks>
  1004. [
  1005. SRCategory("CategoryAttributeAppearance"),
  1006. DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
  1007. SerializationVisibilityAttribute(SerializationVisibility.Attribute),
  1008. SRDescription("DescriptionAttributeDataManager_PaletteCustomColors"),
  1009. TypeConverter(typeof(ColorArrayConverter))
  1010. ]
  1011. public Color[] PaletteCustomColors
  1012. {
  1013. set
  1014. {
  1015. this._paletteCustomColors = value;
  1016. }
  1017. get
  1018. {
  1019. return this._paletteCustomColors;
  1020. }
  1021. }
  1022. #endregion
  1023. #region IDisposable Members
  1024. /// <summary>
  1025. /// Releases unmanaged and - optionally - managed resources
  1026. /// </summary>
  1027. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
  1028. protected override void Dispose(bool disposing)
  1029. {
  1030. if (disposing)
  1031. {
  1032. if (_series != null)
  1033. {
  1034. _series.Dispose();
  1035. _series = null;
  1036. }
  1037. }
  1038. }
  1039. #endregion
  1040. }
  1041. }