webworker pach problem

This commit is contained in:
Yanyan-Wang 2018-07-22 17:55:52 +08:00
parent 9a4951a752
commit 44289cd5dd
8 changed files with 1031 additions and 570 deletions

View File

@ -0,0 +1,126 @@
/* BlobBuilder.js
* A complete BlobBuilder shim
* By Eli Grey
* License: MIT/X11
*/
self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || function (view) {
"use strict";
var
get_class = function (object) {
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
},
FakeBlobBuilder = view.BlobBuilder = function () {},
FakeBlob = view.Blob = function (data, type) {
this.data = data;
this.size = data.length;
this.type = type;
},
FBB_proto = FakeBlobBuilder.prototype = [],
FB_proto = FakeBlob.prototype,
FileReaderSync = view.FileReaderSync,
FileException = function (type) {
this.code = this[this.name = type];
},
file_ex_codes = (
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " +
"NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
).split(" "),
file_ex_code = file_ex_codes.length,
URL = view.URL = view.URL || view.webkitURL || view,
real_create_object_url, real_revoke_object_url, btoa = view.btoa,
ArrayBuffer = view.ArrayBuffer,
can_apply_typed_arrays = false,
can_apply_typed_arrays_test = function (pass) {
can_apply_typed_arrays = !pass;
};
while (file_ex_code--) {
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
}
try {
if (ArrayBuffer) {
can_apply_typed_arrays_test.apply(0, new Uint8Array(1));
}
} catch (ex) {}
if (!URL.createObjectURL) {
URL = {};
}
real_create_object_url = URL.createObjectURL;
real_revoke_object_url = URL.revokeObjectURL;
URL.createObjectURL = function (blob) {
var type = blob.type;
if (type === null) {
type = "application/octet-stream";
}
if (blob instanceof FakeBlob) {
if (btoa) {
return "data:" + type + ";base64," + btoa(blob.data);
} else {
return "data:" + type + "," + encodeURIComponent(blob.data);
}
} else if (real_create_object_url) {
return real_create_object_url.call(URL, blob);
}
};
URL.revokeObjectURL = function (object_url) {
if (object_url.substring(0, 5) !== "data:" && real_revoke_object_url) {
real_revoke_object_url.call(URL, object_url);
}
};
FBB_proto.append = function (data /*, endings*/ ) {
var bb = this;
// decode data to a binary string
if (ArrayBuffer && data instanceof ArrayBuffer) {
if (can_apply_typed_arrays) {
bb.push(String.fromCharCode.apply(String, new Uint8Array(data)));
} else {
var
str = "",
buf = new Uint8Array(data),
i = 0,
buf_len = buf.length;
for (; i < buf_len; i++) {
str += String.fromCharCode(buf[i]);
}
}
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
if (FileReaderSync) {
var fr = new FileReaderSync;
bb.push(fr.readAsBinaryString(data));
} else {
// async FileReader won't work as BlobBuilder is sync
throw new FileException("NOT_READABLE_ERR");
}
} else if (data instanceof FakeBlob) {
bb.push(data.data);
} else {
if (typeof data !== "string") {
data += ""; // convert unsupported types to strings
}
// decode UTF-16 to binary string
bb.push(unescape(encodeURIComponent(data)));
}
};
FBB_proto.getBlob = function (type) {
if (!arguments.length) {
type = null;
}
return new FakeBlob(this.join(""), type);
};
FBB_proto.toString = function () {
return "[object BlobBuilder]";
};
FB_proto.slice = function (start, end, type) {
var args = arguments.length;
if (args < 3) {
type = null;
}
return new FakeBlob(
this.data.slice(start, args > 1 ? end : this.data.length), type
);
};
FB_proto.toString = function () {
return "[object Blob]";
};
return FakeBlobBuilder;
}(self);

View File

