Version 3.18.1
Show:

File: charts/js/StackingUtil.js

  1. /**
  2. * Provides functionality for creating stacked series.
  3. *
  4. * @module charts
  5. * @submodule series-stacked
  6. */
  7. var Y_Lang = Y.Lang;
  8. /**
  9. * Utility class used for creating stacked series.
  10. *
  11. * @module charts
  12. * @class StackingUtil
  13. * @constructor
  14. * @submodule series-stacked
  15. */
  16. function StackingUtil(){}
  17. StackingUtil.prototype = {
  18. /**
  19. * Indicates whether the series is stacked.
  20. *
  21. * @property _stacked
  22. * @private
  23. */
  24. _stacked: true,
  25. /**
  26. * @protected
  27. *
  28. * Adjusts coordinate values for stacked series.
  29. *
  30. * @method _stackCoordinates
  31. */
  32. _stackCoordinates: function()
  33. {
  34. if(this.get("direction") === "vertical")
  35. {
  36. this._stackXCoords();
  37. }
  38. else
  39. {
  40. this._stackYCoords();
  41. }
  42. },
  43. /**
  44. * Stacks coordinates for a stacked vertical series.
  45. *
  46. * @method _stackXCoords
  47. * @protected
  48. */
  49. _stackXCoords: function()
  50. {
  51. var order = this.get("order"),
  52. seriesCollection = this.get("seriesTypeCollection"),
  53. i = 0,
  54. xcoords = this.get("xcoords"),
  55. ycoords = this.get("ycoords"),
  56. len,
  57. coord,
  58. prevCoord,
  59. prevOrder,
  60. stackedXCoords = xcoords.concat(),
  61. prevXCoords,
  62. prevYCoords,
  63. nullIndices = [],
  64. nullIndex;
  65. if(order > 0)
  66. {
  67. prevXCoords = seriesCollection[order - 1].get("stackedXCoords");
  68. prevYCoords = seriesCollection[order - 1].get("stackedYCoords");
  69. len = prevXCoords.length;
  70. }
  71. else
  72. {
  73. len = xcoords.length;
  74. }
  75. for(; i < len; i = i + 1)
  76. {
  77. if(Y_Lang.isNumber(xcoords[i]))
  78. {
  79. if(order > 0)
  80. {
  81. prevCoord = prevXCoords[i];
  82. if(!Y_Lang.isNumber(prevCoord))
  83. {
  84. prevOrder = order;
  85. while(prevOrder > - 1 && !Y_Lang.isNumber(prevCoord))
  86. {
  87. prevOrder = prevOrder - 1;
  88. if(prevOrder > -1)
  89. {
  90. prevCoord = seriesCollection[prevOrder].get("stackedXCoords")[i];
  91. }
  92. else
  93. {
  94. prevCoord = this._leftOrigin;
  95. }
  96. }
  97. }
  98. xcoords[i] = xcoords[i] + prevCoord;
  99. }
  100. stackedXCoords[i] = xcoords[i];
  101. }
  102. else
  103. {
  104. nullIndices.push(i);
  105. }
  106. }
  107. this._cleanXNaN(stackedXCoords, ycoords);
  108. len = nullIndices.length;
  109. if(len > 0)
  110. {
  111. for(i = 0; i < len; i = i + 1)
  112. {
  113. nullIndex = nullIndices[i];
  114. coord = order > 0 ? prevXCoords[nullIndex] : this._leftOrigin;
  115. stackedXCoords[nullIndex] = Math.max(stackedXCoords[nullIndex], coord);
  116. }
  117. }
  118. this.set("stackedXCoords", stackedXCoords);
  119. this.set("stackedYCoords", ycoords);
  120. },
  121. /**
  122. * Stacks coordinates for a stacked horizontal series.
  123. *
  124. * @method _stackYCoords
  125. * @protected
  126. */
  127. _stackYCoords: function()
  128. {
  129. var order = this.get("order"),
  130. graphic = this.get("graphic"),
  131. h = graphic.get("height"),
  132. seriesCollection = this.get("seriesTypeCollection"),
  133. i = 0,
  134. xcoords = this.get("xcoords"),
  135. ycoords = this.get("ycoords"),
  136. len,
  137. coord,
  138. prevCoord,
  139. prevOrder,
  140. stackedYCoords = ycoords.concat(),
  141. prevXCoords,
  142. prevYCoords,
  143. nullIndices = [],
  144. nullIndex;
  145. if(order > 0)
  146. {
  147. prevXCoords = seriesCollection[order - 1].get("stackedXCoords");
  148. prevYCoords = seriesCollection[order - 1].get("stackedYCoords");
  149. len = prevYCoords.length;
  150. }
  151. else
  152. {
  153. len = ycoords.length;
  154. }
  155. for(; i < len; i = i + 1)
  156. {
  157. if(Y_Lang.isNumber(ycoords[i]))
  158. {
  159. if(order > 0)
  160. {
  161. prevCoord = prevYCoords[i];
  162. if(!Y_Lang.isNumber(prevCoord))
  163. {
  164. prevOrder = order;
  165. while(prevOrder > - 1 && !Y_Lang.isNumber(prevCoord))
  166. {
  167. prevOrder = prevOrder - 1;
  168. if(prevOrder > -1)
  169. {
  170. prevCoord = seriesCollection[prevOrder].get("stackedYCoords")[i];
  171. }
  172. else
  173. {
  174. prevCoord = this._bottomOrigin;
  175. }
  176. }
  177. }
  178. ycoords[i] = prevCoord - (h - ycoords[i]);
  179. }
  180. stackedYCoords[i] = ycoords[i];
  181. }
  182. else
  183. {
  184. nullIndices.push(i);
  185. }
  186. }
  187. this._cleanYNaN(xcoords, stackedYCoords);
  188. len = nullIndices.length;
  189. if(len > 0)
  190. {
  191. for(i = 0; i < len; i = i + 1)
  192. {
  193. nullIndex = nullIndices[i];
  194. coord = order > 0 ? prevYCoords[nullIndex] : h;
  195. stackedYCoords[nullIndex] = Math.min(stackedYCoords[nullIndex], coord);
  196. }
  197. }
  198. this.set("stackedXCoords", xcoords);
  199. this.set("stackedYCoords", stackedYCoords);
  200. },
  201. /**
  202. * Cleans invalid x-coordinates by calculating their value based on the corresponding y-coordinate, the
  203. * previous valid x-coordinate with its corresponding y-coordinate and the next valid x-coordinate with
  204. * its corresponding y-coordinate. If there is no previous or next valid x-coordinate, the value will not
  205. * be altered.
  206. *
  207. * @method _cleanXNaN
  208. * @param {Array} xcoords An array of x-coordinate values
  209. * @param {Array} ycoords An arry of y-coordinate values
  210. * @private
  211. */
  212. _cleanXNaN: function(xcoords, ycoords)
  213. {
  214. var previousValidIndex,
  215. nextValidIndex,
  216. previousValidX,
  217. previousValidY,
  218. x,
  219. y,
  220. nextValidX,
  221. nextValidY,
  222. isNumber = Y_Lang.isNumber,
  223. m,
  224. i = 0,
  225. len = ycoords.length;
  226. for(; i < len; ++i)
  227. {
  228. x = xcoords[i];
  229. y = ycoords[i];
  230. //if x is invalid, calculate where it should be
  231. if(!isNumber(x) && i > 0 && i < len - 1)
  232. {
  233. previousValidY = ycoords[i - 1];
  234. //check to see if the previous value is valid
  235. previousValidX = this._getPreviousValidCoordValue(xcoords, i);
  236. nextValidY = ycoords[i + 1];
  237. nextValidX = this._getNextValidCoordValue(xcoords, i);
  238. //check to see if the next value is valid
  239. if(isNumber(previousValidX) && isNumber(nextValidX))
  240. {
  241. //calculate slope and solve for x
  242. m = (nextValidY - previousValidY) / (nextValidX - previousValidX);
  243. xcoords[i] = (y + (m * previousValidX) - previousValidY)/m;
  244. }
  245. previousValidIndex = NaN;
  246. nextValidIndex = NaN;
  247. }
  248. }
  249. },
  250. /**
  251. * Returns the previous valid (numeric) value in an array if available.
  252. *
  253. * @method _getPreviousValidCoordValue
  254. * @param {Array} coords Array of values
  255. * @param {Number} index The index in the array in which to begin searching.
  256. * @return Number
  257. * @private
  258. */
  259. _getPreviousValidCoordValue: function(coords, index)
  260. {
  261. var coord,
  262. isNumber = Y_Lang.isNumber,
  263. limit = -1;
  264. while(!isNumber(coord) && index > limit)
  265. {
  266. index = index - 1;
  267. coord = coords[index];
  268. }
  269. return coord;
  270. },
  271. /**
  272. * Returns the next valid (numeric) value in an array if available.
  273. *
  274. * @method _getNextValidCoordValue
  275. * @param {Array} coords Array of values
  276. * @param {Number} index The index in the array in which to begin searching.
  277. * @return Number
  278. * @private
  279. */
  280. _getNextValidCoordValue: function(coords, index)
  281. {
  282. var coord,
  283. isNumber = Y_Lang.isNumber,
  284. limit = coords.length;
  285. while(!isNumber(coord) && index < limit)
  286. {
  287. index = index + 1;
  288. coord = coords[index];
  289. }
  290. return coord;
  291. },
  292. /**
  293. * Cleans invalid y-coordinates by calculating their value based on the corresponding x-coordinate, the
  294. * previous valid y-coordinate with its corresponding x-coordinate and the next valid y-coordinate with
  295. * its corresponding x-coordinate. If there is no previous or next valid y-coordinate, the value will not
  296. * be altered.
  297. *
  298. * @method _cleanYNaN
  299. * @param {Array} xcoords An array of x-coordinate values
  300. * @param {Array} ycoords An arry of y-coordinate values
  301. * @private
  302. */
  303. _cleanYNaN: function(xcoords, ycoords)
  304. {
  305. var previousValidIndex,
  306. nextValidIndex,
  307. previousValidX,
  308. previousValidY,
  309. x,
  310. y,
  311. nextValidX,
  312. nextValidY,
  313. isNumber = Y_Lang.isNumber,
  314. m,
  315. i = 0,
  316. len = xcoords.length;
  317. for(; i < len; ++i)
  318. {
  319. x = xcoords[i];
  320. y = ycoords[i];
  321. //if y is invalid, calculate where it should be
  322. if(!isNumber(y) && i > 0 && i < len - 1)
  323. {
  324. //check to see if the previous value is valid
  325. previousValidX = xcoords[i - 1];
  326. previousValidY = this._getPreviousValidCoordValue(ycoords, i);
  327. //check to see if the next value is valid
  328. nextValidX = xcoords[i + 1];
  329. nextValidY = this._getNextValidCoordValue(ycoords, i);
  330. if(isNumber(previousValidY) && isNumber(nextValidY))
  331. {
  332. //calculate slope and solve for y
  333. m = (nextValidY - previousValidY) / (nextValidX - previousValidX);
  334. ycoords[i] = previousValidY + ((m * x) - (m * previousValidX));
  335. }
  336. previousValidIndex = NaN;
  337. nextValidIndex = NaN;
  338. }
  339. }
  340. }
  341. };
  342. Y.StackingUtil = StackingUtil;