mirror of
https://gitee.com/antv/g6.git
synced 2024-12-05 05:09:07 +08:00
solve conflict while merging mdslayout
This commit is contained in:
commit
49227e9bde
162
demos/mds-layout.html
Normal file
162
demos/mds-layout.html
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mountNode"></div>
|
||||||
|
<script src="../build/g6.js"></script>
|
||||||
|
<script src="../build/mds.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
"nodes": [
|
||||||
|
{"id": "0", "label": "0"},
|
||||||
|
{"id": "1", "label": "1"},
|
||||||
|
{"id": "2", "label": "2"},
|
||||||
|
{"id": "3", "label": "3"},
|
||||||
|
{"id": "4", "label": "4"},
|
||||||
|
{"id": "5", "label": "5"},
|
||||||
|
{"id": "6", "label": "6"},
|
||||||
|
{"id": "7", "label": "7"},
|
||||||
|
{"id": "8", "label": "8"},
|
||||||
|
{"id": "9", "label": "9"},
|
||||||
|
{"id": "10", "label": "10"},
|
||||||
|
{"id": "11", "label": "11"},
|
||||||
|
{"id": "12", "label": "12"},
|
||||||
|
{"id": "13", "label": "13"},
|
||||||
|
{"id": "14", "label": "14"},
|
||||||
|
{"id": "15", "label": "15"},
|
||||||
|
{"id": "16", "label": "16"},
|
||||||
|
{"id": "17", "label": "17"},
|
||||||
|
{"id": "18", "label": "18"},
|
||||||
|
{"id": "19", "label": "19"},
|
||||||
|
{"id": "20", "label": "20"},
|
||||||
|
{"id": "21", "label": "21"},
|
||||||
|
{"id": "22", "label": "22"},
|
||||||
|
{"id": "23", "label": "23"},
|
||||||
|
{"id": "24", "label": "24"},
|
||||||
|
{"id": "25", "label": "25"},
|
||||||
|
{"id": "26", "label": "26"},
|
||||||
|
{"id": "27", "label": "27"},
|
||||||
|
{"id": "28", "label": "28"},
|
||||||
|
{"id": "29", "label": "29"},
|
||||||
|
{"id": "30", "label": "30"},
|
||||||
|
{"id": "31", "label": "31"},
|
||||||
|
{"id": "32", "label": "32"},
|
||||||
|
{"id": "33", "label": "33"}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{"source": "0", "target": "1"},
|
||||||
|
{"source": "0", "target": "2"},
|
||||||
|
{"source": "0", "target": "3"},
|
||||||
|
{"source": "0", "target": "4"},
|
||||||
|
{"source": "0", "target": "5"},
|
||||||
|
{"source": "0", "target": "7"},
|
||||||
|
{"source": "0", "target": "8"},
|
||||||
|
{"source": "0", "target": "9"},
|
||||||
|
{"source": "0", "target": "10"},
|
||||||
|
{"source": "0", "target": "11"},
|
||||||
|
{"source": "0", "target": "13"},
|
||||||
|
{"source": "0", "target": "14"},
|
||||||
|
{"source": "0", "target": "15"},
|
||||||
|
{"source": "0", "target": "16"},
|
||||||
|
{"source": "2", "target": "3"},
|
||||||
|
{"source": "4", "target": "5"},
|
||||||
|
{"source": "4", "target": "6"},
|
||||||
|
{"source": "5", "target": "6"},
|
||||||
|
{"source": "7", "target": "13"},
|
||||||
|
{"source": "8", "target": "14"},
|
||||||
|
{"source": "9", "target": "10"},
|
||||||
|
{"source": "10", "target": "22"},
|
||||||
|
{"source": "10", "target": "14"},
|
||||||
|
{"source": "10", "target": "12"},
|
||||||
|
{"source": "10", "target": "24"},
|
||||||
|
{"source": "10", "target": "21"},
|
||||||
|
{"source": "10", "target": "20"},
|
||||||
|
{"source": "11", "target": "24"},
|
||||||
|
{"source": "11", "target": "22"},
|
||||||
|
{"source": "11", "target": "14"},
|
||||||
|
{"source": "12", "target": "13"},
|
||||||
|
{"source": "16", "target": "17"},
|
||||||
|
{"source": "16", "target": "18"},
|
||||||
|
{"source": "16", "target": "21"},
|
||||||
|
{"source": "16", "target": "22"},
|
||||||
|
{"source": "17", "target": "18"},
|
||||||
|
{"source": "17", "target": "20"},
|
||||||
|
{"source": "18", "target": "19"},
|
||||||
|
{"source": "19", "target": "20"},
|
||||||
|
{"source": "19", "target": "33"},
|
||||||
|
{"source": "19", "target": "22"},
|
||||||
|
{"source": "19", "target": "23"},
|
||||||
|
{"source": "20", "target": "21"},
|
||||||
|
{"source": "21", "target": "22"},
|
||||||
|
{"source": "22", "target": "24"},
|
||||||
|
{"source": "22", "target": "25"},
|
||||||
|
{"source": "22", "target": "26"},
|
||||||
|
{"source": "22", "target": "23"},
|
||||||
|
{"source": "22", "target": "28"},
|
||||||
|
{"source": "22", "target": "30"},
|
||||||
|
{"source": "22", "target": "31"},
|
||||||
|
{"source": "22", "target": "32"},
|
||||||
|
{"source": "22", "target": "33"},
|
||||||
|
{"source": "23", "target": "28"},
|
||||||
|
{"source": "23", "target": "27"},
|
||||||
|
{"source": "23", "target": "29"},
|
||||||
|
{"source": "23", "target": "30"},
|
||||||
|
{"source": "23", "target": "31"},
|
||||||
|
{"source": "23", "target": "33"},
|
||||||
|
{"source": "32", "target": "33"}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
data.edges.forEach((edge, i) => {
|
||||||
|
edge.id = i;
|
||||||
|
});
|
||||||
|
const MDSPlugin = new Mds({
|
||||||
|
center: [ 500, 300 ],
|
||||||
|
linkDistance: 100
|
||||||
|
});
|
||||||
|
const graph = new G6.Graph({
|
||||||
|
container: 'mountNode',
|
||||||
|
width: 1000,
|
||||||
|
height: 600,
|
||||||
|
plugins: [ MDSPlugin ],
|
||||||
|
modes: { default: [ 'drag-node' ] },
|
||||||
|
defaultNode: {
|
||||||
|
size: [20, 20],
|
||||||
|
color: 'steelblue'
|
||||||
|
},
|
||||||
|
defaultEdge: {
|
||||||
|
size: 1,
|
||||||
|
color: '#e2e2e2'
|
||||||
|
},
|
||||||
|
nodeStyle: {
|
||||||
|
default: {
|
||||||
|
lineWidth: 2,
|
||||||
|
fill: '#fff'
|
||||||
|
},
|
||||||
|
selected: {
|
||||||
|
fill: 'steelblue'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edgeStyle: {
|
||||||
|
default: {
|
||||||
|
endArrow: {
|
||||||
|
path: 'M 4,0 L -4,-4 L -4,4 Z',
|
||||||
|
d: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
graph.data({ nodes: data.nodes, edges: data.edges.map((edge, i) => {
|
||||||
|
edge.id = 'edge' + i;
|
||||||
|
return Object.assign({}, edge);
|
||||||
|
}) });
|
||||||
|
MDSPlugin.layout(data);
|
||||||
|
graph.render();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -4,6 +4,7 @@ const G6Plugins = {
|
|||||||
Force: require('./force'),
|
Force: require('./force'),
|
||||||
Radial: require('./radial'),
|
Radial: require('./radial'),
|
||||||
Fruchterman: require('./fruchterman'),
|
Fruchterman: require('./fruchterman'),
|
||||||
|
Mds: require('./mds'),
|
||||||
Dagre: require('./dagre'),
|
Dagre: require('./dagre'),
|
||||||
Menu: require('./menu')
|
Menu: require('./menu')
|
||||||
};
|
};
|
||||||
|
135
plugins/mds/index.js
Normal file
135
plugins/mds/index.js
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
const Base = require('../base');
|
||||||
|
const Util = require('@antv/g6').Util;
|
||||||
|
const Numeric = require('numericjs');
|
||||||
|
|
||||||
|
class Mds extends Base {
|
||||||
|
getDefaultCfgs() {
|
||||||
|
return {
|
||||||
|
maxIteration: null, // 停止迭代的最大迭代数
|
||||||
|
center: [ 0, 0 ], // 布局中心
|
||||||
|
linkDistance: 50, // 默认边长度
|
||||||
|
onLayoutEnd() {}, // 布局完成回调
|
||||||
|
onTick() {} // 每一迭代布局回调
|
||||||
|
};
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
const graph = this.get('graph');
|
||||||
|
const onTick = this.get('onTick');
|
||||||
|
const tick = () => {
|
||||||
|
onTick && onTick();
|
||||||
|
graph.refreshPositions();
|
||||||
|
};
|
||||||
|
this.set('tick', tick);
|
||||||
|
}
|
||||||
|
layout(data) {
|
||||||
|
const self = this;
|
||||||
|
self.set('data', data);
|
||||||
|
const graph = self.get('graph');
|
||||||
|
const center = self.get('center');
|
||||||
|
const nodes = data.nodes;
|
||||||
|
if (nodes.length === 0) return;
|
||||||
|
else if (nodes.length === 1) {
|
||||||
|
nodes[0].x = center[0];
|
||||||
|
nodes[0].y = center[1];
|
||||||
|
}
|
||||||
|
const linkDistance = self.get('linkDistance');
|
||||||
|
|
||||||
|
// 如果正在布局,忽略布局请求
|
||||||
|
if (self.isTicking()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// layout
|
||||||
|
let focusNode = self.get('focusNode');
|
||||||
|
if (Util.isString(focusNode)) {
|
||||||
|
let found = false;
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
if (nodes[i].id === focusNode) {
|
||||||
|
focusNode = nodes[i];
|
||||||
|
self.set('focusNode', focusNode);
|
||||||
|
found = true;
|
||||||
|
i = nodes.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) focusNode = null;
|
||||||
|
}
|
||||||
|
// default focus node
|
||||||
|
if (!focusNode) {
|
||||||
|
focusNode = nodes[0];
|
||||||
|
if (!focusNode) return;
|
||||||
|
self.set('focusNode', focusNode);
|
||||||
|
}
|
||||||
|
// the graph-theoretic distance (shortest path distance) matrix
|
||||||
|
const adjMatrix = Util.getAdjMatrix(data, false);
|
||||||
|
const D = Util.floydWarshall(adjMatrix);
|
||||||
|
self.set('distances', D);
|
||||||
|
|
||||||
|
// scale the ideal edge length acoording to linkDistance
|
||||||
|
const scaledD = Util.scaleMatrix(D, linkDistance);
|
||||||
|
self.set('scaledDistances', scaledD);
|
||||||
|
|
||||||
|
// get positions by MDS
|
||||||
|
const positions = self.runMDS();
|
||||||
|
self.set('positions', positions);
|
||||||
|
positions.forEach((p, i) => {
|
||||||
|
nodes[i].x = p[0] + center[0];
|
||||||
|
nodes[i].y = p[1] + center[1];
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.refreshPositions();
|
||||||
|
const onLayoutEnd = self.get('onLayoutEnd');
|
||||||
|
onLayoutEnd();
|
||||||
|
}
|
||||||
|
updateLayout(cfg) {
|
||||||
|
const self = this;
|
||||||
|
const data = cfg.data;
|
||||||
|
if (data) {
|
||||||
|
self.set('data', data);
|
||||||
|
}
|
||||||
|
if (self.get('ticking')) {
|
||||||
|
// stop layout
|
||||||
|
self.set('ticking', false);
|
||||||
|
}
|
||||||
|
Object.keys(cfg).forEach(key => {
|
||||||
|
self.set(key, cfg[key]);
|
||||||
|
});
|
||||||
|
self.layout(data);
|
||||||
|
}
|
||||||
|
runMDS() {
|
||||||
|
const self = this;
|
||||||
|
const dimension = 2;
|
||||||
|
const distances = self.get('scaledDistances');
|
||||||
|
|
||||||
|
// square distances
|
||||||
|
const M = Numeric.mul(-0.5, Numeric.pow(distances, 2));
|
||||||
|
|
||||||
|
// double centre the rows/columns
|
||||||
|
function mean(A) { return Numeric.div(Numeric.add.apply(null, A), A.length); }
|
||||||
|
const rowMeans = mean(M),
|
||||||
|
colMeans = mean(Numeric.transpose(M)),
|
||||||
|
totalMean = mean(rowMeans);
|
||||||
|
|
||||||
|
for (let i = 0; i < M.length; ++i) {
|
||||||
|
for (let j = 0; j < M[0].length; ++j) {
|
||||||
|
M[i][j] += totalMean - rowMeans[i] - colMeans[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// take the SVD of the double centred matrix, and return the
|
||||||
|
// points from it
|
||||||
|
const ret = Numeric.svd(M);
|
||||||
|
const eigenValues = Numeric.sqrt(ret.S);
|
||||||
|
return ret.U.map(function(row) {
|
||||||
|
return Numeric.mul(row, eigenValues).splice(0, dimension);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isTicking() {
|
||||||
|
return this.get('ticking');
|
||||||
|
}
|
||||||
|
destroy() {
|
||||||
|
if (this.get('ticking')) {
|
||||||
|
this.getSimulation().stop();
|
||||||
|
}
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = Mds;
|
Loading…
Reference in New Issue
Block a user