@ -0,0 +1,94 @@
/**
* @fileOverview body
* @author shiwu.wyy@antfin.com
*/
// represents a body(a point mass) and its position
class Body {
constructor(params) {
/**
* the id of this body, the same with the node id
* @type {number}
*/
this.id = params.id;
/**
* the position of this body
* @type {number}
*/
this.rx = params.rx;
/**
* the position of this body
* @type {number}
*/
this.ry = params.ry;
/**
* the force acting on this body
* @type {number}
*/
this.fx = 0;
/**
* the force acting on this body
* @type {number}
*/
this.fy = 0;
/**
* the mass of this body, =1 for a node
* @type {number}
*/
this.mass = params.mass;
/**
* the degree of the node represented by this body
* @type {number}
*/
this.degree = params.degree;
/**
* the parameter for repulsive force, = kr
* @type {number}
*/
this.G = params.G;
}
// returns the euclidean distance
distanceTo(bo) {
const dx = this.rx - bo.rx;
const dy = this.ry - bo.ry;
return Math.hypot(dx, dy);
}
setPos(x, y) {
this.rx = x;
this.ry = y;
}
// resets the forces
resetForce() {
this.fx = 0;
this.fy = 0;
}
addForce(b) {
const dx = b.rx - this.rx;
const dy = b.ry - this.ry;
let dist = Math.hypot(dx, dy);
dist = dist < 0.0001 ? 0.0001 : dist;
// the repulsive defined by force atlas 2
const F = (this.G * (this.degree + 1) * (b.degree + 1)) / dist;
this.fx += F * dx / dist;
this.fy += F * dy / dist;
}
// if quad contains this body
in(quad) {
return quad.contains(this.rx, this.ry);
}
// returns a new body
add(bo) {
const nenw_mass = this.mass + bo.mass;
const x = (this.rx * this.mass + bo.rx * bo.mass) / nenw_mass;
const y = (this.ry * this.mass + bo.ry * bo.mass) / nenw_mass;
const dg = this.degree + bo.degree;
const params = {
rx: x,
ry: y,
mass: nenw_mass,
degree: dg
};
return new Body(params);
}
}
module.exports = Body;

View File

@ -0,0 +1,201 @@
// 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;
// // }

View File

@ -4,10 +4,7 @@
*/
const G6 = require('@antv/g6');
const Util = G6.Util;
const Body = require('./quadTree/body');
const Quad = require('./quadTree/quad');
const QuadTree = require('./quadTree/quadTree');
// const Worker = require('worker-loader?inline!./worker');
const Worker = require('worker-loader?name=[name].js!./layout.worker');
// import StaticsWorker from 'worker-loader?inline!./statistics.worker.js';
class Layout {
@ -127,295 +124,41 @@ class Layout {
x: width / 2,
y: height / 2
};
// const worker = new Worker({ type: 'module' });// { type: 'module' }
// the whidth of each nodes
// const widths = [];
// const size = nodes.length;
// for (let i = 0; i < size; i += 1) {
// widths[i] = graph.getNodes()[i].getBBox().maxX - graph.getNodes()[i].getBBox().minX;
// }
// console.log(worker)
// const obj = {
// nodes,
// edges,
// kr,
// kg,
// mode,
// prev_overlapping,
// dissuade_hubs,
// barnes_hut,
// max_iteration,
// ks,
// ksmax,
// tao,
// center,
// widths
// };
// obj = JSON.parse(JSON.stringify(obj));
// worker.postMessage(obj);
// worker.onmessage = function(event) {
// console.log(event.data);
// };
const widths = [];
const size = nodes.length;
const esize = edges.length;
let SG = 0;
const bodies = [];
for (let i = 0; i < size; i += 1) {
nodes[i].index = i;
nodes[i].degree = 0;
nodes[i].x = Math.random() * 1000;
nodes[i].y = Math.random() * 1000;
widths[i] = graph.getNodes()[i].getBBox().maxX;
}
for (let i = 0; i < esize; i += 1) {
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;
}
const kr_prime = 100;
let iter = max_iteration;
const prevo_iter = 50;
let Forces = [];
const pre_Forces = [];
for (let i = 0; i < size; i += 1) {
Forces[2 * i] = 0;
Forces[2 * i + 1] = 0;
if (barnes_hut) {
let params = {
id: i,
rx: nodes[i].x,
ry: nodes[i].y,
mass: 1,
G: kr,
degree: nodes[i].degree
};
bodies[i] = new Body(params);
params = null;
}
}
do {
for (let i = 0; i < size; 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 = this.getAttrForces(graph, nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths);
// 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 = this.getOptRepGraForces(graph, nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies);
} else {
Forces = this.getRepGraForces(graph, nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths);
}
// update the positions
const res = this.updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
nodes = res[0];
SG = res[1];
iter -= 1;
} while (iter > 0);
}
getAttrForces(graph, nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths) {
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();
// const source_node, target_node;
// const source_idx, 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] ;
if (prev_overlapping && iter < prevo_iter) eucli_dis = eucli_dis - graph.find(source_node.id).getBBox().maxX - graph.find(target_node.id).getBBox().maxX;
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;
}
getRepGraForces(graph, nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths) {
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];
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 - whidths[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;
}
getOptRepGraForces(graph, 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
const obj = {
nodes,
edges,
kr,
kg,
mode,
prev_overlapping,
dissuade_hubs,
barnes_hut,
max_iteration,
ks,
ksmax,
tao,
center,
widths
};
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;
const worker = new Worker();// { type: 'module' }
worker.postMessage(obj);
worker.onmessage = function(event) {
this.nodes = event.data;
const graph_nodes = graph.getNodes();
for (let i = 0; i < size; i += 1) {
const model = graph_nodes[i].getModel();
model.x = this.nodes[i].x;
model.y = this.nodes[i].y;
}
graph.changeLayout();
};
}
updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao) {
let swgns = [];
let trans = [];
// swg(G) and tra(G)
let swgG = 0;
let traG = 0;
for (let i = 0; i < size; i += 1) {
const minus = [Forces[2 * i] - pre_Forces[2 * i],
Forces[2 * i + 1] - pre_Forces[2 * i + 1]
];
const minus_norm = Math.hypot(minus[0], minus[1]);
const add = [Forces[2 * i] + pre_Forces[2 * i],
Forces[2 * i + 1] + pre_Forces[2 * i + 1]
];
const add_norm = Math.hypot(add[0], add[1]);
swgns[i] = minus_norm;
trans[i] = add_norm / 2;
swgG += (nodes[i].degree + 1) * swgns[i];
traG += (nodes[i].degree + 1) * trans[i];
}
let pre_SG = SG;
SG = tao * traG / swgG;
if (pre_SG !== 0) {
SG = SG > (1.5 * pre_SG) ? (1.5 * pre_SG) : SG;
}
// update the node positions
for (let i = 0; i < size; i += 1) {
let Sn = ks * SG / (1 + SG * Math.sqrt(swgns[i]));
let absForce = Math.hypot(Forces[2 * i], Forces[2 * i + 1]);
absForce = absForce < 0.0001 ? 0.0001 : absForce;
const max = ksmax / absForce;
Sn = Sn > max ? max : Sn;
const Dn_x = Sn * Forces[2 * i];
const Dn_y = Sn * Forces[2 * i + 1];
nodes[i].x += Dn_x;
nodes[i].y += Dn_y;
}
swgns = null;
trans = null;
pre_SG = null;
return [nodes, SG];
}
}
module.exports = Layout;

