jqplot.BezierCurveRenderer.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * jqPlot
  3. * Pure JavaScript plotting plugin using jQuery
  4. *
  5. * Version: @VERSION
  6. * Revision: @REVISION
  7. *
  8. * Copyright (c) 2009-2013 Chris Leonello
  9. * jqPlot is currently available for use in all personal or commercial projects
  10. * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
  11. * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
  12. * choose the license that best suits your project and use it accordingly.
  13. *
  14. * Although not required, the author would appreciate an email letting him
  15. * know of any substantial use of jqPlot. You can reach the author at:
  16. * chris at jqplot dot com or see http://www.jqplot.com/info.php .
  17. *
  18. * If you are feeling kind and generous, consider supporting the project by
  19. * making a donation at: http://www.jqplot.com/donate.php .
  20. *
  21. * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
  22. *
  23. * version 2007.04.27
  24. * author Ash Searle
  25. * http://hexmen.com/blog/2007/03/printf-sprintf/
  26. * http://hexmen.com/js/sprintf.js
  27. * The author (Ash Searle) has placed this code in the public domain:
  28. * "This code is unrestricted: you are free to use it however you like."
  29. *
  30. */
  31. (function($) {
  32. // Class: $.jqplot.BezierCurveRenderer.js
  33. // Renderer which draws lines as stacked bezier curves.
  34. // Data for the line will not be specified as an array of
  35. // [x, y] data point values, but as a an array of [start piont, bezier curve]
  36. // So, the line is specified as: [[xstart, ystart], [cp1x, cp1y, cp2x, cp2y, xend, yend]].
  37. $.jqplot.BezierCurveRenderer = function(){
  38. $.jqplot.LineRenderer.call(this);
  39. };
  40. $.jqplot.BezierCurveRenderer.prototype = new $.jqplot.LineRenderer();
  41. $.jqplot.BezierCurveRenderer.prototype.constructor = $.jqplot.BezierCurveRenderer;
  42. // Method: setGridData
  43. // converts the user data values to grid coordinates and stores them
  44. // in the gridData array.
  45. // Called with scope of a series.
  46. $.jqplot.BezierCurveRenderer.prototype.setGridData = function(plot) {
  47. // recalculate the grid data
  48. var xp = this._xaxis.series_u2p;
  49. var yp = this._yaxis.series_u2p;
  50. // this._plotData should be same as this.data
  51. var data = this.data;
  52. this.gridData = [];
  53. this._prevGridData = [];
  54. // if seriesIndex = 0, fill to x axis.
  55. // if seriesIndex > 0, fill to previous series data.
  56. var idx = this.index;
  57. if (data.length == 2) {
  58. if (idx == 0) {
  59. this.gridData = [
  60. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  61. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  62. xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),
  63. xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
  64. [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)],
  65. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
  66. ];
  67. }
  68. else {
  69. var psd = plot.series[idx-1].data;
  70. this.gridData = [
  71. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  72. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  73. xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),
  74. xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
  75. [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])],
  76. [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]),
  77. xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),
  78. xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
  79. ];
  80. }
  81. }
  82. else {
  83. if (idx == 0) {
  84. this.gridData = [
  85. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  86. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  87. xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),
  88. xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
  89. [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)],
  90. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
  91. ];
  92. }
  93. else {
  94. var psd = plot.series[idx-1].data;
  95. this.gridData = [
  96. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  97. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  98. xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),
  99. xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
  100. [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])],
  101. [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]),
  102. xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),
  103. xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
  104. ];
  105. }
  106. }
  107. };
  108. // Method: makeGridData
  109. // converts any arbitrary data values to grid coordinates and
  110. // returns them. This method exists so that plugins can use a series'
  111. // linerenderer to generate grid data points without overwriting the
  112. // grid data associated with that series.
  113. // Called with scope of a series.
  114. $.jqplot.BezierCurveRenderer.prototype.makeGridData = function(data, plot) {
  115. // recalculate the grid data
  116. var xp = this._xaxis.series_u2p;
  117. var yp = this._yaxis.series_u2p;
  118. var gd = [];
  119. var pgd = [];
  120. // if seriesIndex = 0, fill to x axis.
  121. // if seriesIndex > 0, fill to previous series data.
  122. var idx = this.index;
  123. if (data.length == 2) {
  124. if (idx == 0) {
  125. gd = [
  126. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  127. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  128. xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),
  129. xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
  130. [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)],
  131. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
  132. ];
  133. }
  134. else {
  135. var psd = plot.series[idx-1].data;
  136. gd = [
  137. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  138. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  139. xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),
  140. xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
  141. [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])],
  142. [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]),
  143. xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),
  144. xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
  145. ];
  146. }
  147. }
  148. else {
  149. if (idx == 0) {
  150. gd = [
  151. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  152. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  153. xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),
  154. xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
  155. [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)],
  156. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
  157. ];
  158. }
  159. else {
  160. var psd = plot.series[idx-1].data;
  161. gd = [
  162. [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])],
  163. [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]),
  164. xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),
  165. xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
  166. [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])],
  167. [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]),
  168. xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),
  169. xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
  170. ];
  171. }
  172. }
  173. return gd;
  174. };
  175. // called within scope of series.
  176. $.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) {
  177. var i;
  178. ctx.save();
  179. if (gd.length) {
  180. if (this.showLine) {
  181. ctx.save();
  182. var opts = (options != null) ? options : {};
  183. ctx.fillStyle = opts.fillStyle || this.color;
  184. ctx.beginPath();
  185. ctx.moveTo(gd[0][0], gd[0][1]);
  186. ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]);
  187. ctx.lineTo(gd[2][0], gd[2][1]);
  188. if (gd[3].length == 2) {
  189. ctx.lineTo(gd[3][0], gd[3][1]);
  190. }
  191. else {
  192. ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]);
  193. }
  194. ctx.closePath();
  195. ctx.fill();
  196. ctx.restore();
  197. }
  198. }
  199. ctx.restore();
  200. };
  201. $.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) {
  202. // This is a no-op, shadows drawn with lines.
  203. };
  204. $.jqplot.BezierAxisRenderer = function() {
  205. $.jqplot.LinearAxisRenderer.call(this);
  206. };
  207. $.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
  208. $.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer;
  209. // Axes on a plot with Bezier Curves
  210. $.jqplot.BezierAxisRenderer.prototype.init = function(options){
  211. $.extend(true, this, options);
  212. var db = this._dataBounds;
  213. // Go through all the series attached to this axis and find
  214. // the min/max bounds for this axis.
  215. for (var i=0; i<this._series.length; i++) {
  216. var s = this._series[i];
  217. var d = s.data;
  218. if (d.length == 4) {
  219. for (var j=0; j<d.length; j++) {
  220. if (this.name == 'xaxis' || this.name == 'x2axis') {
  221. if (d[j][0] < db.min || db.min == null) {
  222. db.min = d[j][0];
  223. }
  224. if (d[j][0] > db.max || db.max == null) {
  225. db.max = d[j][0];
  226. }
  227. }
  228. else {
  229. if (d[j][1] < db.min || db.min == null) {
  230. db.min = d[j][1];
  231. }
  232. if (d[j][1] > db.max || db.max == null) {
  233. db.max = d[j][1];
  234. }
  235. }
  236. }
  237. }
  238. else {
  239. if (this.name == 'xaxis' || this.name == 'x2axis') {
  240. if (d[0][0] < db.min || db.min == null) {
  241. db.min = d[0][0];
  242. }
  243. if (d[0][0] > db.max || db.max == null) {
  244. db.max = d[0][0];
  245. }
  246. for (var j=0; j<5; j+=2) {
  247. if (d[1][j] < db.min || db.min == null) {
  248. db.min = d[1][j];
  249. }
  250. if (d[1][j] > db.max || db.max == null) {
  251. db.max = d[1][j];
  252. }
  253. }
  254. }
  255. else {
  256. if (d[0][1] < db.min || db.min == null) {
  257. db.min = d[0][1];
  258. }
  259. if (d[0][1] > db.max || db.max == null) {
  260. db.max = d[0][1];
  261. }
  262. for (var j=1; j<6; j+=2) {
  263. if (d[1][j] < db.min || db.min == null) {
  264. db.min = d[1][j];
  265. }
  266. if (d[1][j] > db.max || db.max == null) {
  267. db.max = d[1][j];
  268. }
  269. }
  270. }
  271. }
  272. }
  273. };
  274. // setup default renderers for axes and legend so user doesn't have to
  275. // called with scope of plot
  276. function preInit(target, data, options) {
  277. options = options || {};
  278. options.axesDefaults = $.extend(true, {pad:0}, options.axesDefaults);
  279. options.seriesDefaults = options.seriesDefaults || {};
  280. options.legend = $.extend(true, {placement:'outside'}, options.legend);
  281. // only set these if there is a pie series
  282. var setopts = false;
  283. if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) {
  284. setopts = true;
  285. }
  286. else if (options.series) {
  287. for (var i=0; i < options.series.length; i++) {
  288. if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) {
  289. setopts = true;
  290. }
  291. }
  292. }
  293. if (setopts) {
  294. options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer;
  295. }
  296. }
  297. $.jqplot.preInitHooks.push(preInit);
  298. })(jQuery);