提交 6670af76 编写于 作者: L lang

Symbol draggable, force enhancement

上级 6d4a5265
...@@ -130,8 +130,9 @@ define(function (require) { ...@@ -130,8 +130,9 @@ define(function (require) {
symbol: 'circle', symbol: 'circle',
symbolSize: 10, symbolSize: 10,
// roam: false, draggable: false,
roam: false,
roamDetail: { roamDetail: {
x: 0, x: 0,
y: 0, y: 0,
......
...@@ -81,9 +81,30 @@ define(function (require) { ...@@ -81,9 +81,30 @@ define(function (require) {
this._updateController(seriesModel, coordSys, api); this._updateController(seriesModel, coordSys, api);
clearTimeout(this._layoutTimeout); clearTimeout(this._layoutTimeout);
if (seriesModel.forceLayout) {
this._startForceLayoutIteration(seriesModel.forceLayout); var forceLayout = seriesModel.forceLayout;
if (forceLayout) {
this._startForceLayoutIteration(forceLayout);
} }
// Update draggable
data.eachItemGraphicEl(function (el, idx) {
var draggable = data.getItemModel(idx).get('draggable');
if (draggable && forceLayout) {
el.on('drag', function () {
forceLayout.warmUp();
forceLayout.setFixed(idx);
// Write position back to layout
data.setItemLayout(idx, el.position);
}).on('dragend', function () {
forceLayout.setUnfixed(idx);
});
}
else {
el.off('drag');
}
el.setDraggable(draggable);
}, this);
}, },
_startForceLayoutIteration: function (forceLayout) { _startForceLayoutIteration: function (forceLayout) {
......
...@@ -55,15 +55,23 @@ define(function (require) { ...@@ -55,15 +55,23 @@ define(function (require) {
// Formula in 'Graph Drawing by Force-directed Placement' // Formula in 'Graph Drawing by Force-directed Placement'
// var k = scale * Math.sqrt(width * height / nodes.length); // var k = scale * Math.sqrt(width * height / nodes.length);
// var k2 = k * k; // var k2 = k * k;
var friction = 0.6; var friction = 0.6;
return { return {
warmUp: function () { warmUp: function () {
friction = 0.7; friction = 0.5;
},
setFixed: function (idx) {
nodes[idx].fixed = true;
}, },
setUnfixed: function (idx) {
nodes[idx].fixed = false;
},
step: function (cb) { step: function (cb) {
var v12 = []; var v12 = [];
var nLen = nodes.length; var nLen = nodes.length;
...@@ -77,18 +85,20 @@ define(function (require) { ...@@ -77,18 +85,20 @@ define(function (require) {
var w = n2.w / (n1.w + n2.w); var w = n2.w / (n1.w + n2.w);
vec2.normalize(v12, v12); vec2.normalize(v12, v12);
scaleAndAdd(n1.p, n1.p, v12, w * d * friction); !n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction);
scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction); !n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction);
} }
// Gravity // Gravity
// for (var i = 0; i < nLen; i++) { for (var i = 0; i < nLen; i++) {
// var n = nodes[i]; var n = nodes[i];
// vec2.sub(v12, center, n.p); if (!n.fixed) {
// var d = vec2.len(v12); vec2.sub(v12, center, n.p);
// vec2.scale(v12, v12, 1 / d); // var d = vec2.len(v12);
// var gravityFactor = gravity; // vec2.scale(v12, v12, 1 / d);
// vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction); // var gravityFactor = gravity;
// } vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction);
}
}
// Repulsive // Repulsive
// PENDING // PENDING
...@@ -104,22 +114,23 @@ define(function (require) { ...@@ -104,22 +114,23 @@ define(function (require) {
d = 1; d = 1;
} }
var repFact = (n1.rep + n2.rep) / d / d; var repFact = (n1.rep + n2.rep) / d / d;
scaleAndAdd(n1.pp, n1.pp, v12, repFact); !n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact);
scaleAndAdd(n2.pp, n2.pp, v12, -repFact); !n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact);
} }
} }
var v = []; var v = [];
for (var i = 0; i < nLen; i++) { for (var i = 0; i < nLen; i++) {
var n = nodes[i]; var n = nodes[i];
vec2.sub(v, n.p, n.pp); if (!n.fixed) {
vec2.scaleAndAdd(n.p, n.p, v, friction); vec2.sub(v, n.p, n.pp);
vec2.scaleAndAdd(n.p, n.p, v, friction);
vec2.copy(n.pp, n.p); vec2.copy(n.pp, n.p);
}
} }
friction = friction * 0.99; friction = friction * 0.995;
cb && cb(nodes, edges, friction < 0.002); cb && cb(nodes, edges, friction < 0.01);
} }
}; };
}; };
......
...@@ -4,6 +4,7 @@ define(function (require) { ...@@ -4,6 +4,7 @@ define(function (require) {
var numberUtil = require('../../util/number'); var numberUtil = require('../../util/number');
var simpleLayoutHelper = require('./simpleLayoutHelper'); var simpleLayoutHelper = require('./simpleLayoutHelper');
var circularLayoutHelper = require('./circularLayoutHelper'); var circularLayoutHelper = require('./circularLayoutHelper');
var vec2 = require('zrender/core/vector');
return function (ecModel, api) { return function (ecModel, api) {
ecModel.eachSeriesByType('graph', function (graphSeries) { ecModel.eachSeriesByType('graph', function (graphSeries) {
...@@ -60,10 +61,17 @@ define(function (require) { ...@@ -60,10 +61,17 @@ define(function (require) {
}); });
var oldStep = forceInstance.step; var oldStep = forceInstance.step;
forceInstance.step = function (cb) { forceInstance.step = function (cb) {
for (var i = 0, l = nodes.length; i < l; i++) {
if (nodes[i].fixed) {
// Write back to layout instance
vec2.copy(nodes[i].p, graph.getNodeByIndex(i).getLayout());
}
}
oldStep(function (nodes, edges, stopped) { oldStep(function (nodes, edges, stopped) {
for (var i = 0, l = nodes.length; i < l; i++) { for (var i = 0, l = nodes.length; i < l; i++) {
var node = graph.getNodeByIndex(i); if (!nodes[i].fixed) {
node.setLayout(nodes[i].p); graph.getNodeByIndex(i).setLayout(nodes[i].p);
}
preservedPoints[nodeData.getId(i)] = nodes[i].p; preservedPoints[nodeData.getId(i)] = nodes[i].p;
} }
for (var i = 0, l = edges.length; i < l; i++) { for (var i = 0, l = edges.length; i < l; i++) {
...@@ -86,6 +94,9 @@ define(function (require) { ...@@ -86,6 +94,9 @@ define(function (require) {
}; };
graphSeries.forceLayout = forceInstance; graphSeries.forceLayout = forceInstance;
graphSeries.preservedPoints = preservedPoints; graphSeries.preservedPoints = preservedPoints;
// Step to get the layout
forceInstance.step();
} }
else { else {
// Remove prev injected forceLayout instance // Remove prev injected forceLayout instance
......
...@@ -29,6 +29,10 @@ define(function (require) { ...@@ -29,6 +29,10 @@ define(function (require) {
var symbolProto = Symbol.prototype; var symbolProto = Symbol.prototype;
function driftSymbol(dx, dy) {
this.parent.drift(dx, dy);
}
symbolProto._createSymbol = function (symbolType, data, idx) { symbolProto._createSymbol = function (symbolType, data, idx) {
// Remove paths created before // Remove paths created before
this.removeAll(); this.removeAll();
...@@ -47,6 +51,8 @@ define(function (require) { ...@@ -47,6 +51,8 @@ define(function (require) {
z2: 100, z2: 100,
scale: [0, 0] scale: [0, 0]
}); });
// Rewrite drift method
symbolPath.drift = driftSymbol;
var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
...@@ -99,6 +105,12 @@ define(function (require) { ...@@ -99,6 +105,12 @@ define(function (require) {
symbolPath.zlevel = zlevel; symbolPath.zlevel = zlevel;
symbolPath.z = z; symbolPath.z = z;
}; };
symbolProto.setDraggable = function (draggable) {
var symbolPath = this.childAt(0);
symbolPath.draggable = draggable;
symbolPath.cursor = draggable ? 'move' : 'pointer';
};
/** /**
* Update symbol properties * Update symbol properties
* @param {module:echarts/data/List} data * @param {module:echarts/data/List} data
......
...@@ -13,10 +13,14 @@ define(function (require) { ...@@ -13,10 +13,14 @@ define(function (require) {
} }
var linkNameList = []; var linkNameList = [];
var validEdges = [];
for (var i = 0; i < edges.length; i++) { for (var i = 0; i < edges.length; i++) {
var link = edges[i]; var link = edges[i];
linkNameList[i] = zrUtil.retrieve(link.id, link.source + ' - ' + link.target); // addEdge may fail when source or target not exists
graph.addEdge(link.source, link.target, i); if (graph.addEdge(link.source, link.target, i)) {
validEdges.push(link);
linkNameList.push(zrUtil.retrieve(link.id, link.source + ' - ' + link.target));
}
} }
// FIXME // FIXME
...@@ -26,11 +30,13 @@ define(function (require) { ...@@ -26,11 +30,13 @@ define(function (require) {
var edgeData = new List(['value'], hostModel); var edgeData = new List(['value'], hostModel);
nodeData.initData(nodes); nodeData.initData(nodes);
edgeData.initData(edges, linkNameList); edgeData.initData(validEdges, linkNameList);
graph.setEdgeData(edgeData); graph.setEdgeData(edgeData);
linkList.linkToGraph(nodeData, graph); linkList.linkToGraph(nodeData, graph);
// Update dataIndex of nodes and edges because invalid edge may be removed
graph.update();
return graph; return graph;
}; };
......
...@@ -81,8 +81,13 @@ define(function(require) { ...@@ -81,8 +81,13 @@ define(function(require) {
graphProto.addNode = function (id, dataIndex) { graphProto.addNode = function (id, dataIndex) {
var nodesMap = this._nodesMap; var nodesMap = this._nodesMap;
// Assign dataIndex as id if not exists
if (id == null) {
id = dataIndex;
}
if (nodesMap[id]) { if (nodesMap[id]) {
return nodesMap[id]; return;
} }
var node = new Node(id, dataIndex); var node = new Node(id, dataIndex);
...@@ -134,8 +139,9 @@ define(function(require) { ...@@ -134,8 +139,9 @@ define(function(require) {
} }
var key = n1.id + '-' + n2.id; var key = n1.id + '-' + n2.id;
// PENDING
if (edgesMap[key]) { if (edgesMap[key]) {
return edgesMap[key]; return;
} }
var edge = new Edge(n1, n2, dataIndex); var edge = new Edge(n1, n2, dataIndex);
......
...@@ -77,6 +77,9 @@ ...@@ -77,6 +77,9 @@
height: '25%', height: '25%',
force: { force: {
// initLayout: 'circular' // initLayout: 'circular'
// gravity: 0
repulsion: 100,
edgeLength: 5
}, },
edges: item.edges.map(function (e) { edges: item.edges.map(function (e) {
return { return {
......
...@@ -52,7 +52,6 @@ ...@@ -52,7 +52,6 @@
node.x = node.y = null; node.x = node.y = null;
}); });
chart.setOption({ chart.setOption({
tooltip: {},
legend: [{ legend: [{
// selectedMode: 'single', // selectedMode: 'single',
data: categories.map(function (a) { data: categories.map(function (a) {
...@@ -71,6 +70,7 @@ ...@@ -71,6 +70,7 @@
categories: categories, categories: categories,
animation: false, animation: false,
roam: true, roam: true,
draggable: true,
force: { force: {
repulsion: 100 repulsion: 100
}, },
......
<html>
<head>
<meta charset="utf-8">
<script src="esl.js"></script>
<script src="config.js"></script>
<script src="lib/jquery.min.js"></script>
<script src="lib/dat.gui.min.js"></script>
</head>
<body>
<style>
html, body, #main {
width: 100%;
height: 100%;
margin: 0;
}
</style>
<div id="main"></div>
<script>
require([
'echarts',
'echarts/parser/gexf',
'echarts/chart/graph',
'echarts/component/title',
'echarts/component/legend',
'echarts/component/geo',
'echarts/component/tooltip',
'echarts/component/dataRange'
], function (echarts) {
var chart = echarts.init(document.getElementById('main'), null, {
renderer: 'canvas'
});
var data = [];
var edges = [];
chart.setOption({
series: [{
type: 'graph',
layout: 'force',
animation: false,
data: data,
force: {
// initLayout: 'circular'
// gravity: 0
repulsion: 100,
edgeLength: 5
},
edges: edges
}]
});
setInterval(function () {
data.push({
id: data.length
});
var source = Math.round((data.length - 1) * Math.random());
var target = Math.round((data.length - 1) * Math.random());
if (source !== target) {
edges.push({
source: source,
target: target
});
}
chart.setOption({
series: [{
data: data,
edges: edges
}]
});
console.log('nodes: ' + data.length);
console.log('links: ' + data.length);
}, 500);
});
</script>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册