View File

@ -0,0 +1,392 @@
const Body = require('./body');
const Quad = require('./quad');
const QuadTree = require('./quadTree');
const ForceWorker = require('./force.worker');
// const ForceWorker = require('./force.worker');
// console.log('ass absdd')
const force_worker = new ForceWorker();
onmessage = function(event) {
let {
nodes,
edges,
kr,
kg,
mode,
prev_overlapping,
dissuade_hubs,
barnes_hut,
max_iteration,
ks,
ksmax,
tao,
center,
widths
} = event.data;
const size = nodes.length;
const esize = edges.length;
let SG = 0;
const bodies = [];
for (let i = 0; i < size; i += 1) {
nodes[i].index = i;
nodes[i].degree = 0;
nodes[i].x = Math.random() * 1000;
nodes[i].y = Math.random() * 1000;
}
for (let i = 0; i < esize; i += 1) {
let node1;
let node2;
for (let j = 0; j < size; j += 1) {
if (nodes[j].id === edges[i].source) {
node1 = nodes[j];
} else if (nodes[j].id === edges[i].target) {
node2 = nodes[j];
}
}
// // 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;
}
const kr_prime = 100;
let iter = max_iteration;
const prevo_iter = 50;
let Forces = [];
const pre_Forces = [];
for (let i = 0; i < size; i += 1) {
Forces[2 * i] = 0;
Forces[2 * i + 1] = 0;
if (barnes_hut) {
let params = {
id: i,
rx: nodes[i].x,
ry: nodes[i].y,
mass: 1,
G: kr,
degree: nodes[i].degree
};
bodies[i] = new Body(params);
params = null;
}
}
let worker_back = 0;
let workerList = [];
const worker_num = 10;
const worker_capacity = Math.ceil(size / worker_num);
const worker_edge_capacity = Math.ceil(esize / worker_num);
do {
for (let i = 0; i < size; 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;
}
if (!barnes_hut) {
workerList = [];
for (let i = 0; i < worker_num; i += 1) {
// console.log(i);
// const force_worker = new ForceWorker();
// workerList.push(new ForceWorker());
// const start_idx = i * worker_capacity;
// let end_idx = start_idx + worker_capacity - 1;
// end_idx = end_idx > size ? size : end_idx;
// const estart_idx = i * worker_edge_capacity;
// let eend_idx = estart_idx + worker_edge_capacity - 1;
// eend_idx = eend_idx > esize ? esize : eend_idx;
// force_worker.postMessage({
// params: event.data,
// pre_Forces,
// Forces,
// iter,
// prevo_iter,
// kr_prime,
// index: i,
// start: start_idx,
// end: end_idx,
// estart: estart_idx,
// eend: eend_idx
// });
// force_worker.onMessage = function(event) {
// worker_back += 1;
// const start = event.data.start;
// const end = event.data.end;
// const worker_Forces = event.data.Forces;
// for (let i = start; i < end; i += 1) {
// Forces[2 * i] = worker_Forces[i].x;
// Forces[2 * i + 1].y = worker_Forces[i].y;
// }
// if (worker_back >= worker_num) {
// // // update the positions
// const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
// nodes = res[0];
// SG = res[1];
// iter -= 1;
// return;
// }
// };
}
// workerList.map(function(worker, idx) {
// worker.addEventListener('message', function(e) {
// workerList.map(function(_worker) {
// _worker.terminate();
// });
// });
// const start_idx = idx * worker_capacity;
// let end_idx = start_idx + worker_capacity - 1;
// end_idx = end_idx > size ? size : end_idx;
// const estart_idx = idx * worker_edge_capacity;
// let eend_idx = estart_idx + worker_edge_capacity - 1;
// eend_idx = eend_idx > esize ? esize : eend_idx;
// worker.postMessage({
// params: event.data,
// pre_Forces,
// Forces,
// iter,
// prevo_iter,
// kr_prime,
// index: idx,
// start: start_idx,
// end: end_idx,
// estart: estart_idx,
// eend: eend_idx
// });
// worker.onMessage = function(event) {
// worker_back += 1;
// const start = event.data.start;
// const end = event.data.end;
// const worker_Forces = event.data.Forces;
// for (let i = start; i < end; i += 1) {
// Forces[2 * i] = worker_Forces[i].x;
// Forces[2 * i + 1].y = worker_Forces[i].y;
// }
// if (worker_back >= worker_num) {
// // // update the positions
// const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
// nodes = res[0];
// SG = res[1];
// iter -= 1;
// return;
// }
// };
// });
} else {
// // attractive forces, existing on every actual edge
Forces = getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths);
// // // 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);
}
// // update the positions
const res = updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
nodes = res[0];
SG = res[1];
iter -= 1;
}
} while (iter > 0);
self.postMessage(nodes);
};
function getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths) {
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();
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) {
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];
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;
}
function updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao) {
let swgns = [];
let trans = [];
// swg(G) and tra(G)
let swgG = 0;
let traG = 0;
for (let i = 0; i < size; i += 1) {
const minus = [Forces[2 * i] - pre_Forces[2 * i],
Forces[2 * i + 1] - pre_Forces[2 * i + 1]
];
const minus_norm = Math.hypot(minus[0], minus[1]);
const add = [Forces[2 * i] + pre_Forces[2 * i],
Forces[2 * i + 1] + pre_Forces[2 * i + 1]
];
const add_norm = Math.hypot(add[0], add[1]);
swgns[i] = minus_norm;
trans[i] = add_norm / 2;
swgG += (nodes[i].degree + 1) * swgns[i];
traG += (nodes[i].degree + 1) * trans[i];
}
let pre_SG = SG;
SG = tao * traG / swgG;
if (pre_SG !== 0) {
SG = SG > (1.5 * pre_SG) ? (1.5 * pre_SG) : SG;
}
// update the node positions
for (let i = 0; i < size; i += 1) {
let Sn = ks * SG / (1 + SG * Math.sqrt(swgns[i]));
let absForce = Math.hypot(Forces[2 * i], Forces[2 * i + 1]);
absForce = absForce < 0.0001 ? 0.0001 : absForce;
const max = ksmax / absForce;
Sn = Sn > max ? max : Sn;
const Dn_x = Sn * Forces[2 * i];
const Dn_y = Sn * Forces[2 * i + 1];
nodes[i].x += Dn_x;
nodes[i].y += Dn_y;
}
swgns = null;
trans = null;
pre_SG = null;
return [nodes, SG];
}

