Stacked.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.charting.plot2d.Stacked"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.charting.plot2d.Stacked"] = true;
  8. dojo.provide("dojox.charting.plot2d.Stacked");
  9. dojo.require("dojox.charting.plot2d.common");
  10. dojo.require("dojox.charting.plot2d.Default");
  11. dojo.require("dojox.lang.functional");
  12. dojo.require("dojox.lang.functional.sequence");
  13. dojo.require("dojox.lang.functional.reversed");
  14. (function(){
  15. var df = dojox.lang.functional, dc = dojox.charting.plot2d.common,
  16. purgeGroup = df.lambda("item.purgeGroup()");
  17. dojo.declare("dojox.charting.plot2d.Stacked", dojox.charting.plot2d.Default, {
  18. // summary:
  19. // Like the default plot, Stacked sets up lines, areas and markers
  20. // in a stacked fashion (values on the y axis added to each other)
  21. // as opposed to a direct one.
  22. getSeriesStats: function(){
  23. // summary:
  24. // Calculate the min/max on all attached series in both directions.
  25. // returns: Object
  26. // {hmin, hmax, vmin, vmax} min/max in both directions.
  27. var stats = dc.collectStackedStats(this.series);
  28. this._maxRunLength = stats.hmax;
  29. return stats;
  30. },
  31. render: function(dim, offsets){
  32. // summary:
  33. // Run the calculations for any axes for this plot.
  34. // dim: Object
  35. // An object in the form of { width, height }
  36. // offsets: Object
  37. // An object of the form { l, r, t, b}.
  38. // returns: dojox.charting.plot2d.Stacked
  39. // A reference to this plot for functional chaining.
  40. if(this._maxRunLength <= 0){
  41. return this;
  42. }
  43. // stack all values
  44. var acc = df.repeat(this._maxRunLength, "-> 0", 0);
  45. for(var i = 0; i < this.series.length; ++i){
  46. var run = this.series[i];
  47. for(var j = 0; j < run.data.length; ++j){
  48. var v = run.data[j];
  49. if(v !== null){
  50. if(isNaN(v)){ v = 0; }
  51. acc[j] += v;
  52. }
  53. }
  54. }
  55. // draw runs in backwards
  56. if(this.zoom && !this.isDataDirty()){
  57. return this.performZoom(dim, offsets);
  58. }
  59. this.resetEvents();
  60. this.dirty = this.isDirty();
  61. if(this.dirty){
  62. dojo.forEach(this.series, purgeGroup);
  63. this._eventSeries = {};
  64. this.cleanGroup();
  65. var s = this.group;
  66. df.forEachRev(this.series, function(item){ item.cleanGroup(s); });
  67. }
  68. var t = this.chart.theme, events = this.events(),
  69. ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler),
  70. vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler);
  71. for(var i = this.series.length - 1; i >= 0; --i){
  72. var run = this.series[i];
  73. if(!this.dirty && !run.dirty){
  74. t.skip();
  75. this._reconnectEvents(run.name);
  76. continue;
  77. }
  78. run.cleanGroup();
  79. var theme = t.next(this.opt.areas ? "area" : "line", [this.opt, run], true),
  80. s = run.group, outline,
  81. lpoly = dojo.map(acc, function(v, i){
  82. return {
  83. x: ht(i + 1) + offsets.l,
  84. y: dim.height - offsets.b - vt(v)
  85. };
  86. }, this);
  87. var lpath = this.opt.tension ? dc.curve(lpoly, this.opt.tension) : "";
  88. if(this.opt.areas){
  89. var apoly = dojo.clone(lpoly);
  90. if(this.opt.tension){
  91. var p=dc.curve(apoly, this.opt.tension);
  92. p += " L" + lpoly[lpoly.length - 1].x + "," + (dim.height - offsets.b) +
  93. " L" + lpoly[0].x + "," + (dim.height - offsets.b) +
  94. " L" + lpoly[0].x + "," + lpoly[0].y;
  95. run.dyn.fill = s.createPath(p).setFill(theme.series.fill).getFill();
  96. } else {
  97. apoly.push({x: lpoly[lpoly.length - 1].x, y: dim.height - offsets.b});
  98. apoly.push({x: lpoly[0].x, y: dim.height - offsets.b});
  99. apoly.push(lpoly[0]);
  100. run.dyn.fill = s.createPolyline(apoly).setFill(theme.series.fill).getFill();
  101. }
  102. }
  103. if(this.opt.lines || this.opt.markers){
  104. if(theme.series.outline){
  105. outline = dc.makeStroke(theme.series.outline);
  106. outline.width = 2 * outline.width + theme.series.stroke.width;
  107. }
  108. }
  109. if(this.opt.markers){
  110. run.dyn.marker = theme.symbol;
  111. }
  112. var frontMarkers, outlineMarkers, shadowMarkers;
  113. if(theme.series.shadow && theme.series.stroke){
  114. var shadow = theme.series.shadow,
  115. spoly = dojo.map(lpoly, function(c){
  116. return {x: c.x + shadow.dx, y: c.y + shadow.dy};
  117. });
  118. if(this.opt.lines){
  119. if(this.opt.tension){
  120. run.dyn.shadow = s.createPath(dc.curve(spoly, this.opt.tension)).setStroke(shadow).getStroke();
  121. } else {
  122. run.dyn.shadow = s.createPolyline(spoly).setStroke(shadow).getStroke();
  123. }
  124. }
  125. if(this.opt.markers){
  126. shadow = theme.marker.shadow;
  127. shadowMarkers = dojo.map(spoly, function(c){
  128. return s.createPath("M" + c.x + " " + c.y + " " + theme.symbol).
  129. setStroke(shadow).setFill(shadow.color);
  130. }, this);
  131. }
  132. }
  133. if(this.opt.lines){
  134. if(outline){
  135. if(this.opt.tension){
  136. run.dyn.outline = s.createPath(lpath).setStroke(outline).getStroke();
  137. } else {
  138. run.dyn.outline = s.createPolyline(lpoly).setStroke(outline).getStroke();
  139. }
  140. }
  141. if(this.opt.tension){
  142. run.dyn.stroke = s.createPath(lpath).setStroke(theme.series.stroke).getStroke();
  143. } else {
  144. run.dyn.stroke = s.createPolyline(lpoly).setStroke(theme.series.stroke).getStroke();
  145. }
  146. }
  147. if(this.opt.markers){
  148. frontMarkers = new Array(lpoly.length);
  149. outlineMarkers = new Array(lpoly.length);
  150. outline = null;
  151. if(theme.marker.outline){
  152. outline = dc.makeStroke(theme.marker.outline);
  153. outline.width = 2 * outline.width + (theme.marker.stroke ? theme.marker.stroke.width : 0);
  154. }
  155. dojo.forEach(lpoly, function(c, i){
  156. var path = "M" + c.x + " " + c.y + " " + theme.symbol;
  157. if(outline){
  158. outlineMarkers[i] = s.createPath(path).setStroke(outline);
  159. }
  160. frontMarkers[i] = s.createPath(path).setStroke(theme.marker.stroke).setFill(theme.marker.fill);
  161. }, this);
  162. if(events){
  163. var eventSeries = new Array(frontMarkers.length);
  164. dojo.forEach(frontMarkers, function(s, i){
  165. var o = {
  166. element: "marker",
  167. index: i,
  168. run: run,
  169. shape: s,
  170. outline: outlineMarkers[i] || null,
  171. shadow: shadowMarkers && shadowMarkers[i] || null,
  172. cx: lpoly[i].x,
  173. cy: lpoly[i].y,
  174. x: i + 1,
  175. y: run.data[i]
  176. };
  177. this._connectEvents(o);
  178. eventSeries[i] = o;
  179. }, this);
  180. this._eventSeries[run.name] = eventSeries;
  181. }else{
  182. delete this._eventSeries[run.name];
  183. }
  184. }
  185. run.dirty = false;
  186. // update the accumulator
  187. for(var j = 0; j < run.data.length; ++j){
  188. var v = run.data[j];
  189. if(v !== null){
  190. if(isNaN(v)){ v = 0; }
  191. acc[j] -= v;
  192. }
  193. }
  194. }
  195. this.dirty = false;
  196. return this; // dojox.charting.plot2d.Stacked
  197. }
  198. });
  199. })();
  200. }