PageRenderTime 36ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/ChartParts/Scales/ChartSeriesScale.cs

#
C# | 356 lines | 263 code | 19 blank | 74 comment | 68 complexity | cd5c88045a86f4292e5cc76644ce1c8b MD5 | raw file
  1. // <copyright file="ChartSeriesScale.cs" company="Oleg V. Polikarpotchkin">
  2. // Copyright © 2008 Oleg V. Polikarpotchkin. All Right Reserved
  3. // </copyright>
  4. // <author>Oleg V. Polikarpotchkin</author>
  5. // <email>ov-p@yandex.ru</email>
  6. // <date>2009-01-02</date>
  7. // <summary>OpenWPFChart library. Scale based on discrete Series of values.</summary>
  8. // <revision>$Id: ChartSeriesScale.cs 27192 2009-08-22 07:20:19Z unknown $</revision>
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.Linq;
  14. using System.Reflection;
  15. using System.Text;
  16. namespace OpenWPFChart.Parts
  17. {
  18. /// <summary>
  19. /// Linear Chart Scale based on discrete Series of values (e.g. the <c>string</c> collection).
  20. /// </summary>
  21. /// <remarks>
  22. /// This class Scale property means pixel count between two adjacent series items.
  23. /// <para>This class is adorned with the <see cref="ChartScaleGroupAttribute"/> which marks
  24. /// the class as belonging to the "Series Chart Scales" group with this class type as the Tag.</para>
  25. /// </remarks>
  26. [ChartScaleGroup(typeof(ChartSeriesScale), "Series", GroupDescription="Series Chart Scales")]
  27. public class ChartSeriesScale : ChartScale
  28. {
  29. #region Constructors
  30. /// <summary>
  31. /// Initializes a new instance of the <see cref="ChartSeriesScale"/> class.
  32. /// </summary>
  33. /// <overloads>
  34. /// <summary><see cref="ChartSeriesScale"/> constructors.</summary>
  35. /// <remarks>
  36. /// Parametrized constructor arranges the Scale properties so that it fills itself
  37. /// confortable in the screen extent propvided.
  38. /// </remarks>
  39. /// </overloads>
  40. public ChartSeriesScale() { }
  41. /// <summary>
  42. /// Initializes a new instance of the <see cref="ChartSeriesScale"/> class.
  43. /// </summary>
  44. /// <remarks>
  45. /// Arranges the Scale so that it fills itself confortable in the screen extent propvided.
  46. /// </remarks>
  47. /// <param name="series">The series.</param>
  48. /// <param name="start">Scale start.</param>
  49. /// <param name="stop">Scale stop.</param>
  50. /// <param name="extent">Scale visual extent in pixels.</param>
  51. public ChartSeriesScale(IEnumerable<object> series, object start, object stop, double extent)
  52. {
  53. if (series.Contains(start) && series.Contains(stop) && start != stop)
  54. {
  55. Series = series;
  56. Start = start;
  57. Stop = stop;
  58. int startIndex = -1, stopIndex = -1, i = 0;
  59. foreach (object item in series)
  60. {
  61. if (object.ReferenceEquals(item, start))
  62. startIndex = i;
  63. if (object.ReferenceEquals(item, stop))
  64. stopIndex = i;
  65. i++;
  66. }
  67. Debug.Assert(startIndex >= 0, "startIndex >= 0");
  68. Debug.Assert(stopIndex >= 0, "stopIndex >= 0");
  69. Debug.Assert(startIndex != stopIndex, "startIndex != stopIndex");
  70. Scale = extent / Math.Abs(startIndex - stopIndex);
  71. LongTickAnchor = start;
  72. LongTickRate = 3;
  73. }
  74. }
  75. #endregion Constructors
  76. /// <inheritdoc />
  77. public override double Extent
  78. {
  79. get
  80. {
  81. if (!IsConsistent)
  82. return -1.0;
  83. int startIndex = -1, stopIndex = -1, i = 0;
  84. foreach (object item in series)
  85. {
  86. if (object.ReferenceEquals(item, Start))
  87. startIndex = i;
  88. if (object.ReferenceEquals(item, Stop))
  89. stopIndex = i;
  90. i++;
  91. }
  92. Debug.Assert(startIndex >= 0, "startIndex >= 0");
  93. Debug.Assert(stopIndex >= 0, "stopIndex >= 0");
  94. Debug.Assert(startIndex != stopIndex, "startIndex != stopIndex");
  95. return Scale * Math.Abs(startIndex - stopIndex);
  96. }
  97. set
  98. {
  99. if (IsConsistent)
  100. {
  101. int startIndex = -1, stopIndex = -1, i = 0;
  102. foreach (object item in series)
  103. {
  104. if (object.ReferenceEquals(item, Start))
  105. startIndex = i;
  106. if (object.ReferenceEquals(item, Stop))
  107. stopIndex = i;
  108. i++;
  109. }
  110. Debug.Assert(startIndex >= 0, "startIndex >= 0");
  111. Debug.Assert(stopIndex >= 0, "stopIndex >= 0");
  112. Debug.Assert(startIndex != stopIndex, "startIndex != stopIndex");
  113. double factor = Math.Abs(startIndex - stopIndex);
  114. double extent = Scale * factor;
  115. if (extent != value)
  116. Scale = value / factor;
  117. }
  118. }
  119. }
  120. /// <inheritdoc />
  121. public override ChartScale Create(Type targetType)
  122. {
  123. if (!IsConsistent)
  124. return null;
  125. if (targetType == null)
  126. throw new ArgumentNullException("targetType");
  127. if (this.GetType() == targetType)
  128. return Clone() as ChartScale;
  129. ConstructorInfo ci = targetType.GetConstructor(new Type[] {
  130. typeof(IEnumerable<object>), typeof(object), typeof(object), typeof(double) });
  131. if (ci == null)
  132. return null;
  133. ChartScale newScale = ci.Invoke(new object[] { Series, Start, Stop, Extent }) as ChartScale;
  134. if (newScale.IsConsistent)
  135. return newScale;
  136. return null;
  137. }
  138. #region Series
  139. IEnumerable series;
  140. /// <summary>
  141. /// Gets or sets the <see cref="Series"/> property.
  142. /// </summary>
  143. /// <remarks>
  144. /// The series must not contain reference to the same object.
  145. /// </remarks>
  146. /// <value><see cref="Series"/> iterator.</value>
  147. public IEnumerable Series
  148. {
  149. get { return series; }
  150. set
  151. {
  152. if (series != value)
  153. {
  154. series = value;
  155. NotifyPropertyChanged("Series");
  156. }
  157. }
  158. }
  159. #endregion Series
  160. #region LongTickAnchor
  161. object longTickAnchor;
  162. /// <summary>
  163. /// Gets or sets the <see cref="LongTickAnchor"/> property.
  164. /// </summary>
  165. /// <remarks>
  166. /// <para>Represents the position of some long tick.</para>
  167. /// <para>Returned value is coersed to one of the <see cref="Series"/> objects.</para>
  168. /// </remarks>
  169. /// <value>One of the <see cref="Series"/> objects.</value>
  170. public object LongTickAnchor
  171. {
  172. get
  173. {
  174. if (Series == null)
  175. return longTickAnchor;
  176. List<object> list = Series.Cast<object>().ToList();
  177. if (list.Count == 0 || list.IndexOf(longTickAnchor) >= 0)
  178. return longTickAnchor;
  179. return list[0];
  180. }
  181. set
  182. {
  183. if (longTickAnchor != value)
  184. {
  185. longTickAnchor = value;
  186. NotifyPropertyChanged("LongTickAnchor");
  187. }
  188. }
  189. }
  190. #endregion LongTickAnchor
  191. #region LongTickRate
  192. int longTickRate = 10;
  193. /// <summary>
  194. /// Gets or sets the <see cref="LongTickRate"/> property.
  195. /// </summary>
  196. /// <remarks>
  197. /// <para>Represents long ticks rate; e.g.
  198. /// <list>
  199. /// if <see cref="LongTickRate"/> == 1 then all ticks will be long.
  200. /// if <see cref="LongTickRate"/> == 2 then will be one short tick between two long ticks.
  201. /// if <see cref="LongTickRate"/> == 10 then every tenth tick will be long.
  202. /// </list>
  203. /// </para>
  204. /// <para>Must be positive.</para>
  205. /// </remarks>
  206. /// <value>Long ticks rate.</value>
  207. public int LongTickRate
  208. {
  209. get { return longTickRate; }
  210. set
  211. {
  212. if (longTickRate != value)
  213. {
  214. if (value <= 0)
  215. throw new ArgumentException("LongTickRate must be positive", "value");
  216. longTickRate = value;
  217. NotifyPropertyChanged("LongTickRate");
  218. }
  219. }
  220. }
  221. #endregion LongTickRate
  222. /// <inheritdoc />
  223. public override bool IsConsistent
  224. {
  225. get
  226. {
  227. if (!base.IsConsistent || Series == null)
  228. return false;
  229. // Check if the Start and Stop Values belonge to the Series.
  230. List<object> list = Series.Cast<object>().ToList();
  231. int start = list.IndexOf(Start), stop = list.IndexOf(Stop);
  232. return (start >= 0 && stop >= 0 && start != stop);
  233. }
  234. }
  235. /// <inheritdoc />
  236. public override double ToPixels(object value)
  237. {
  238. if (!IsConsistent)
  239. throw new InvalidOperationException("Object isn't properly initialized");
  240. List<object> list = Series.Cast<object>().ToList();
  241. int valueIndex = list.IndexOf(value);
  242. int start = list.IndexOf(Start), stop = list.IndexOf(Stop);
  243. double scale = Scale;
  244. if (valueIndex < 0)
  245. throw new ArgumentException("Object isn't in the Series", "value");
  246. if (start < stop)
  247. return (valueIndex - start) * scale;
  248. else // (start > stop)
  249. return (start - valueIndex) * scale;
  250. }
  251. /// <inheritdoc />
  252. public override object FromPixels(double value)
  253. {
  254. if (!IsConsistent)
  255. throw new InvalidOperationException("Object isn't properly initialized");
  256. List<object> list = Series.Cast<object>().ToList();
  257. int start = list.IndexOf(Start), stop = list.IndexOf(Stop);
  258. double scale = Scale;
  259. int valueIndex;
  260. if (start < stop)
  261. valueIndex = (int)(value / scale) + start;
  262. else // (start > stop)
  263. valueIndex = start - (int)(value / scale);
  264. if (valueIndex < 0 || valueIndex >= list.Count)
  265. throw new ArgumentOutOfRangeException("Object isn't in the Series", "value");
  266. return list.ElementAt(valueIndex);
  267. }
  268. #region Ticks iterator
  269. /// <inheritdoc />
  270. public override IEnumerable<ScaleTick> Ticks()
  271. {
  272. if (!IsConsistent)
  273. yield break;
  274. List<object> list = Series.Cast<object>().ToList();
  275. int longTickRate = LongTickRate;
  276. int longTickAnchorIndex = list.IndexOf(LongTickAnchor);
  277. int start = list.IndexOf(Start), stop = list.IndexOf(Stop);
  278. if (start <= stop)
  279. {
  280. for (int i = start; i <= stop; ++i)
  281. {
  282. if ((i - longTickAnchorIndex) % longTickRate == 0)
  283. yield return new ScaleTick(list[i], true);
  284. else
  285. yield return new ScaleTick(list[i], false);
  286. }
  287. }
  288. else // start > stop
  289. {
  290. for (int i = start; i >= stop; --i)
  291. {
  292. if ((i - longTickAnchorIndex) % longTickRate == 0)
  293. yield return new ScaleTick(list[i], true);
  294. else
  295. yield return new ScaleTick(list[i], false);
  296. }
  297. }
  298. }
  299. #endregion Ticks iterator
  300. #region IDataErrorInfo Overrides
  301. /// <inheritdoc />
  302. protected override string ObjectErrorInfo
  303. {
  304. get
  305. {
  306. StringBuilder sb = new StringBuilder();
  307. string s = GetDataErrorInfo("Start");
  308. if (!string.IsNullOrEmpty(s))
  309. sb.Append(s);
  310. s = base.ObjectErrorInfo;
  311. if (!string.IsNullOrEmpty(s))
  312. {
  313. if (sb.Length > 0)
  314. sb.Append(Environment.NewLine);
  315. sb.Append(s);
  316. }
  317. if (sb.Length > 0)
  318. return sb.ToString();
  319. return null;
  320. }
  321. }
  322. /// <inheritdoc />
  323. protected override string GetDataErrorInfo(string columnName)
  324. {
  325. switch (columnName)
  326. {
  327. case "Start":
  328. case "Stop":
  329. if (Start == Stop)
  330. return "Start and Stop must not be equal";
  331. break;
  332. }
  333. return base.GetDataErrorInfo(columnName);
  334. }
  335. #endregion IDataErrorInfo Overrides
  336. }
  337. }