View File

@ -0,0 +1,97 @@
/**
* @fileOverview quad
* @author shiwu.wyy@antfin.com
*/
class Quad {
constructor(params) {
/**
* the center position of this quad
* @type {number}
*/
this.xmid = params.xmid;
/**
* the center position of this quad
* @type {number}
*/
this.ymid = params.ymid;
/**
* the length of this quad
* @type {number}
*/
this.length = params.length;
/**
* the mass center of this quad
* @type {number}
*/
this.mass_center = params.mass_center;
/**
* the mass of this quad
* @type {number}
*/
this.mass = params.mass;
}
getLength() {
return this.length;
}
contains(x, y) {
const halfLen = this.length / 2;
return (x <= this.xmid + halfLen &&
x >= this.xmid - halfLen &&
y <= this.ymid + halfLen &&
y >= this.ymid - halfLen);
}
// northwest quadrant
NW() {
const x = this.xmid - this.length / 4;
const y = this.ymid + this.length / 4;
const len = this.length / 2;
const params = {
xmid: x,
ymid: y,
length: len
};
const NW = new Quad(params);
return NW;
}
// northeast
NE() {
const x = this.xmid + this.length / 4;
const y = this.ymid + this.length / 4;
const len = this.length / 2;
const params = {
xmid: x,
ymid: y,
length: len
};
const NE = new Quad(params);
return NE;
}
// southwest
SW() {
const x = this.xmid - this.length / 4;
const y = this.ymid - this.length / 4;
const len = this.length / 2;
const params = {
xmid: x,
ymid: y,
length: len
};
const SW = new Quad(params);
return SW;
}
// southeast
SE() {
const x = this.xmid + this.length / 4;
const y = this.ymid - this.length / 4;
const len = this.length / 2;
const params = {
xmid: x,
ymid: y,
length: len
};
const SE = new Quad(params);
return SE;
}
}
module.exports = Quad;

