|
|
- ;(function(undefined) {
- 'use strict';
-
- /**
- * Sigma ForceAtlas2.5 Webworker
- * ==============================
- *
- * Author: Guillaume Plique (Yomguithereal)
- * Algorithm author: Mathieu Jacomy @ Sciences Po Medialab & WebAtlas
- * Version: 1.0.3
- */
-
- var _root = this,
- inWebWorker = !('document' in _root);
-
- /**
- * Worker Function Wrapper
- * ------------------------
- *
- * The worker has to be wrapped into a single stringified function
- * to be passed afterwards as a BLOB object to the supervisor.
- */
- var Worker = function(undefined) {
- 'use strict';
-
- /**
- * Worker settings and properties
- */
- var W = {
-
- // Properties
- ppn: 10,
- ppe: 3,
- ppr: 9,
- maxForce: 10,
- iterations: 0,
- converged: false,
-
- // Possible to change through config
- settings: {
- linLogMode: false,
- outboundAttractionDistribution: false,
- adjustSizes: false,
- edgeWeightInfluence: 0,
- scalingRatio: 1,
- strongGravityMode: false,
- gravity: 1,
- slowDown: 1,
- barnesHutOptimize: false,
- barnesHutTheta: 0.5,
- startingIterations: 1,
- iterationsPerRender: 1
- }
- };
-
- var NodeMatrix,
- EdgeMatrix,
- RegionMatrix;
-
- /**
- * Helpers
- */
- function extend() {
- var i,
- k,
- res = {},
- l = arguments.length;
-
- for (i = l - 1; i >= 0; i--)
- for (k in arguments[i])
- res[k] = arguments[i][k];
- return res;
- }
-
- function __emptyObject(obj) {
- var k;
-
- for (k in obj)
- if (!('hasOwnProperty' in obj) || obj.hasOwnProperty(k))
- delete obj[k];
-
- return obj;
- }
-
- /**
- * Matrices properties accessors
- */
- var nodeProperties = {
- x: 0,
- y: 1,
- dx: 2,
- dy: 3,
- old_dx: 4,
- old_dy: 5,
- mass: 6,
- convergence: 7,
- size: 8,
- fixed: 9
- };
-
- var edgeProperties = {
- source: 0,
- target: 1,
- weight: 2
- };
-
- var regionProperties = {
- node: 0,
- centerX: 1,
- centerY: 2,
- size: 3,
- nextSibling: 4,
- firstChild: 5,
- mass: 6,
- massCenterX: 7,
- massCenterY: 8
- };
-
- function np(i, p) {
-
- // DEBUG: safeguards
- if ((i % W.ppn) !== 0)
- throw 'np: non correct (' + i + ').';
- if (i !== parseInt(i))
- throw 'np: non int.';
-
- if (p in nodeProperties)
- return i + nodeProperties[p];
- else
- throw 'ForceAtlas2.Worker - ' +
- 'Inexistant node property given (' + p + ').';
- }
-
- function ep(i, p) {
-
- // DEBUG: safeguards
- if ((i % W.ppe) !== 0)
- throw 'ep: non correct (' + i + ').';
- if (i !== parseInt(i))
- throw 'ep: non int.';
-
- if (p in edgeProperties)
- return i + edgeProperties[p];
- else
- throw 'ForceAtlas2.Worker - ' +
- 'Inexistant edge property given (' + p + ').';
- }
-
- function rp(i, p) {
-
- // DEBUG: safeguards
- if ((i % W.ppr) !== 0)
- throw 'rp: non correct (' + i + ').';
- if (i !== parseInt(i))
- throw 'rp: non int.';
-
- if (p in regionProperties)
- return i + regionProperties[p];
- else
- throw 'ForceAtlas2.Worker - ' +
- 'Inexistant region property given (' + p + ').';
- }
-
- // DEBUG
- function nan(v) {
- if (isNaN(v))
- throw 'NaN alert!';
- }
-
-
- /**
- * Algorithm initialization
- */
-
- function init(nodes, edges, config) {
- config = config || {};
- var i, l;
-
- // Matrices
- NodeMatrix = nodes;
- EdgeMatrix = edges;
-
- // Length
- W.nodesLength = NodeMatrix.length;
- W.edgesLength = EdgeMatrix.length;
-
- // Merging configuration
- configure(config);
- }
-
- function configure(o) {
- W.settings = extend(o, W.settings);
- }
-
- /**
- * Algorithm pass
- */
-
- // MATH: get distances stuff and power 2 issues
- function pass() {
- var a, i, j, l, r, n, n1, n2, e, w, g, k, m;
-
- var outboundAttCompensation,
- coefficient,
- xDist,
- yDist,
- ewc,
- mass,
- distance,
- size,
- factor;
-
- // 1) Initializing layout data
- //-----------------------------
-
- // Resetting positions & computing max values
- for (n = 0; n < W.nodesLength; n += W.ppn) {
- NodeMatrix[np(n, 'old_dx')] = NodeMatrix[np(n, 'dx')];
- NodeMatrix[np(n, 'old_dy')] = NodeMatrix[np(n, 'dy')];
- NodeMatrix[np(n, 'dx')] = 0;
- NodeMatrix[np(n, 'dy')] = 0;
- }
-
- // If outbound attraction distribution, compensate
- if (W.settings.outboundAttractionDistribution) {
- outboundAttCompensation = 0;
- for (n = 0; n < W.nodesLength; n += W.ppn) {
- outboundAttCompensation += NodeMatrix[np(n, 'mass')];
- }
-
- outboundAttCompensation /= W.nodesLength;
- }
-
-
- // 1.bis) Barnes-Hut computation
- //------------------------------
-
- if (W.settings.barnesHutOptimize) {
-
- var minX = Infinity,
- maxX = -Infinity,
- minY = Infinity,
- maxY = -Infinity,
- q, q0, q1, q2, q3;
-
- // Setting up
- // RegionMatrix = new Float32Array(W.nodesLength / W.ppn * 4 * W.ppr);
- RegionMatrix = [];
-
- // Computing min and max values
- for (n = 0; n < W.nodesLength; n += W.ppn) {
- minX = Math.min(minX, NodeMatrix[np(n, 'x')]);
- maxX = Math.max(maxX, NodeMatrix[np(n, 'x')]);
- minY = Math.min(minY, NodeMatrix[np(n, 'y')]);
- maxY = Math.max(maxY, NodeMatrix[np(n, 'y')]);
- }
-
- // Build the Barnes Hut root region
- RegionMatrix[rp(0, 'node')] = -1;
- RegionMatrix[rp(0, 'centerX')] = (minX + maxX) / 2;
- RegionMatrix[rp(0, 'centerY')] = (minY + maxY) / 2;
- RegionMatrix[rp(0, 'size')] = Math.max(maxX - minX, maxY - minY);
- RegionMatrix[rp(0, 'nextSibling')] = -1;
- RegionMatrix[rp(0, 'firstChild')] = -1;
- RegionMatrix[rp(0, 'mass')] = 0;
- RegionMatrix[rp(0, 'massCenterX')] = 0;
- RegionMatrix[rp(0, 'massCenterY')] = 0;
-
- // Add each node in the tree
- l = 1;
- for (n = 0; n < W.nodesLength; n += W.ppn) {
-
- // Current region, starting with root
- r = 0;
-
- while (true) {
- // Are there sub-regions?
-
- // We look at first child index
- if (RegionMatrix[rp(r, 'firstChild')] >= 0) {
-
- // There are sub-regions
-
- // We just iterate to find a "leave" of the tree
- // that is an empty region or a region with a single node
- // (see next case)
-
- // Find the quadrant of n
- if (NodeMatrix[np(n, 'x')] < RegionMatrix[rp(r, 'centerX')]) {
-
- if (NodeMatrix[np(n, 'y')] < RegionMatrix[rp(r, 'centerY')]) {
-
- // Top Left quarter
- q = RegionMatrix[rp(r, 'firstChild')];
- }
- else {
-
- // Bottom Left quarter
- q = RegionMatrix[rp(r, 'firstChild')] + W.ppr;
- }
- }
- else {
- if (NodeMatrix[np(n, 'y')] < RegionMatrix[rp(r, 'centerY')]) {
-
- // Top Right quarter
- q = RegionMatrix[rp(r, 'firstChild')] + W.ppr * 2;
- }
- else {
-
- // Bottom Right quarter
- q = RegionMatrix[rp(r, 'firstChild')] + W.ppr * 3;
- }
- }
-
- // Update center of mass and mass (we only do it for non-leave regions)
- RegionMatrix[rp(r, 'massCenterX')] =
- (RegionMatrix[rp(r, 'massCenterX')] * RegionMatrix[rp(r, 'mass')] +
- NodeMatrix[np(n, 'x')] * NodeMatrix[np(n, 'mass')]) /
- (RegionMatrix[rp(r, 'mass')] + NodeMatrix[np(n, 'mass')]);
-
- RegionMatrix[rp(r, 'massCenterY')] =
- (RegionMatrix[rp(r, 'massCenterY')] * RegionMatrix[rp(r, 'mass')] +
- NodeMatrix[np(n, 'y')] * NodeMatrix[np(n, 'mass')]) /
- (RegionMatrix[rp(r, 'mass')] + NodeMatrix[np(n, 'mass')]);
-
- RegionMatrix[rp(r, 'mass')] += NodeMatrix[np(n, 'mass')];
-
- // Iterate on the right quadrant
- r = q;
- continue;
- }
- else {
-
- // There are no sub-regions: we are in a "leave"
-
- // Is there a node in this leave?
- if (RegionMatrix[rp(r, 'node')] < 0) {
-
- // There is no node in region:
- // we record node n and go on
- RegionMatrix[rp(r, 'node')] = n;
- break;
- }
- else {
-
- // There is a node in this region
-
- // We will need to create sub-regions, stick the two
- // nodes (the old one r[0] and the new one n) in two
- // subregions. If they fall in the same quadrant,
- // we will iterate.
-
- // Create sub-regions
- RegionMatrix[rp(r, 'firstChild')] = l * W.ppr;
- w = RegionMatrix[rp(r, 'size')] / 2; // new size (half)
-
- // NOTE: we use screen coordinates
- // from Top Left to Bottom Right
-
- // Top Left sub-region
- g = RegionMatrix[rp(r, 'firstChild')];
-
- RegionMatrix[rp(g, 'node')] = -1;
- RegionMatrix[rp(g, 'centerX')] = RegionMatrix[rp(r, 'centerX')] - w;
- RegionMatrix[rp(g, 'centerY')] = RegionMatrix[rp(r, 'centerY')] - w;
- RegionMatrix[rp(g, 'size')] = w;
- RegionMatrix[rp(g, 'nextSibling')] = g + W.ppr;
- RegionMatrix[rp(g, 'firstChild')] = -1;
- RegionMatrix[rp(g, 'mass')] = 0;
- RegionMatrix[rp(g, 'massCenterX')] = 0;
- RegionMatrix[rp(g, 'massCenterY')] = 0;
-
- // Bottom Left sub-region
- g += W.ppr;
- RegionMatrix[rp(g, 'node')] = -1;
- RegionMatrix[rp(g, 'centerX')] = RegionMatrix[rp(r, 'centerX')] - w;
- RegionMatrix[rp(g, 'centerY')] = RegionMatrix[rp(r, 'centerY')] + w;
- RegionMatrix[rp(g, 'size')] = w;
- RegionMatrix[rp(g, 'nextSibling')] = g + W.ppr;
- RegionMatrix[rp(g, 'firstChild')] = -1;
- RegionMatrix[rp(g, 'mass')] = 0;
- RegionMatrix[rp(g, 'massCenterX')] = 0;
- RegionMatrix[rp(g, 'massCenterY')] = 0;
-
- // Top Right sub-region
- g += W.ppr;
- RegionMatrix[rp(g, 'node')] = -1;
- RegionMatrix[rp(g, 'centerX')] = RegionMatrix[rp(r, 'centerX')] + w;
- RegionMatrix[rp(g, 'centerY')] = RegionMatrix[rp(r, 'centerY')] - w;
- RegionMatrix[rp(g, 'size')] = w;
- RegionMatrix[rp(g, 'nextSibling')] = g + W.ppr;
- RegionMatrix[rp(g, 'firstChild')] = -1;
- RegionMatrix[rp(g, 'mass')] = 0;
- RegionMatrix[rp(g, 'massCenterX')] = 0;
- RegionMatrix[rp(g, 'massCenterY')] = 0;
-
- // Bottom Right sub-region
- g += W.ppr;
- RegionMatrix[rp(g, 'node')] = -1;
- RegionMatrix[rp(g, 'centerX')] = RegionMatrix[rp(r, 'centerX')] + w;
- RegionMatrix[rp(g, 'centerY')] = RegionMatrix[rp(r, 'centerY')] + w;
- RegionMatrix[rp(g, 'size')] = w;
- RegionMatrix[rp(g, 'nextSibling')] = RegionMatrix[rp(r, 'nextSibling')];
- RegionMatrix[rp(g, 'firstChild')] = -1;
- RegionMatrix[rp(g, 'mass')] = 0;
- RegionMatrix[rp(g, 'massCenterX')] = 0;
- RegionMatrix[rp(g, 'massCenterY')] = 0;
-
- l += 4;
-
- // Now the goal is to find two different sub-regions
- // for the two nodes: the one previously recorded (r[0])
- // and the one we want to add (n)
-
- // Find the quadrant of the old node
- if (NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'x')] < RegionMatrix[rp(r, 'centerX')]) {
- if (NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'y')] < RegionMatrix[rp(r, 'centerY')]) {
-
- // Top Left quarter
- q = RegionMatrix[rp(r, 'firstChild')];
- }
- else {
-
- // Bottom Left quarter
- q = RegionMatrix[rp(r, 'firstChild')] + W.ppr;
- }
- }
- else {
- if (NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'y')] < RegionMatrix[rp(r, 'centerY')]) {
-
- // Top Right quarter
- q = RegionMatrix[rp(r, 'firstChild')] + W.ppr * 2;
- }
- else {
-
- // Bottom Right quarter
- q = RegionMatrix[rp(r, 'firstChild')] + W.ppr * 3;
- }
- }
-
- // We remove r[0] from the region r, add its mass to r and record it in q
- RegionMatrix[rp(r, 'mass')] = NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'mass')];
- RegionMatrix[rp(r, 'massCenterX')] = NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'x')];
- RegionMatrix[rp(r, 'massCenterY')] = NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'y')];
-
- RegionMatrix[rp(q, 'node')] = RegionMatrix[rp(r, 'node')];
- RegionMatrix[rp(r, 'node')] = -1;
-
- // Find the quadrant of n
- if (NodeMatrix[np(n, 'x')] < RegionMatrix[rp(r, 'centerX')]) {
- if (NodeMatrix[np(n, 'y')] < RegionMatrix[rp(r, 'centerY')]) {
-
- // Top Left quarter
- q2 = RegionMatrix[rp(r, 'firstChild')];
- }
- else {
- // Bottom Left quarter
- q2 = RegionMatrix[rp(r, 'firstChild')] + W.ppr;
- }
- }
- else {
- if(NodeMatrix[np(n, 'y')] < RegionMatrix[rp(r, 'centerY')]) {
-
- // Top Right quarter
- q2 = RegionMatrix[rp(r, 'firstChild')] + W.ppr * 2;
- }
- else {
-
- // Bottom Right quarter
- q2 = RegionMatrix[rp(r, 'firstChild')] + W.ppr * 3;
- }
- }
-
- if (q === q2) {
-
- // If both nodes are in the same quadrant,
- // we have to try it again on this quadrant
- r = q;
- continue;
- }
-
- // If both quadrants are different, we record n
- // in its quadrant
- RegionMatrix[rp(q2, 'node')] = n;
- break;
- }
- }
- }
- }
- }
-
-
- // 2) Repulsion
- //--------------
- // NOTES: adjustSizes = antiCollision & scalingRatio = coefficient
-
- if (W.settings.barnesHutOptimize) {
- coefficient = W.settings.scalingRatio;
-
- // Applying repulsion through regions
- for (n = 0; n < W.nodesLength; n += W.ppn) {
-
- // Computing leaf quad nodes iteration
-
- r = 0; // Starting with root region
- while (true) {
-
- if (RegionMatrix[rp(r, 'firstChild')] >= 0) {
-
- // The region has sub-regions
-
- // We run the Barnes Hut test to see if we are at the right distance
- distance = Math.sqrt(
- (Math.pow(NodeMatrix[np(n, 'x')] - RegionMatrix[rp(r, 'massCenterX')], 2)) +
- (Math.pow(NodeMatrix[np(n, 'y')] - RegionMatrix[rp(r, 'massCenterY')], 2))
- );
-
- if (2 * RegionMatrix[rp(r, 'size')] / distance < W.settings.barnesHutTheta) {
-
- // We treat the region as a single body, and we repulse
-
- xDist = NodeMatrix[np(n, 'x')] - RegionMatrix[rp(r, 'massCenterX')];
- yDist = NodeMatrix[np(n, 'y')] - RegionMatrix[rp(r, 'massCenterY')];
-
- if (W.settings.adjustSizes) {
-
- //-- Linear Anti-collision Repulsion
- if (distance > 0) {
- factor = coefficient * NodeMatrix[np(n, 'mass')] *
- RegionMatrix[rp(r, 'mass')] / distance / distance;
-
- NodeMatrix[np(n, 'dx')] += xDist * factor;
- NodeMatrix[np(n, 'dy')] += yDist * factor;
- }
- else if (distance < 0) {
- factor = -coefficient * NodeMatrix[np(n, 'mass')] *
- RegionMatrix[rp(r, 'mass')] / distance;
-
- NodeMatrix[np(n, 'dx')] += xDist * factor;
- NodeMatrix[np(n, 'dy')] += yDist * factor;
- }
- }
- else {
-
- //-- Linear Repulsion
- if (distance > 0) {
- factor = coefficient * NodeMatrix[np(n, 'mass')] *
- RegionMatrix[rp(r, 'mass')] / distance / distance;
-
- NodeMatrix[np(n, 'dx')] += xDist * factor;
- NodeMatrix[np(n, 'dy')] += yDist * factor;
- }
- }
-
- // When this is done, we iterate. We have to look at the next sibling.
- if (RegionMatrix[rp(r, 'nextSibling')] < 0)
- break; // No next sibling: we have finished the tree
- r = RegionMatrix[rp(r, 'nextSibling')];
- continue;
-
- }
- else {
-
- // The region is too close and we have to look at sub-regions
- r = RegionMatrix[rp(r, 'firstChild')];
- continue;
- }
-
- }
- else {
-
- // The region has no sub-region
- // If there is a node r[0] and it is not n, then repulse
-
- if (RegionMatrix[rp(r, 'node')] >= 0 && RegionMatrix[rp(r, 'node')] !== n) {
- xDist = NodeMatrix[np(n, 'x')] - NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'x')];
- yDist = NodeMatrix[np(n, 'y')] - NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'y')];
-
- distance = Math.sqrt(xDist * xDist + yDist * yDist);
-
- if (W.settings.adjustSizes) {
-
- //-- Linear Anti-collision Repulsion
- if (distance > 0) {
- factor = coefficient * NodeMatrix[np(n, 'mass')] *
- NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'mass')] / distance / distance;
-
- NodeMatrix[np(n, 'dx')] += xDist * factor;
- NodeMatrix[np(n, 'dy')] += yDist * factor;
- }
- else if (distance < 0) {
- factor = -coefficient * NodeMatrix[np(n, 'mass')] *
- NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'mass')] / distance;
-
- NodeMatrix[np(n, 'dx')] += xDist * factor;
- NodeMatrix[np(n, 'dy')] += yDist * factor;
- }
- }
- else {
-
- //-- Linear Repulsion
- if (distance > 0) {
- factor = coefficient * NodeMatrix[np(n, 'mass')] *
- NodeMatrix[np(RegionMatrix[rp(r, 'node')], 'mass')] / distance / distance;
-
- NodeMatrix[np(n, 'dx')] += xDist * factor;
- NodeMatrix[np(n, 'dy')] += yDist * factor;
- }
- }
-
- }
-
- // When this is done, we iterate. We have to look at the next sibling.
- if (RegionMatrix[rp(r, 'nextSibling')] < 0)
- break; // No next sibling: we have finished the tree
- r = RegionMatrix[rp(r, 'nextSibling')];
- continue;
- }
- }
- }
- }
- else {
- coefficient = W.settings.scalingRatio;
-
- // Square iteration
- for (n1 = 0; n1 < W.nodesLength; n1 += W.ppn) {
- for (n2 = 0; n2 < n1; n2 += W.ppn) {
-
- // Common to both methods
- xDist = NodeMatrix[np(n1, 'x')] - NodeMatrix[np(n2, 'x')];
- yDist = NodeMatrix[np(n1, 'y')] - NodeMatrix[np(n2, 'y')];
-
- if (W.settings.adjustSizes) {
-
- //-- Anticollision Linear Repulsion
- distance = Math.sqrt(xDist * xDist + yDist * yDist) -
- NodeMatrix[np(n1, 'size')] -
- NodeMatrix[np(n2, 'size')];
-
- if (distance > 0) {
- factor = coefficient *
- NodeMatrix[np(n1, 'mass')] *
- NodeMatrix[np(n2, 'mass')] /
- distance / distance;
-
- // Updating nodes' dx and dy
- NodeMatrix[np(n1, 'dx')] += xDist * factor;
- NodeMatrix[np(n1, 'dy')] += yDist * factor;
-
- NodeMatrix[np(n2, 'dx')] += xDist * factor;
- NodeMatrix[np(n2, 'dy')] += yDist * factor;
- }
- else if (distance < 0) {
- factor = 100 * coefficient *
- NodeMatrix[np(n1, 'mass')] *
- NodeMatrix[np(n2, 'mass')];
-
- // Updating nodes' dx and dy
- NodeMatrix[np(n1, 'dx')] += xDist * factor;
- NodeMatrix[np(n1, 'dy')] += yDist * factor;
-
- NodeMatrix[np(n2, 'dx')] -= xDist * factor;
- NodeMatrix[np(n2, 'dy')] -= yDist * factor;
- }
- }
- else {
-
- //-- Linear Repulsion
- distance = Math.sqrt(xDist * xDist + yDist * yDist);
-
- if (distance > 0) {
- factor = coefficient *
- NodeMatrix[np(n1, 'mass')] *
- NodeMatrix[np(n2, 'mass')] /
- distance / distance;
-
- // Updating nodes' dx and dy
- NodeMatrix[np(n1, 'dx')] += xDist * factor;
- NodeMatrix[np(n1, 'dy')] += yDist * factor;
-
- NodeMatrix[np(n2, 'dx')] -= xDist * factor;
- NodeMatrix[np(n2, 'dy')] -= yDist * factor;
- }
- }
- }
- }
- }
-
-
- // 3) Gravity
- //------------
- g = W.settings.gravity / W.settings.scalingRatio;
- coefficient = W.settings.scalingRatio;
- for (n = 0; n < W.nodesLength; n += W.ppn) {
- factor = 0;
-
- // Common to both methods
- xDist = NodeMatrix[np(n, 'x')];
- yDist = NodeMatrix[np(n, 'y')];
- distance = Math.sqrt(
- Math.pow(xDist, 2) + Math.pow(yDist, 2)
- );
-
- if (W.settings.strongGravityMode) {
-
- //-- Strong gravity
- if (distance > 0)
- factor = coefficient * NodeMatrix[np(n, 'mass')] * g;
- }
- else {
-
- //-- Linear Anti-collision Repulsion n
- if (distance > 0)
- factor = coefficient * NodeMatrix[np(n, 'mass')] * g / distance;
- }
-
- // Updating node's dx and dy
- NodeMatrix[np(n, 'dx')] -= xDist * factor;
- NodeMatrix[np(n, 'dy')] -= yDist * factor;
- }
-
-
-
- // 4) Attraction
- //---------------
- coefficient = 1 *
- (W.settings.outboundAttractionDistribution ?
- outboundAttCompensation :
- 1);
-
- // TODO: simplify distance
- // TODO: coefficient is always used as -c --> optimize?
- for (e = 0; e < W.edgesLength; e += W.ppe) {
- n1 = EdgeMatrix[ep(e, 'source')];
- n2 = EdgeMatrix[ep(e, 'target')];
- w = EdgeMatrix[ep(e, 'weight')];
-
- // Edge weight influence
- ewc = Math.pow(w, W.settings.edgeWeightInfluence);
-
- // Common measures
- xDist = NodeMatrix[np(n1, 'x')] - NodeMatrix[np(n2, 'x')];
- yDist = NodeMatrix[np(n1, 'y')] - NodeMatrix[np(n2, 'y')];
-
- // Applying attraction to nodes
- if (W.settings.adjustSizes) {
-
- distance = Math.sqrt(
- (Math.pow(xDist, 2) + Math.pow(yDist, 2)) -
- NodeMatrix[np(n1, 'size')] -
- NodeMatrix[np(n2, 'size')]
- );
-
- if (W.settings.linLogMode) {
- if (W.settings.outboundAttractionDistribution) {
-
- //-- LinLog Degree Distributed Anti-collision Attraction
- if (distance > 0) {
- factor = -coefficient * ewc * Math.log(1 + distance) /
- distance /
- NodeMatrix[np(n1, 'mass')];
- }
- }
- else {
-
- //-- LinLog Anti-collision Attraction
- if (distance > 0) {
- factor = -coefficient * ewc * Math.log(1 + distance) / distance;
- }
- }
- }
- else {
- if (W.settings.outboundAttractionDistribution) {
-
- //-- Linear Degree Distributed Anti-collision Attraction
- if (distance > 0) {
- factor = -coefficient * ewc / NodeMatrix[np(n1, 'mass')];
- }
- }
- else {
-
- //-- Linear Anti-collision Attraction
- if (distance > 0) {
- factor = -coefficient * ewc;
- }
- }
- }
- }
- else {
-
- distance = Math.sqrt(
- Math.pow(xDist, 2) + Math.pow(yDist, 2)
- );
-
- if (W.settings.linLogMode) {
- if (W.settings.outboundAttractionDistribution) {
-
- //-- LinLog Degree Distributed Attraction
- if (distance > 0) {
- factor = -coefficient * ewc * Math.log(1 + distance) /
- distance /
- NodeMatrix[np(n1, 'mass')];
- }
- }
- else {
-
- //-- LinLog Attraction
- if (distance > 0)
- factor = -coefficient * ewc * Math.log(1 + distance) / distance;
- }
- }
- else {
- if (W.settings.outboundAttractionDistribution) {
-
- //-- Linear Attraction Mass Distributed
- // NOTE: Distance is set to 1 to override next condition
- distance = 1;
- factor = -coefficient * ewc / NodeMatrix[np(n1, 'mass')];
- }
- else {
-
- //-- Linear Attraction
- // NOTE: Distance is set to 1 to override next condition
- distance = 1;
- factor = -coefficient * ewc;
- }
- }
- }
-
- // Updating nodes' dx and dy
- // TODO: if condition or factor = 1?
- if (distance > 0) {
-
- // Updating nodes' dx and dy
- NodeMatrix[np(n1, 'dx')] += xDist * factor;
- NodeMatrix[np(n1, 'dy')] += yDist * factor;
-
- NodeMatrix[np(n2, 'dx')] -= xDist * factor;
- NodeMatrix[np(n2, 'dy')] -= yDist * factor;
- }
- }
-
-
- // 5) Apply Forces
- //-----------------
- var force,
- swinging,
- traction,
- nodespeed;
-
- // MATH: sqrt and square distances
- if (W.settings.adjustSizes) {
-
- for (n = 0; n < W.nodesLength; n += W.ppn) {
- if (!NodeMatrix[np(n, 'fixed')]) {
- force = Math.sqrt(
- Math.pow(NodeMatrix[np(n, 'dx')], 2) +
- Math.pow(NodeMatrix[np(n, 'dy')], 2)
- );
-
- if (force > W.maxForce) {
- NodeMatrix[np(n, 'dx')] =
- NodeMatrix[np(n, 'dx')] * W.maxForce / force;
- NodeMatrix[np(n, 'dy')] =
- NodeMatrix[np(n, 'dy')] * W.maxForce / force;
- }
-
- swinging = NodeMatrix[np(n, 'mass')] *
- Math.sqrt(
- (NodeMatrix[np(n, 'old_dx')] - NodeMatrix[np(n, 'dx')]) *
- (NodeMatrix[np(n, 'old_dx')] - NodeMatrix[np(n, 'dx')]) +
- (NodeMatrix[np(n, 'old_dy')] - NodeMatrix[np(n, 'dy')]) *
- (NodeMatrix[np(n, 'old_dy')] - NodeMatrix[np(n, 'dy')])
- );
-
- traction = Math.sqrt(
- (NodeMatrix[np(n, 'old_dx')] + NodeMatrix[np(n, 'dx')]) *
- (NodeMatrix[np(n, 'old_dx')] + NodeMatrix[np(n, 'dx')]) +
- (NodeMatrix[np(n, 'old_dy')] + NodeMatrix[np(n, 'dy')]) *
- (NodeMatrix[np(n, 'old_dy')] + NodeMatrix[np(n, 'dy')])
- ) / 2;
-
- nodespeed =
- 0.1 * Math.log(1 + traction) / (1 + Math.sqrt(swinging));
-
- // Updating node's positon
- NodeMatrix[np(n, 'x')] =
- NodeMatrix[np(n, 'x')] + NodeMatrix[np(n, 'dx')] *
- (nodespeed / W.settings.slowDown);
- NodeMatrix[np(n, 'y')] =
- NodeMatrix[np(n, 'y')] + NodeMatrix[np(n, 'dy')] *
- (nodespeed / W.settings.slowDown);
- }
- }
- }
- else {
-
- for (n = 0; n < W.nodesLength; n += W.ppn) {
- if (!NodeMatrix[np(n, 'fixed')]) {
-
- swinging = NodeMatrix[np(n, 'mass')] *
- Math.sqrt(
- (NodeMatrix[np(n, 'old_dx')] - NodeMatrix[np(n, 'dx')]) *
- (NodeMatrix[np(n, 'old_dx')] - NodeMatrix[np(n, 'dx')]) +
- (NodeMatrix[np(n, 'old_dy')] - NodeMatrix[np(n, 'dy')]) *
- (NodeMatrix[np(n, 'old_dy')] - NodeMatrix[np(n, 'dy')])
- );
-
- traction = Math.sqrt(
- (NodeMatrix[np(n, 'old_dx')] + NodeMatrix[np(n, 'dx')]) *
- (NodeMatrix[np(n, 'old_dx')] + NodeMatrix[np(n, 'dx')]) +
- (NodeMatrix[np(n, 'old_dy')] + NodeMatrix[np(n, 'dy')]) *
- (NodeMatrix[np(n, 'old_dy')] + NodeMatrix[np(n, 'dy')])
- ) / 2;
-
- nodespeed = NodeMatrix[np(n, 'convergence')] *
- Math.log(1 + traction) / (1 + Math.sqrt(swinging));
-
- // Updating node convergence
- NodeMatrix[np(n, 'convergence')] =
- Math.min(1, Math.sqrt(
- nodespeed *
- (Math.pow(NodeMatrix[np(n, 'dx')], 2) +
- Math.pow(NodeMatrix[np(n, 'dy')], 2)) /
- (1 + Math.sqrt(swinging))
- ));
-
- // Updating node's positon
- NodeMatrix[np(n, 'x')] =
- NodeMatrix[np(n, 'x')] + NodeMatrix[np(n, 'dx')] *
- (nodespeed / W.settings.slowDown);
- NodeMatrix[np(n, 'y')] =
- NodeMatrix[np(n, 'y')] + NodeMatrix[np(n, 'dy')] *
- (nodespeed / W.settings.slowDown);
- }
- }
- }
-
- // Counting one more iteration
- W.iterations++;
- }
-
- /**
- * Message reception & sending
- */
-
- // Sending data back to the supervisor
- var sendNewCoords;
-
- if (typeof window !== 'undefined' && window.document) {
-
- // From same document as sigma
- sendNewCoords = function() {
- var e;
-
- if (document.createEvent) {
- e = document.createEvent('Event');
- e.initEvent('newCoords', true, false);
- }
- else {
- e = document.createEventObject();
- e.eventType = 'newCoords';
- }
-
- e.eventName = 'newCoords';
- e.data = {
- nodes: NodeMatrix.buffer
- };
- requestAnimationFrame(function() {
- document.dispatchEvent(e);
- });
- };
- }
- else {
-
- // From a WebWorker
- sendNewCoords = function() {
- self.postMessage(
- {nodes: NodeMatrix.buffer},
- [NodeMatrix.buffer]
- );
- };
- }
-
- // Algorithm run
- function run(n) {
- for (var i = 0; i < n; i++)
- pass();
- sendNewCoords();
- }
-
- // On supervisor message
- var listener = function(e) {
- switch (e.data.action) {
- case 'start':
- init(
- new Float32Array(e.data.nodes),
- new Float32Array(e.data.edges),
- e.data.config
- );
-
- // First iteration(s)
- run(W.settings.startingIterations);
- break;
-
- case 'loop':
- NodeMatrix = new Float32Array(e.data.nodes);
- run(W.settings.iterationsPerRender);
- break;
-
- case 'config':
-
- // Merging new settings
- configure(e.data.config);
- break;
-
- case 'kill':
-
- // Deleting context for garbage collection
- __emptyObject(W);
- NodeMatrix = null;
- EdgeMatrix = null;
- RegionMatrix = null;
- self.removeEventListener('message', listener);
- break;
-
- default:
- }
- };
-
- // Adding event listener
- self.addEventListener('message', listener);
- };
-
-
- /**
- * Exporting
- * ----------
- *
- * Crush the worker function and make it accessible by sigma's instances so
- * the supervisor can call it.
- */
- function crush(fnString) {
- var pattern,
- i,
- l;
-
- var np = [
- 'x',
- 'y',
- 'dx',
- 'dy',
- 'old_dx',
- 'old_dy',
- 'mass',
- 'convergence',
- 'size',
- 'fixed'
- ];
-
- var ep = [
- 'source',
- 'target',
- 'weight'
- ];
-
- var rp = [
- 'node',
- 'centerX',
- 'centerY',
- 'size',
- 'nextSibling',
- 'firstChild',
- 'mass',
- 'massCenterX',
- 'massCenterY'
- ];
-
- // rp
- // NOTE: Must go first
- for (i = 0, l = rp.length; i < l; i++) {
- pattern = new RegExp('rp\\(([^,]*), \'' + rp[i] + '\'\\)', 'g');
- fnString = fnString.replace(
- pattern,
- (i === 0) ? '$1' : '$1 + ' + i
- );
- }
-
- // np
- for (i = 0, l = np.length; i < l; i++) {
- pattern = new RegExp('np\\(([^,]*), \'' + np[i] + '\'\\)', 'g');
- fnString = fnString.replace(
- pattern,
- (i === 0) ? '$1' : '$1 + ' + i
- );
- }
-
- // ep
- for (i = 0, l = ep.length; i < l; i++) {
- pattern = new RegExp('ep\\(([^,]*), \'' + ep[i] + '\'\\)', 'g');
- fnString = fnString.replace(
- pattern,
- (i === 0) ? '$1' : '$1 + ' + i
- );
- }
-
- return fnString;
- }
-
- // Exporting
- function getWorkerFn() {
- var fnString = crush ? crush(Worker.toString()) : Worker.toString();
- return ';(' + fnString + ').call(this);';
- }
-
- if (inWebWorker) {
-
- // We are in a webworker, so we launch the Worker function
- eval(getWorkerFn());
- }
- else {
-
- // We are requesting the worker from sigma, we retrieve it therefore
- if (typeof sigma === 'undefined')
- throw 'sigma is not declared';
-
- sigma.prototype.getForceAtlas2Worker = getWorkerFn;
- }
- }).call(this);
|