b7a63b67e9c24185cb8d94aeb3f8ab4d8d1986ac.svn-base 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /**
  2. * eventRiver 布局算法
  3. * @module echarts/layout/eventRiver
  4. * @author clmtulip (车丽美, clmtulip@gmail.com)
  5. */
  6. define(function(require) {
  7. function eventRiverLayout(series, intervalX, area) {
  8. var space = 4;
  9. var scale = intervalX;
  10. function importanceSort(a, b) {
  11. var x = a.importance;
  12. var y = b.importance;
  13. return ((x > y) ? -1 : ((x < y) ? 1 : 0));
  14. }
  15. /**
  16. * 查询数组中元素的index
  17. */
  18. function indexOf(array, value) {
  19. if (array.indexOf) {
  20. return array.indexOf(value);
  21. }
  22. for (var i = 0, len = array.length; i < len; i++) {
  23. if (array[i] === value) {
  24. return i;
  25. }
  26. }
  27. return -1;
  28. }
  29. // step 0. calculate event importance and sort descending
  30. for (var i = 0; i < series.length; i++) {
  31. for (var j = 0; j < series[i].data.length; j++) {
  32. if (series[i].data[j].weight == null) {
  33. series[i].data[j].weight = 1;
  34. }
  35. var importance = 0;
  36. for (var k = 0; k < series[i].data[j].evolution.length; k++) {
  37. importance += series[i].data[j].evolution[k].valueScale;
  38. }
  39. series[i].data[j].importance = importance * series[i].data[j].weight;
  40. }
  41. series[i].data.sort(importanceSort);
  42. }
  43. // step 1. 计算每个group的重要值importance,并按递减顺序排序
  44. for (var i = 0; i < series.length; i++) {
  45. if (series[i].weight == null) {
  46. series[i].weight = 1;
  47. }
  48. var importance = 0;
  49. for (var j = 0; j < series[i].data.length; j++) {
  50. importance += series[i].data[j].weight;
  51. }
  52. series[i].importance = importance * series[i].weight;
  53. }
  54. // 根据importance对groups进行递减排序
  55. series.sort(importanceSort);
  56. // step 3. set bubble positions in group order, then in event order
  57. // 找到包含所有事件的时间段,即最小和最大时间点
  58. var minTime = Number.MAX_VALUE;
  59. var maxTime = 0;
  60. for (var i = 0; i < series.length; i++) {
  61. for (var j = 0; j < series[i].data.length; j++) {
  62. for (var k = 0; k < series[i].data[j].evolution.length; k++) {
  63. var time = series[i].data[j].evolution[k].timeScale;
  64. minTime = Math.min(minTime, time);
  65. maxTime = Math.max(maxTime, time);
  66. }
  67. }
  68. }
  69. //console.log('minTime: ' + minTime);
  70. //console.log('maxTime: ' + maxTime);
  71. // 时间范围 即 x轴显示的起始范围
  72. minTime = ~~minTime;
  73. maxTime = ~~maxTime;
  74. // 气泡之间的间隙
  75. var flagForOffset = (function () {
  76. var length = maxTime - minTime + 1 + (~~intervalX);
  77. if (length <= 0){
  78. return [0];
  79. }
  80. var result = [];
  81. while (length--){
  82. result.push(0);
  83. }
  84. return result;
  85. })();
  86. var flagForPos = flagForOffset.slice(0);
  87. var bubbleData = [];
  88. var totalMaxy = 0;
  89. var totalOffset = 0;
  90. for (var i = 0; i < series.length; i++) {
  91. for (var j = 0; j < series[i].data.length; j++) {
  92. var e = series[i].data[j];
  93. e.time = [];
  94. e.value = [];
  95. var tmp;
  96. var maxy = 0;
  97. for (var k = 0; k < series[i].data[j].evolution.length; k++) {
  98. tmp = series[i].data[j].evolution[k];
  99. e.time.push(tmp.timeScale);
  100. e.value.push(tmp.valueScale);
  101. maxy = Math.max(maxy, tmp.valueScale);
  102. }
  103. // 边界计算
  104. bubbleBound(e, intervalX, minTime);
  105. // 得到可以放置的位置
  106. e.y = findLocation(flagForPos, e, function (e, index){return e.ypx[index];});
  107. // 得到偏移量
  108. e._offset = findLocation(flagForOffset, e, function (){ return space;});
  109. totalMaxy = Math.max(totalMaxy, e.y + maxy);
  110. totalOffset = Math.max(totalOffset, e._offset);
  111. bubbleData.push(e);
  112. }
  113. }
  114. // 映射到显示区域内
  115. scaleY(bubbleData, area, totalMaxy, totalOffset);
  116. }
  117. /**
  118. * 映射到显示区域内
  119. */
  120. function scaleY(bubbleData, area, maxY, offset) {
  121. var height = area.height;
  122. var offsetScale = offset / height > 0.5 ? 0.5 : 1;
  123. var yBase = area.y;
  124. var yScale = (area.height - offset) / maxY;
  125. for (var i = 0, length = bubbleData.length; i < length; i++){
  126. var e = bubbleData[i];
  127. e.y = yBase + yScale * e.y + e._offset * offsetScale;
  128. delete e.time;
  129. delete e.value;
  130. delete e.xpx;
  131. delete e.ypx;
  132. delete e._offset;
  133. // 修改值域范围
  134. var evolutionList = e.evolution;
  135. for (var k = 0, klen = evolutionList.length; k < klen; k++) {
  136. evolutionList[k].valueScale *= yScale;
  137. }
  138. }
  139. }
  140. /**
  141. * 得到两点式的方程函数 y = k*x + b
  142. * @param {number} x0 起点横坐标
  143. * @param {number} y0 起点纵坐标
  144. * @param {number} x1 终点横坐标
  145. * @param {number} y1 终点纵坐标
  146. * @returns {Function} 输入为横坐标 返回纵坐标s
  147. */
  148. function line(x0, y0, x1, y1){
  149. // 横坐标相同,应该抛出错误
  150. if (x0 === x1) {
  151. throw new Error('x0 is equal with x1!!!');
  152. }
  153. // 纵坐标相同
  154. if (y0 === y1) {
  155. return function () {
  156. return y0;
  157. }
  158. }
  159. var k = (y0 - y1) / (x0 - x1);
  160. var b = (y1 * x0 - y0 * x1) / (x0 - x1);
  161. return function (x) {
  162. return k * x + b;
  163. }
  164. }
  165. /**
  166. * 计算当前气泡的值经过的边界
  167. * @param {object} e 气泡的值
  168. * @param {array} e.time 时间范围
  169. * @param {array} e.value 值域范围
  170. * @param {number} intervalX 气泡尾巴长度
  171. */
  172. function bubbleBound(e, intervalX, minX){
  173. var space = ~~intervalX;
  174. var length = e.time.length;
  175. e.xpx = [];
  176. e.ypx = [];
  177. var i = 0;
  178. var x0 = 0;
  179. var x1 = 0;
  180. var y0 = 0;
  181. var y1 = 0;
  182. var newline;
  183. for(; i < length; i++){
  184. x0 = ~~e.time[i];
  185. y0 = e.value[i] / 2;
  186. if (i === length - 1) {
  187. // i = length - 1 ~ += intervalX
  188. x1 = x0 + space;
  189. y1 = 0;
  190. } else {
  191. x1 = ~~(e.time[i + 1]);
  192. y1 = e.value[i + 1] / 2;
  193. }
  194. // to line
  195. newline = line(x0, y0, x1, y1);
  196. //
  197. for (var x = x0; x < x1; x++){
  198. e.xpx.push(x - minX);
  199. e.ypx.push(newline(x));
  200. }
  201. }
  202. e.xpx.push(x1 - minX);
  203. e.ypx.push(y1);
  204. }
  205. function findLocation(flags, e, yvalue){
  206. var pos = 0;
  207. var length = e.xpx.length;
  208. var i = 0;
  209. var y;
  210. for(; i < length; i++){
  211. y = yvalue(e, i);
  212. pos = Math.max(pos, y + flags[e.xpx[i]]);
  213. }
  214. // reset flags
  215. for(i = 0; i < length; i++){
  216. y = yvalue(e, i);
  217. flags[e.xpx[i]] = pos + y;
  218. }
  219. return pos;
  220. }
  221. return eventRiverLayout;
  222. });