View File

@ -0,0 +1,91 @@
/**
* @fileOverview quadTree
* @author shiwu.wyy@antfin.com
*/
class QuadTree {
// each quadtree represents a quadrant and an aggregate body
// that represents all bodies inside the quadrant
constructor(param) {
/**
* (aggregated) body in this quad
* @type {object}
*/
this.body = null;
/**
* tree representing the northwest quadrant
* @type {object}
*/
this.quad = null;
this.NW = null;
this.NE = null;
this.SW = null;
this.SE = null;
/**
* threshold
* @type {number}
*/
this.theta = 0.5;
if (param != null) this.quad = param;
}
// insert a body(node) into the tree
insert(bo) {
// if this node does not contain a body, put the new body bo here
if (this.body == null) {
this.body = bo;
return;
}
// internal node
if (!this._isExternal()) {
// update mass info
this.body = this.body.add(bo);
// insert body into quadrant
this._putBody(bo);
} else { // external node
// divide this region into four children
this.NW = new QuadTree(this.quad.NW());
this.NE = new QuadTree(this.quad.NE());
this.SW = new QuadTree(this.quad.SW());
this.SE = new QuadTree(this.quad.SE());
// insert this body and bo
this._putBody(this.body);
this._putBody(bo);
// update the mass info
this.body = this.body.add(bo);
}
}
// inserts bo into a quad
_putBody(bo) {
if (bo.in(this.quad.NW())) this.NW.insert(bo);
else if (bo.in(this.quad.NE())) this.NE.insert(bo);
else if (bo.in(this.quad.SW())) this.SW.insert(bo);
else if (bo.in(this.quad.SE())) this.SE.insert(bo);
}
_isExternal() {
// four children are null
return (this.NW == null && this.NE == null && this.SW == null && this.SE == null);
}
// update the forces
updateForce(bo) {
if (this.body == null || bo === this.body) {
return;
}
// if the current node is external
if (this._isExternal()) bo.addForce(this.body);
// internal nodes
else {
const s = this.quad.getLength();
const d = this.body.distanceTo(bo);
// b is far enough
if ((s / d) < this.theta) bo.addForce(this.body);
else {
this.NW.updateForce(bo);
this.NE.updateForce(bo);
this.SW.updateForce(bo);
this.SE.updateForce(bo);
}
}
}
}
module.exports = QuadTree;

View File

