提交 2cd1a1fc 编写于 作者: P pah100

merge

define(function (require) {
var zrUtil = require('zrender/core/util');
require('../coord/cartesian/Grid');
require('./bar/BarSeries');
require('./bar/BarView');
require('./bar/barLayoutGrid');
var barLayoutGrid = require('../layout/barGrid');
require('../echarts').registerLayout(zrUtil.curry(barLayoutGrid, 'bar'));
// Visual coding for legend
require('../echarts').registerVisualCoding(function (ecModel) {
ecModel.eachSeriesByType('bar', function (seriesSymbol) {
......
......@@ -2,7 +2,11 @@ define(function (require) {
require('./line/LineSeries');
require('./line/LineView');
require('./line/lineLayout');
require('./line/lineVisual');
var zrUtil = require('zrender/core/util');
require('../echarts').registerLayout(zrUtil.curry(
require('../layout/points'), 'line'
));
});
\ No newline at end of file
......@@ -3,6 +3,7 @@ define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var symbolCreator = require('../../util/symbol');
return require('../../echarts').extendChartView({
......@@ -22,10 +23,14 @@ define(function(require) {
var coordinateSystem = seriesModel.coordinateSystem;
var isCoordinateSystemPolar = coordinateSystem.type === 'polar';
// if (isCoordinateSystemPolar && points.length > 2) {
// // Close polyline
// points.push(Array.prototype.slice.call(points[0]));
// }
if (
isCoordinateSystemPolar
&& points.length > 2
&& coordinateSystem.getAngleAxis().type === 'category'
) {
// Close polyline
points.push(Array.prototype.slice.call(points[0]));
}
// Initialization animation
if (!this._data) {
......@@ -53,6 +58,7 @@ define(function(require) {
this._polyline = polyline;
}
else {
// FIXME Handle the situation of adding and removing data
// this._polyline.animateShape()
// .when(500, {
// points: points
......@@ -68,6 +74,10 @@ define(function(require) {
this._data = data;
},
_drawSymbols: function (data) {
},
_createGridClipShape: function (cartesian, api) {
var xAxis = cartesian.getAxis('x');
var yAxis = cartesian.getAxis('y');
......
define(function (require) {
require('./scatter/scatterLayout');
var zrUtil = require('zrender/core/util');
require('./scatter/scatterVisual');
require('./scatter/ScatterSeries');
require('./scatter/ScatterView');
require('../echarts').registerLayout(zrUtil.curry(
require('../layout/points'), 'scatter'
));
});
\ No newline at end of file
define(function (require) {
require('../../echarts').registerLayout(function (ecModel, api) {
ecModel.eachSeriesByType('scatter', function (scatterSeries) {
var coordinateSystem = scatterSeries.coordinateSystem;
var data = scatterSeries.getData();
var coords = coordinateSystem.dataToCoords(data);
data.each(function (dataItem, idx) {
var coord = coords[idx];
dataItem.layout = {
x: coord[0],
y: coord[1]
};
});
});
});
});
\ No newline at end of file
......@@ -319,7 +319,7 @@ define(function (require) {
*/
_doLayout: function (ecModel, event) {
zrUtil.each(this._layouts, function (layout) {
layout(ecModel, event);
layout.update(ecModel, event);
});
zrUtil.each(layoutFuncs, function (layout) {
layout(ecModel, event);
......
/**
* Chord layout
* @module echarts/layout/Chord
* @author pissang(http://github.com/pissang)
*/
define(function (require) {
var ChordLayout = function (opts) {
opts = opts || {};
/**
* 是否排序组(即图的节点), 可以是`ascending`, `descending`, 或者为空不排序
* @type {string}
*/
this.sort = opts.sort || null;
/**
* 是否排序子组(即图中每个节点的邻接边), 可以是`ascending`, `descending`, 或者为空不排序
* @type {string}
*/
this.sortSub = opts.sortSub || null;
this.padding = 0.05;
this.startAngle = opts.startAngle || 0;
this.clockWise = opts.clockWise == null ? false : opts.clockWise;
this.center = opts.center || [0, 0];
this.directed = true;
};
/**
* 对指定的一个或多个 Graph 运行 chord 布局
* 可以有多个 Graph, 后面几个 Graph 的节点是第一个 Graph 的节点的子集(ID一一对应)
*
* 布局结果保存在第一个 Graph 的每个节点的 layout.startAngle 和 layout.endAngle.
* 以及每个图的边的 layout.startAngle 和 layout.endAngle
*
* @param {Array.<module:echarts/data/Graph>|module:echarts/data/Graph} graphs
*/
ChordLayout.prototype.run = function (graphs) {
if (!(graphs instanceof Array)) {
graphs = [graphs];
}
var gl = graphs.length;
if (!gl) {
return;
}
var graph0 = graphs[0];
var nl = graph0.nodes.length;
var groups = [];
var sumSize = 0;
// 使用第一个 graph 的节点
for (var i = 0; i < nl; i++) {
var g0node = graph0.nodes[i];
var group = {
size: 0,
subGroups: [],
node: g0node
};
groups.push(group);
var sumWeight = 0;
// 合并所有 Graph 的 边
for (var k = 0; k < graphs.length; k++) {
var graph = graphs[k];
var node = graph.getNodeById(g0node.id);
// 节点可能没有值被过滤掉了
if (!node) {
continue;
}
group.size += node.layout.size;
// PENDING
var edges = this.directed ? node.outEdges : node.edges;
for (var j = 0; j < edges.length; j++) {
var e = edges[j];
var w = e.layout.weight;
group.subGroups.push({
weight: w,
edge: e,
graph: graph
});
sumWeight += w;
}
}
sumSize += group.size;
// Sum sub group weights to group size
var multiplier = group.size / sumWeight;
for (var j = 0; j < group.subGroups.length; j++) {
group.subGroups[j].weight *= multiplier;
}
if (this.sortSub === 'ascending') {
group.subGroups.sort(compareSubGroups);
}
else if (this.sort === 'descending') {
group.subGroups.sort(compareSubGroups);
group.subGroups.reverse();
}
}
if (this.sort === 'ascending') {
groups.sort(compareGroups);
}
else if (this.sort === 'descending') {
groups.sort(compareGroups);
groups.reverse();
}
var multiplier = (Math.PI * 2 - this.padding * nl) / sumSize;
var angle = this.startAngle;
var sign = this.clockWise ? 1 : -1;
// Calculate angles
for (var i = 0; i < nl; i++) {
var group = groups[i];
group.node.layout.startAngle = angle;
group.node.layout.endAngle = angle + sign * group.size * multiplier;
group.node.layout.subGroups = [];
for (var j = 0; j < group.subGroups.length; j++) {
var subGroup = group.subGroups[j];
subGroup.edge.layout.startAngle = angle;
angle += sign * subGroup.weight * multiplier;
subGroup.edge.layout.endAngle = angle;
}
angle = group.node.layout.endAngle + sign * this.padding;
}
};
var compareSubGroups = function (a, b) {
return a.weight - b.weight;
};
var compareGroups = function (a, b) {
return a.size - b.size;
};
return ChordLayout;
});
\ No newline at end of file
/**
* Edge bundling laytout
*
* Use MINGLE algorithm
* Multilevel agglomerative edge bundling for visualizing large graphs
*
* @module echarts/layout/EdgeBundling
*/
define(function (require) {
var KDTree = require('../util/KDTree');
var vec2 = require('zrender/core/vector');
var v2Create = vec2.create;
var v2DistSquare = vec2.distSquare;
var v2Dist = vec2.dist;
var v2Copy = vec2.copy;
var v2Clone = vec2.clone;
function squaredDistance(a, b) {
a = a.array;
b = b.array;
var x = b[0] - a[0];
var y = b[1] - a[1];
var z = b[2] - a[2];
var w = b[3] - a[3];
return x * x + y * y + z * z + w * w;
}
function CoarsenedEdge(group) {
this.points = [
group.mp0, group.mp1
];
this.group = group;
}
function Edge(edge) {
var points = edge.points;
// Sort on y
if (
points[0][1] < points[1][1]
// If coarsened edge is flipped, the final composition of meet point
// will be unordered
|| edge instanceof CoarsenedEdge
) {
this.array = [points[0][0], points[0][1], points[1][0], points[1][1]];
this._startPoint = points[0];
this._endPoint = points[1];
}
else {
this.array = [points[1][0], points[1][1], points[0][0], points[0][1]];
this._startPoint = points[1];
this._endPoint = points[0];
}
this.ink = v2Dist(points[0], points[1]);
this.edge = edge;
this.group = null;
}
Edge.prototype.getStartPoint = function () {
return this._startPoint;
};
Edge.prototype.getEndPoint = function () {
return this._endPoint;
};
function BundledEdgeGroup() {
this.edgeList = [];
this.mp0 = v2Create();
this.mp1 = v2Create();
this.ink = 0;
}
BundledEdgeGroup.prototype.addEdge = function (edge) {
edge.group = this;
this.edgeList.push(edge);
};
BundledEdgeGroup.prototype.removeEdge = function (edge) {
edge.group = null;
this.edgeList.splice(this.edgeList.indexOf(edge), 1);
};
/**
* @constructor
* @alias module:echarts/layout/EdgeBundling
*/
function EdgeBundling() {
this.maxNearestEdge = 6;
this.maxTurningAngle = Math.PI / 4;
this.maxIteration = 20;
}
EdgeBundling.prototype = {
constructor: EdgeBundling,
run: function (rawEdges) {
var res = this._iterate(rawEdges);
var nIterate = 0;
while (nIterate++ < this.maxIteration) {
var coarsenedEdges = [];
for (var i = 0; i < res.groups.length; i++) {
coarsenedEdges.push(new CoarsenedEdge(res.groups[i]));
}
var newRes = this._iterate(coarsenedEdges);
if (newRes.savedInk <= 0) {
break;
} else {
res = newRes;
}
}
// Get new edges
var newEdges = [];
function pointApproxEqual(p0, p1) {
// Use Float32Array may affect the precision
return v2DistSquare(p0, p1) < 1e-10;
}
// Clone all points to make sure all points in edge will not reference to the same array
// And clean the duplicate points
function cleanEdgePoints(edgePoints, rawEdgePoints) {
var res = [];
var off = 0;
for (var i = 0; i < edgePoints.length; i++) {
if (! (off > 0 && pointApproxEqual(edgePoints[i], res[off - 1]))) {
res[off++] = v2Clone(edgePoints[i]);
}
}
// Edge has been reversed
if (rawEdgePoints[0] && !pointApproxEqual(res[0], rawEdgePoints[0])) {
res = res.reverse();
}
return res;
}
var buildNewEdges = function (groups, fromEdgePoints) {
var newEdgePoints;
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
if (
group.edgeList[0]
&& (group.edgeList[0].edge instanceof CoarsenedEdge)
) {
var newGroups = [];
for (var j = 0; j < group.edgeList.length; j++) {
newGroups.push(group.edgeList[j].edge.group);
}
if (! fromEdgePoints) {
newEdgePoints = [];
} else {
newEdgePoints = fromEdgePoints.slice();
}
newEdgePoints.unshift(group.mp0);
newEdgePoints.push(group.mp1);
buildNewEdges(newGroups, newEdgePoints);
} else {
// console.log(group.edgeList.length);
for (var j = 0; j < group.edgeList.length; j++) {
var edge = group.edgeList[j];
if (! fromEdgePoints) {
newEdgePoints = [];
} else {
newEdgePoints = fromEdgePoints.slice();
}
newEdgePoints.unshift(group.mp0);
newEdgePoints.push(group.mp1);
newEdgePoints.unshift(edge.getStartPoint());
newEdgePoints.push(edge.getEndPoint());
newEdges.push({
points: cleanEdgePoints(newEdgePoints, edge.edge.points),
rawEdge: edge.edge
});
}
}
}
};
buildNewEdges(res.groups);
return newEdges;
},
_iterate: function (rawEdges) {
var edges = [];
var groups = [];
var totalSavedInk = 0;
for (var i = 0; i < rawEdges.length; i++) {
var edge = new Edge(rawEdges[i]);
edges.push(edge);
}
var tree = new KDTree(edges, 4);
var nearests = [];
var _mp0 = v2Create();
var _mp1 = v2Create();
var _newGroupInk = 0;
var mp0 = v2Create();
var mp1 = v2Create();
var newGroupInk = 0;
for (var i = 0; i < edges.length; i++) {
var edge = edges[i];
if (edge.group) {
// Edge have been groupped
continue;
}
tree.nearestN(
edge, this.maxNearestEdge,
squaredDistance, nearests
);
var maxSavedInk = 0;
var mostSavingInkEdge = null;
var lastCheckedGroup = null;
for (var j = 0; j < nearests.length; j++) {
var nearest = nearests[j];
var savedInk = 0;
if (nearest.group) {
if (nearest.group !== lastCheckedGroup) {
lastCheckedGroup = nearest.group;
_newGroupInk = this._calculateGroupEdgeInk(
nearest.group, edge, _mp0, _mp1
);
savedInk = nearest.group.ink + edge.ink - _newGroupInk;
}
}
else {
_newGroupInk = this._calculateEdgeEdgeInk(
edge, nearest, _mp0, _mp1
);
savedInk = nearest.ink + edge.ink - _newGroupInk;
}
if (savedInk > maxSavedInk) {
maxSavedInk = savedInk;
mostSavingInkEdge = nearest;
v2Copy(mp1, _mp1);
v2Copy(mp0, _mp0);
newGroupInk = _newGroupInk;
}
}
if (mostSavingInkEdge) {
totalSavedInk += maxSavedInk;
var group;
if (! mostSavingInkEdge.group) {
group = new BundledEdgeGroup();
groups.push(group);
group.addEdge(mostSavingInkEdge);
}
group = mostSavingInkEdge.group;
// Use the meet point and group ink calculated before
v2Copy(group.mp0, mp0);
v2Copy(group.mp1, mp1);
group.ink = newGroupInk;
mostSavingInkEdge.group.addEdge(edge);
}
else {
var group = new BundledEdgeGroup();
groups.push(group);
v2Copy(group.mp0, edge.getStartPoint());
v2Copy(group.mp1, edge.getEndPoint());
group.ink = edge.ink;
group.addEdge(edge);
}
}
return {
groups: groups,
edges: edges,
savedInk: totalSavedInk
};
},
_calculateEdgeEdgeInk: (function () {
var startPointSet = [];
var endPointSet = [];
return function (e0, e1, mp0, mp1) {
startPointSet[0] = e0.getStartPoint();
startPointSet[1] = e1.getStartPoint();
endPointSet[0] = e0.getEndPoint();
endPointSet[1] = e1.getEndPoint();
this._calculateMeetPoints(
startPointSet, endPointSet, mp0, mp1
);
var ink = v2Dist(startPointSet[0], mp0)
+ v2Dist(mp0, mp1)
+ v2Dist(mp1, endPointSet[0])
+ v2Dist(startPointSet[1], mp0)
+ v2Dist(mp1, endPointSet[1]);
return ink;
};
})(),
_calculateGroupEdgeInk: function (group, edgeTryAdd, mp0, mp1) {
var startPointSet = [];
var endPointSet = [];
for (var i = 0; i < group.edgeList.length; i++) {
var edge = group.edgeList[i];
startPointSet.push(edge.getStartPoint());
endPointSet.push(edge.getEndPoint());
}
startPointSet.push(edgeTryAdd.getStartPoint());
endPointSet.push(edgeTryAdd.getEndPoint());
this._calculateMeetPoints(
startPointSet, endPointSet, mp0, mp1
);
var ink = v2Dist(mp0, mp1);
for (var i = 0; i < startPointSet.length; i++) {
ink += v2Dist(startPointSet[i], mp0)
+ v2Dist(endPointSet[i], mp1);
}
return ink;
},
/**
* Calculating the meet points
* @method
* @param {Array} startPointSet Start points set of bundled edges
* @param {Array} endPointSet End points set of bundled edges
* @param {Array.<number>} mp0 Output meet point 0
* @param {Array.<number>} mp1 Output meet point 1
*/
_calculateMeetPoints: (function () {
var cp0 = v2Create();
var cp1 = v2Create();
return function (startPointSet, endPointSet, mp0, mp1) {
vec2.set(cp0, 0, 0);
vec2.set(cp1, 0, 0);
var len = startPointSet.length;
// Calculate the centroid of start points set
for (var i = 0; i < len; i++) {
vec2.add(cp0, cp0, startPointSet[i]);
}
vec2.scale(cp0, cp0, 1 / len);
// Calculate the centroid of end points set
len = endPointSet.length;
for (var i = 0; i < len; i++) {
vec2.add(cp1, cp1, endPointSet[i]);
}
vec2.scale(cp1, cp1, 1 / len);
this._limitTurningAngle(
startPointSet, cp0, cp1, mp0
);
this._limitTurningAngle(
endPointSet, cp1, cp0, mp1
);
};
})(),
_limitTurningAngle: (function () {
var v10 = v2Create();
var vTmp = v2Create();
var project = v2Create();
var tmpOut = v2Create();
return function (pointSet, p0, p1, out) {
// Limit the max turning angle
var maxTurningAngleCos = Math.cos(this.maxTurningAngle);
var maxTurningAngleTan = Math.tan(this.maxTurningAngle);
vec2.sub(v10, p0, p1);
vec2.normalize(v10, v10);
// Simply copy the centroid point if no need to turn the angle
vec2.copy(out, p0);
var maxMovement = 0;
for (var i = 0; i < pointSet.length; i++) {
var p = pointSet[i];
vec2.sub(vTmp, p, p0);
var len = vec2.len(vTmp);
vec2.scale(vTmp, vTmp, 1 / len);
var turningAngleCos = vec2.dot(vTmp, v10);
// Turning angle is to large
if (turningAngleCos < maxTurningAngleCos) {
// Calculat p's project point on vector p1-p0
// and distance to the vector
vec2.scaleAndAdd(
project, p0, v10, len * turningAngleCos
);
var distance = v2Dist(project, p);
// Use the max turning angle to calculate the new meet point
var d = distance / maxTurningAngleTan;
vec2.scaleAndAdd(tmpOut, project, v10, -d);
var movement = v2DistSquare(tmpOut, p0);
if (movement > maxMovement) {
maxMovement = movement;
vec2.copy(out, tmpOut);
}
}
}
};
})()
};
return EdgeBundling;
});
\ No newline at end of file
/**
* 力导向布局
* @module echarts/layout/Force
* @author pissang(http://github.com/pissang)
*/
define(function(require) {
var ForceLayoutWorker = require('./forceLayoutWorker');
var vec2 = require('zrender/coer/vector');
var requestAnimationFrame = window.requestAnimationFrame
|| window.msRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| function (func) {setTimeout(func, 16);};
var ArrayCtor = typeof(Float32Array) == 'undefined' ? Array : Float32Array;
var workerUrl;
// function getToken() {
// return Math.round((new Date()).getTime() / 100) % 10000000;
// }
function createWorkerUrl() {
if (
typeof(Worker) !== 'undefined' &&
typeof(Blob) !== 'undefined'
) {
try {
var blob = new Blob([ForceLayoutWorker.getWorkerCode()]);
workerUrl = window.URL.createObjectURL(blob);
}
catch (e) {
workerUrl = '';
}
}
return workerUrl;
}
var ForceLayout = function(opts) {
if (typeof(workerUrl) === 'undefined') {
createWorkerUrl();
}
opts = opts || {};
// 配置项
this.width = opts.width || 500;
this.height = opts.height || 500;
this.center = opts.center || [this.width / 2, this.height / 2];
this.ratioScaling = opts.ratioScaling || false;
this.scaling = opts.scaling || 1;
this.gravity = typeof(opts.gravity) !== 'undefined'
? opts.gravity : 1;
this.large = opts.large || false;
this.preventNodeOverlap = opts.preventNodeOverlap || false;
this.preventNodeEdgeOverlap = opts.preventNodeEdgeOverlap || false;
this.maxSpeedIncrease = opts.maxSpeedIncrease || 1;
this.onupdate = opts.onupdate || function () {};
this.temperature = opts.temperature || 1;
this.coolDown = opts.coolDown || 0.99;
this._layout = null;
this._layoutWorker = null;
var self = this;
var _$onupdate = this._$onupdate;
this._$onupdate = function(e) {
_$onupdate.call(self, e);
};
};
ForceLayout.prototype.updateConfig = function () {
var width = this.width;
var height = this.height;
var size = Math.min(width, height);
var config = {
center: this.center,
width: this.ratioScaling ? width : size,
height: this.ratioScaling ? height : size,
scaling: this.scaling || 1.0,
gravity: this.gravity || 1.0,
barnesHutOptimize: this.large,
preventNodeOverlap: this.preventNodeOverlap,
preventNodeEdgeOverlap: this.preventNodeEdgeOverlap,
maxSpeedIncrease: this.maxSpeedIncrease
};
if (this._layoutWorker) {
this._layoutWorker.postMessage({
cmd: 'updateConfig',
config: config
});
}
else {
for (var name in config) {
this._layout[name] = config[name];
}
}
};
ForceLayout.prototype.init = function (graph, useWorker) {
if (this._layoutWorker) {
this._layoutWorker.terminate();
this._layoutWorker = null;
}
if (workerUrl && useWorker) {
try {
if (!this._layoutWorker) {
this._layoutWorker = new Worker(workerUrl);
this._layoutWorker.onmessage = this._$onupdate;
}
this._layout = null;
}
catch (e) { // IE10-11 will throw security error when using blog url
this._layoutWorker = null;
if (!this._layout) {
this._layout = new ForceLayoutWorker();
}
}
}
else {
if (!this._layout) {
this._layout = new ForceLayoutWorker();
}
}
this.temperature = 1;
this.graph = graph;
// 节点数据
var len = graph.nodes.length;
var positionArr = new ArrayCtor(len * 2);
var massArr = new ArrayCtor(len);
var sizeArr = new ArrayCtor(len);
for (var i = 0; i < len; i++) {
var n = graph.nodes[i];
positionArr[i * 2] = n.layout.position[0];
positionArr[i * 2 + 1] = n.layout.position[1];
massArr[i] = typeof(n.layout.mass) === 'undefined'
? 1 : n.layout.mass;
sizeArr[i] = typeof(n.layout.size) === 'undefined'
? 1 : n.layout.size;
n.layout.__index = i;
}
// 边数据
len = graph.edges.length;
var edgeArr = new ArrayCtor(len * 2);
var edgeWeightArr = new ArrayCtor(len);
for (var i = 0; i < len; i++) {
var edge = graph.edges[i];
edgeArr[i * 2] = edge.node1.layout.__index;
edgeArr[i * 2 + 1] = edge.node2.layout.__index;
edgeWeightArr[i] = edge.layout.weight || 1;
}
if (this._layoutWorker) {
this._layoutWorker.postMessage({
cmd: 'init',
nodesPosition: positionArr,
nodesMass: massArr,
nodesSize: sizeArr,
edges: edgeArr,
edgesWeight: edgeWeightArr
});
}
else {
this._layout.initNodes(positionArr, massArr, sizeArr);
this._layout.initEdges(edgeArr, edgeWeightArr);
}
this.updateConfig();
};
ForceLayout.prototype.step = function (steps) {
var nodes = this.graph.nodes;
if (this._layoutWorker) {
// Sync back
var positionArr = new ArrayCtor(nodes.length * 2);
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
positionArr[i * 2] = n.layout.position[0];
positionArr[i * 2 + 1] = n.layout.position[1];
}
this._layoutWorker.postMessage(positionArr.buffer, [positionArr.buffer]);
this._layoutWorker.postMessage({
cmd: 'update',
steps: steps,
temperature: this.temperature,
coolDown: this.coolDown
});
for (var i = 0; i < steps; i++) {
this.temperature *= this.coolDown;
}
}
else {
requestAnimationFrame(this._$onupdate);
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
vec2.copy(this._layout.nodes[i].position, n.layout.position);
}
for (var i = 0; i < steps; i++) {
this._layout.temperature = this.temperature;
this._layout.update();
this.temperature *= this.coolDown;
}
}
};
ForceLayout.prototype._$onupdate = function (e) {
if (this._layoutWorker) {
var positionArr = new Float32Array(e.data);
for (var i = 0; i < this.graph.nodes.length; i++) {
var n = this.graph.nodes[i];
n.layout.position[0] = positionArr[i * 2];
n.layout.position[1] = positionArr[i * 2 + 1];
}
this.onupdate && this.onupdate();
}
else if (this._layout) {
for (var i = 0; i < this.graph.nodes.length; i++) {
var n = this.graph.nodes[i];
vec2.copy(n.layout.position, this._layout.nodes[i].position);
}
this.onupdate && this.onupdate();
}
};
ForceLayout.prototype.dispose = function() {
if (this._layoutWorker) {
this._layoutWorker.terminate();
}
this._layoutWorker = null;
this._layout = null;
};
return ForceLayout;
});
\ No newline at end of file
/**
* Tree layout
* @module echarts/layout/Tree
* @author pissang(http://github.com/pissang)
*/
define(function (require) {
var vec2 = require('zrender/tool/vector');
function TreeLayout(opts) {
opts = opts || {};
this.nodePadding = opts.nodePadding || 30;
this.layerPadding = opts.layerPadding || 100;
this._layerOffsets = [];
this._layers = [];
}
TreeLayout.prototype.run = function (tree) {
this._layerOffsets.length = 0;
for (var i = 0; i < tree.root.height + 1; i++) {
this._layerOffsets[i] = 0;
this._layers[i] = [];
}
this._updateNodeXPosition(tree.root);
var root = tree.root;
this._updateNodeYPosition(root, 0, root.layout.height);
};
TreeLayout.prototype._updateNodeXPosition = function (node) {
var minX = Infinity;
var maxX = -Infinity;
node.layout.position = node.layout.position || vec2.create();
for (var i = 0; i < node.children.length; i++) {
var child = node.children[i];
this._updateNodeXPosition(child);
var x = child.layout.position[0];
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
}
if (node.children.length > 0) {
node.layout.position[0] = (minX + maxX) / 2;
} else {
node.layout.position[0] = 0;
}
var off = this._layerOffsets[node.depth] || 0;
if (off > node.layout.position[0]) {
var shift = off - node.layout.position[0];
this._shiftSubtree(node, shift);
for (var i = node.depth + 1; i < node.height + node.depth; i++) {
this._layerOffsets[i] += shift;
}
}
this._layerOffsets[node.depth] = node.layout.position[0] + node.layout.width + this.nodePadding;
this._layers[node.depth].push(node);
};
TreeLayout.prototype._shiftSubtree = function (root, offset) {
root.layout.position[0] += offset;
for (var i = 0; i < root.children.length; i++) {
this._shiftSubtree(root.children[i], offset);
}
};
TreeLayout.prototype._updateNodeYPosition = function (node, y, prevLayerHeight) {
node.layout.position[1] = y;
var layerHeight = 0;
for (var i = 0; i < node.children.length; i++) {
layerHeight = Math.max(node.children[i].layout.height, layerHeight);
}
var layerPadding = this.layerPadding;
if (typeof(layerPadding) === 'function') {
layerPadding = layerPadding(node.depth);
}
for (var i = 0; i < node.children.length; i++) {
this._updateNodeYPosition(node.children[i], y + layerPadding + prevLayerHeight, layerHeight);
}
};
return TreeLayout;
});
\ No newline at end of file
/**
* TreeMap layout
* @module echarts/layout/TreeMap
* @author loutongbing(loutongbing@126.com)
*/
define(function (require) {
function TreeMapLayout(opts) {
this.x = opts.x;
this.y = opts.y;
this.width = opts.width;
this.height = opts.height;
}
TreeMapLayout.prototype.run = function (areas) {
var out = [];
this._squarify(areas, {
x: this.x,
y: this.y,
width: this.width,
height: this.height
}, out);
return out;
};
TreeMapLayout.prototype._squarify = function (areas, row, out) {
var layoutDirection = 'VERTICAL';
var width = row.width;
var height = row.height;
if (row.width < row.height) {
layoutDirection = 'HORIZONTAL';
width = row.height;
height = row.width;
}
// 把考虑方向与位置的因素剥离出来,只考虑怎么排列,运行完毕之后再修正
var shapeArr = this._getShapeListInAbstractRow(
areas, width, height
);
// 首先换算出虚拟的x、y坐标
for (var i = 0; i < shapeArr.length; i++) {
shapeArr[i].x = 0;
shapeArr[i].y = 0;
for (var j = 0; j < i; j++) {
shapeArr[i].y += shapeArr[j].height;
}
}
var nextRow = {};
// 根据虚拟的shapeArr计算真实的小矩形
if (layoutDirection == 'VERTICAL') {
for (var k = 0; k < shapeArr.length; k++) {
out.push(
{
x: shapeArr[k].x + row.x,
y: shapeArr[k].y + row.y,
width: shapeArr[k].width,
height: shapeArr[k].height
}
);
}
nextRow = {
x: shapeArr[0].width + row.x,
y: row.y,
width: row.width - shapeArr[0].width,
height: row.height
};
}
else {
for (var l = 0; l < shapeArr.length; l++) {
out.push(
{
x: shapeArr[l].y + row.x,
y: shapeArr[l].x + row.y,
width: shapeArr[l].height,
height: shapeArr[l].width
}
);
}
nextRow = {
x: row.x,
y: row.y + shapeArr[0].width, // 注意是虚拟形状下的width
width: row.width,
height: row.height - shapeArr[0].width // 注意是虚拟形状下的width
};
}
// 下一步的矩形数组要剔除已经填充过的矩形
var nextAreaArr = areas.slice(shapeArr.length);
if (nextAreaArr.length === 0) {
return;
}
else {
this._squarify(
nextAreaArr,
nextRow,
out
);
}
};
TreeMapLayout.prototype._getShapeListInAbstractRow = function (
areas,
width,
height
) {
// 如果只剩下一个了,直接返回
if (areas.length === 1) {
return [
{
width: width,
height: height
}
];
}
// 填充进入的个数,从填充一个开始到填充所有小矩形,
// 纵横比最优时break并保留结果
for (var count = 1; count < areas.length; count++) {
var shapeArr0 = this._placeFixedNumberRectangles(
areas.slice(0, count),
width,
height
);
var shapeArr1 = this._placeFixedNumberRectangles(
areas.slice(0, count + 1),
width,
height
);
if (this._isFirstBetter(shapeArr0, shapeArr1)) {
return shapeArr0;
}
}
};
// 确定数量进行填充
TreeMapLayout.prototype._placeFixedNumberRectangles = function (
areaSubArr,
width,
height
) {
var count = areaSubArr.length;
// 声明返回值-每个矩形的形状(长宽)之数组
// 例如:
/*[
{
width: 11
height: 12
},
{
width: 11
height: 22
}
]*/
var shapeArr = [];
// 求出面积总和
var sum = 0;
for (var i = 0; i < areaSubArr.length; i++) {
sum += areaSubArr[i];
}
var cellWidth = sum / height;
for (var j = 0; j < count; j++) {
var cellHeight = height * areaSubArr[j] / sum;
shapeArr.push(
{
width: cellWidth,
height: cellHeight
}
);
}
return shapeArr;
};
// 相邻的两种填充方式放进去,比较是不是前一个的纵横比较小
TreeMapLayout.prototype._isFirstBetter = function (
shapeArr0,
shapeArr1
) {
var ratio0 = shapeArr0[0].height / shapeArr0[0].width;
ratio0 = (ratio0 > 1) ? 1 / ratio0 : ratio0;
var ratio1 = shapeArr1[0].height / shapeArr1[0].width;
ratio1 = (ratio1 > 1) ? 1 / ratio1 : ratio1;
if (Math.abs(ratio0 - 1) <= Math.abs(ratio1 - 1)) {
return true;
}
return false;
};
return TreeMapLayout;
});
\ No newline at end of file
此差异已折叠。
/**
* @file 主要功能
* @author clmtulip(车丽美, clmtulip@gmail.com) liyong(liyong1239@163.com)
*/
define(function (require) {
function ZeroArray(option) {
this.defaultOption = {type: 'RECT'};
this._init(option);
}
ZeroArray.prototype = {
RECT: '_calculateRect',
_init: function (option) {
this._initOption(option);
this._initCanvas();
},
_initOption: function (option) {
for (k in option) {
this.defaultOption[k] = option[k];
}
},
_initCanvas: function () {
var canvas = document.createElement("canvas");
canvas.width = 1;
canvas.height = 1;
var ratio = Math.sqrt(canvas.getContext("2d").getImageData(0, 0, 1, 1).data.length >> 2);
canvas.width = this.defaultOption.width;
canvas.height = this.defaultOption.height;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
}
this.canvas = canvas;
this.ctx = ctx;
this.ratio = ratio;
},
/**执行计算, 并返回
*
* @param callback
* 返回 {initarr, area, maxHit, maxWit} 给callback
*/
calculate: function (callback, callbackObj) {
var calType = this.defaultOption.type,
calmethod = this[calType];
this[calmethod].call(this, callback, callbackObj);
},
/**
* callback 函数的 正确执行
* @param result 计算后的结果,{initarr, area, maxHit, maxWit}
* @param callback 计算成功之后的回调函数
* @param callbackObj 回调函数的执行作用域
* @private
*/
_calculateReturn: function (result, callback, callbackObj) {
callback.call(callbackObj, result);
},
_calculateRect: function (callback, callbackObj) {
var result = {},
width = this.defaultOption.width >> 5 << 5,
height = this.defaultOption.height;
// 初始化数组
result.initarr = this._rectZeroArray(width * height);
// 总面积
result.area = width * height;
// 最大高度
result.maxHit = height;
// 最大宽度
result.maxWit = width;
// 边界
result.imgboard = this._rectBoard(width, height);
this._calculateReturn(result, callback, callbackObj);
},
_rectBoard: function (width, height) {
var row = [];
for (var i = 0; i < height; i++) {
row.push({
y: i,
start: 0,
end: width
})
}
var cloumn = [];
for (var i = 0; i < width; i++) {
cloumn.push({
x: i,
start: 0,
end: height
})
}
return {row: row, cloumn: cloumn};
},
_rectZeroArray: function (num) {
var a = [],
n = num,
i = -1;
while (++i < n) a[i] = 0;
return a;
}
};
return ZeroArray;
});
\ No newline at end of file
......@@ -116,11 +116,11 @@ define(function(require) {
return result;
}
function barLayoutGrid(ecModel, api) {
function barLayoutGrid(seriesType, ecModel, api) {
var barWidthAndOffset = calBarWidthAndOffset(
zrUtil.filter(
ecModel.getSeriesByType('bar'),
ecModel.getSeriesByType(seriesType),
function (seriesModel) {
return seriesModel.coordinateSystem
&& seriesModel.coordinateSystem.type === 'cartesian2d'
......@@ -130,7 +130,7 @@ define(function(require) {
var lastStackCoords = {};
ecModel.eachSeriesByType('bar', function (seriesModel) {
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
var data = seriesModel.getData();
var cartesian = seriesModel.coordinateSystem;
......@@ -180,7 +180,5 @@ define(function(require) {
}, this);
}
require('../../echarts').registerLayout(barLayoutGrid);
return barLayoutGrid;
});
\ No newline at end of file
/**
* 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;
});
此差异已折叠。
define(function (require) {
require('../../echarts').registerLayout(function (ecModel, api) {
ecModel.eachSeriesByType('line', function (lineSeries) {
return function (seriesType, ecModel, api) {
ecModel.eachSeriesByType(seriesType, function (lineSeries) {
var data = lineSeries.getData();
var coords = lineSeries.coordinateSystem.dataToCoords(data);
......@@ -18,5 +16,5 @@ define(function (require) {
}
});
});
});
}
});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册