123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- /**
- * eventRiver 布局算法
- * @module echarts/layout/eventRiver
- * @author clmtulip (车丽美, clmtulip@gmail.com)
- */
- define(function(require) {
- function eventRiverLayout(series, intervalX, area) {
- var space = 4;
- var scale = intervalX;
- function importanceSort(a, b) {
- var x = a.importance;
- var y = b.importance;
- return ((x > y) ? -1 : ((x < y) ? 1 : 0));
- }
- /**
- * 查询数组中元素的index
- */
- function indexOf(array, value) {
- if (array.indexOf) {
- return array.indexOf(value);
- }
- for (var i = 0, len = array.length; i < len; i++) {
- if (array[i] === value) {
- return i;
- }
- }
- return -1;
- }
- // step 0. calculate event importance and sort descending
- for (var i = 0; i < series.length; i++) {
- for (var j = 0; j < series[i].data.length; j++) {
- if (series[i].data[j].weight == null) {
- series[i].data[j].weight = 1;
- }
- var importance = 0;
- for (var k = 0; k < series[i].data[j].evolution.length; k++) {
- importance += series[i].data[j].evolution[k].valueScale;
- }
- series[i].data[j].importance = importance * series[i].data[j].weight;
- }
- series[i].data.sort(importanceSort);
- }
- // step 1. 计算每个group的重要值importance,并按递减顺序排序
- for (var i = 0; i < series.length; i++) {
- if (series[i].weight == null) {
- series[i].weight = 1;
- }
- var importance = 0;
- for (var j = 0; j < series[i].data.length; j++) {
- importance += series[i].data[j].weight;
- }
- series[i].importance = importance * series[i].weight;
- }
- // 根据importance对groups进行递减排序
- series.sort(importanceSort);
- // step 3. set bubble positions in group order, then in event order
- // 找到包含所有事件的时间段,即最小和最大时间点
- var minTime = Number.MAX_VALUE;
- var maxTime = 0;
- for (var i = 0; i < series.length; i++) {
- for (var j = 0; j < series[i].data.length; j++) {
- for (var k = 0; k < series[i].data[j].evolution.length; k++) {
- var time = series[i].data[j].evolution[k].timeScale;
- minTime = Math.min(minTime, time);
- maxTime = Math.max(maxTime, time);
- }
- }
- }
- //console.log('minTime: ' + minTime);
- //console.log('maxTime: ' + maxTime);
- // 时间范围 即 x轴显示的起始范围
- minTime = ~~minTime;
- maxTime = ~~maxTime;
- // 气泡之间的间隙
- var flagForOffset = (function () {
- var length = maxTime - minTime + 1 + (~~intervalX);
- if (length <= 0){
- return [0];
- }
- var result = [];
- while (length--){
- result.push(0);
- }
- return result;
- })();
- var flagForPos = flagForOffset.slice(0);
- var bubbleData = [];
- var totalMaxy = 0;
- var totalOffset = 0;
- for (var i = 0; i < series.length; i++) {
- for (var j = 0; j < series[i].data.length; j++) {
- var e = series[i].data[j];
- e.time = [];
- e.value = [];
- var tmp;
- var maxy = 0;
- for (var k = 0; k < series[i].data[j].evolution.length; k++) {
- tmp = series[i].data[j].evolution[k];
- e.time.push(tmp.timeScale);
- e.value.push(tmp.valueScale);
- maxy = Math.max(maxy, tmp.valueScale);
- }
- // 边界计算
- bubbleBound(e, intervalX, minTime);
- // 得到可以放置的位置
- e.y = findLocation(flagForPos, e, function (e, index){return e.ypx[index];});
- // 得到偏移量
- e._offset = findLocation(flagForOffset, e, function (){ return space;});
- totalMaxy = Math.max(totalMaxy, e.y + maxy);
- totalOffset = Math.max(totalOffset, e._offset);
- bubbleData.push(e);
- }
- }
- // 映射到显示区域内
- scaleY(bubbleData, area, totalMaxy, totalOffset);
- }
- /**
- * 映射到显示区域内
- */
- function scaleY(bubbleData, area, maxY, offset) {
- var height = area.height;
- var offsetScale = offset / height > 0.5 ? 0.5 : 1;
- var yBase = area.y;
- var yScale = (area.height - offset) / maxY;
- for (var i = 0, length = bubbleData.length; i < length; i++){
- var e = bubbleData[i];
- e.y = yBase + yScale * e.y + e._offset * offsetScale;
- delete e.time;
- delete e.value;
- delete e.xpx;
- delete e.ypx;
- delete e._offset;
- // 修改值域范围
- var evolutionList = e.evolution;
- for (var k = 0, klen = evolutionList.length; k < klen; k++) {
- evolutionList[k].valueScale *= yScale;
- }
- }
- }
- /**
- * 得到两点式的方程函数 y = k*x + b
- * @param {number} x0 起点横坐标
- * @param {number} y0 起点纵坐标
- * @param {number} x1 终点横坐标
- * @param {number} y1 终点纵坐标
- * @returns {Function} 输入为横坐标 返回纵坐标s
- */
- function line(x0, y0, x1, y1){
- // 横坐标相同,应该抛出错误
- if (x0 === x1) {
- throw new Error('x0 is equal with x1!!!');
- }
- // 纵坐标相同
- if (y0 === y1) {
- return function () {
- return y0;
- }
- }
- var k = (y0 - y1) / (x0 - x1);
- var b = (y1 * x0 - y0 * x1) / (x0 - x1);
- return function (x) {
- return k * x + b;
- }
- }
- /**
- * 计算当前气泡的值经过的边界
- * @param {object} e 气泡的值
- * @param {array} e.time 时间范围
- * @param {array} e.value 值域范围
- * @param {number} intervalX 气泡尾巴长度
- */
- function bubbleBound(e, intervalX, minX){
- var space = ~~intervalX;
- var length = e.time.length;
- e.xpx = [];
- e.ypx = [];
- var i = 0;
- var x0 = 0;
- var x1 = 0;
- var y0 = 0;
- var y1 = 0;
- var newline;
- for(; i < length; i++){
- x0 = ~~e.time[i];
- y0 = e.value[i] / 2;
- if (i === length - 1) {
- // i = length - 1 ~ += intervalX
- x1 = x0 + space;
- y1 = 0;
- } else {
- x1 = ~~(e.time[i + 1]);
- y1 = e.value[i + 1] / 2;
- }
- // to line
- newline = line(x0, y0, x1, y1);
- //
- for (var x = x0; x < x1; x++){
- e.xpx.push(x - minX);
- e.ypx.push(newline(x));
- }
- }
- e.xpx.push(x1 - minX);
- e.ypx.push(y1);
- }
- function findLocation(flags, e, yvalue){
- var pos = 0;
- var length = e.xpx.length;
- var i = 0;
- var y;
- for(; i < length; i++){
- y = yvalue(e, i);
- pos = Math.max(pos, y + flags[e.xpx[i]]);
- }
- // reset flags
- for(i = 0; i < length; i++){
- y = yvalue(e, i);
- flags[e.xpx[i]] = pos + y;
- }
- return pos;
- }
- return eventRiverLayout;
- });
|