diff --git a/src/chart/bar.js b/src/chart/bar.js index 9c3a22feb1c45c7dc8e9db5dbe15ec246231802c..1ade7bf545eb981aafd556b0e96bc2202a44386b 100644 --- a/src/chart/bar.js +++ b/src/chart/bar.js @@ -1,11 +1,15 @@ 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) { diff --git a/src/chart/line.js b/src/chart/line.js index d49ca7483762f0b8f43513340d50558252b3be6c..f50df898267a80774184db267e2ab49a911a53d8 100644 --- a/src/chart/line.js +++ b/src/chart/line.js @@ -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 diff --git a/src/chart/line/LineView.js b/src/chart/line/LineView.js index 47788189b9e0702342db78ee242765766f2a7b47..5b251c14f8904d2b61a4896f7c383c99389eb8de 100644 --- a/src/chart/line/LineView.js +++ b/src/chart/line/LineView.js @@ -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'); diff --git a/src/chart/scatter.js b/src/chart/scatter.js index 55734fba192d900cbdfe8f51647c4d103cde25f1..0c9db94153ac0417c3a50c1f3d0496d8187460c8 100644 --- a/src/chart/scatter.js +++ b/src/chart/scatter.js @@ -1,8 +1,13 @@ 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 diff --git a/src/chart/scatter/scatterLayout.js b/src/chart/scatter/scatterLayout.js deleted file mode 100644 index c07541d2734129a61c930c98fb8092998c181673..0000000000000000000000000000000000000000 --- a/src/chart/scatter/scatterLayout.js +++ /dev/null @@ -1,19 +0,0 @@ -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 diff --git a/src/echarts.js b/src/echarts.js index 43e76cb5ec99820e9a16a342d7e675a97f2fb009..8fdde9ad29e2d1a7ac4d28ecce7fc7f3254c4c22 100644 --- a/src/echarts.js +++ b/src/echarts.js @@ -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); diff --git a/src/layout/Chord.js b/src/layout/Chord.js deleted file mode 100644 index 85d48a4d38882f06c5f0b8f3c16d9145878ebf66..0000000000000000000000000000000000000000 --- a/src/layout/Chord.js +++ /dev/null @@ -1,145 +0,0 @@ -/** - * 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} 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 diff --git a/src/layout/EdgeBundling.js b/src/layout/EdgeBundling.js deleted file mode 100644 index ba0115799c91970f0dc5c4c6ae60a9e4e7c4cc8f..0000000000000000000000000000000000000000 --- a/src/layout/EdgeBundling.js +++ /dev/null @@ -1,415 +0,0 @@ -/** - * 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.} mp0 Output meet point 0 - * @param {Array.} 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 diff --git a/src/layout/Force.js b/src/layout/Force.js deleted file mode 100644 index 05a15e43018d472f7f64097e280c75614b60bdf0..0000000000000000000000000000000000000000 --- a/src/layout/Force.js +++ /dev/null @@ -1,248 +0,0 @@ -/** - * 力导向布局 - * @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 diff --git a/src/layout/Tree.js b/src/layout/Tree.js deleted file mode 100644 index 302608566eab7d261b6c676b848759286b119aa8..0000000000000000000000000000000000000000 --- a/src/layout/Tree.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * 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 diff --git a/src/layout/TreeMap.js b/src/layout/TreeMap.js deleted file mode 100644 index 43a116300981bd346bafc7b1d2c1710ad3456155..0000000000000000000000000000000000000000 --- a/src/layout/TreeMap.js +++ /dev/null @@ -1,188 +0,0 @@ -/** - * 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 diff --git a/src/layout/WordCloud.js b/src/layout/WordCloud.js deleted file mode 100644 index 9b6607add6e125dbe414dcf1de9923380a5dd69d..0000000000000000000000000000000000000000 --- a/src/layout/WordCloud.js +++ /dev/null @@ -1,692 +0,0 @@ -/** - * @author clmtulip(车丽美, clmtulip@gmail.com) liyong(liyong1239@163.com) - */ - -// Modified from d3-cloud -/* -Copyright (c) 2013, Jason Davies. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * The name Jason Davies may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL JASON DAVIES BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -define(function (require) { - var ZeroArray = require('../layout/WordCloudRectZero'); - var zrUtil = require('zrender/tool/util'); - - function CloudLayout(option) { - this._init(option); - } - - CloudLayout.prototype = { - /** - * 启动计算过程 - */ - start: function () { - var board = null; - var maxWit = 0; - var maxHit = 0; - var maxArea = 0; - var i = -1; - var tags = []; - var maxBounds = null; /*图片的最大可用边框*/ - // 根据配置 初始化 字符云数组 - var data = this.wordsdata; - var dfop = this.defaultOption; - var wordletype = dfop.wordletype; - var size = dfop.size; - var that = this; - - //得到 ZeroArray 传递过来的 初始值 和面积 最大宽度等值 - var zeroArrayObj = new ZeroArray({ - type: wordletype.type, - width: size[0], - height: size[1] - }); - - zeroArrayObj.calculate(function (options) { - board = options.initarr; - maxWit = options.maxWit; - maxHit = options.maxHit; - maxArea = options.area; - maxBounds = options.imgboard; - startStep(); - }, this); - - return this; - - function startStep() { - that.totalArea = maxArea; - //字体大小的确定,可配置,根据开关 确定是否要根据面积来定。。 - if (wordletype.autoSizeCal.enable) { - that._autoCalTextSize(data, maxArea, maxWit, maxHit, wordletype.autoSizeCal.minSize); - } - - if (dfop.timer) { - clearInterval(dfop.timer); - } - dfop.timer = setInterval(step, 0); - step(); - } - - function step() { - var start = +new Date; - var n = data.length; - var d; - while (+new Date - start < dfop.timeInterval && ++i < n && dfop.timer) { - d = data[i]; - // x y 的初始值 - d.x = size[0] >> 1; - d.y = size[1] >> 1; - //得到每个 标签所占用的具体的像素点 用 0 1 标记, 每32位作为一个数字 - that._cloudSprite(d, data, i); - //place 放置每个字符, 成功后 以 event 事件通知 - if (d.hasText && that._place(board, d, maxBounds)) { - - tags.push(d); - // event.word(d); - // Temporary hack - d.x -= size[0] >> 1; - d.y -= size[1] >> 1; - } - - } - //全部放置完成 停止cloud,并触发 'end'事件 - if (i >= n) { - that.stop(); - that._fixTagPosition(tags); - dfop.endcallback(tags); - } - } - }, - - _fixTagPosition: function (tags) { - var center = this.defaultOption.center; - - for (var i = 0, len = tags.length; i < len; i++) { - tags[i].x += center[0]; - tags[i].y += center[1]; - } - }, - - /** - * 停止计算过程 - */ - stop: function () { - if (this.defaultOption.timer) { - clearInterval(this.defaultOption.timer); - this.defaultOption.timer = null; - } - return this; - }, - - /** - * 计算结束后 执行V - * @param v - */ - end: function (v) { - if (v) { - this.defaultOption.endcallback = v; - } - return this; - }, - - /** - * 初始化 - * @param option - * @private - */ - _init: function (option) { - this.defaultOption = {}; - - /*初始化属性*/ - this._initProperty(option); - /*初始化方法*/ - this._initMethod(option); - /*初始化 canvas 画板*/ - this._initCanvas(); - /*初始化数据*/ - this._initData(option.data); - }, - - _initData: function (datas) { - var that = this; - var thatop = that.defaultOption; - this.wordsdata = datas.map(function (d, i) { - d.text = thatop.text.call(that, d, i); - - d.font = thatop.font.call(that, d, i); - d.style = thatop.fontStyle.call(that, d, i); - d.weight = thatop.fontWeight.call(that, d, i); - - // 字体的旋转角度 - d.rotate = thatop.rotate.call(that, d, i); - // 字体的大小 单位px - d.size = ~~thatop.fontSize.call(that, d, i); - // 字体间的间距 - d.padding = thatop.padding.call(that, d, i); - - return d; - }).sort(function (a, b) { - return b.value - a.value; - }); - }, - - /** - * 默认函数的定义 及初始化过程 - * @private - */ - _initMethod: function (option) { - /*数据初始化中要用到的函数*/ - var dfop = this.defaultOption; - dfop.text = (option.text) ? functor(option.text) : cloudText; - dfop.font = (option.font) ? functor(option.font) : cloudFont; - dfop.fontSize = (option.fontSize) ? functor(option.fontSize) : cloudFontSize; - dfop.fontStyle = (option.fontStyle) ? functor(option.fontStyle) : cloudFontNormal; - dfop.fontWeight = (option.fontWeight) ? functor(option.fontWeight) : cloudFontNormal; - - dfop.rotate = (option.rotate) ? newCloudRotate(option.rotate) : cloudRotate; - - dfop.padding = (option.padding) ? functor(option.padding) : cloudPadding; - - dfop.center = option.center; - - /*其它函数*/ - dfop.spiral = archimedeanSpiral; - dfop.endcallback = function () { - }; - dfop.rectangularSpiral = rectangularSpiral; - dfop.archimedeanSpiral = archimedeanSpiral; - - function cloudText(d) { - return d.name; - } - - function cloudFont() { - return "sans-serif"; - } - - function cloudFontNormal() { - return "normal"; - } - - function cloudFontSize(d) { - return d.value; - } - - function cloudRotate() { - return 0; - } - - function newCloudRotate(rotate) { - return function () { - return rotate[Math.round(Math.random() * (rotate.length - 1))] - } - } - - function cloudPadding() { - return 0; - } - - function archimedeanSpiral(size) { - var e = size[0] / size[1]; - return function (t) { - return [ - e * (t *= .1) * Math.cos(t), - t * Math.sin(t) - ]; - }; - } - - function rectangularSpiral(size) { - var dy = 4; - var dx = dy * size[0] / size[1]; - var x = 0; - var y = 0; - return function (t) { - var sign = t < 0 ? -1 : 1; - // See triangular numbers: T_n = n * (n + 1) / 2. - switch ((Math.sqrt(1 + 4 * sign * t) - sign) & 3) { - case 0: - x += dx; - break; - case 1: - y += dy; - break; - case 2: - x -= dx; - break; - default: - y -= dy; - break; - } - return [ - x, - y - ]; - }; - } - - function functor(v) { - return typeof v === "function" ? v : function () { - return v; - }; - } - - }, - - _initProperty: function (option) { - var dfop = this.defaultOption; - //默认值 - dfop.size = option.size || [ - 256, - 256 - ]; - dfop.wordletype = option.wordletype; - dfop.words = option.words || []; - dfop.timeInterval = Infinity; - dfop.timer = null; - dfop.spirals = { - archimedean: dfop.archimedeanSpiral, - rectangular: dfop.rectangularSpiral - }; - - zrUtil.merge(dfop, { - size: [ - 256, - 256 - ], - wordletype: { - type: 'RECT', - areaPresent: .058, - autoSizeCal: { - enable: true, - minSize: 12 - } - } - }); - }, - - _initCanvas: function () { - var cloudRadians = Math.PI / 180; - var cw = 1 << 11 >> 5; - var ch = 1 << 11; - var canvas; - var ratio = 1; - - if (typeof document !== "undefined") { - canvas = document.createElement("canvas"); - canvas.width = 1; - canvas.height = 1; - ratio = Math.sqrt( - canvas.getContext("2d") - .getImageData(0, 0, 1, 1) - .data.length >> 2 - ); - canvas.width = (cw << 5) / ratio; - canvas.height = ch / ratio; - } - else { - // Attempt to use node-canvas. - canvas = new Canvas(cw << 5, ch); - } - - var c = canvas.getContext("2d"); - c.fillStyle = c.strokeStyle = "red"; - c.textAlign = "center"; - - this.defaultOption.c = c; - this.defaultOption.cw = cw; - this.defaultOption.ch = ch; - this.defaultOption.ratio = ratio; - this.defaultOption.cloudRadians = cloudRadians; - }, - - - _cloudSprite: function (d, data, di) { - if (d.sprite) { - return; - } - - var cw = this.defaultOption.cw; - var ch = this.defaultOption.ch; - var c = this.defaultOption.c; - var ratio = this.defaultOption.ratio; - var cloudRadians = this.defaultOption.cloudRadians; - - c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); - var x = 0; - var y = 0; - var maxh = 0; - var n = data.length; - --di; - while (++di < n) { - d = data[di]; - c.save(); - c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; - //得到 字体 实际的 宽和高 - var w = c.measureText(d.text + "m").width * ratio; - var h = d.size << 1; - //得到 d 所占用的具体的宽度 和高度 并取整 - if (d.rotate) { - var sr = Math.sin(d.rotate * cloudRadians); - var cr = Math.cos(d.rotate * cloudRadians); - var wcr = w * cr; - var wsr = w * sr; - var hcr = h * cr; - var hsr = h * sr; - w = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; - h = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); - } - else { - w = (w + 0x1f) >> 5 << 5; - } - if (h > maxh) { - maxh = h; - } - //如果 字体 超出了 宽度 换更远的一行。。。 - if (x + w >= (cw << 5)) { - x = 0; - y += maxh; - maxh = 0; - } - if (y + h >= ch) { - break; - } - c.translate((x + (w >> 1)) / ratio, (y + (h >> 1)) / ratio); - if (d.rotate) { - c.rotate(d.rotate * cloudRadians); - } - c.fillText(d.text, 0, 0); - if (d.padding) { - c.lineWidth = 2 * d.padding; - c.strokeText(d.text, 0, 0); - } - c.restore(); - d.width = w; - d.height = h; - // xoff yoff 为 d在画板中的坐标位置 - d.xoff = x; - d.yoff = y; - d.x1 = w >> 1; - d.y1 = h >> 1; - d.x0 = -d.x1; - d.y0 = -d.y1; - d.hasText = true; - x += w; - } - //得到 所在 区域的 像素值。。进而 确定 其 y的位置。。。 - var pixels = c.getImageData(0, 0, (cw << 5) / ratio, ch / ratio).data; - var sprite = []; - while (--di >= 0) { - d = data[di]; - if (!d.hasText) { - continue; - } - var w = d.width; - var w32 = w >> 5; - var h = d.y1 - d.y0; - // Zero the buffer - for (var i = 0; i < h * w32; i++) { - sprite[i] = 0; - } - x = d.xoff; - if (x == null) { - return; - } - y = d.yoff; - var seen = 0; - var seenRow = -1; - for (var j = 0; j < h; j++) { - for (var i = 0; i < w; i++) { - var k = w32 * j + (i >> 5); - var m = pixels[((y + j) * (cw << 5) + (x + i)) << 2] ? 1 << (31 - (i % 32)) : 0; - sprite[k] |= m; - seen |= m; - } - if (seen) { - seenRow = j; - } - else { - d.y0++; - h--; - j--; - y++; - } - } - d.y1 = d.y0 + seenRow; - d.sprite = sprite.slice(0, (d.y1 - d.y0) * w32); - } - }, - - _place: function (board, tag, maxBounds) { - - /*判断目前面积总值 是否超过了阈值*/ - /*判断目前该值是否能够放的下*/ - var size = this.defaultOption.size; - var perimeter = [ - {x: 0, y: 0}, - {x: size[0], y: size[1]} - ]; - var startX = tag.x; - var startY = tag.y; - var maxDelta = Math.sqrt(size[0] * size[0] + size[1] * size[1]); - var s = this.defaultOption.spiral(size); - var dt = Math.random() < .5 ? 1 : -1; - var t = -dt; - var dxdy; - var dx; - var dy; - - while (dxdy = s(t += dt)) { - dx = ~~dxdy[0]; - dy = ~~dxdy[1]; - - //当dx dy 都大于 maxDelta 即超出了 画板范围, 停止 放置。。 该值舍弃 - if (Math.min(dx, dy) > maxDelta) { - break; - } - - tag.x = startX + dx; - tag.y = startY + dy; - - if (tag.x + tag.x0 < 0 || tag.y + tag.y0 < 0 || - tag.x + tag.x1 > size[0] || tag.y + tag.y1 > size[1]) { - continue; - } - // TODO only check for collisions within current bounds. - if (!cloudCollide(tag, board, size[0])) { - if (collideRects(tag, maxBounds)) { - var sprite = tag.sprite; - var w = tag.width >> 5; - var sw = size[0] >> 5; - var lx = tag.x - (w << 4); - var sx = lx & 0x7f; - var msx = 32 - sx; - var h = tag.y1 - tag.y0; - var x = (tag.y + tag.y0) * sw + (lx >> 5);// 计算其在画板的 横坐标的位置 - var last; - - //当 该 字能够被正确放置时, 根据该字体的范围 更改标志位范围 - for (var j = 0; j < h; j++) { - last = 0; - for (var i = 0; i <= w; i++) { - board[x + i] |= (last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0); - } - x += sw; - } - delete tag.sprite; - return true; - } - } - } - return false; - - function cloudCollide(tag, board, sw) { - sw >>= 5; - var sprite = tag.sprite; - var w = tag.width >> 5; - var lx = tag.x - (w << 4); - var sx = lx & 0x7f; - var msx = 32 - sx; - var h = tag.y1 - tag.y0; - var x = (tag.y + tag.y0) * sw + (lx >> 5); - var last; - for (var j = 0; j < h; j++) { - last = 0; - for (var i = 0; i <= w; i++) { - if (((last << msx) | (i < w ? (last = sprite[j * w + i]) >>> sx : 0)) - & board[x + i]) { - return true; - } - } - x += sw; - } - return false; - } - - function collideRects(a, maxBounds) { - return maxBounds.row[a.y] && maxBounds.cloumn[a.x] - && a.x >= maxBounds.row[a.y].start - && a.x <= maxBounds.row[a.y].end - && a.y >= maxBounds.cloumn[a.x].start - && a.y <= maxBounds.cloumn[a.x].end; - } - }, - - _autoCalTextSize: function (data, shapeArea, maxwidth, maxheight, minSize) { - //循环 - //面积 归一化 - //计算 每个字体的面积 - var sizesum = sum(data, function (k) { - return k.size; - }); - var i = data.length; - var maxareapre = .25; /*面积归一化后, 字体占总面积的最大百分比*/ - var minTextSize = minSize; /*字体所能缩放的最小大小。。如果字体面积 依旧无法满足上述约束, 字体将不会再缩小*/ - var cw = this.defaultOption.cw; - var ch = this.defaultOption.ch; - var c = this.defaultOption.c; - var ratio = this.defaultOption.ratio; - var cloudRadians = this.defaultOption.cloudRadians; - var d; - var dpre; - - while (i--) { - d = data[i]; - dpre = d.size / sizesum; - if (maxareapre) { - d.areapre = (dpre < maxareapre) ? dpre : maxareapre; - } - else { - d.areapre = dpre; - } - - d.area = shapeArea * d.areapre; - d.totalarea = shapeArea; - measureTextWitHitByarea(d); - } - - //根据面积 计算字体的 size - //根据 最大宽度 和最大高度 重新检测 字体的size, 不符合条件 重新计算,若字体大小已经是 最小size,则 取消计算。。。 - function measureTextWitHitByarea(d) { - c.clearRect(0, 0, (cw << 5) / ratio, ch / ratio); - c.save(); - c.font = d.style + " " + d.weight + " " + ~~((d.size + 1) / ratio) + "px " + d.font; - - //得到 字体 实际的 宽和高 - var w = c.measureText(d.text + "m").width * ratio, - h = d.size << 1; - - //得到 d 所占用的具体的宽度 和高度 并取整 - w = (w + 0x1f) >> 5 << 5; //给定了一个最小值 1 即不会存在 宽度为0的情况 - - c.restore(); - d.aw = w; - d.ah = h; - - - var k, rw, rh; - - //如果 有旋转 测试 旋转后的 宽和高 是否 超过了 最大的大小 - if (d.rotate) { - var sr = Math.sin(d.rotate * cloudRadians); - var cr = Math.cos(d.rotate * cloudRadians); - var wcr = w * cr; - var wsr = w * sr; - var hcr = h * cr; - var hsr = h * sr; - - rw = (Math.max(Math.abs(wcr + hsr), Math.abs(wcr - hsr)) + 0x1f) >> 5 << 5; - rh = ~~Math.max(Math.abs(wsr + hcr), Math.abs(wsr - hcr)); - } - - - //满足条件 不用继续调整的 - // size 为 最小, 或者面积在允许范围内 且 宽和高也在允许范围内 - if ((d.size <= minTextSize) - || (d.rotate && w * h <= d.area && rw <= maxwidth && rh <= maxheight) - || (w * h <= d.area && w <= maxwidth && h <= maxheight)) { - d.area = w * h; - return; - } - - //如果 超过了 最大宽度 或最大高度。。 继续计算 - if (d.rotate && rw > maxwidth && rh > maxheight) {// 有旋转时,超过了 最大宽度和最大高度 - k = Math.min(maxwidth / rw, maxheight / rh); - } - else if (w > maxwidth || h > maxheight) {// 无旋转时,超过了 最大宽度和最大高度 - k = Math.min(maxwidth / w, maxheight / h); - } - else { // 当前 面积 大于 规定的面积时 - k = Math.sqrt(d.area / (d.aw * d.ah)); - } - - d.size = ~~(k * d.size); - - if (d.size < minSize) { - d.size = minSize; - return; - } - - return measureTextWitHitByarea(d); - } - - function sum(dts, callback) { - var j = dts.length; - var ressum = 0; - while (j--) { - ressum += callback(dts[j]); - } - - return ressum; - } - } - - }; - - - return CloudLayout; -}); \ No newline at end of file diff --git a/src/layout/WordCloudRectZero.js b/src/layout/WordCloudRectZero.js deleted file mode 100644 index 615c2103601b9dcd8b4a21738b89926101165d1a..0000000000000000000000000000000000000000 --- a/src/layout/WordCloudRectZero.js +++ /dev/null @@ -1,123 +0,0 @@ -/** - * @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 diff --git a/src/chart/bar/barLayoutGrid.js b/src/layout/barGrid.js similarity index 96% rename from src/chart/bar/barLayoutGrid.js rename to src/layout/barGrid.js index 5b23416dcef62cb81a14ff642b9f5c08e477990e..5c1f42df133c15a7a88964721eee25b22cc0d3fd 100644 --- a/src/chart/bar/barLayoutGrid.js +++ b/src/layout/barGrid.js @@ -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 diff --git a/src/layout/eventRiver.js b/src/layout/eventRiver.js deleted file mode 100644 index 5318ee718dc4b21d192cb8f81cc8b2a7eaa9a5f8..0000000000000000000000000000000000000000 --- a/src/layout/eventRiver.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * 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; -}); diff --git a/src/layout/forceLayoutWorker.js b/src/layout/forceLayoutWorker.js deleted file mode 100644 index 2f4f282b3182618dc4cccb74f9edc37d336b1db1..0000000000000000000000000000000000000000 --- a/src/layout/forceLayoutWorker.js +++ /dev/null @@ -1,781 +0,0 @@ -// 1. Graph Drawing by Force-directed Placement -// 2. http://webatlas.fr/tempshare/ForceAtlas2_Paper.pdf -define(function __echartsForceLayoutWorker(require) { - - 'use strict'; - - var vec2; - // In web worker - var inWorker = typeof(window) === 'undefined' && typeof(require) === 'undefined'; - if (inWorker) { - vec2 = { - create: function(x, y) { - var out = new Float32Array(2); - out[0] = x || 0; - out[1] = y || 0; - return out; - }, - dist: function(a, b) { - var x = b[0] - a[0]; - var y = b[1] - a[1]; - return Math.sqrt(x*x + y*y); - }, - len: function(a) { - var x = a[0]; - var y = a[1]; - return Math.sqrt(x*x + y*y); - }, - scaleAndAdd: function(out, a, b, scale) { - out[0] = a[0] + b[0] * scale; - out[1] = a[1] + b[1] * scale; - return out; - }, - scale: function(out, a, b) { - out[0] = a[0] * b; - out[1] = a[1] * b; - return out; - }, - add: function(out, a, b) { - out[0] = a[0] + b[0]; - out[1] = a[1] + b[1]; - return out; - }, - sub: function(out, a, b) { - out[0] = a[0] - b[0]; - out[1] = a[1] - b[1]; - return out; - }, - dot: function (v1, v2) { - return v1[0] * v2[0] + v1[1] * v2[1]; - }, - normalize: function(out, a) { - var x = a[0]; - var y = a[1]; - var len = x*x + y*y; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - out[0] = a[0] * len; - out[1] = a[1] * len; - } - return out; - }, - negate: function(out, a) { - out[0] = -a[0]; - out[1] = -a[1]; - return out; - }, - copy: function(out, a) { - out[0] = a[0]; - out[1] = a[1]; - return out; - }, - set: function(out, x, y) { - out[0] = x; - out[1] = y; - return out; - } - }; - } - else { - vec2 = require('zrender/tool/vector'); - } - var ArrayCtor = typeof(Float32Array) == 'undefined' ? Array : Float32Array; - - /**************************** - * Class: Region - ***************************/ - - function Region() { - - this.subRegions = []; - - this.nSubRegions = 0; - - this.node = null; - - this.mass = 0; - - this.centerOfMass = null; - - this.bbox = new ArrayCtor(4); - - this.size = 0; - } - - // Reset before update - Region.prototype.beforeUpdate = function() { - for (var i = 0; i < this.nSubRegions; i++) { - this.subRegions[i].beforeUpdate(); - } - this.mass = 0; - if (this.centerOfMass) { - this.centerOfMass[0] = 0; - this.centerOfMass[1] = 0; - } - this.nSubRegions = 0; - this.node = null; - }; - // Clear after update - Region.prototype.afterUpdate = function() { - this.subRegions.length = this.nSubRegions; - for (var i = 0; i < this.nSubRegions; i++) { - this.subRegions[i].afterUpdate(); - } - }; - - Region.prototype.addNode = function(node) { - if (this.nSubRegions === 0) { - if (this.node == null) { - this.node = node; - return; - } - else { - this._addNodeToSubRegion(this.node); - this.node = null; - } - } - this._addNodeToSubRegion(node); - - this._updateCenterOfMass(node); - }; - - Region.prototype.findSubRegion = function(x, y) { - for (var i = 0; i < this.nSubRegions; i++) { - var region = this.subRegions[i]; - if (region.contain(x, y)) { - return region; - } - } - }; - - Region.prototype.contain = function(x, y) { - return this.bbox[0] <= x - && this.bbox[2] >= x - && this.bbox[1] <= y - && this.bbox[3] >= y; - }; - - Region.prototype.setBBox = function(minX, minY, maxX, maxY) { - // Min - this.bbox[0] = minX; - this.bbox[1] = minY; - // Max - this.bbox[2] = maxX; - this.bbox[3] = maxY; - - this.size = (maxX - minX + maxY - minY) / 2; - }; - - Region.prototype._newSubRegion = function() { - var subRegion = this.subRegions[this.nSubRegions]; - if (!subRegion) { - subRegion = new Region(); - this.subRegions[this.nSubRegions] = subRegion; - } - this.nSubRegions++; - return subRegion; - }; - - Region.prototype._addNodeToSubRegion = function(node) { - var subRegion = this.findSubRegion(node.position[0], node.position[1]); - var bbox = this.bbox; - if (!subRegion) { - var cx = (bbox[0] + bbox[2]) / 2; - var cy = (bbox[1] + bbox[3]) / 2; - var w = (bbox[2] - bbox[0]) / 2; - var h = (bbox[3] - bbox[1]) / 2; - - var xi = node.position[0] >= cx ? 1 : 0; - var yi = node.position[1] >= cy ? 1 : 0; - - var subRegion = this._newSubRegion(); - // Min - subRegion.setBBox( - // Min - xi * w + bbox[0], - yi * h + bbox[1], - // Max - (xi + 1) * w + bbox[0], - (yi + 1) * h + bbox[1] - ); - } - - subRegion.addNode(node); - }; - - Region.prototype._updateCenterOfMass = function(node) { - // Incrementally update - if (this.centerOfMass == null) { - this.centerOfMass = vec2.create(); - } - var x = this.centerOfMass[0] * this.mass; - var y = this.centerOfMass[1] * this.mass; - x += node.position[0] * node.mass; - y += node.position[1] * node.mass; - this.mass += node.mass; - this.centerOfMass[0] = x / this.mass; - this.centerOfMass[1] = y / this.mass; - }; - - /**************************** - * Class: Graph Node - ***************************/ - function GraphNode() { - this.position = vec2.create(); - - this.force = vec2.create(); - this.forcePrev = vec2.create(); - - this.speed = vec2.create(); - this.speedPrev = vec2.create(); - - // If repulsionByDegree is true - // mass = inDegree + outDegree + 1 - // Else - // mass is manually set - this.mass = 1; - - this.inDegree = 0; - this.outDegree = 0; - } - - /**************************** - * Class: Graph Edge - ***************************/ - function GraphEdge(node1, node2) { - this.node1 = node1; - this.node2 = node2; - - this.weight = 1; - } - - /**************************** - * Class: ForceLayout - ***************************/ - function ForceLayout() { - - this.barnesHutOptimize = false; - this.barnesHutTheta = 1.5; - - this.repulsionByDegree = false; - - this.preventNodeOverlap = false; - this.preventNodeEdgeOverlap = false; - - this.strongGravity = true; - - this.gravity = 1.0; - this.scaling = 1.0; - - this.edgeWeightInfluence = 1.0; - - this.center = [0, 0]; - this.width = 500; - this.height = 500; - - this.maxSpeedIncrease = 1; - - this.nodes = []; - this.edges = []; - - this.bbox = new ArrayCtor(4); - - this._rootRegion = new Region(); - this._rootRegion.centerOfMass = vec2.create(); - - this._massArr = null; - - this._k = 0; - } - - ForceLayout.prototype.nodeToNodeRepulsionFactor = function (mass, d, k) { - return k * k * mass / d; - }; - - ForceLayout.prototype.edgeToNodeRepulsionFactor = function (mass, d, k) { - return k * mass / d; - }; - - ForceLayout.prototype.attractionFactor = function (w, d, k) { - return w * d / k; - }; - - ForceLayout.prototype.initNodes = function(positionArr, massArr, sizeArr) { - - this.temperature = 1.0; - - var nNodes = positionArr.length / 2; - this.nodes.length = 0; - var haveSize = typeof(sizeArr) !== 'undefined'; - - for (var i = 0; i < nNodes; i++) { - var node = new GraphNode(); - node.position[0] = positionArr[i * 2]; - node.position[1] = positionArr[i * 2 + 1]; - node.mass = massArr[i]; - if (haveSize) { - node.size = sizeArr[i]; - } - this.nodes.push(node); - } - - this._massArr = massArr; - if (haveSize) { - this._sizeArr = sizeArr; - } - }; - - ForceLayout.prototype.initEdges = function(edgeArr, edgeWeightArr) { - var nEdges = edgeArr.length / 2; - this.edges.length = 0; - var edgeHaveWeight = typeof(edgeWeightArr) !== 'undefined'; - - for (var i = 0; i < nEdges; i++) { - var sIdx = edgeArr[i * 2]; - var tIdx = edgeArr[i * 2 + 1]; - var sNode = this.nodes[sIdx]; - var tNode = this.nodes[tIdx]; - - if (!sNode || !tNode) { - continue; - } - sNode.outDegree++; - tNode.inDegree++; - var edge = new GraphEdge(sNode, tNode); - - if (edgeHaveWeight) { - edge.weight = edgeWeightArr[i]; - } - - this.edges.push(edge); - } - }; - - ForceLayout.prototype.update = function() { - - var nNodes = this.nodes.length; - - this.updateBBox(); - - this._k = 0.4 * this.scaling * Math.sqrt(this.width * this.height / nNodes); - - if (this.barnesHutOptimize) { - this._rootRegion.setBBox( - this.bbox[0], this.bbox[1], - this.bbox[2], this.bbox[3] - ); - this._rootRegion.beforeUpdate(); - for (var i = 0; i < nNodes; i++) { - this._rootRegion.addNode(this.nodes[i]); - } - this._rootRegion.afterUpdate(); - } - else { - // Update center of mass of whole graph - var mass = 0; - var centerOfMass = this._rootRegion.centerOfMass; - vec2.set(centerOfMass, 0, 0); - for (var i = 0; i < nNodes; i++) { - var node = this.nodes[i]; - mass += node.mass; - vec2.scaleAndAdd(centerOfMass, centerOfMass, node.position, node.mass); - } - if (mass > 0) { - vec2.scale(centerOfMass, centerOfMass, 1 / mass); - } - } - - this.updateForce(); - - this.updatePosition(); - }; - - ForceLayout.prototype.updateForce = function () { - var nNodes = this.nodes.length; - // Reset forces - for (var i = 0; i < nNodes; i++) { - var node = this.nodes[i]; - vec2.copy(node.forcePrev, node.force); - vec2.copy(node.speedPrev, node.speed); - vec2.set(node.force, 0, 0); - } - - this.updateNodeNodeForce(); - - if (this.gravity > 0) { - this.updateGravityForce(); - } - - this.updateEdgeForce(); - - if (this.preventNodeEdgeOverlap) { - this.updateNodeEdgeForce(); - } - }; - - ForceLayout.prototype.updatePosition = function () { - var nNodes = this.nodes.length; - // Apply forces - // var speed = vec2.create(); - var v = vec2.create(); - for (var i = 0; i < nNodes; i++) { - var node = this.nodes[i]; - var speed = node.speed; - - // var swing = vec2.dist(node.force, node.forcePrev); - // // var swing = 30; - // vec2.scale(node.force, node.force, 1 / (1 + Math.sqrt(swing))); - vec2.scale(node.force, node.force, 1 / 30); - - // contraint force - var df = vec2.len(node.force) + 0.1; - var scale = Math.min(df, 500.0) / df; - vec2.scale(node.force, node.force, scale); - - vec2.add(speed, speed, node.force); - vec2.scale(speed, speed, this.temperature); - - // Prevent swinging - // Limited the increase of speed up to 100% each step - // TODO adjust by nodes number - // TODO First iterate speed control - vec2.sub(v, speed, node.speedPrev); - var swing = vec2.len(v); - if (swing > 0) { - vec2.scale(v, v, 1 / swing); - var base = vec2.len(node.speedPrev); - if (base > 0) { - swing = Math.min(swing / base, this.maxSpeedIncrease) * base; - vec2.scaleAndAdd(speed, node.speedPrev, v, swing); - } - } - - // constraint speed - var ds = vec2.len(speed); - var scale = Math.min(ds, 100.0) / (ds + 0.1); - vec2.scale(speed, speed, scale); - - vec2.add(node.position, node.position, speed); - } - }; - - ForceLayout.prototype.updateNodeNodeForce = function () { - var nNodes = this.nodes.length; - // Compute forces - // Repulsion - for (var i = 0; i < nNodes; i++) { - var na = this.nodes[i]; - if (this.barnesHutOptimize) { - this.applyRegionToNodeRepulsion(this._rootRegion, na); - } - else { - for (var j = i + 1; j < nNodes; j++) { - var nb = this.nodes[j]; - this.applyNodeToNodeRepulsion(na, nb, false); - } - } - } - }; - - ForceLayout.prototype.updateGravityForce = function () { - for (var i = 0; i < this.nodes.length; i++) { - this.applyNodeGravity(this.nodes[i]); - } - }; - - ForceLayout.prototype.updateEdgeForce = function () { - // Attraction - for (var i = 0; i < this.edges.length; i++) { - this.applyEdgeAttraction(this.edges[i]); - } - }; - - ForceLayout.prototype.updateNodeEdgeForce = function () { - for (var i = 0; i < this.nodes.length; i++) { - for (var j = 0; j < this.edges.length; j++) { - this.applyEdgeToNodeRepulsion(this.edges[j], this.nodes[i]); - } - } - }; - - ForceLayout.prototype.applyRegionToNodeRepulsion = (function() { - var v = vec2.create(); - return function applyRegionToNodeRepulsion(region, node) { - if (region.node) { // Region is a leaf - this.applyNodeToNodeRepulsion(region.node, node, true); - } - else { - // Static region and node - if (region.mass === 0 && node.mass === 0) { - return; - } - vec2.sub(v, node.position, region.centerOfMass); - var d2 = v[0] * v[0] + v[1] * v[1]; - if (d2 > this.barnesHutTheta * region.size * region.size) { - var factor = this._k * this._k * (node.mass + region.mass) / (d2 + 1); - vec2.scaleAndAdd(node.force, node.force, v, factor * 2); - } - else { - for (var i = 0; i < region.nSubRegions; i++) { - this.applyRegionToNodeRepulsion(region.subRegions[i], node); - } - } - } - }; - })(); - - ForceLayout.prototype.applyNodeToNodeRepulsion = (function() { - var v = vec2.create(); - return function applyNodeToNodeRepulsion(na, nb, oneWay) { - if (na === nb) { - return; - } - // Two static node - if (na.mass === 0 && nb.mass === 0) { - return; - } - - vec2.sub(v, na.position, nb.position); - var d2 = v[0] * v[0] + v[1] * v[1]; - - // PENDING - if (d2 === 0) { - return; - } - - var factor; - var mass = na.mass + nb.mass; - var d = Math.sqrt(d2); - - // Normalize v - vec2.scale(v, v, 1 / d); - - if (this.preventNodeOverlap) { - d = d - na.size - nb.size; - if (d > 0) { - factor = this.nodeToNodeRepulsionFactor( - mass, d, this._k - ); - } - else if (d <= 0) { - // A stronger repulsion if overlap - factor = this._k * this._k * 10 * mass; - } - } - else { - factor = this.nodeToNodeRepulsionFactor( - mass, d, this._k - ); - } - - if (!oneWay) { - vec2.scaleAndAdd(na.force, na.force, v, factor * 2); - } - vec2.scaleAndAdd(nb.force, nb.force, v, -factor * 2); - }; - })(); - - ForceLayout.prototype.applyEdgeAttraction = (function() { - var v = vec2.create(); - return function applyEdgeAttraction(edge) { - var na = edge.node1; - var nb = edge.node2; - - vec2.sub(v, na.position, nb.position); - var d = vec2.len(v); - - var w; - if (this.edgeWeightInfluence === 0) { - w = 1; - } - else if (this.edgeWeightInfluence == 1) { - w = edge.weight; - } - else { - w = Math.pow(edge.weight, this.edgeWeightInfluence); - } - - var factor; - - if (this.preventOverlap) { - d = d - na.size - nb.size; - if (d <= 0) { - // No attraction - return; - } - } - - var factor = this.attractionFactor(w, d, this._k); - - vec2.scaleAndAdd(na.force, na.force, v, -factor); - vec2.scaleAndAdd(nb.force, nb.force, v, factor); - }; - })(); - - ForceLayout.prototype.applyNodeGravity = (function() { - var v = vec2.create(); - return function(node) { - // PENDING Move to centerOfMass or [0, 0] ? - // vec2.sub(v, this._rootRegion.centerOfMass, node.position); - // vec2.negate(v, node.position); - vec2.sub(v, this.center, node.position); - if (this.width > this.height) { - // Stronger gravity on y axis - v[1] *= this.width / this.height; - } - else { - // Stronger gravity on x axis - v[0] *= this.height / this.width; - } - var d = vec2.len(v) / 100; - - if (this.strongGravity) { - vec2.scaleAndAdd(node.force, node.force, v, d * this.gravity * node.mass); - } - else { - vec2.scaleAndAdd(node.force, node.force, v, this.gravity * node.mass / (d + 1)); - } - }; - })(); - - ForceLayout.prototype.applyEdgeToNodeRepulsion = (function () { - var v12 = vec2.create(); - var v13 = vec2.create(); - var p = vec2.create(); - return function (e, n3) { - var n1 = e.node1; - var n2 = e.node2; - - if (n1 === n3 || n2 === n3) { - return; - } - - vec2.sub(v12, n2.position, n1.position); - vec2.sub(v13, n3.position, n1.position); - - var len12 = vec2.len(v12); - vec2.scale(v12, v12, 1 / len12); - var len = vec2.dot(v12, v13); - - // n3 can't project on line n1-n2 - if (len < 0 || len > len12) { - return; - } - - // Project point - vec2.scaleAndAdd(p, n1.position, v12, len); - - // n3 distance to line n1-n2 - var dist = vec2.dist(p, n3.position) - n3.size; - var factor = this.edgeToNodeRepulsionFactor( - n3.mass, Math.max(dist, 0.1), 100 - ); - // Use v12 as normal vector - vec2.sub(v12, n3.position, p); - vec2.normalize(v12, v12); - vec2.scaleAndAdd(n3.force, n3.force, v12, factor); - - // PENDING - vec2.scaleAndAdd(n1.force, n1.force, v12, -factor); - vec2.scaleAndAdd(n2.force, n2.force, v12, -factor); - }; - })(); - - ForceLayout.prototype.updateBBox = function() { - var minX = Infinity; - var minY = Infinity; - var maxX = -Infinity; - var maxY = -Infinity; - for (var i = 0; i < this.nodes.length; i++) { - var pos = this.nodes[i].position; - minX = Math.min(minX, pos[0]); - minY = Math.min(minY, pos[1]); - maxX = Math.max(maxX, pos[0]); - maxY = Math.max(maxY, pos[1]); - } - this.bbox[0] = minX; - this.bbox[1] = minY; - this.bbox[2] = maxX; - this.bbox[3] = maxY; - }; - - ForceLayout.getWorkerCode = function() { - var str = __echartsForceLayoutWorker.toString(); - return str.slice(str.indexOf('{') + 1, str.lastIndexOf('return')); - }; - - /**************************** - * Main process - ***************************/ - - /* jshint ignore:start */ - if (inWorker) { - var forceLayout = null; - - self.onmessage = function(e) { - // Position read back - if (e.data instanceof ArrayBuffer) { - if (!forceLayout) return; - - var positionArr = new Float32Array(e.data); - var nNodes = positionArr.length / 2; - for (var i = 0; i < nNodes; i++) { - var node = forceLayout.nodes[i]; - node.position[0] = positionArr[i * 2]; - node.position[1] = positionArr[i * 2 + 1]; - } - return; - } - - switch(e.data.cmd) { - case 'init': - if (!forceLayout) { - forceLayout = new ForceLayout(); - } - forceLayout.initNodes(e.data.nodesPosition, e.data.nodesMass, e.data.nodesSize); - forceLayout.initEdges(e.data.edges, e.data.edgesWeight); - break; - case 'updateConfig': - if (forceLayout) { - for (var name in e.data.config) { - forceLayout[name] = e.data.config[name]; - } - } - break; - case 'update': - var steps = e.data.steps; - - if (forceLayout) { - var nNodes = forceLayout.nodes.length; - var positionArr = new Float32Array(nNodes * 2); - - forceLayout.temperature = e.data.temperature; - - for (var i = 0; i < steps; i++) { - forceLayout.update(); - forceLayout.temperature *= e.data.coolDown; - } - // Callback - for (var i = 0; i < nNodes; i++) { - var node = forceLayout.nodes[i]; - positionArr[i * 2] = node.position[0]; - positionArr[i * 2 + 1] = node.position[1]; - } - - self.postMessage(positionArr.buffer, [positionArr.buffer]); - } - else { - // Not initialzied yet - var emptyArr = new Float32Array(); - // Post transfer object - self.postMessage(emptyArr.buffer, [emptyArr.buffer]); - } - break; - } - }; - } - /* jshint ignore:end */ - - return ForceLayout; -}); \ No newline at end of file diff --git a/src/layout/pie.js b/src/layout/pie.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/chart/line/lineLayout.js b/src/layout/points.js similarity index 77% rename from src/chart/line/lineLayout.js rename to src/layout/points.js index 838d7dc25856df6cb1067ce216fa9eb27c7d6376..eabdb5d406a11a177c7c605aace23c7ba590ebaa 100644 --- a/src/chart/line/lineLayout.js +++ b/src/layout/points.js @@ -1,8 +1,6 @@ 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