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

Symbol draggable, force enhancement

上级 6d4a5265
......@@ -130,8 +130,9 @@ define(function (require) {
symbol: 'circle',
symbolSize: 10,
// roam: false,
draggable: false,
roam: false,
roamDetail: {
x: 0,
y: 0,
......
......@@ -81,9 +81,30 @@ define(function (require) {
this._updateController(seriesModel, coordSys, api);
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) {
......
......@@ -55,15 +55,23 @@ define(function (require) {
// Formula in 'Graph Drawing by Force-directed Placement'
// var k = scale * Math.sqrt(width * height / nodes.length);
// var k2 = k * k;
var friction = 0.6;
return {
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) {
var v12 = [];
var nLen = nodes.length;
......@@ -77,18 +85,20 @@ define(function (require) {
var w = n2.w / (n1.w + n2.w);
vec2.normalize(v12, v12);
scaleAndAdd(n1.p, n1.p, v12, w * d * friction);
scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction);
!n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction);
!n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction);
}
// Gravity
// for (var i = 0; i < nLen; i++) {
// var n = nodes[i];
// vec2.sub(v12, center, n.p);
// var d = vec2.len(v12);
// vec2.scale(v12, v12, 1 / d);
// var gravityFactor = gravity;
// vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction);
// }
for (var i = 0; i < nLen; i++) {
var n = nodes[i];
if (!n.fixed) {
vec2.sub(v12, center, n.p);
// var d = vec2.len(v12);
// vec2.scale(v12, v12, 1 / d);
// var gravityFactor = gravity;
vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction);
}
}
// Repulsive
// PENDING
......@@ -104,22 +114,23 @@ define(function (require) {
d = 1;
}
var repFact = (n1.rep + n2.rep) / d / d;
scaleAndAdd(n1.pp, n1.pp, v12, repFact);
scaleAndAdd(n2.pp, n2.pp, v12, -repFact);
!n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact);
!n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact);
}
}
var v = [];
for (var i = 0; i < nLen; i++) {
var n = nodes[i];
vec2.sub(v, n.p, n.pp);
vec2.scaleAndAdd(n.p, n.p, v, friction);
vec2.copy(n.pp, n.p);
if (!n.fixed) {
vec2.sub(v, n.p, n.pp);
vec2.scaleAndAdd(n.p, n.p, v, friction);
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) {
var numberUtil = require('../../util/number');
var simpleLayoutHelper = require('./simpleLayoutHelper');
var circularLayoutHelper = require('./circularLayoutHelper');
var vec2 = require('zrender/core/vector');
return function (ecModel, api) {
ecModel.eachSeriesByType('graph', function (graphSeries) {
......@@ -60,10 +61,17 @@ define(function (require) {
});
var oldStep = forceInstance.step;
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) {
for (var i = 0, l = nodes.length; i < l; i++) {
var node = graph.getNodeByIndex(i);
node.setLayout(nodes[i].p);
if (!nodes[i].fixed) {
graph.getNodeByIndex(i).setLayout(nodes[i].p);
}
preservedPoints[nodeData.getId(i)] = nodes[i].p;
}
for (var i = 0, l = edges.length; i < l; i++) {
......@@ -86,6 +94,9 @@ define(function (require) {
};
graphSeries.forceLayout = forceInstance;
graphSeries.preservedPoints = preservedPoints;
// Step to get the layout
forceInstance.step();
}
else {
// Remove prev injected forceLayout instance
......
......@@ -29,6 +29,10 @@ define(function (require) {
var symbolProto = Symbol.prototype;
function driftSymbol(dx, dy) {
this.parent.drift(dx, dy);
}
symbolProto._createSymbol = function (symbolType, data, idx) {
// Remove paths created before
this.removeAll();
......@@ -47,6 +51,8 @@ define(function (require) {
z2: 100,
scale: [0, 0]
});
// Rewrite drift method
symbolPath.drift = driftSymbol;
var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
......@@ -99,6 +105,12 @@ define(function (require) {
symbolPath.zlevel = zlevel;
symbolPath.z = z;
};
symbolProto.setDraggable = function (draggable) {
var symbolPath = this.childAt(0);
symbolPath.draggable = draggable;
symbolPath.cursor = draggable ? 'move' : 'pointer';
};
/**
* Update symbol properties
* @param {module:echarts/data/List} data
......
......@@ -13,10 +13,14 @@ define(function (require) {
}
var linkNameList = [];
var validEdges = [];
for (var i = 0; i < edges.length; i++) {
var link = edges[i];
linkNameList[i] = zrUtil.retrieve(link.id, link.source + ' - ' + link.target);
graph.addEdge(link.source, link.target, i);
// addEdge may fail when source or target not exists
if (graph.addEdge(link.source, link.target, i)) {
validEdges.push(link);
linkNameList.push(zrUtil.retrieve(link.id, link.source + ' - ' + link.target));
}
}
// FIXME
......@@ -26,11 +30,13 @@ define(function (require) {
var edgeData = new List(['value'], hostModel);
nodeData.initData(nodes);
edgeData.initData(edges, linkNameList);
edgeData.initData(validEdges, linkNameList);
graph.setEdgeData(edgeData);
linkList.linkToGraph(nodeData, graph);
// Update dataIndex of nodes and edges because invalid edge may be removed
graph.update();
return graph;
};
......
......@@ -81,8 +81,13 @@ define(function(require) {
graphProto.addNode = function (id, dataIndex) {
var nodesMap = this._nodesMap;
// Assign dataIndex as id if not exists
if (id == null) {
id = dataIndex;
}
if (nodesMap[id]) {
return nodesMap[id];
return;
}
var node = new Node(id, dataIndex);
......@@ -134,8 +139,9 @@ define(function(require) {
}
var key = n1.id + '-' + n2.id;
// PENDING
if (edgesMap[key]) {
return edgesMap[key];
return;
}
var edge = new Edge(n1, n2, dataIndex);
......
......@@ -77,6 +77,9 @@
height: '25%',
force: {
// initLayout: 'circular'
// gravity: 0
repulsion: 100,
edgeLength: 5
},
edges: item.edges.map(function (e) {
return {
......
......@@ -52,7 +52,6 @@
node.x = node.y = null;
});
chart.setOption({
tooltip: {},
legend: [{
// selectedMode: 'single',
data: categories.map(function (a) {
......@@ -71,6 +70,7 @@
categories: categories,
animation: false,
roam: true,
draggable: true,
force: {
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.
先完成此消息的编辑!
想要评论请 注册