graphology-layout-forceatlas2
Advanced tools
Comparing version 0.6.1 to 0.7.0
@@ -9,3 +9,3 @@ /** | ||
adjustSizes: false, | ||
edgeWeightInfluence: 0, | ||
edgeWeightInfluence: 1, | ||
scalingRatio: 1, | ||
@@ -12,0 +12,0 @@ strongGravityMode: false, |
156
helpers.js
@@ -11,4 +11,4 @@ /** | ||
*/ | ||
var PPN = 10, | ||
PPE = 3; | ||
var PPN = 10; | ||
var PPE = 3; | ||
@@ -22,16 +22,14 @@ /** | ||
*/ | ||
exports.assign = function(target) { | ||
exports.assign = function (target) { | ||
target = target || {}; | ||
var objects = Array.prototype.slice.call(arguments).slice(1), | ||
i, | ||
k, | ||
l; | ||
i, | ||
k, | ||
l; | ||
for (i = 0, l = objects.length; i < l; i++) { | ||
if (!objects[i]) | ||
continue; | ||
if (!objects[i]) continue; | ||
for (k in objects[i]) | ||
target[k] = objects[i][k]; | ||
for (k in objects[i]) target[k] = objects[i][k]; | ||
} | ||
@@ -48,47 +46,65 @@ | ||
*/ | ||
exports.validateSettings = function(settings) { | ||
if ('linLogMode' in settings && | ||
typeof settings.linLogMode !== 'boolean') | ||
exports.validateSettings = function (settings) { | ||
if ('linLogMode' in settings && typeof settings.linLogMode !== 'boolean') | ||
return {message: 'the `linLogMode` setting should be a boolean.'}; | ||
if ('outboundAttractionDistribution' in settings && | ||
typeof settings.outboundAttractionDistribution !== 'boolean') | ||
return {message: 'the `outboundAttractionDistribution` setting should be a boolean.'}; | ||
if ( | ||
'outboundAttractionDistribution' in settings && | ||
typeof settings.outboundAttractionDistribution !== 'boolean' | ||
) | ||
return { | ||
message: | ||
'the `outboundAttractionDistribution` setting should be a boolean.' | ||
}; | ||
if ('adjustSizes' in settings && | ||
typeof settings.adjustSizes !== 'boolean') | ||
if ('adjustSizes' in settings && typeof settings.adjustSizes !== 'boolean') | ||
return {message: 'the `adjustSizes` setting should be a boolean.'}; | ||
if ('edgeWeightInfluence' in settings && | ||
typeof settings.edgeWeightInfluence !== 'number' && | ||
settings.edgeWeightInfluence < 0) | ||
return {message: 'the `edgeWeightInfluence` setting should be a number >= 0.'}; | ||
if ( | ||
'edgeWeightInfluence' in settings && | ||
typeof settings.edgeWeightInfluence !== 'number' && | ||
settings.edgeWeightInfluence < 1 | ||
) | ||
return { | ||
message: 'the `edgeWeightInfluence` setting should be a number > 0.' | ||
}; | ||
if ('scalingRatio' in settings && | ||
typeof settings.scalingRatio !== 'number' && | ||
settings.scalingRatio < 0) | ||
if ( | ||
'scalingRatio' in settings && | ||
typeof settings.scalingRatio !== 'number' && | ||
settings.scalingRatio < 0 | ||
) | ||
return {message: 'the `scalingRatio` setting should be a number >= 0.'}; | ||
if ('strongGravityMode' in settings && | ||
typeof settings.strongGravityMode !== 'boolean') | ||
if ( | ||
'strongGravityMode' in settings && | ||
typeof settings.strongGravityMode !== 'boolean' | ||
) | ||
return {message: 'the `strongGravityMode` setting should be a boolean.'}; | ||
if ('gravity' in settings && | ||
typeof settings.gravity !== 'number' && | ||
settings.gravity < 0) | ||
if ( | ||
'gravity' in settings && | ||
typeof settings.gravity !== 'number' && | ||
settings.gravity < 0 | ||
) | ||
return {message: 'the `gravity` setting should be a number >= 0.'}; | ||
if ('slowDown' in settings && | ||
typeof settings.slowDown !== 'number' && | ||
settings.slowDown < 0) | ||
if ( | ||
'slowDown' in settings && | ||
typeof settings.slowDown !== 'number' && | ||
settings.slowDown < 0 | ||
) | ||
return {message: 'the `slowDown` setting should be a number >= 0.'}; | ||
if ('barnesHutOptimize' in settings && | ||
typeof settings.barnesHutOptimize !== 'boolean') | ||
if ( | ||
'barnesHutOptimize' in settings && | ||
typeof settings.barnesHutOptimize !== 'boolean' | ||
) | ||
return {message: 'the `barnesHutOptimize` setting should be a boolean.'}; | ||
if ('barnesHutTheta' in settings && | ||
typeof settings.barnesHutTheta !== 'number' && | ||
settings.barnesHutTheta < 0) | ||
if ( | ||
'barnesHutTheta' in settings && | ||
typeof settings.barnesHutTheta !== 'number' && | ||
settings.barnesHutTheta < 0 | ||
) | ||
return {message: 'the `barnesHutTheta` setting should be a number >= 0.'}; | ||
@@ -102,18 +118,18 @@ | ||
* | ||
* @param {Graph} graph - Target graph. | ||
* @return {object} - Both matrices. | ||
* @param {Graph} graph - Target graph. | ||
* @param {string|null} weightAttribute - Name of the edge weight attribute. | ||
* @return {object} - Both matrices. | ||
*/ | ||
exports.graphToByteArrays = function(graph) { | ||
var order = graph.order, | ||
size = graph.size, | ||
index = {}, | ||
j; | ||
exports.graphToByteArrays = function (graph, weightAttribute) { | ||
var order = graph.order; | ||
var size = graph.size; | ||
var index = {}; | ||
var j; | ||
var NodeMatrix = new Float32Array(order * PPN), | ||
EdgeMatrix = new Float32Array(size * PPE); | ||
var NodeMatrix = new Float32Array(order * PPN); | ||
var EdgeMatrix = new Float32Array(size * PPE); | ||
// Iterate through nodes | ||
j = 0; | ||
graph.forEachNode(function(node, attr) { | ||
graph.forEachNode(function (node, attr) { | ||
// Node index | ||
@@ -137,9 +153,18 @@ index[node] = j; | ||
// Iterate through edges | ||
var weightGetter = function (attr) { | ||
if (!weightAttribute) return 1; | ||
var w = attr[weightAttribute]; | ||
if (typeof w !== 'number' || isNaN(w)) w = 1; | ||
return w; | ||
}; | ||
j = 0; | ||
graph.forEachEdge(function(edge, attr, source, target) { | ||
graph.forEachEdge(function (_, attr, source, target) { | ||
// Populating byte array | ||
EdgeMatrix[j] = index[source]; | ||
EdgeMatrix[j + 1] = index[target]; | ||
EdgeMatrix[j + 2] = attr.weight || 0; | ||
EdgeMatrix[j + 2] = weightGetter(attr); | ||
j += PPE; | ||
@@ -160,13 +185,16 @@ }); | ||
*/ | ||
exports.assignLayoutChanges = function(graph, NodeMatrix) { | ||
exports.assignLayoutChanges = function (graph, NodeMatrix) { | ||
var i = 0; | ||
graph.updateEachNodeAttributes(function(node, attr) { | ||
attr.x = NodeMatrix[i]; | ||
attr.y = NodeMatrix[i + 1]; | ||
graph.updateEachNodeAttributes( | ||
function (node, attr) { | ||
attr.x = NodeMatrix[i]; | ||
attr.y = NodeMatrix[i + 1]; | ||
i += PPN; | ||
i += PPN; | ||
return attr; | ||
}, {attributes: ['x', 'y']}); | ||
return attr; | ||
}, | ||
{attributes: ['x', 'y']} | ||
); | ||
}; | ||
@@ -181,5 +209,5 @@ | ||
*/ | ||
exports.collectLayoutChanges = function(graph, NodeMatrix) { | ||
exports.collectLayoutChanges = function (graph, NodeMatrix) { | ||
var nodes = graph.nodes(), | ||
positions = {}; | ||
positions = {}; | ||
@@ -207,3 +235,5 @@ for (var i = 0, j = 0, l = NodeMatrix.length; i < l; i += PPN) { | ||
var code = fn.toString(); | ||
var objectUrl = xURL.createObjectURL(new Blob(['(' + code + ').call(this);'], {type: 'text/javascript'})); | ||
var objectUrl = xURL.createObjectURL( | ||
new Blob(['(' + code + ').call(this);'], {type: 'text/javascript'}) | ||
); | ||
var worker = new Worker(objectUrl); | ||
@@ -210,0 +240,0 @@ xURL.revokeObjectURL(objectUrl); |
import Graph from 'graphology-types'; | ||
type LayoutMapping = {[key: string]: {x: number, y: number}}; | ||
type LayoutMapping = {[key: string]: {x: number; y: number}}; | ||
export type ForceAtlas2Settings = { | ||
linLogMode?: boolean, | ||
outboundAttractionDistribution?: boolean, | ||
adjustSizes?: boolean, | ||
edgeWeightInfluence?: number, | ||
scalingRatio?: number, | ||
strongGravityMode?: boolean, | ||
gravity?: number, | ||
slowDown?: number, | ||
barnesHutOptimize?: boolean, | ||
barnesHutTheta?: number | ||
linLogMode?: boolean; | ||
outboundAttractionDistribution?: boolean; | ||
adjustSizes?: boolean; | ||
edgeWeightInfluence?: number; | ||
scalingRatio?: number; | ||
strongGravityMode?: boolean; | ||
gravity?: number; | ||
slowDown?: number; | ||
barnesHutOptimize?: boolean; | ||
barnesHutTheta?: number; | ||
}; | ||
export type ForceAtlas2LayoutOptions = { | ||
iterations: number, | ||
settings?: ForceAtlas2Settings | ||
attributes?: { | ||
weight?: string; | ||
}; | ||
iterations: number; | ||
settings?: ForceAtlas2Settings; | ||
weighted: boolean; | ||
}; | ||
@@ -22,0 +26,0 @@ |
38
index.js
@@ -8,4 +8,4 @@ /** | ||
var isGraph = require('graphology-utils/is-graph'), | ||
iterate = require('./iterate.js'), | ||
helpers = require('./helpers.js'); | ||
iterate = require('./iterate.js'), | ||
helpers = require('./helpers.js'); | ||
@@ -20,2 +20,5 @@ var DEFAULT_SETTINGS = require('./defaults.js'); | ||
* @param {object|number} params - If number, params.iterations, else: | ||
* @param {object} attributes - Attribute names: | ||
* @param {string} weight - Name of the edge weight attribute. | ||
* @param {boolean} weighted - Whether to take edge weights into account. | ||
* @param {number} iterations - Number of iterations. | ||
@@ -27,6 +30,7 @@ * @param {object} [settings] - Settings. | ||
if (!isGraph(graph)) | ||
throw new Error('graphology-layout-forceatlas2: the given graph is not a valid graphology instance.'); | ||
throw new Error( | ||
'graphology-layout-forceatlas2: the given graph is not a valid graphology instance.' | ||
); | ||
if (typeof params === 'number') | ||
params = {iterations: params}; | ||
if (typeof params === 'number') params = {iterations: params}; | ||
@@ -36,18 +40,28 @@ var iterations = params.iterations; | ||
if (typeof iterations !== 'number') | ||
throw new Error('graphology-layout-forceatlas2: invalid number of iterations.'); | ||
throw new Error( | ||
'graphology-layout-forceatlas2: invalid number of iterations.' | ||
); | ||
if (iterations <= 0) | ||
throw new Error('graphology-layout-forceatlas2: you should provide a positive number of iterations.'); | ||
throw new Error( | ||
'graphology-layout-forceatlas2: you should provide a positive number of iterations.' | ||
); | ||
var attributes = params.attributes || {}; | ||
var weightAttribute = params.weighted ? attributes.weight || 'weight' : null; | ||
// Validating settings | ||
var settings = helpers.assign({}, DEFAULT_SETTINGS, params.settings), | ||
validationError = helpers.validateSettings(settings); | ||
var settings = helpers.assign({}, DEFAULT_SETTINGS, params.settings); | ||
var validationError = helpers.validateSettings(settings); | ||
if (validationError) | ||
throw new Error('graphology-layout-forceatlas2: ' + validationError.message); | ||
throw new Error( | ||
'graphology-layout-forceatlas2: ' + validationError.message | ||
); | ||
// Building matrices | ||
var matrices = helpers.graphToByteArrays(graph), | ||
i; | ||
var matrices = helpers.graphToByteArrays(graph, weightAttribute); | ||
var i; | ||
// Iterating | ||
@@ -54,0 +68,0 @@ for (i = 0; i < iterations; i++) |
524
iterate.js
@@ -12,26 +12,26 @@ /* eslint no-constant-condition: 0 */ | ||
*/ | ||
var NODE_X = 0, | ||
NODE_Y = 1, | ||
NODE_DX = 2, | ||
NODE_DY = 3, | ||
NODE_OLD_DX = 4, | ||
NODE_OLD_DY = 5, | ||
NODE_MASS = 6, | ||
NODE_CONVERGENCE = 7, | ||
NODE_SIZE = 8, | ||
NODE_FIXED = 9; | ||
var NODE_X = 0; | ||
var NODE_Y = 1; | ||
var NODE_DX = 2; | ||
var NODE_DY = 3; | ||
var NODE_OLD_DX = 4; | ||
var NODE_OLD_DY = 5; | ||
var NODE_MASS = 6; | ||
var NODE_CONVERGENCE = 7; | ||
var NODE_SIZE = 8; | ||
var NODE_FIXED = 9; | ||
var EDGE_SOURCE = 0, | ||
EDGE_TARGET = 1, | ||
EDGE_WEIGHT = 2; | ||
var EDGE_SOURCE = 0; | ||
var EDGE_TARGET = 1; | ||
var EDGE_WEIGHT = 2; | ||
var REGION_NODE = 0, | ||
REGION_CENTER_X = 1, | ||
REGION_CENTER_Y = 2, | ||
REGION_SIZE = 3, | ||
REGION_NEXT_SIBLING = 4, | ||
REGION_FIRST_CHILD = 5, | ||
REGION_MASS = 6, | ||
REGION_MASS_CENTER_X = 7, | ||
REGION_MASS_CENTER_Y = 8; | ||
var REGION_NODE = 0; | ||
var REGION_CENTER_X = 1; | ||
var REGION_CENTER_Y = 2; | ||
var REGION_SIZE = 3; | ||
var REGION_NEXT_SIBLING = 4; | ||
var REGION_FIRST_CHILD = 5; | ||
var REGION_MASS = 6; | ||
var REGION_MASS_CENTER_X = 7; | ||
var REGION_MASS_CENTER_Y = 8; | ||
@@ -43,5 +43,5 @@ var SUBDIVISION_ATTEMPTS = 3; | ||
*/ | ||
var PPN = 10, | ||
PPE = 3, | ||
PPR = 9; | ||
var PPN = 10; | ||
var PPE = 3; | ||
var PPR = 9; | ||
@@ -59,3 +59,2 @@ var MAX_FORCE = 10; | ||
module.exports = function iterate(options, NodeMatrix, EdgeMatrix) { | ||
// Initializing variables | ||
@@ -65,3 +64,3 @@ var l, r, n, n1, n2, rn, e, w, g, s; | ||
var order = NodeMatrix.length, | ||
size = EdgeMatrix.length; | ||
size = EdgeMatrix.length; | ||
@@ -72,9 +71,3 @@ var adjustSizes = options.adjustSizes; | ||
var outboundAttCompensation, | ||
coefficient, | ||
xDist, | ||
yDist, | ||
ewc, | ||
distance, | ||
factor; | ||
var outboundAttCompensation, coefficient, xDist, yDist, ewc, distance, factor; | ||
@@ -101,6 +94,5 @@ var RegionMatrix = []; | ||
outboundAttCompensation /= (order / PPN); | ||
outboundAttCompensation /= order / PPN; | ||
} | ||
// 1.bis) Barnes-Hut computation | ||
@@ -110,9 +102,10 @@ //------------------------------ | ||
if (options.barnesHutOptimize) { | ||
// Setting up | ||
var minX = Infinity, | ||
maxX = -Infinity, | ||
minY = Infinity, | ||
maxY = -Infinity, | ||
q, q2, subdivisionAttempts; | ||
maxX = -Infinity, | ||
minY = Infinity, | ||
maxY = -Infinity, | ||
q, | ||
q2, | ||
subdivisionAttempts; | ||
@@ -128,8 +121,8 @@ // Computing min and max values | ||
// squarify bounds, it's a quadtree | ||
var dx = maxX - minX, dy = maxY - minY; | ||
var dx = maxX - minX, | ||
dy = maxY - minY; | ||
if (dx > dy) { | ||
minY -= (dx - dy) / 2; | ||
maxY = minY + dx; | ||
} | ||
else { | ||
} else { | ||
minX -= (dy - dx) / 2; | ||
@@ -153,3 +146,2 @@ maxX = minX + dy; | ||
for (n = 0; n < order; n += PPN) { | ||
// Current region, starting with root | ||
@@ -164,3 +156,2 @@ r = 0; | ||
if (RegionMatrix[r + REGION_FIRST_CHILD] >= 0) { | ||
// There are sub-regions | ||
@@ -174,22 +165,14 @@ | ||
if (NodeMatrix[n + NODE_X] < RegionMatrix[r + REGION_CENTER_X]) { | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
else { | ||
} else { | ||
// Bottom Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR; | ||
} | ||
} | ||
else { | ||
} else { | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Right quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2; | ||
} | ||
else { | ||
} else { | ||
// Bottom Right quarter | ||
@@ -202,9 +185,11 @@ q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3; | ||
RegionMatrix[r + REGION_MASS_CENTER_X] = | ||
(RegionMatrix[r + REGION_MASS_CENTER_X] * RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_X] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS_CENTER_X] * | ||
RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_X] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS] + NodeMatrix[n + NODE_MASS]); | ||
RegionMatrix[r + REGION_MASS_CENTER_Y] = | ||
(RegionMatrix[r + REGION_MASS_CENTER_Y] * RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_Y] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS_CENTER_Y] * | ||
RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_Y] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS] + NodeMatrix[n + NODE_MASS]); | ||
@@ -217,5 +202,3 @@ | ||
continue; | ||
} | ||
else { | ||
} else { | ||
// There are no sub-regions: we are in a "leaf" | ||
@@ -225,3 +208,2 @@ | ||
if (RegionMatrix[r + REGION_NODE] < 0) { | ||
// There is no node in region: | ||
@@ -231,5 +213,3 @@ // we record node n and go on | ||
break; | ||
} | ||
else { | ||
} else { | ||
// There is a node in this region | ||
@@ -253,4 +233,6 @@ | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
@@ -266,4 +248,6 @@ RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR; | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
@@ -279,4 +263,6 @@ RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR; | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
@@ -292,6 +278,9 @@ RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR; | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
RegionMatrix[g + REGION_NEXT_SIBLING] = RegionMatrix[r + REGION_NEXT_SIBLING]; | ||
RegionMatrix[g + REGION_NEXT_SIBLING] = | ||
RegionMatrix[r + REGION_NEXT_SIBLING]; | ||
RegionMatrix[g + REGION_FIRST_CHILD] = -1; | ||
@@ -309,22 +298,24 @@ RegionMatrix[g + REGION_MASS] = 0; | ||
// Find the quadrant of the old node | ||
if (NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X] < RegionMatrix[r + REGION_CENTER_X]) { | ||
if (NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
if ( | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X] < | ||
RegionMatrix[r + REGION_CENTER_X] | ||
) { | ||
if ( | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < | ||
RegionMatrix[r + REGION_CENTER_Y] | ||
) { | ||
// Top Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
else { | ||
} else { | ||
// Bottom Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR; | ||
} | ||
} | ||
else { | ||
if (NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
} else { | ||
if ( | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < | ||
RegionMatrix[r + REGION_CENTER_Y] | ||
) { | ||
// Top Right quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2; | ||
} | ||
else { | ||
} else { | ||
// Bottom Right quarter | ||
@@ -336,5 +327,8 @@ q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3; | ||
// We remove r[0] from the region r, add its mass to r and record it in q | ||
RegionMatrix[r + REGION_MASS] = NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_MASS]; | ||
RegionMatrix[r + REGION_MASS_CENTER_X] = NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X]; | ||
RegionMatrix[r + REGION_MASS_CENTER_Y] = NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y]; | ||
RegionMatrix[r + REGION_MASS] = | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_MASS]; | ||
RegionMatrix[r + REGION_MASS_CENTER_X] = | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X]; | ||
RegionMatrix[r + REGION_MASS_CENTER_Y] = | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y]; | ||
@@ -347,19 +341,13 @@ RegionMatrix[q + REGION_NODE] = RegionMatrix[r + REGION_NODE]; | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Left quarter | ||
q2 = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
else { | ||
} else { | ||
// Bottom Left quarter | ||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR; | ||
} | ||
} | ||
else { | ||
} else { | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Right quarter | ||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2; | ||
} | ||
else { | ||
} else { | ||
// Bottom Right quarter | ||
@@ -371,3 +359,2 @@ q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3; | ||
if (q === q2) { | ||
// If both nodes are in the same quadrant, | ||
@@ -378,4 +365,3 @@ // we have to try it again on this quadrant | ||
continue; // while | ||
} | ||
else { | ||
} else { | ||
// we are out of precision here, and we cannot subdivide anymore | ||
@@ -386,3 +372,2 @@ // but we have to break the loop anyway | ||
} | ||
} | ||
@@ -400,3 +385,2 @@ | ||
// 2) Repulsion | ||
@@ -411,3 +395,2 @@ //-------------- | ||
for (n = 0; n < order; n += PPN) { | ||
// Computing leaf quad nodes iteration | ||
@@ -417,12 +400,15 @@ | ||
while (true) { | ||
if (RegionMatrix[r + REGION_FIRST_CHILD] >= 0) { | ||
// The region has sub-regions | ||
// We run the Barnes Hut test to see if we are at the right distance | ||
distance = ( | ||
(Math.pow(NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X], 2)) + | ||
(Math.pow(NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y], 2)) | ||
); | ||
distance = | ||
Math.pow( | ||
NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X], | ||
2 | ||
) + | ||
Math.pow( | ||
NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y], | ||
2 | ||
); | ||
@@ -432,21 +418,26 @@ s = RegionMatrix[r + REGION_SIZE]; | ||
if ((4 * s * s) / distance < thetaSquared) { | ||
// We treat the region as a single body, and we repulse | ||
xDist = NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X]; | ||
yDist = NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y]; | ||
xDist = | ||
NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X]; | ||
yDist = | ||
NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y]; | ||
if (adjustSizes === true) { | ||
//-- Linear Anti-collision Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS]) / | ||
distance; | ||
NodeMatrix[n + NODE_DX] += xDist * factor; | ||
NodeMatrix[n + NODE_DY] += yDist * factor; | ||
} | ||
else if (distance < 0) { | ||
factor = -coefficient * NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS] / Math.sqrt(distance); | ||
} else if (distance < 0) { | ||
factor = | ||
(-coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS]) / | ||
Math.sqrt(distance); | ||
@@ -456,9 +447,10 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS]) / | ||
distance; | ||
@@ -472,9 +464,6 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
r = RegionMatrix[r + REGION_NEXT_SIBLING]; | ||
if (r < 0) | ||
break; // No next sibling: we have finished the tree | ||
if (r < 0) break; // No next sibling: we have finished the tree | ||
continue; | ||
} | ||
else { | ||
} else { | ||
// The region is too close and we have to look at sub-regions | ||
@@ -484,6 +473,3 @@ r = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
} | ||
else { | ||
} else { | ||
// The region has no sub-region | ||
@@ -500,14 +486,18 @@ // If there is a node r[0] and it is not n, then repulse | ||
if (adjustSizes === true) { | ||
//-- Linear Anti-collision Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS]) / | ||
distance; | ||
NodeMatrix[n + NODE_DX] += xDist * factor; | ||
NodeMatrix[n + NODE_DY] += yDist * factor; | ||
} | ||
else if (distance < 0) { | ||
factor = -coefficient * NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS] / Math.sqrt(distance); | ||
} else if (distance < 0) { | ||
factor = | ||
(-coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS]) / | ||
Math.sqrt(distance); | ||
@@ -517,9 +507,10 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS]) / | ||
distance; | ||
@@ -530,3 +521,2 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
} | ||
} | ||
@@ -537,4 +527,3 @@ | ||
if (r < 0) | ||
break; // No next sibling: we have finished the tree | ||
if (r < 0) break; // No next sibling: we have finished the tree | ||
@@ -545,4 +534,3 @@ continue; | ||
} | ||
} | ||
else { | ||
} else { | ||
coefficient = options.scalingRatio; | ||
@@ -553,3 +541,2 @@ | ||
for (n2 = 0; n2 < n1; n2 += PPN) { | ||
// Common to both methods | ||
@@ -560,5 +547,5 @@ xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X]; | ||
if (adjustSizes === true) { | ||
//-- Anticollision Linear Repulsion | ||
distance = Math.sqrt(xDist * xDist + yDist * yDist) - | ||
distance = | ||
Math.sqrt(xDist * xDist + yDist * yDist) - | ||
NodeMatrix[n1 + NODE_SIZE] - | ||
@@ -568,6 +555,8 @@ NodeMatrix[n2 + NODE_SIZE]; | ||
if (distance > 0) { | ||
factor = coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS] / | ||
distance / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS]) / | ||
distance / | ||
distance; | ||
@@ -580,5 +569,6 @@ // Updating nodes' dx and dy | ||
NodeMatrix[n2 + NODE_DY] += yDist * factor; | ||
} | ||
else if (distance < 0) { | ||
factor = 100 * coefficient * | ||
} else if (distance < 0) { | ||
factor = | ||
100 * | ||
coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
@@ -594,5 +584,3 @@ NodeMatrix[n2 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Repulsion | ||
@@ -602,6 +590,8 @@ distance = Math.sqrt(xDist * xDist + yDist * yDist); | ||
if (distance > 0) { | ||
factor = coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS] / | ||
distance / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS]) / | ||
distance / | ||
distance; | ||
@@ -620,3 +610,2 @@ // Updating nodes' dx and dy | ||
// 3) Gravity | ||
@@ -632,17 +621,11 @@ //------------ | ||
yDist = NodeMatrix[n + NODE_Y]; | ||
distance = Math.sqrt( | ||
Math.pow(xDist, 2) + Math.pow(yDist, 2) | ||
); | ||
distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); | ||
if (options.strongGravityMode) { | ||
//-- Strong gravity | ||
if (distance > 0) | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * g; | ||
} | ||
else { | ||
if (distance > 0) factor = coefficient * NodeMatrix[n + NODE_MASS] * g; | ||
} else { | ||
//-- Linear Anti-collision Repulsion n | ||
if (distance > 0) | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * g / distance; | ||
factor = (coefficient * NodeMatrix[n + NODE_MASS] * g) / distance; | ||
} | ||
@@ -657,6 +640,4 @@ | ||
//--------------- | ||
coefficient = 1 * | ||
(options.outboundAttractionDistribution ? | ||
outboundAttCompensation : | ||
1); | ||
coefficient = | ||
1 * (options.outboundAttractionDistribution ? outboundAttCompensation : 1); | ||
@@ -679,7 +660,7 @@ // TODO: simplify distance | ||
if (adjustSizes === true) { | ||
distance = Math.sqrt( | ||
(Math.pow(xDist, 2) + Math.pow(yDist, 2)) - | ||
NodeMatrix[n1 + NODE_SIZE] - | ||
NodeMatrix[n2 + NODE_SIZE] | ||
Math.pow(xDist, 2) + | ||
Math.pow(yDist, 2) - | ||
NodeMatrix[n1 + NODE_SIZE] - | ||
NodeMatrix[n2 + NODE_SIZE] | ||
); | ||
@@ -689,28 +670,22 @@ | ||
if (options.outboundAttractionDistribution) { | ||
//-- LinLog Degree Distributed Anti-collision Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc * Math.log(1 + distance) / | ||
distance / | ||
NodeMatrix[n1 + NODE_MASS]; | ||
factor = | ||
(-coefficient * ewc * Math.log(1 + distance)) / | ||
distance / | ||
NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- LinLog Anti-collision Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc * Math.log(1 + distance) / distance; | ||
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance; | ||
} | ||
} | ||
} | ||
else { | ||
} else { | ||
if (options.outboundAttractionDistribution) { | ||
//-- Linear Degree Distributed Anti-collision Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc / NodeMatrix[n1 + NODE_MASS]; | ||
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Anti-collision Attraction | ||
@@ -722,36 +697,26 @@ if (distance > 0) { | ||
} | ||
} | ||
else { | ||
} else { | ||
distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); | ||
distance = Math.sqrt( | ||
Math.pow(xDist, 2) + Math.pow(yDist, 2) | ||
); | ||
if (options.linLogMode) { | ||
if (options.outboundAttractionDistribution) { | ||
//-- LinLog Degree Distributed Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc * Math.log(1 + distance) / | ||
factor = | ||
(-coefficient * ewc * Math.log(1 + distance)) / | ||
distance / | ||
NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- LinLog Attraction | ||
if (distance > 0) | ||
factor = -coefficient * ewc * Math.log(1 + distance) / distance; | ||
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance; | ||
} | ||
} | ||
else { | ||
} else { | ||
if (options.outboundAttractionDistribution) { | ||
//-- Linear Attraction Mass Distributed | ||
// NOTE: Distance is set to 1 to override next condition | ||
distance = 1; | ||
factor = -coefficient * ewc / NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
else { | ||
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_MASS]; | ||
} else { | ||
//-- Linear Attraction | ||
@@ -768,3 +733,2 @@ // NOTE: Distance is set to 1 to override next condition | ||
if (distance > 0) { | ||
// Updating nodes' dx and dy | ||
@@ -779,15 +743,8 @@ NodeMatrix[n1 + NODE_DX] += xDist * factor; | ||
// 5) Apply Forces | ||
//----------------- | ||
var force, | ||
swinging, | ||
traction, | ||
nodespeed, | ||
newX, | ||
newY; | ||
var force, swinging, traction, nodespeed, newX, newY; | ||
// MATH: sqrt and square distances | ||
if (adjustSizes === true) { | ||
for (n = 0; n < order; n += PPN) { | ||
@@ -797,3 +754,3 @@ if (NodeMatrix[n + NODE_FIXED] !== 1) { | ||
Math.pow(NodeMatrix[n + NODE_DX], 2) + | ||
Math.pow(NodeMatrix[n + NODE_DY], 2) | ||
Math.pow(NodeMatrix[n + NODE_DY], 2) | ||
); | ||
@@ -803,75 +760,82 @@ | ||
NodeMatrix[n + NODE_DX] = | ||
NodeMatrix[n + NODE_DX] * MAX_FORCE / force; | ||
(NodeMatrix[n + NODE_DX] * MAX_FORCE) / force; | ||
NodeMatrix[n + NODE_DY] = | ||
NodeMatrix[n + NODE_DY] * MAX_FORCE / force; | ||
(NodeMatrix[n + NODE_DY] * MAX_FORCE) / force; | ||
} | ||
swinging = NodeMatrix[n + NODE_MASS] * | ||
swinging = | ||
NodeMatrix[n + NODE_MASS] * | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
); | ||
traction = Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
traction = | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
nodespeed = | ||
0.1 * Math.log(1 + traction) / (1 + Math.sqrt(swinging)); | ||
nodespeed = (0.1 * Math.log(1 + traction)) / (1 + Math.sqrt(swinging)); | ||
// Updating node's positon | ||
newX = NodeMatrix[n + NODE_X] + NodeMatrix[n + NODE_DX] * | ||
(nodespeed / options.slowDown); | ||
newX = | ||
NodeMatrix[n + NODE_X] + | ||
NodeMatrix[n + NODE_DX] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_X] = newX; | ||
newY = NodeMatrix[n + NODE_Y] + NodeMatrix[n + NODE_DY] * | ||
(nodespeed / options.slowDown); | ||
newY = | ||
NodeMatrix[n + NODE_Y] + | ||
NodeMatrix[n + NODE_DY] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_Y] = newY; | ||
} | ||
} | ||
} | ||
else { | ||
} else { | ||
for (n = 0; n < order; n += PPN) { | ||
if (NodeMatrix[n + NODE_FIXED] !== 1) { | ||
swinging = NodeMatrix[n + NODE_MASS] * | ||
swinging = | ||
NodeMatrix[n + NODE_MASS] * | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
); | ||
traction = Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
traction = | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
nodespeed = NodeMatrix[n + NODE_CONVERGENCE] * | ||
Math.log(1 + traction) / (1 + Math.sqrt(swinging)); | ||
nodespeed = | ||
(NodeMatrix[n + NODE_CONVERGENCE] * Math.log(1 + traction)) / | ||
(1 + Math.sqrt(swinging)); | ||
// Updating node convergence | ||
NodeMatrix[n + NODE_CONVERGENCE] = | ||
Math.min(1, Math.sqrt( | ||
nodespeed * | ||
(Math.pow(NodeMatrix[n + NODE_DX], 2) + | ||
Math.pow(NodeMatrix[n + NODE_DY], 2)) / | ||
(1 + Math.sqrt(swinging)) | ||
)); | ||
NodeMatrix[n + NODE_CONVERGENCE] = Math.min( | ||
1, | ||
Math.sqrt( | ||
(nodespeed * | ||
(Math.pow(NodeMatrix[n + NODE_DX], 2) + | ||
Math.pow(NodeMatrix[n + NODE_DY], 2))) / | ||
(1 + Math.sqrt(swinging)) | ||
) | ||
); | ||
// Updating node's positon | ||
newX = NodeMatrix[n + NODE_X] + NodeMatrix[n + NODE_DX] * | ||
(nodespeed / options.slowDown); | ||
newX = | ||
NodeMatrix[n + NODE_X] + | ||
NodeMatrix[n + NODE_DX] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_X] = newX; | ||
newY = NodeMatrix[n + NODE_Y] + NodeMatrix[n + NODE_DY] * | ||
(nodespeed / options.slowDown); | ||
newY = | ||
NodeMatrix[n + NODE_Y] + | ||
NodeMatrix[n + NODE_DY] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_Y] = newY; | ||
@@ -878,0 +842,0 @@ } |
The MIT License (MIT) | ||
Copyright (c) 2016-2017 Guillaume Plique (Yomguithereal) | ||
Copyright (c) 2016-2021 Guillaume Plique (Yomguithereal) | ||
@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
{ | ||
"name": "graphology-layout-forceatlas2", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"description": "ForceAtlas 2 layout algorithm for graphology.", | ||
@@ -18,5 +18,3 @@ "main": "index.js", | ||
"clean": "rimraf webworker.js", | ||
"lint": "eslint '*.js'", | ||
"prepublish": "npm run clean && npm test && npm run lint && npm run template", | ||
"prepublishOnly": "npm run prepublish", | ||
"prepublishOnly": "npm run clean && npm test && npm run template", | ||
"template": "node ./scripts/template-webworker.js > webworker.js", | ||
@@ -27,3 +25,3 @@ "test": "mocha test.js" | ||
"type": "git", | ||
"url": "git+https://github.com/graphology/graphology-layout-forceatlas2.git" | ||
"url": "git+https://github.com/graphology/graphology.git" | ||
}, | ||
@@ -42,22 +40,5 @@ "keywords": [ | ||
"bugs": { | ||
"url": "https://github.com/graphology/graphology-layout-forceatlas2/issues" | ||
"url": "https://github.com/graphology/graphology/issues" | ||
}, | ||
"homepage": "https://github.com/graphology/graphology-layout-forceatlas2#readme", | ||
"devDependencies": { | ||
"@yomguithereal/eslint-config": "^4.0.0", | ||
"eslint": "^7.28.0", | ||
"graphology": "0.19.3", | ||
"graphology-generators": "^0.11.0", | ||
"graphology-layout": "^0.4.0", | ||
"graphology-types": "^0.19.2", | ||
"mocha": "^8.4.0", | ||
"rimraf": "^3.0.2", | ||
"seedrandom": "^3.0.5" | ||
}, | ||
"eslintConfig": { | ||
"extends": "@yomguithereal/eslint-config", | ||
"globals": { | ||
"Float32Array": true | ||
} | ||
}, | ||
"homepage": "https://github.com/graphology/graphology#readme", | ||
"peerDependencies": { | ||
@@ -64,0 +45,0 @@ "graphology-types": ">=0.19.0" |
@@ -1,3 +0,1 @@ | ||
[](https://travis-ci.org/graphology/graphology-layout-forceatlas2) | ||
# Graphology ForceAtlas2 | ||
@@ -19,7 +17,7 @@ | ||
* [Pre-requisite](#pre-requisite) | ||
* [Settings](#settings) | ||
* [Synchronous layout](#synchronous-layout) | ||
* [Webworker](#webworker) | ||
* [#.inferSettings](#infersettings) | ||
- [Pre-requisite](#pre-requisite) | ||
- [Settings](#settings) | ||
- [Synchronous layout](#synchronous-layout) | ||
- [Webworker](#webworker) | ||
- [#.inferSettings](#infersettings) | ||
@@ -34,12 +32,12 @@ ### Pre-requisites | ||
* **adjustSizes** *?boolean* [`false`]: should the node's sizes be taken into account? | ||
* **barnesHutOptimize** *?boolean* [`false`]: whether to use the Barnes-Hut approximation to compute repulsion in `O(n*log(n))` rather than default `O(n^2)`, `n` being the number of nodes. | ||
* **barnesHutTheta** *?number* [`0.5`]: Barnes-Hut approximation theta parameter. | ||
* **edgeWeightInfluence** *?number* [`0`]: influence of the edge's weights on the layout. | ||
* **gravity** *?number* [`1`]: strength of the layout's gravity. | ||
* **linLogMode** *?boolean* [`false`]: whether to use Noack's LinLog model. | ||
* **outboundAttractionDistribution** *?boolean* [`false`] | ||
* **scalingRatio** *?number* [`1`] | ||
* **slowDown** *?number* [`1`] | ||
* **strongGravityMode** *?boolean* [`false`] | ||
- **adjustSizes** _?boolean_ [`false`]: should the node's sizes be taken into account? | ||
- **barnesHutOptimize** _?boolean_ [`false`]: whether to use the Barnes-Hut approximation to compute repulsion in `O(n*log(n))` rather than default `O(n^2)`, `n` being the number of nodes. | ||
- **barnesHutTheta** _?number_ [`0.5`]: Barnes-Hut approximation theta parameter. | ||
- **edgeWeightInfluence** _?number_ [`1`]: influence of the edge's weights on the layout. To consider edge weight, don't forget to pass `weighted` as `true` when applying the [synchronous layout](#synchronous-layout) or when instantiating the [worker](#webworker). | ||
- **gravity** _?number_ [`1`]: strength of the layout's gravity. | ||
- **linLogMode** _?boolean_ [`false`]: whether to use Noack's LinLog model. | ||
- **outboundAttractionDistribution** _?boolean_ [`false`] | ||
- **scalingRatio** _?number_ [`1`] | ||
- **slowDown** _?number_ [`1`] | ||
- **strongGravityMode** _?boolean_ [`false`] | ||
@@ -65,8 +63,11 @@ ### Synchronous layout | ||
*Arguments* | ||
_Arguments_ | ||
* **graph** *Graph*: target graph. | ||
* **options** *object*: options: | ||
- **iterations** *number*: number of iterations to perform. | ||
- **settings** *?object*: the layout's settings (see [#settings](#settings)). | ||
- **graph** _Graph_: target graph. | ||
- **options** _object_: options: | ||
- **iterations** _number_: number of iterations to perform. | ||
- **attributes** _?object_: an object containing custom attribute name mapping: | ||
- **weight** _?string_ [`weight`]: name of the edge weight attribute. | ||
- **weighted** _?boolean_ [`false`]: whether to consider edge weight. | ||
- **settings** _?object_: the layout's settings (see [#settings](#settings)). | ||
@@ -77,3 +78,3 @@ ### Webworker | ||
*Example* | ||
_Example_ | ||
@@ -83,6 +84,7 @@ ```js | ||
const layout = new FA2Layout(graph); | ||
// The parameters are the same as for the synchronous version, minus `iterations` of course | ||
const layout = new FA2Layout(graph, {settings: {gravity: 1}, weighted: true}); | ||
// To start the layout | ||
layout.start({settings: {gravity: 1}}); | ||
layout.start(); | ||
@@ -106,3 +108,6 @@ // To stop the layout | ||
const sensibleSettings = forceAtlas2.inferSettings(graph); | ||
const positions = forceAtlas2(graph, {iterations: 50, settings: sensibleSettings}); | ||
const positions = forceAtlas2(graph, { | ||
iterations: 50, | ||
settings: sensibleSettings | ||
}); | ||
@@ -109,0 +114,0 @@ // Alternatively using the graph's order instead of a graph instance |
549
webworker.js
@@ -8,8 +8,7 @@ /** | ||
module.exports = function worker() { | ||
var NODES, | ||
EDGES; | ||
var NODES, EDGES; | ||
var moduleShim = {}; | ||
(function() { | ||
(function () { | ||
/* eslint no-constant-condition: 0 */ | ||
@@ -26,26 +25,26 @@ /** | ||
*/ | ||
var NODE_X = 0, | ||
NODE_Y = 1, | ||
NODE_DX = 2, | ||
NODE_DY = 3, | ||
NODE_OLD_DX = 4, | ||
NODE_OLD_DY = 5, | ||
NODE_MASS = 6, | ||
NODE_CONVERGENCE = 7, | ||
NODE_SIZE = 8, | ||
NODE_FIXED = 9; | ||
var NODE_X = 0; | ||
var NODE_Y = 1; | ||
var NODE_DX = 2; | ||
var NODE_DY = 3; | ||
var NODE_OLD_DX = 4; | ||
var NODE_OLD_DY = 5; | ||
var NODE_MASS = 6; | ||
var NODE_CONVERGENCE = 7; | ||
var NODE_SIZE = 8; | ||
var NODE_FIXED = 9; | ||
var EDGE_SOURCE = 0, | ||
EDGE_TARGET = 1, | ||
EDGE_WEIGHT = 2; | ||
var EDGE_SOURCE = 0; | ||
var EDGE_TARGET = 1; | ||
var EDGE_WEIGHT = 2; | ||
var REGION_NODE = 0, | ||
REGION_CENTER_X = 1, | ||
REGION_CENTER_Y = 2, | ||
REGION_SIZE = 3, | ||
REGION_NEXT_SIBLING = 4, | ||
REGION_FIRST_CHILD = 5, | ||
REGION_MASS = 6, | ||
REGION_MASS_CENTER_X = 7, | ||
REGION_MASS_CENTER_Y = 8; | ||
var REGION_NODE = 0; | ||
var REGION_CENTER_X = 1; | ||
var REGION_CENTER_Y = 2; | ||
var REGION_SIZE = 3; | ||
var REGION_NEXT_SIBLING = 4; | ||
var REGION_FIRST_CHILD = 5; | ||
var REGION_MASS = 6; | ||
var REGION_MASS_CENTER_X = 7; | ||
var REGION_MASS_CENTER_Y = 8; | ||
@@ -57,5 +56,5 @@ var SUBDIVISION_ATTEMPTS = 3; | ||
*/ | ||
var PPN = 10, | ||
PPE = 3, | ||
PPR = 9; | ||
var PPN = 10; | ||
var PPE = 3; | ||
var PPR = 9; | ||
@@ -73,3 +72,2 @@ var MAX_FORCE = 10; | ||
moduleShim.exports = function iterate(options, NodeMatrix, EdgeMatrix) { | ||
// Initializing variables | ||
@@ -79,3 +77,3 @@ var l, r, n, n1, n2, rn, e, w, g, s; | ||
var order = NodeMatrix.length, | ||
size = EdgeMatrix.length; | ||
size = EdgeMatrix.length; | ||
@@ -86,9 +84,3 @@ var adjustSizes = options.adjustSizes; | ||
var outboundAttCompensation, | ||
coefficient, | ||
xDist, | ||
yDist, | ||
ewc, | ||
distance, | ||
factor; | ||
var outboundAttCompensation, coefficient, xDist, yDist, ewc, distance, factor; | ||
@@ -115,6 +107,5 @@ var RegionMatrix = []; | ||
outboundAttCompensation /= (order / PPN); | ||
outboundAttCompensation /= order / PPN; | ||
} | ||
// 1.bis) Barnes-Hut computation | ||
@@ -124,9 +115,10 @@ //------------------------------ | ||
if (options.barnesHutOptimize) { | ||
// Setting up | ||
var minX = Infinity, | ||
maxX = -Infinity, | ||
minY = Infinity, | ||
maxY = -Infinity, | ||
q, q2, subdivisionAttempts; | ||
maxX = -Infinity, | ||
minY = Infinity, | ||
maxY = -Infinity, | ||
q, | ||
q2, | ||
subdivisionAttempts; | ||
@@ -142,8 +134,8 @@ // Computing min and max values | ||
// squarify bounds, it's a quadtree | ||
var dx = maxX - minX, dy = maxY - minY; | ||
var dx = maxX - minX, | ||
dy = maxY - minY; | ||
if (dx > dy) { | ||
minY -= (dx - dy) / 2; | ||
maxY = minY + dx; | ||
} | ||
else { | ||
} else { | ||
minX -= (dy - dx) / 2; | ||
@@ -167,3 +159,2 @@ maxX = minX + dy; | ||
for (n = 0; n < order; n += PPN) { | ||
// Current region, starting with root | ||
@@ -178,3 +169,2 @@ r = 0; | ||
if (RegionMatrix[r + REGION_FIRST_CHILD] >= 0) { | ||
// There are sub-regions | ||
@@ -188,22 +178,14 @@ | ||
if (NodeMatrix[n + NODE_X] < RegionMatrix[r + REGION_CENTER_X]) { | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
else { | ||
} else { | ||
// Bottom Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR; | ||
} | ||
} | ||
else { | ||
} else { | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Right quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2; | ||
} | ||
else { | ||
} else { | ||
// Bottom Right quarter | ||
@@ -216,9 +198,11 @@ q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3; | ||
RegionMatrix[r + REGION_MASS_CENTER_X] = | ||
(RegionMatrix[r + REGION_MASS_CENTER_X] * RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_X] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS_CENTER_X] * | ||
RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_X] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS] + NodeMatrix[n + NODE_MASS]); | ||
RegionMatrix[r + REGION_MASS_CENTER_Y] = | ||
(RegionMatrix[r + REGION_MASS_CENTER_Y] * RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_Y] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS_CENTER_Y] * | ||
RegionMatrix[r + REGION_MASS] + | ||
NodeMatrix[n + NODE_Y] * NodeMatrix[n + NODE_MASS]) / | ||
(RegionMatrix[r + REGION_MASS] + NodeMatrix[n + NODE_MASS]); | ||
@@ -231,5 +215,3 @@ | ||
continue; | ||
} | ||
else { | ||
} else { | ||
// There are no sub-regions: we are in a "leaf" | ||
@@ -239,3 +221,2 @@ | ||
if (RegionMatrix[r + REGION_NODE] < 0) { | ||
// There is no node in region: | ||
@@ -245,5 +226,3 @@ // we record node n and go on | ||
break; | ||
} | ||
else { | ||
} else { | ||
// There is a node in this region | ||
@@ -267,4 +246,6 @@ | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
@@ -280,4 +261,6 @@ RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR; | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] - w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
@@ -293,4 +276,6 @@ RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR; | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] - w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
@@ -306,6 +291,9 @@ RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR; | ||
RegionMatrix[g + REGION_NODE] = -1; | ||
RegionMatrix[g + REGION_CENTER_X] = RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_CENTER_X] = | ||
RegionMatrix[r + REGION_CENTER_X] + w; | ||
RegionMatrix[g + REGION_CENTER_Y] = | ||
RegionMatrix[r + REGION_CENTER_Y] + w; | ||
RegionMatrix[g + REGION_SIZE] = w; | ||
RegionMatrix[g + REGION_NEXT_SIBLING] = RegionMatrix[r + REGION_NEXT_SIBLING]; | ||
RegionMatrix[g + REGION_NEXT_SIBLING] = | ||
RegionMatrix[r + REGION_NEXT_SIBLING]; | ||
RegionMatrix[g + REGION_FIRST_CHILD] = -1; | ||
@@ -323,22 +311,24 @@ RegionMatrix[g + REGION_MASS] = 0; | ||
// Find the quadrant of the old node | ||
if (NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X] < RegionMatrix[r + REGION_CENTER_X]) { | ||
if (NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
if ( | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X] < | ||
RegionMatrix[r + REGION_CENTER_X] | ||
) { | ||
if ( | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < | ||
RegionMatrix[r + REGION_CENTER_Y] | ||
) { | ||
// Top Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
else { | ||
} else { | ||
// Bottom Left quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR; | ||
} | ||
} | ||
else { | ||
if (NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
} else { | ||
if ( | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y] < | ||
RegionMatrix[r + REGION_CENTER_Y] | ||
) { | ||
// Top Right quarter | ||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2; | ||
} | ||
else { | ||
} else { | ||
// Bottom Right quarter | ||
@@ -350,5 +340,8 @@ q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3; | ||
// We remove r[0] from the region r, add its mass to r and record it in q | ||
RegionMatrix[r + REGION_MASS] = NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_MASS]; | ||
RegionMatrix[r + REGION_MASS_CENTER_X] = NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X]; | ||
RegionMatrix[r + REGION_MASS_CENTER_Y] = NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y]; | ||
RegionMatrix[r + REGION_MASS] = | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_MASS]; | ||
RegionMatrix[r + REGION_MASS_CENTER_X] = | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_X]; | ||
RegionMatrix[r + REGION_MASS_CENTER_Y] = | ||
NodeMatrix[RegionMatrix[r + REGION_NODE] + NODE_Y]; | ||
@@ -361,19 +354,13 @@ RegionMatrix[q + REGION_NODE] = RegionMatrix[r + REGION_NODE]; | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Left quarter | ||
q2 = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
else { | ||
} else { | ||
// Bottom Left quarter | ||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR; | ||
} | ||
} | ||
else { | ||
} else { | ||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) { | ||
// Top Right quarter | ||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2; | ||
} | ||
else { | ||
} else { | ||
// Bottom Right quarter | ||
@@ -385,3 +372,2 @@ q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3; | ||
if (q === q2) { | ||
// If both nodes are in the same quadrant, | ||
@@ -392,4 +378,3 @@ // we have to try it again on this quadrant | ||
continue; // while | ||
} | ||
else { | ||
} else { | ||
// we are out of precision here, and we cannot subdivide anymore | ||
@@ -400,3 +385,2 @@ // but we have to break the loop anyway | ||
} | ||
} | ||
@@ -414,3 +398,2 @@ | ||
// 2) Repulsion | ||
@@ -425,3 +408,2 @@ //-------------- | ||
for (n = 0; n < order; n += PPN) { | ||
// Computing leaf quad nodes iteration | ||
@@ -431,12 +413,15 @@ | ||
while (true) { | ||
if (RegionMatrix[r + REGION_FIRST_CHILD] >= 0) { | ||
// The region has sub-regions | ||
// We run the Barnes Hut test to see if we are at the right distance | ||
distance = ( | ||
(Math.pow(NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X], 2)) + | ||
(Math.pow(NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y], 2)) | ||
); | ||
distance = | ||
Math.pow( | ||
NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X], | ||
2 | ||
) + | ||
Math.pow( | ||
NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y], | ||
2 | ||
); | ||
@@ -446,21 +431,26 @@ s = RegionMatrix[r + REGION_SIZE]; | ||
if ((4 * s * s) / distance < thetaSquared) { | ||
// We treat the region as a single body, and we repulse | ||
xDist = NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X]; | ||
yDist = NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y]; | ||
xDist = | ||
NodeMatrix[n + NODE_X] - RegionMatrix[r + REGION_MASS_CENTER_X]; | ||
yDist = | ||
NodeMatrix[n + NODE_Y] - RegionMatrix[r + REGION_MASS_CENTER_Y]; | ||
if (adjustSizes === true) { | ||
//-- Linear Anti-collision Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS]) / | ||
distance; | ||
NodeMatrix[n + NODE_DX] += xDist * factor; | ||
NodeMatrix[n + NODE_DY] += yDist * factor; | ||
} | ||
else if (distance < 0) { | ||
factor = -coefficient * NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS] / Math.sqrt(distance); | ||
} else if (distance < 0) { | ||
factor = | ||
(-coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS]) / | ||
Math.sqrt(distance); | ||
@@ -470,9 +460,10 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
RegionMatrix[r + REGION_MASS]) / | ||
distance; | ||
@@ -486,9 +477,6 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
r = RegionMatrix[r + REGION_NEXT_SIBLING]; | ||
if (r < 0) | ||
break; // No next sibling: we have finished the tree | ||
if (r < 0) break; // No next sibling: we have finished the tree | ||
continue; | ||
} | ||
else { | ||
} else { | ||
// The region is too close and we have to look at sub-regions | ||
@@ -498,6 +486,3 @@ r = RegionMatrix[r + REGION_FIRST_CHILD]; | ||
} | ||
} | ||
else { | ||
} else { | ||
// The region has no sub-region | ||
@@ -514,14 +499,18 @@ // If there is a node r[0] and it is not n, then repulse | ||
if (adjustSizes === true) { | ||
//-- Linear Anti-collision Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS]) / | ||
distance; | ||
NodeMatrix[n + NODE_DX] += xDist * factor; | ||
NodeMatrix[n + NODE_DY] += yDist * factor; | ||
} | ||
else if (distance < 0) { | ||
factor = -coefficient * NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS] / Math.sqrt(distance); | ||
} else if (distance < 0) { | ||
factor = | ||
(-coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS]) / | ||
Math.sqrt(distance); | ||
@@ -531,9 +520,10 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Repulsion | ||
if (distance > 0) { | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS] / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n + NODE_MASS] * | ||
NodeMatrix[rn + NODE_MASS]) / | ||
distance; | ||
@@ -544,3 +534,2 @@ NodeMatrix[n + NODE_DX] += xDist * factor; | ||
} | ||
} | ||
@@ -551,4 +540,3 @@ | ||
if (r < 0) | ||
break; // No next sibling: we have finished the tree | ||
if (r < 0) break; // No next sibling: we have finished the tree | ||
@@ -559,4 +547,3 @@ continue; | ||
} | ||
} | ||
else { | ||
} else { | ||
coefficient = options.scalingRatio; | ||
@@ -567,3 +554,2 @@ | ||
for (n2 = 0; n2 < n1; n2 += PPN) { | ||
// Common to both methods | ||
@@ -574,5 +560,5 @@ xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X]; | ||
if (adjustSizes === true) { | ||
//-- Anticollision Linear Repulsion | ||
distance = Math.sqrt(xDist * xDist + yDist * yDist) - | ||
distance = | ||
Math.sqrt(xDist * xDist + yDist * yDist) - | ||
NodeMatrix[n1 + NODE_SIZE] - | ||
@@ -582,6 +568,8 @@ NodeMatrix[n2 + NODE_SIZE]; | ||
if (distance > 0) { | ||
factor = coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS] / | ||
distance / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS]) / | ||
distance / | ||
distance; | ||
@@ -594,5 +582,6 @@ // Updating nodes' dx and dy | ||
NodeMatrix[n2 + NODE_DY] += yDist * factor; | ||
} | ||
else if (distance < 0) { | ||
factor = 100 * coefficient * | ||
} else if (distance < 0) { | ||
factor = | ||
100 * | ||
coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
@@ -608,5 +597,3 @@ NodeMatrix[n2 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Repulsion | ||
@@ -616,6 +603,8 @@ distance = Math.sqrt(xDist * xDist + yDist * yDist); | ||
if (distance > 0) { | ||
factor = coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS] / | ||
distance / distance; | ||
factor = | ||
(coefficient * | ||
NodeMatrix[n1 + NODE_MASS] * | ||
NodeMatrix[n2 + NODE_MASS]) / | ||
distance / | ||
distance; | ||
@@ -634,3 +623,2 @@ // Updating nodes' dx and dy | ||
// 3) Gravity | ||
@@ -646,17 +634,11 @@ //------------ | ||
yDist = NodeMatrix[n + NODE_Y]; | ||
distance = Math.sqrt( | ||
Math.pow(xDist, 2) + Math.pow(yDist, 2) | ||
); | ||
distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); | ||
if (options.strongGravityMode) { | ||
//-- Strong gravity | ||
if (distance > 0) | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * g; | ||
} | ||
else { | ||
if (distance > 0) factor = coefficient * NodeMatrix[n + NODE_MASS] * g; | ||
} else { | ||
//-- Linear Anti-collision Repulsion n | ||
if (distance > 0) | ||
factor = coefficient * NodeMatrix[n + NODE_MASS] * g / distance; | ||
factor = (coefficient * NodeMatrix[n + NODE_MASS] * g) / distance; | ||
} | ||
@@ -671,6 +653,4 @@ | ||
//--------------- | ||
coefficient = 1 * | ||
(options.outboundAttractionDistribution ? | ||
outboundAttCompensation : | ||
1); | ||
coefficient = | ||
1 * (options.outboundAttractionDistribution ? outboundAttCompensation : 1); | ||
@@ -693,7 +673,7 @@ // TODO: simplify distance | ||
if (adjustSizes === true) { | ||
distance = Math.sqrt( | ||
(Math.pow(xDist, 2) + Math.pow(yDist, 2)) - | ||
NodeMatrix[n1 + NODE_SIZE] - | ||
NodeMatrix[n2 + NODE_SIZE] | ||
Math.pow(xDist, 2) + | ||
Math.pow(yDist, 2) - | ||
NodeMatrix[n1 + NODE_SIZE] - | ||
NodeMatrix[n2 + NODE_SIZE] | ||
); | ||
@@ -703,28 +683,22 @@ | ||
if (options.outboundAttractionDistribution) { | ||
//-- LinLog Degree Distributed Anti-collision Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc * Math.log(1 + distance) / | ||
distance / | ||
NodeMatrix[n1 + NODE_MASS]; | ||
factor = | ||
(-coefficient * ewc * Math.log(1 + distance)) / | ||
distance / | ||
NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- LinLog Anti-collision Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc * Math.log(1 + distance) / distance; | ||
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance; | ||
} | ||
} | ||
} | ||
else { | ||
} else { | ||
if (options.outboundAttractionDistribution) { | ||
//-- Linear Degree Distributed Anti-collision Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc / NodeMatrix[n1 + NODE_MASS]; | ||
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- Linear Anti-collision Attraction | ||
@@ -736,36 +710,26 @@ if (distance > 0) { | ||
} | ||
} | ||
else { | ||
} else { | ||
distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); | ||
distance = Math.sqrt( | ||
Math.pow(xDist, 2) + Math.pow(yDist, 2) | ||
); | ||
if (options.linLogMode) { | ||
if (options.outboundAttractionDistribution) { | ||
//-- LinLog Degree Distributed Attraction | ||
if (distance > 0) { | ||
factor = -coefficient * ewc * Math.log(1 + distance) / | ||
factor = | ||
(-coefficient * ewc * Math.log(1 + distance)) / | ||
distance / | ||
NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
} | ||
else { | ||
} else { | ||
//-- LinLog Attraction | ||
if (distance > 0) | ||
factor = -coefficient * ewc * Math.log(1 + distance) / distance; | ||
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance; | ||
} | ||
} | ||
else { | ||
} else { | ||
if (options.outboundAttractionDistribution) { | ||
//-- Linear Attraction Mass Distributed | ||
// NOTE: Distance is set to 1 to override next condition | ||
distance = 1; | ||
factor = -coefficient * ewc / NodeMatrix[n1 + NODE_MASS]; | ||
} | ||
else { | ||
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_MASS]; | ||
} else { | ||
//-- Linear Attraction | ||
@@ -782,3 +746,2 @@ // NOTE: Distance is set to 1 to override next condition | ||
if (distance > 0) { | ||
// Updating nodes' dx and dy | ||
@@ -793,15 +756,8 @@ NodeMatrix[n1 + NODE_DX] += xDist * factor; | ||
// 5) Apply Forces | ||
//----------------- | ||
var force, | ||
swinging, | ||
traction, | ||
nodespeed, | ||
newX, | ||
newY; | ||
var force, swinging, traction, nodespeed, newX, newY; | ||
// MATH: sqrt and square distances | ||
if (adjustSizes === true) { | ||
for (n = 0; n < order; n += PPN) { | ||
@@ -811,3 +767,3 @@ if (NodeMatrix[n + NODE_FIXED] !== 1) { | ||
Math.pow(NodeMatrix[n + NODE_DX], 2) + | ||
Math.pow(NodeMatrix[n + NODE_DY], 2) | ||
Math.pow(NodeMatrix[n + NODE_DY], 2) | ||
); | ||
@@ -817,75 +773,82 @@ | ||
NodeMatrix[n + NODE_DX] = | ||
NodeMatrix[n + NODE_DX] * MAX_FORCE / force; | ||
(NodeMatrix[n + NODE_DX] * MAX_FORCE) / force; | ||
NodeMatrix[n + NODE_DY] = | ||
NodeMatrix[n + NODE_DY] * MAX_FORCE / force; | ||
(NodeMatrix[n + NODE_DY] * MAX_FORCE) / force; | ||
} | ||
swinging = NodeMatrix[n + NODE_MASS] * | ||
swinging = | ||
NodeMatrix[n + NODE_MASS] * | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
); | ||
traction = Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
traction = | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
nodespeed = | ||
0.1 * Math.log(1 + traction) / (1 + Math.sqrt(swinging)); | ||
nodespeed = (0.1 * Math.log(1 + traction)) / (1 + Math.sqrt(swinging)); | ||
// Updating node's positon | ||
newX = NodeMatrix[n + NODE_X] + NodeMatrix[n + NODE_DX] * | ||
(nodespeed / options.slowDown); | ||
newX = | ||
NodeMatrix[n + NODE_X] + | ||
NodeMatrix[n + NODE_DX] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_X] = newX; | ||
newY = NodeMatrix[n + NODE_Y] + NodeMatrix[n + NODE_DY] * | ||
(nodespeed / options.slowDown); | ||
newY = | ||
NodeMatrix[n + NODE_Y] + | ||
NodeMatrix[n + NODE_DY] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_Y] = newY; | ||
} | ||
} | ||
} | ||
else { | ||
} else { | ||
for (n = 0; n < order; n += PPN) { | ||
if (NodeMatrix[n + NODE_FIXED] !== 1) { | ||
swinging = NodeMatrix[n + NODE_MASS] * | ||
swinging = | ||
NodeMatrix[n + NODE_MASS] * | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
(NodeMatrix[n + NODE_OLD_DX] - NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] - NodeMatrix[n + NODE_DY]) | ||
); | ||
traction = Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
traction = | ||
Math.sqrt( | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) * | ||
(NodeMatrix[n + NODE_OLD_DX] + NodeMatrix[n + NODE_DX]) + | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) * | ||
(NodeMatrix[n + NODE_OLD_DY] + NodeMatrix[n + NODE_DY]) | ||
) / 2; | ||
nodespeed = NodeMatrix[n + NODE_CONVERGENCE] * | ||
Math.log(1 + traction) / (1 + Math.sqrt(swinging)); | ||
nodespeed = | ||
(NodeMatrix[n + NODE_CONVERGENCE] * Math.log(1 + traction)) / | ||
(1 + Math.sqrt(swinging)); | ||
// Updating node convergence | ||
NodeMatrix[n + NODE_CONVERGENCE] = | ||
Math.min(1, Math.sqrt( | ||
nodespeed * | ||
(Math.pow(NodeMatrix[n + NODE_DX], 2) + | ||
Math.pow(NodeMatrix[n + NODE_DY], 2)) / | ||
(1 + Math.sqrt(swinging)) | ||
)); | ||
NodeMatrix[n + NODE_CONVERGENCE] = Math.min( | ||
1, | ||
Math.sqrt( | ||
(nodespeed * | ||
(Math.pow(NodeMatrix[n + NODE_DX], 2) + | ||
Math.pow(NodeMatrix[n + NODE_DY], 2))) / | ||
(1 + Math.sqrt(swinging)) | ||
) | ||
); | ||
// Updating node's positon | ||
newX = NodeMatrix[n + NODE_X] + NodeMatrix[n + NODE_DX] * | ||
(nodespeed / options.slowDown); | ||
newX = | ||
NodeMatrix[n + NODE_X] + | ||
NodeMatrix[n + NODE_DX] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_X] = newX; | ||
newY = NodeMatrix[n + NODE_Y] + NodeMatrix[n + NODE_DY] * | ||
(nodespeed / options.slowDown); | ||
newY = | ||
NodeMatrix[n + NODE_Y] + | ||
NodeMatrix[n + NODE_DY] * (nodespeed / options.slowDown); | ||
NodeMatrix[n + NODE_Y] = newY; | ||
@@ -904,3 +867,3 @@ } | ||
self.addEventListener('message', function(event) { | ||
self.addEventListener('message', function (event) { | ||
var data = event.data; | ||
@@ -910,18 +873,16 @@ | ||
if (data.edges) | ||
EDGES = new Float32Array(data.edges); | ||
if (data.edges) EDGES = new Float32Array(data.edges); | ||
// Running the iteration | ||
iterate( | ||
data.settings, | ||
NODES, | ||
EDGES | ||
); | ||
iterate(data.settings, NODES, EDGES); | ||
// Sending result to supervisor | ||
self.postMessage({ | ||
nodes: NODES.buffer | ||
}, [NODES.buffer]); | ||
self.postMessage( | ||
{ | ||
nodes: NODES.buffer | ||
}, | ||
[NODES.buffer] | ||
); | ||
}); | ||
}; | ||
@@ -5,3 +5,7 @@ import Graph from 'graphology-types'; | ||
export type FA2LayoutSupervisorParameters = { | ||
settings?: ForceAtlas2Settings | ||
attributes?: { | ||
weight?: string; | ||
}; | ||
settings?: ForceAtlas2Settings; | ||
weighted: boolean; | ||
}; | ||
@@ -8,0 +12,0 @@ |
@@ -9,4 +9,4 @@ /** | ||
var workerFunction = require('./webworker.js'), | ||
isGraph = require('graphology-utils/is-graph'), | ||
helpers = require('./helpers.js'); | ||
isGraph = require('graphology-utils/is-graph'), | ||
helpers = require('./helpers.js'); | ||
@@ -28,10 +28,17 @@ var DEFAULT_SETTINGS = require('./defaults.js'); | ||
if (!isGraph(graph)) | ||
throw new Error('graphology-layout-forceatlas2/worker: the given graph is not a valid graphology instance.'); | ||
throw new Error( | ||
'graphology-layout-forceatlas2/worker: the given graph is not a valid graphology instance.' | ||
); | ||
var attributes = params.attributes || {}; | ||
var weightAttribute = params.weighted ? attributes.weight || 'weight' : null; | ||
// Validating settings | ||
var settings = helpers.assign({}, DEFAULT_SETTINGS, params.settings), | ||
validationError = helpers.validateSettings(settings); | ||
var settings = helpers.assign({}, DEFAULT_SETTINGS, params.settings); | ||
var validationError = helpers.validateSettings(settings); | ||
if (validationError) | ||
throw new Error('graphology-layout-forceatlas2/worker: ' + validationError.message); | ||
throw new Error( | ||
'graphology-layout-forceatlas2/worker: ' + validationError.message | ||
); | ||
@@ -42,2 +49,3 @@ // Properties | ||
this.settings = settings; | ||
this.weightAttribute = weightAttribute; | ||
this.matrices = null; | ||
@@ -53,10 +61,8 @@ this.running = false; | ||
this.handleGraphUpdate = function() { | ||
if (self.worker) | ||
self.worker.terminate(); | ||
this.handleGraphUpdate = function () { | ||
if (self.worker) self.worker.terminate(); | ||
if (respawnFrame) | ||
clearTimeout(respawnFrame); | ||
if (respawnFrame) clearTimeout(respawnFrame); | ||
respawnFrame = setTimeout(function() { | ||
respawnFrame = setTimeout(function () { | ||
respawnFrame = undefined; | ||
@@ -79,5 +85,4 @@ self.spawnWorker(); | ||
*/ | ||
FA2LayoutSupervisor.prototype.spawnWorker = function() { | ||
if (this.worker) | ||
this.worker.terminate(); | ||
FA2LayoutSupervisor.prototype.spawnWorker = function () { | ||
if (this.worker) this.worker.terminate(); | ||
@@ -98,5 +103,4 @@ this.worker = helpers.createWorker(workerFunction); | ||
*/ | ||
FA2LayoutSupervisor.prototype.handleMessage = function(event) { | ||
if (!this.running) | ||
return; | ||
FA2LayoutSupervisor.prototype.handleMessage = function (event) { | ||
if (!this.running) return; | ||
@@ -118,3 +122,3 @@ var matrix = new Float32Array(event.data.nodes); | ||
*/ | ||
FA2LayoutSupervisor.prototype.askForIterations = function(withEdges) { | ||
FA2LayoutSupervisor.prototype.askForIterations = function (withEdges) { | ||
var matrices = this.matrices; | ||
@@ -144,11 +148,12 @@ | ||
*/ | ||
FA2LayoutSupervisor.prototype.start = function() { | ||
FA2LayoutSupervisor.prototype.start = function () { | ||
if (this.killed) | ||
throw new Error('graphology-layout-forceatlas2/worker.start: layout was killed.'); | ||
throw new Error( | ||
'graphology-layout-forceatlas2/worker.start: layout was killed.' | ||
); | ||
if (this.running) | ||
return this; | ||
if (this.running) return this; | ||
// Building matrices | ||
this.matrices = helpers.graphToByteArrays(this.graph); | ||
this.matrices = helpers.graphToByteArrays(this.graph, this.weightAttribute); | ||
@@ -166,3 +171,3 @@ this.running = true; | ||
*/ | ||
FA2LayoutSupervisor.prototype.stop = function() { | ||
FA2LayoutSupervisor.prototype.stop = function () { | ||
this.running = false; | ||
@@ -178,5 +183,4 @@ | ||
*/ | ||
FA2LayoutSupervisor.prototype.kill = function() { | ||
if (this.killed) | ||
return this; | ||
FA2LayoutSupervisor.prototype.kill = function () { | ||
if (this.killed) return this; | ||
@@ -183,0 +187,0 @@ this.running = false; |
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
76347
0
1903
0
1
111