@ -1,283 +0,0 @@
// const Body = require('./quadTree/body');
// const Quad = require('./quadTree/quad');
// const QuadTree = require('./quadTree/quadTree');
//http://javascript.ruanyifeng.com/htmlapi/webworker.html
// importScripts('./quadTree/body.js', './quadTree/quad.js', './quadTree/quadTree.js');
// import Body from './quadTree/body';
console.log('bbaaaa');
// self.onmessage = function(event) {
// let {
// nodes,
// edges,
// kr,
// kg,
// mode,
// prev_overlapping,
// dissuade_hubs,
// barnes_hut,
// max_iteration,
// ks,
// ksmax,
// tao,
// center,
// widths
// } = event.data;
// console.log(widths);
// const size = nodes.length;
// const esize = edges.length;
// let SG = 0;
// const bodies = [];
// for (let i = 0; i < size; i += 1) {
// nodes[i].index = i;
// nodes[i].degree = 0;
// nodes[i].x = Math.random() * 1000;
// nodes[i].y = Math.random() * 1000;
// }
// for (let i = 0; i < esize; i += 1) {
// let node1, node2;
// for (let j = 0; j < size; j += 1) {
// if (nodes[j].id === edges[i].source) {
// node1 = nodes[j];
// } else if (nodes[j].id === edges[i].target) {
// node2 = nodes[j];
// }
// }
// // 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;
// }
// const kr_prime = 100;
// let iter = max_iteration;
// const prevo_iter = 50;
// let Forces = [];
// const pre_Forces = [];
// for (let i = 0; i < size; i += 1) {
// Forces[2 * i] = 0;
// Forces[2 * i + 1] = 0;
// if (barnes_hut) {
// let params = { id: i, rx: nodes[i].x, ry: nodes[i].y, mass: 1, G: kr, degree: nodes[i].degree };
// bodies[i] = new Body(params);
// params = null;
// }
// }
// do {
// for (let i = 0; i < size; 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 = this.getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths);
// // 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 = this.getOptRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, bodies);
// } else {
// Forces = this.getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths);
// }
// // update the positions
// const res = this.updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao);
// nodes = res[0];
// SG = res[1];
// iter -= 1;
// } while (iter > 0);
// }
// console.log(nodes);
// self.postMessage(nodes);
// getAttrForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, widths) {
// 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();
// const source_node, target_node;
// const source_idx, 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;
// }
// getRepGraForces(nodes, edges, size, esize, prev_overlapping, dissuade_hubs, mode, iter, prevo_iter, Forces, kr, kr_prime, kg, center, widths) {
// 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 ];
// 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 - whidths[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;
// }
// 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;
// }
// updatePos(size, nodes, Forces, pre_Forces, SG, ks, ksmax, tao) {
// let swgns = [];
// let trans = [];
// // swg(G) and tra(G)
// let swgG = 0;
// let traG = 0;
// for (let i = 0; i < size; i += 1) {
// const minus = [ Forces[2 * i] - pre_Forces[2 * i],
// Forces[2 * i + 1] - pre_Forces[2 * i + 1]
// ];
// const minus_norm = Math.hypot(minus[0], minus[1]);
// const add = [ Forces[2 * i] + pre_Forces[2 * i],
// Forces[2 * i + 1] + pre_Forces[2 * i + 1]
// ];
// const add_norm = Math.hypot(add[0], add[1]);
// swgns[i] = minus_norm;
// trans[i] = add_norm / 2;
// swgG += (nodes[i].degree + 1) * swgns[i];
// traG += (nodes[i].degree + 1) * trans[i];
// }
// let pre_SG = SG;
// SG = tao * traG / swgG;
// if (pre_SG !== 0) {
// SG = SG > (1.5 * pre_SG) ? (1.5 * pre_SG) : SG;
// }
// // update the node positions
// for (let i = 0; i < size; i += 1) {
// let Sn = ks * SG / (1 + SG * Math.sqrt(swgns[i]));
// let absForce = Math.hypot(Forces[2 * i], Forces[2 * i + 1]);
// absForce = absForce < 0.0001 ? 0.0001 : absForce;
// const max = ksmax / absForce;
// Sn = Sn > max ? max : Sn;
// const Dn_x = Sn * Forces[2 * i];
// const Dn_y = Sn * Forces[2 * i + 1];
// nodes[i].x += Dn_x;
// nodes[i].y += Dn_y;
// }
// swgns = null;
// trans = null;
// pre_SG = null;
// return [ nodes, SG ];
// }