mirror of
https://gitee.com/antv/g6.git
synced 2024-12-05 21:28:33 +08:00
refactor: isolate the calculated data from original data model
This commit is contained in:
parent
2d5e3bac1a
commit
206cc0a492
@ -11,6 +11,7 @@
|
||||
<script src="../build/plugin.tool.mapper.js"></script>
|
||||
<script src="../build/plugin.tool.fisheye.js"></script>
|
||||
<script src="../build/plugin.util.extractSubgraph.js"></script>
|
||||
<!-- <script src="../build/plugin.util.maxSpanningForest.js"></script> -->
|
||||
<script src="../build/plugin.tool.highlightSubgraph.js"></script>
|
||||
<script src="../build/plugin.edge.quadraticCurve.js"></script>
|
||||
<script src="../build/plugin.tool.minimap.js"></script>
|
||||
@ -94,10 +95,13 @@
|
||||
const highlighter = new Highlighter();
|
||||
const textDisplay = new G6.Plugins['tool.textDisplay']();
|
||||
|
||||
const Util = G6.Util;
|
||||
|
||||
graph = new G6.Graph({
|
||||
id: 'mountNode', // dom id
|
||||
fitView: 'cc',
|
||||
plugins: [new Plugin({
|
||||
plugins: [
|
||||
new Plugin({
|
||||
max_iteration: 600,
|
||||
kg: 10,
|
||||
prev_overlapping: true
|
||||
@ -135,6 +139,7 @@
|
||||
}
|
||||
});
|
||||
graph.read(data);
|
||||
// Util.maxSpanningForest(graph, {});
|
||||
|
||||
const edges = graph.getEdges();
|
||||
for (let i = 0; i < edges.length; i += 1) {
|
||||
@ -190,7 +195,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
const Util = G6.Util;
|
||||
|
||||
function clickMenu(ev) {
|
||||
let type = 'in';
|
||||
|
@ -1,201 +0,0 @@
|
||||
// console.log('aa');
|
||||
// onmessage = function(event) {
|
||||
// const {
|
||||
// nodes,
|
||||
// edges,
|
||||
// kr,
|
||||
// kg,
|
||||
// mode,
|
||||
// prev_overlapping,
|
||||
// dissuade_hubs,
|
||||
// barnes_hut,
|
||||
// ks,
|
||||
// ksmax,
|
||||
// tao,
|
||||
// center,
|
||||
// widths
|
||||
// } = event.data.params;
|
||||
// let {
|
||||
// pre_Forces,
|
||||
// Forces,
|
||||
// iter,
|
||||
// prevo_iter,
|
||||
// kr_prime,
|
||||
// start,
|
||||
// end,
|
||||
// estart,
|
||||
// eend
|
||||
// } = event.data;
|
||||
|
||||
// const size = nodes.length;
|
||||
// const esize = edges.length;
|
||||
|
||||
// for (let i = start; i < end; i += 1) {
|
||||
// pre_Forces[2 * i] = Forces[2 * i];
|
||||
// pre_Forces[2 * i + 1] = Forces[2 * i + 1];
|
||||
// Forces[2 * i] = 0;
|
||||
// Forces[2 * i + 1] = 0;
|
||||
// }
|
||||
// // // attractive forces, existing on every actual edge
|
||||
// Forces = getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, estart, eend);
|
||||
// // // // repulsive forces and Gravity, existing on every node pair
|
||||
// // // // if prev_overlapping, using the no-optimized method in the last prevo_iter instead.
|
||||
// // if (barnes_hut && ((prev_overlapping && iter > prevo_iter) || !prev_overlapping)) {
|
||||
// // Forces = getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies);
|
||||
// // } else {
|
||||
// Forces = getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, start, end);
|
||||
// // }
|
||||
// // // update the positions
|
||||
// // const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
|
||||
// // nodes = res[0];
|
||||
// // SG = res[1];
|
||||
// // iter -= 1;
|
||||
// };
|
||||
|
||||
|
||||
// function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, estart, eend) {
|
||||
// for (let i = estart; i < eend; i += 1) {
|
||||
// // const source_node = graph.find(edges[i].source).getModel();
|
||||
// // const target_node = graph.find(edges[i].target).getModel();
|
||||
// let source_node;
|
||||
// let target_node;
|
||||
// let source_idx;
|
||||
// let target_idx;
|
||||
// for (let j = 0; j < size; j += 1) {
|
||||
// if (nodes[j].id === edges[i].source) {
|
||||
// source_node = nodes[j];
|
||||
// source_idx = j;
|
||||
// } else if (nodes[j].id === edges[i].target) {
|
||||
// target_node = nodes[j];
|
||||
// target_idx = j;
|
||||
// }
|
||||
// }
|
||||
// let dir = [ target_node.x - source_node.x, target_node.y - source_node.y ];
|
||||
// let eucli_dis = Math.hypot(dir[0], dir[1]);
|
||||
// eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
||||
// dir[0] = dir[0] / eucli_dis;
|
||||
// dir[1] = dir[1] / eucli_dis;
|
||||
// // the force
|
||||
// if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[source_idx] - widths[target_idx];
|
||||
// let Fa1 = eucli_dis;
|
||||
// let Fa2 = Fa1;
|
||||
// if (mode === 'linlog') {
|
||||
// Fa1 = Math.log(1 + eucli_dis);
|
||||
// Fa2 = Fa1;
|
||||
// }
|
||||
// if (dissuade_hubs) {
|
||||
// Fa1 = eucli_dis / source_node.degree;
|
||||
// Fa2 = eucli_dis / target_node.degree;
|
||||
// }
|
||||
// if (prev_overlapping && iter < prevo_iter && eucli_dis <= 0) {
|
||||
// Fa1 = 0;
|
||||
// Fa2 = 0;
|
||||
// } else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) {
|
||||
// Fa1 = eucli_dis;
|
||||
// Fa2 = eucli_dis;
|
||||
// }
|
||||
// Forces[2 * source_node.index] += Fa1 * dir[0];
|
||||
// Forces[2 * target_node.index] -= Fa2 * dir[0];
|
||||
// Forces[2 * source_node.index + 1] += Fa1 * dir[1];
|
||||
// Forces[2 * target_node.index + 1] -= Fa2 * dir[1];
|
||||
// dir = null;
|
||||
// }
|
||||
// return Forces;
|
||||
// }
|
||||
|
||||
// function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, start, end) {
|
||||
// for (let i = start; i < end; i += 1) {
|
||||
// for (let j = i + 1; j < size; j += 1) {
|
||||
// let dir = [nodes[j].x - nodes[i].x, nodes[j].y - nodes[i].y];
|
||||
// let eucli_dis = Math.hypot(dir[0], dir[1]);
|
||||
// eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
||||
// dir[0] = dir[0] / eucli_dis;
|
||||
// dir[1] = dir[1] / eucli_dis;
|
||||
|
||||
// if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[i] - widths[j];
|
||||
|
||||
// let Fr = kr * (nodes[i].degree + 1) * (nodes[j].degree + 1) / eucli_dis;
|
||||
|
||||
// if (prev_overlapping && iter < prevo_iter && eucli_dis < 0) {
|
||||
// Fr = kr_prime * (nodes[i].degree + 1) * (nodes[j].degree + 1);
|
||||
// } else if (prev_overlapping && iter < prevo_iter && eucli_dis === 0) {
|
||||
// Fr = 0;
|
||||
// } else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) {
|
||||
// Fr = kr * (nodes[i].degree + 1) * (nodes[j].degree + 1) / eucli_dis;
|
||||
// }
|
||||
// Forces[2 * i] -= Fr * dir[0];
|
||||
// Forces[2 * j] += Fr * dir[0];
|
||||
// Forces[2 * i + 1] -= Fr * dir[1];
|
||||
// Forces[2 * j + 1] += Fr * dir[1];
|
||||
// dir = null;
|
||||
// }
|
||||
|
||||
// // gravity
|
||||
// let dir = [nodes[i].x - center.x, nodes[i].y - center.y];
|
||||
// const eucli_dis = Math.hypot(dir[0], dir[1]);
|
||||
// dir[0] = dir[0] / eucli_dis;
|
||||
// dir[1] = dir[1] / eucli_dis;
|
||||
// const Fg = kg * (nodes[i].degree + 1);
|
||||
// Forces[2 * i] -= Fg * dir[0];
|
||||
// Forces[2 * i + 1] -= Fg * dir[1];
|
||||
// dir = null;
|
||||
// }
|
||||
// return Forces;
|
||||
// }
|
||||
|
||||
// // function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, ct, bodies) {
|
||||
// // let minx = 9e10,
|
||||
// // maxx = -9e10,
|
||||
// // miny = 9e10,
|
||||
// // maxy = -9e10;
|
||||
// // for (let i = 0; i < size; i += 1) {
|
||||
// // bodies[i].setPos(nodes[i].x, nodes[i].y);
|
||||
// // if (nodes[i].x >= maxx) maxx = nodes[i].x;
|
||||
// // if (nodes[i].x <= minx) minx = nodes[i].x;
|
||||
// // if (nodes[i].y >= maxy) maxy = nodes[i].y;
|
||||
// // if (nodes[i].y <= miny) miny = nodes[i].y;
|
||||
// // }
|
||||
|
||||
// // let width = Math.max(maxx - minx, maxy - miny);
|
||||
|
||||
// // let quad_params = {
|
||||
// // xmid: (maxx + minx) / 2,
|
||||
// // ymid: (maxy + miny) / 2,
|
||||
// // length: width,
|
||||
// // mass_center: ct,
|
||||
// // mass: size
|
||||
// // };
|
||||
// // let quad = new Quad(quad_params);
|
||||
// // let quad_tree = new QuadTree(quad);
|
||||
|
||||
// // // build the tree, insert the nodes(quads) into the tree
|
||||
// // for (let i = 0; i < size; i += 1) {
|
||||
// // if (bodies[i].in(quad)) quad_tree.insert(bodies[i]);
|
||||
// // }
|
||||
// // // update the repulsive forces and the gravity.
|
||||
// // for (let i = 0; i < size; i += 1) {
|
||||
// // bodies[i].resetForce();
|
||||
// // quad_tree.updateForce(bodies[i]);
|
||||
// // Forces[2 * i] -= bodies[i].fx;
|
||||
// // Forces[2 * i + 1] -= bodies[i].fy;
|
||||
|
||||
// // // gravity
|
||||
// // let dir = [nodes[i].x - ct.x, nodes[i].y - ct.y];
|
||||
// // let eucli_dis = Math.hypot(dir[0], dir[1]);
|
||||
// // eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
||||
// // dir[0] = dir[0] / eucli_dis;
|
||||
// // dir[1] = dir[1] / eucli_dis;
|
||||
// // let Fg = kg * (nodes[i].degree + 1);
|
||||
// // Forces[2 * i] -= Fg * dir[0];
|
||||
// // Forces[2 * i + 1] -= Fg * dir[1];
|
||||
|
||||
// // eucli_dis = null;
|
||||
// // Fg = null;
|
||||
// // dir = null;
|
||||
// // }
|
||||
// // quad_params = null;
|
||||
// // quad = null;
|
||||
// // quad_tree = null;
|
||||
// // width = null;
|
||||
// // return Forces;
|
||||
// // }
|
@ -24,9 +24,14 @@ onmessage = function(event) {
|
||||
|
||||
let SG = 0;
|
||||
const bodies = [];
|
||||
const degrees = [];
|
||||
const idmap = {};
|
||||
|
||||
for (let i = 0; i < size; i += 1) {
|
||||
nodes[i].index = i;
|
||||
nodes[i].degree = 0;
|
||||
idmap[nodes[i].id] = i;
|
||||
degrees[i] = 0;
|
||||
// nodes[i].index = i;
|
||||
// nodes[i].degree = 0;
|
||||
nodes[i].x = Math.random() * 1000;
|
||||
nodes[i].y = Math.random() * 1000;
|
||||
}
|
||||
@ -42,8 +47,10 @@ onmessage = function(event) {
|
||||
}
|
||||
// // const node1 = graph.find(edges[i].source).getModel();
|
||||
// // const node2 = graph.find(edges[i].target).getModel();
|
||||
nodes[node1.index].degree += 1;
|
||||
nodes[node2.index].degree += 1;
|
||||
// nodes[node1.index].degree += 1;
|
||||
// nodes[node2.index].degree += 1;
|
||||
degrees[idmap[node1.id]] += 1;
|
||||
degrees[idmap[node2.id]] += 1;
|
||||
}
|
||||
|
||||
const kr_prime = 100;
|
||||
@ -62,7 +69,7 @@ onmessage = function(event) {
|
||||
ry: nodes[i].y,
|
||||
mass: 1,
|
||||
G: kr,
|
||||
degree: nodes[i].degree
|
||||
degree: degrees[i]
|
||||
};
|
||||
bodies[i] = new Body(params);
|
||||
params = null;
|
||||
@ -77,16 +84,16 @@ onmessage = function(event) {
|
||||
Forces[2 * i + 1] = 0;
|
||||
}
|
||||
// // attractive forces, existing on every actual edge
|
||||
Forces = getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths);
|
||||
Forces = getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, idmap, degrees);
|
||||
// // // repulsive forces and Gravity, existing on every node pair
|
||||
// // // if prev_overlapping, using the no-optimized method in the last prevo_iter instead.
|
||||
if (barnes_hut && ((prev_overlapping && iter > prevo_iter) || !prev_overlapping)) {
|
||||
Forces = getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies);
|
||||
Forces = getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies, degrees);
|
||||
} else {
|
||||
Forces = getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths);
|
||||
Forces = getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, degrees);
|
||||
}
|
||||
// // update the positions
|
||||
const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
|
||||
const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees);
|
||||
nodes = res[0];
|
||||
SG = res[1];
|
||||
iter -= 1;
|
||||
@ -94,7 +101,7 @@ onmessage = function(event) {
|
||||
self.postMessage(nodes);
|
||||
};
|
||||
|
||||
function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths) {
|
||||
function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths, idmap, degrees) {
|
||||
for (let i = 0; i < esize; i += 1) {
|
||||
// const source_node = graph.find(edges[i].source).getModel();
|
||||
// const target_node = graph.find(edges[i].target).getModel();
|
||||
@ -125,8 +132,10 @@ function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hub
|
||||
Fa2 = Fa1;
|
||||
}
|
||||
if (dissuade_hubs) {
|
||||
Fa1 = eucli_dis / source_node.degree;
|
||||
Fa2 = eucli_dis / target_node.degree;
|
||||
// Fa1 = eucli_dis / source_node.degree;
|
||||
// Fa2 = eucli_dis / target_node.degree;
|
||||
Fa1 = eucli_dis / degrees[source_idx];
|
||||
Fa2 = eucli_dis / degrees[target_idx];
|
||||
}
|
||||
if (prev_overlapping && iter < prevo_iter && eucli_dis <= 0) {
|
||||
Fa1 = 0;
|
||||
@ -135,16 +144,20 @@ function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hub
|
||||
Fa1 = eucli_dis;
|
||||
Fa2 = eucli_dis;
|
||||
}
|
||||
Forces[2 * source_node.index] += Fa1 * dir[0];
|
||||
Forces[2 * target_node.index] -= Fa2 * dir[0];
|
||||
Forces[2 * source_node.index + 1] += Fa1 * dir[1];
|
||||
Forces[2 * target_node.index + 1] -= Fa2 * dir[1];
|
||||
// Forces[2 * source_node.index] += Fa1 * dir[0];
|
||||
// Forces[2 * target_node.index] -= Fa2 * dir[0];
|
||||
// Forces[2 * source_node.index + 1] += Fa1 * dir[1];
|
||||
// Forces[2 * target_node.index + 1] -= Fa2 * dir[1];
|
||||
Forces[2 * idmap[source_node.id]] += Fa1 * dir[0];
|
||||
Forces[2 * idmap[target_node.id]] -= Fa2 * dir[0];
|
||||
Forces[2 * idmap[source_node.id] + 1] += Fa1 * dir[1];
|
||||
Forces[2 * idmap[target_node.id] + 1] -= Fa2 * dir[1];
|
||||
dir = null;
|
||||
}
|
||||
return Forces;
|
||||
}
|
||||
|
||||
function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths) {
|
||||
function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths, degrees) {
|
||||
for (let i = 0; i < size; i += 1) {
|
||||
for (let j = i + 1; j < size; j += 1) {
|
||||
let dir = [ nodes[j].x - nodes[i].x, nodes[j].y - nodes[i].y ];
|
||||
@ -155,14 +168,14 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h
|
||||
|
||||
if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - widths[i] - widths[j];
|
||||
|
||||
let Fr = kr * (nodes[i].degree + 1) * (nodes[j].degree + 1) / eucli_dis;
|
||||
let Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucli_dis;
|
||||
|
||||
if (prev_overlapping && iter < prevo_iter && eucli_dis < 0) {
|
||||
Fr = kr_prime * (nodes[i].degree + 1) * (nodes[j].degree + 1);
|
||||
Fr = kr_prime * (degrees[i] + 1) * (degrees[j] + 1);
|
||||
} else if (prev_overlapping && iter < prevo_iter && eucli_dis === 0) {
|
||||
Fr = 0;
|
||||
} else if (prev_overlapping && iter < prevo_iter && eucli_dis > 0) {
|
||||
Fr = kr * (nodes[i].degree + 1) * (nodes[j].degree + 1) / eucli_dis;
|
||||
Fr = kr * (degrees[i] + 1) * (degrees[j] + 1) / eucli_dis;
|
||||
}
|
||||
Forces[2 * i] -= Fr * dir[0];
|
||||
Forces[2 * j] += Fr * dir[0];
|
||||
@ -176,7 +189,7 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h
|
||||
const eucli_dis = Math.hypot(dir[0], dir[1]);
|
||||
dir[0] = dir[0] / eucli_dis;
|
||||
dir[1] = dir[1] / eucli_dis;
|
||||
const Fg = kg * (nodes[i].degree + 1);
|
||||
const Fg = kg * (degrees[i] + 1);
|
||||
Forces[2 * i] -= Fg * dir[0];
|
||||
Forces[2 * i + 1] -= Fg * dir[1];
|
||||
dir = null;
|
||||
@ -184,7 +197,7 @@ function getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_h
|
||||
return Forces;
|
||||
}
|
||||
|
||||
function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, ct, bodies) {
|
||||
function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, ct, bodies, degrees) {
|
||||
let minx = 9e10,
|
||||
maxx = -9e10,
|
||||
miny = 9e10,
|
||||
@ -226,7 +239,7 @@ function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuad
|
||||
eucli_dis = eucli_dis < 0.0001 ? 0.0001 : eucli_dis;
|
||||
dir[0] = dir[0] / eucli_dis;
|
||||
dir[1] = dir[1] / eucli_dis;
|
||||
let Fg = kg * (nodes[i].degree + 1);
|
||||
let Fg = kg * (degrees[i] + 1);
|
||||
Forces[2 * i] -= Fg * dir[0];
|
||||
Forces[2 * i + 1] -= Fg * dir[1];
|
||||
|
||||
@ -241,7 +254,7 @@ function getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuad
|
||||
return Forces;
|
||||
}
|
||||
|
||||
function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao) {
|
||||
function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao, degrees) {
|
||||
let swgns = [];
|
||||
let trans = [];
|
||||
// swg(G) and tra(G)
|
||||
@ -260,8 +273,8 @@ function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao) {
|
||||
swgns[i] = minus_norm;
|
||||
trans[i] = add_norm / 2;
|
||||
|
||||
swgG += (nodes[i].degree + 1) * swgns[i];
|
||||
traG += (nodes[i].degree + 1) * trans[i];
|
||||
swgG += (degrees[i] + 1) * swgns[i];
|
||||
traG += (degrees[i] + 1) * trans[i];
|
||||
}
|
||||
|
||||
let pre_SG = SG;
|
||||
|
@ -1,7 +1,9 @@
|
||||
## Fisheye
|
||||
|
||||
Fisheye is a magnifying lens for graph exploration. We inplement 'Graphical Fisheye Views' with polar coordinate system: ftp://ftp.cs.brown.edu/pub/techreports/93/cs93-40.pdf
|
||||
|
||||
params:
|
||||
- radius: the radius of the fisheye lens. Default: 200.
|
||||
- d: the magnification factor of the fisheye lens. Default: 1.
|
||||
|
||||
## use
|
||||
|
||||
|
@ -2,6 +2,15 @@
|
||||
|
||||
Highlight a subgraph and weaken the rest of the graph.
|
||||
|
||||
interface:
|
||||
- highlightSubgraph(hl_items)
|
||||
hightlight a subgraph
|
||||
params:
|
||||
- hl_items: the items which will be highlighted
|
||||
|
||||
- restoreGraph()
|
||||
restore the graph to the un-lighlighted style.
|
||||
|
||||
## use
|
||||
|
||||
simple use.
|
||||
|
@ -11,7 +11,6 @@ const Size = require('@antv/g2/src/component/legend/size');
|
||||
const Attr = require('@antv/attr');
|
||||
const Util = G6.Util;
|
||||
const Scale = require('@antv/scale');
|
||||
|
||||
class Plugin {
|
||||
constructor(itemType, dim, channel, range, otherCfg) {
|
||||
Util.mix(this, {
|
||||
|
63
plugins/util.maxSpanningTree/index.js
Normal file
63
plugins/util.maxSpanningTree/index.js
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @fileOverview 图分析模版
|
||||
* @author huangtonger@aliyun.com
|
||||
* 保留字段:
|
||||
* node.vx, node.vy, node.x, node.y
|
||||
* node.to, node.from
|
||||
* node.visited, node.edges, node.links
|
||||
* edge.isTreeEdge、edge.lineWidth
|
||||
* 可配置字段:
|
||||
* node.rank 分层权重
|
||||
* node.label 节点标签
|
||||
*/
|
||||
const G6 = require('@antv/g6');
|
||||
const Forest = require('./maxSpanningForest');
|
||||
const Layout = require('../layout.forceAtlas2/layout');
|
||||
const Util = G6.Util;
|
||||
|
||||
const maxSpanningForest = {
|
||||
span(graph, layoutCfg) {
|
||||
// class Plugin {
|
||||
// constructor(options) {
|
||||
|
||||
const layout = {
|
||||
auto: 'once', // true false once
|
||||
processer: new Layout({
|
||||
kr: 50,
|
||||
kg: 8.0,
|
||||
mode: 'common',
|
||||
prev_overlapping: true,
|
||||
dissuade_hubs: false,
|
||||
max_iteration: 1000,
|
||||
barnes_hut: true,
|
||||
ks: 0.1,
|
||||
ksmax: 10,
|
||||
tao: 0.1,
|
||||
...layoutCfg
|
||||
})
|
||||
};
|
||||
|
||||
graph.on('beforeinit', () => {
|
||||
const graph_layout = graph.get('layout');
|
||||
if (!graph_layout) {
|
||||
graph.set('layout', layout);
|
||||
}
|
||||
});
|
||||
graph.on('beforerender', () => {
|
||||
const data = graph.getSource();
|
||||
const {
|
||||
nodes,
|
||||
edges
|
||||
} = data;
|
||||
const forest = Forest(nodes, edges);
|
||||
forest.edges.forEach(edge => {
|
||||
edge.isTreeEdge = true;
|
||||
});
|
||||
graph.addFilter(item => {
|
||||
return !item.isEdge || item.getModel().isTreeEdge;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Util.mix(Util, maxSpanningForest);
|
112
plugins/util.maxSpanningTree/maxSpanningForest.js
Normal file
112
plugins/util.maxSpanningTree/maxSpanningForest.js
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @fileOverview 最大生成森林
|
||||
* @author huangtonger@aliyun.com
|
||||
*/
|
||||
const G6 = require('@antv/g6');
|
||||
const { Util } = G6;
|
||||
const maxSpanningTree = require('./maxSpanningTree');
|
||||
|
||||
function maxSpanningForest(nodes, edges) {
|
||||
const connectedSubsets = [];
|
||||
const forest = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
maxRankNode: null
|
||||
};
|
||||
const nodeMap = {};
|
||||
let maxRank = -Infinity;
|
||||
Util.each(nodes, node => {
|
||||
node.links = [];
|
||||
node.edges = [];
|
||||
nodeMap[node.id] = node;
|
||||
if (Util.isNil(node.weight)) {
|
||||
node.weight = 1;
|
||||
}
|
||||
if (Util.isNil(node.rank)) {
|
||||
if (node.weight) {
|
||||
node.rank = node.weight;
|
||||
} else {
|
||||
node.rank = 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
Util.each(edges, (edge, i) => {
|
||||
const source = nodeMap[edge.source];
|
||||
const target = nodeMap[edge.target];
|
||||
source.links.push(nodes.indexOf(target));
|
||||
target.links.push(nodes.indexOf(source));
|
||||
source.edges.push(i);
|
||||
target.edges.push(i);
|
||||
if (Util.isNil(edge.weight)) {
|
||||
edge.weight = 1;
|
||||
}
|
||||
});
|
||||
Util.each(nodes, (node, i) => {
|
||||
if (!node.visited) {
|
||||
connectedSubsets.push(getConnectedSubset(i, nodes, edges));
|
||||
}
|
||||
});
|
||||
Util.each(nodes, node => {
|
||||
delete node.links;
|
||||
delete node.edges;
|
||||
delete node.visited;
|
||||
});
|
||||
Util.each(connectedSubsets, connectedSubset => {
|
||||
const tree = maxSpanningTree(connectedSubset);
|
||||
const root = connectedSubset.root;
|
||||
forest.nodes = forest.nodes.concat(tree.nodes);
|
||||
forest.edges = forest.edges.concat(tree.edges);
|
||||
if (root.rank > maxRank) {
|
||||
maxRank = root.rank;
|
||||
forest.maxRankNode = root;
|
||||
}
|
||||
});
|
||||
return forest;
|
||||
}
|
||||
// 获取连通子集
|
||||
function getConnectedSubset(start, nodes, edges) {
|
||||
const connectedSubset = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
root: null
|
||||
};
|
||||
const edgeMap = {};
|
||||
const subEdges = [];
|
||||
let maxRank = -Infinity;
|
||||
dfs(start, nodes, index => {
|
||||
const node = nodes[index];
|
||||
if (node.rank > maxRank) {
|
||||
maxRank = node.rank;
|
||||
connectedSubset.root = node;
|
||||
}
|
||||
connectedSubset.nodes.push(node);
|
||||
connectedSubset.edges = connectedSubset.edges.concat(node.edges);
|
||||
});
|
||||
// 边索引去重
|
||||
Util.each(connectedSubset.edges, edgeIndex => {
|
||||
if (!edgeMap[edgeIndex]) {
|
||||
subEdges.push(edges[edgeIndex]);
|
||||
edgeMap[edgeIndex] = true;
|
||||
}
|
||||
});
|
||||
connectedSubset.edges = subEdges;
|
||||
return connectedSubset;
|
||||
}
|
||||
|
||||
// 深度优先遍历算法
|
||||
function dfs(start, nodes, callback) {
|
||||
const listToExplore = [ start ];
|
||||
nodes[ start ].visited = true;
|
||||
callback(start);
|
||||
while (listToExplore.length > 0) {
|
||||
const nodeIndex = listToExplore.pop();
|
||||
nodes[ nodeIndex ].links.forEach(function(childIndex) {
|
||||
if (!nodes[ childIndex ].visited) {
|
||||
nodes[ childIndex ].visited = true;
|
||||
callback(childIndex);
|
||||
listToExplore.push(childIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = maxSpanningForest;
|
71
plugins/util.maxSpanningTree/maxSpanningTree.js
Normal file
71
plugins/util.maxSpanningTree/maxSpanningTree.js
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @fileOverview 基于 prim 获取有向图最大生成树 算法
|
||||
* https://zh.wikipedia.org/wiki/%E6%99%AE%E6%9E%97%E5%A7%86%E7%AE%97%E6%B3%95
|
||||
* @author huangtonger@aliyun.com
|
||||
*/
|
||||
const G6 = require('@antv/g6');
|
||||
const { Util } = G6;
|
||||
|
||||
let treeNodes = [];
|
||||
let treeEdges = [];
|
||||
|
||||
function maxSpanningTree(connectedSubset) {
|
||||
const nodes = connectedSubset.nodes;
|
||||
const edges = connectedSubset.edges;
|
||||
const root = connectedSubset.root;
|
||||
const nodeMap = {};
|
||||
Util.each(nodes, node => {
|
||||
node.to = [];
|
||||
node.from = [];
|
||||
nodeMap[node.id] = node;
|
||||
});
|
||||
Util.each(edges, edge => {
|
||||
const source = nodeMap[edge.source];
|
||||
const target = nodeMap[edge.target];
|
||||
source.to.push(edge);
|
||||
target.from.push(edge);
|
||||
});
|
||||
treeNodes = [ root ];
|
||||
treeEdges = [];
|
||||
while (treeNodes.length !== nodes.length) {
|
||||
let maxWeight = -Infinity;
|
||||
let maxEdge;
|
||||
let direct = 'target';
|
||||
Util.each(treeNodes, treeNode => {
|
||||
Util.each(treeNode.to, edge => {
|
||||
if (treeNodes.indexOf(nodeMap[edge[direct]]) === -1) {
|
||||
if (edge.weight > maxWeight) {
|
||||
maxWeight = edge.weight;
|
||||
maxEdge = edge;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if (!maxEdge) {
|
||||
direct = 'source';
|
||||
Util.each(treeNodes, treeNode => {
|
||||
Util.each(treeNode.from, edge => {
|
||||
if (treeNodes.indexOf(nodeMap[edge[direct]]) === -1) {
|
||||
if (edge.weight > maxWeight) {
|
||||
maxWeight = edge.weight;
|
||||
maxEdge = edge;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
treeNodes.push(nodeMap[maxEdge[direct]]);
|
||||
treeEdges.push(maxEdge);
|
||||
}
|
||||
// 清除标记
|
||||
Util.each(nodes, node => {
|
||||
delete node.to;
|
||||
delete node.from;
|
||||
});
|
||||
return {
|
||||
nodes: treeNodes,
|
||||
edges: treeEdges
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = maxSpanningTree;
|
Loading…
Reference in New Issue
Block a user