feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
793
web/node_modules/graphology-layout-forceatlas2/iterate.js
generated
vendored
Normal file
793
web/node_modules/graphology-layout-forceatlas2/iterate.js
generated
vendored
Normal file
@@ -0,0 +1,793 @@
|
||||
/* eslint no-constant-condition: 0 */
|
||||
/**
|
||||
* Graphology ForceAtlas2 Iteration
|
||||
* =================================
|
||||
*
|
||||
* Function used to perform a single iteration of the algorithm.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Matrices properties accessors.
|
||||
*/
|
||||
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;
|
||||
var EDGE_TARGET = 1;
|
||||
var EDGE_WEIGHT = 2;
|
||||
|
||||
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;
|
||||
|
||||
var SUBDIVISION_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
var PPN = 10;
|
||||
var PPE = 3;
|
||||
var PPR = 9;
|
||||
|
||||
var MAX_FORCE = 10;
|
||||
|
||||
/**
|
||||
* Function used to perform a single interation of the algorithm.
|
||||
*
|
||||
* @param {object} options - Layout options.
|
||||
* @param {Float32Array} NodeMatrix - Node data.
|
||||
* @param {Float32Array} EdgeMatrix - Edge data.
|
||||
* @return {object} - Some metadata.
|
||||
*/
|
||||
module.exports = function iterate(options, NodeMatrix, EdgeMatrix) {
|
||||
// Initializing variables
|
||||
var l, r, n, n1, n2, rn, e, w, g, s;
|
||||
|
||||
var order = NodeMatrix.length,
|
||||
size = EdgeMatrix.length;
|
||||
|
||||
var adjustSizes = options.adjustSizes;
|
||||
|
||||
var thetaSquared = options.barnesHutTheta * options.barnesHutTheta;
|
||||
|
||||
var outboundAttCompensation, coefficient, xDist, yDist, ewc, distance, factor;
|
||||
|
||||
var RegionMatrix = [];
|
||||
|
||||
// 1) Initializing layout data
|
||||
//-----------------------------
|
||||
|
||||
// Resetting positions & computing max values
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
NodeMatrix[n + NODE_OLD_DX] = NodeMatrix[n + NODE_DX];
|
||||
NodeMatrix[n + NODE_OLD_DY] = NodeMatrix[n + NODE_DY];
|
||||
NodeMatrix[n + NODE_DX] = 0;
|
||||
NodeMatrix[n + NODE_DY] = 0;
|
||||
}
|
||||
|
||||
// If outbound attraction distribution, compensate
|
||||
if (options.outboundAttractionDistribution) {
|
||||
outboundAttCompensation = 0;
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
outboundAttCompensation += NodeMatrix[n + NODE_MASS];
|
||||
}
|
||||
|
||||
outboundAttCompensation /= order / PPN;
|
||||
}
|
||||
|
||||
// 1.bis) Barnes-Hut computation
|
||||
//------------------------------
|
||||
|
||||
if (options.barnesHutOptimize) {
|
||||
// Setting up
|
||||
var minX = Infinity,
|
||||
maxX = -Infinity,
|
||||
minY = Infinity,
|
||||
maxY = -Infinity,
|
||||
q,
|
||||
q2,
|
||||
subdivisionAttempts;
|
||||
|
||||
// Computing min and max values
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
minX = Math.min(minX, NodeMatrix[n + NODE_X]);
|
||||
maxX = Math.max(maxX, NodeMatrix[n + NODE_X]);
|
||||
minY = Math.min(minY, NodeMatrix[n + NODE_Y]);
|
||||
maxY = Math.max(maxY, NodeMatrix[n + NODE_Y]);
|
||||
}
|
||||
|
||||
// squarify bounds, it's a quadtree
|
||||
var dx = maxX - minX,
|
||||
dy = maxY - minY;
|
||||
if (dx > dy) {
|
||||
minY -= (dx - dy) / 2;
|
||||
maxY = minY + dx;
|
||||
} else {
|
||||
minX -= (dy - dx) / 2;
|
||||
maxX = minX + dy;
|
||||
}
|
||||
|
||||
// Build the Barnes Hut root region
|
||||
RegionMatrix[0 + REGION_NODE] = -1;
|
||||
RegionMatrix[0 + REGION_CENTER_X] = (minX + maxX) / 2;
|
||||
RegionMatrix[0 + REGION_CENTER_Y] = (minY + maxY) / 2;
|
||||
RegionMatrix[0 + REGION_SIZE] = Math.max(maxX - minX, maxY - minY);
|
||||
RegionMatrix[0 + REGION_NEXT_SIBLING] = -1;
|
||||
RegionMatrix[0 + REGION_FIRST_CHILD] = -1;
|
||||
RegionMatrix[0 + REGION_MASS] = 0;
|
||||
RegionMatrix[0 + REGION_MASS_CENTER_X] = 0;
|
||||
RegionMatrix[0 + REGION_MASS_CENTER_Y] = 0;
|
||||
|
||||
// Add each node in the tree
|
||||
l = 1;
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
// Current region, starting with root
|
||||
r = 0;
|
||||
subdivisionAttempts = SUBDIVISION_ATTEMPTS;
|
||||
|
||||
while (true) {
|
||||
// Are there sub-regions?
|
||||
|
||||
// We look at first child index
|
||||
if (RegionMatrix[r + REGION_FIRST_CHILD] >= 0) {
|
||||
// There are sub-regions
|
||||
|
||||
// We just iterate to find a "leaf" 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[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 {
|
||||
// Bottom Left quarter
|
||||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR;
|
||||
}
|
||||
} else {
|
||||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) {
|
||||
// Top Right quarter
|
||||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2;
|
||||
} else {
|
||||
// Bottom Right quarter
|
||||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Update center of mass and mass (we only do it for non-leave regions)
|
||||
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] + 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] + NodeMatrix[n + NODE_MASS]);
|
||||
|
||||
RegionMatrix[r + REGION_MASS] += NodeMatrix[n + NODE_MASS];
|
||||
|
||||
// Iterate on the right quadrant
|
||||
r = q;
|
||||
continue;
|
||||
} else {
|
||||
// There are no sub-regions: we are in a "leaf"
|
||||
|
||||
// Is there a node in this leave?
|
||||
if (RegionMatrix[r + REGION_NODE] < 0) {
|
||||
// There is no node in region:
|
||||
// we record node n and go on
|
||||
RegionMatrix[r + REGION_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[r + REGION_FIRST_CHILD] = l * PPR;
|
||||
w = RegionMatrix[r + REGION_SIZE] / 2; // new size (half)
|
||||
|
||||
// NOTE: we use screen coordinates
|
||||
// from Top Left to Bottom Right
|
||||
|
||||
// Top Left sub-region
|
||||
g = RegionMatrix[r + REGION_FIRST_CHILD];
|
||||
|
||||
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_SIZE] = w;
|
||||
RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR;
|
||||
RegionMatrix[g + REGION_FIRST_CHILD] = -1;
|
||||
RegionMatrix[g + REGION_MASS] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_X] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_Y] = 0;
|
||||
|
||||
// Bottom Left sub-region
|
||||
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_SIZE] = w;
|
||||
RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR;
|
||||
RegionMatrix[g + REGION_FIRST_CHILD] = -1;
|
||||
RegionMatrix[g + REGION_MASS] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_X] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_Y] = 0;
|
||||
|
||||
// Top Right sub-region
|
||||
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_SIZE] = w;
|
||||
RegionMatrix[g + REGION_NEXT_SIBLING] = g + PPR;
|
||||
RegionMatrix[g + REGION_FIRST_CHILD] = -1;
|
||||
RegionMatrix[g + REGION_MASS] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_X] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_Y] = 0;
|
||||
|
||||
// Bottom Right sub-region
|
||||
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_SIZE] = w;
|
||||
RegionMatrix[g + REGION_NEXT_SIBLING] =
|
||||
RegionMatrix[r + REGION_NEXT_SIBLING];
|
||||
RegionMatrix[g + REGION_FIRST_CHILD] = -1;
|
||||
RegionMatrix[g + REGION_MASS] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_X] = 0;
|
||||
RegionMatrix[g + REGION_MASS_CENTER_Y] = 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[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 {
|
||||
// Bottom Left quarter
|
||||
q = RegionMatrix[r + REGION_FIRST_CHILD] + PPR;
|
||||
}
|
||||
} 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 {
|
||||
// Bottom Right quarter
|
||||
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[q + REGION_NODE] = RegionMatrix[r + REGION_NODE];
|
||||
RegionMatrix[r + REGION_NODE] = -1;
|
||||
|
||||
// Find the quadrant of n
|
||||
if (NodeMatrix[n + NODE_X] < RegionMatrix[r + REGION_CENTER_X]) {
|
||||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) {
|
||||
// Top Left quarter
|
||||
q2 = RegionMatrix[r + REGION_FIRST_CHILD];
|
||||
} else {
|
||||
// Bottom Left quarter
|
||||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR;
|
||||
}
|
||||
} else {
|
||||
if (NodeMatrix[n + NODE_Y] < RegionMatrix[r + REGION_CENTER_Y]) {
|
||||
// Top Right quarter
|
||||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 2;
|
||||
} else {
|
||||
// Bottom Right quarter
|
||||
q2 = RegionMatrix[r + REGION_FIRST_CHILD] + PPR * 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (q === q2) {
|
||||
// If both nodes are in the same quadrant,
|
||||
// we have to try it again on this quadrant
|
||||
if (subdivisionAttempts--) {
|
||||
r = q;
|
||||
continue; // while
|
||||
} else {
|
||||
// we are out of precision here, and we cannot subdivide anymore
|
||||
// but we have to break the loop anyway
|
||||
subdivisionAttempts = SUBDIVISION_ATTEMPTS;
|
||||
break; // while
|
||||
}
|
||||
}
|
||||
|
||||
// If both quadrants are different, we record n
|
||||
// in its quadrant
|
||||
RegionMatrix[q2 + REGION_NODE] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Repulsion
|
||||
//--------------
|
||||
// NOTES: adjustSizes = antiCollision & scalingRatio = coefficient
|
||||
|
||||
if (options.barnesHutOptimize) {
|
||||
coefficient = options.scalingRatio;
|
||||
|
||||
// Applying repulsion through regions
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
// Computing leaf quad nodes iteration
|
||||
|
||||
r = 0; // Starting with root region
|
||||
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
|
||||
);
|
||||
|
||||
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];
|
||||
|
||||
if (adjustSizes === true) {
|
||||
//-- Linear Anti-collision Repulsion
|
||||
if (distance > 0) {
|
||||
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);
|
||||
|
||||
NodeMatrix[n + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n + NODE_DY] += yDist * factor;
|
||||
}
|
||||
} else {
|
||||
//-- Linear Repulsion
|
||||
if (distance > 0) {
|
||||
factor =
|
||||
(coefficient *
|
||||
NodeMatrix[n + NODE_MASS] *
|
||||
RegionMatrix[r + REGION_MASS]) /
|
||||
distance;
|
||||
|
||||
NodeMatrix[n + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n + NODE_DY] += yDist * factor;
|
||||
}
|
||||
}
|
||||
|
||||
// When this is done, we iterate. We have to look at the next sibling.
|
||||
r = RegionMatrix[r + REGION_NEXT_SIBLING];
|
||||
if (r < 0) break; // No next sibling: we have finished the tree
|
||||
|
||||
continue;
|
||||
} else {
|
||||
// The region is too close and we have to look at sub-regions
|
||||
r = RegionMatrix[r + REGION_FIRST_CHILD];
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// The region has no sub-region
|
||||
// If there is a node r[0] and it is not n, then repulse
|
||||
rn = RegionMatrix[r + REGION_NODE];
|
||||
|
||||
if (rn >= 0 && rn !== n) {
|
||||
xDist = NodeMatrix[n + NODE_X] - NodeMatrix[rn + NODE_X];
|
||||
yDist = NodeMatrix[n + NODE_Y] - NodeMatrix[rn + NODE_Y];
|
||||
|
||||
distance = xDist * xDist + yDist * yDist;
|
||||
|
||||
if (adjustSizes === true) {
|
||||
//-- Linear Anti-collision Repulsion
|
||||
if (distance > 0) {
|
||||
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);
|
||||
|
||||
NodeMatrix[n + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n + NODE_DY] += yDist * factor;
|
||||
}
|
||||
} else {
|
||||
//-- Linear Repulsion
|
||||
if (distance > 0) {
|
||||
factor =
|
||||
(coefficient *
|
||||
NodeMatrix[n + NODE_MASS] *
|
||||
NodeMatrix[rn + NODE_MASS]) /
|
||||
distance;
|
||||
|
||||
NodeMatrix[n + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n + NODE_DY] += yDist * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When this is done, we iterate. We have to look at the next sibling.
|
||||
r = RegionMatrix[r + REGION_NEXT_SIBLING];
|
||||
|
||||
if (r < 0) break; // No next sibling: we have finished the tree
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
coefficient = options.scalingRatio;
|
||||
|
||||
// Square iteration
|
||||
for (n1 = 0; n1 < order; n1 += PPN) {
|
||||
for (n2 = 0; n2 < n1; n2 += PPN) {
|
||||
// Common to both methods
|
||||
xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X];
|
||||
yDist = NodeMatrix[n1 + NODE_Y] - NodeMatrix[n2 + NODE_Y];
|
||||
|
||||
if (adjustSizes === true) {
|
||||
//-- Anticollision Linear Repulsion
|
||||
distance =
|
||||
Math.sqrt(xDist * xDist + yDist * yDist) -
|
||||
NodeMatrix[n1 + NODE_SIZE] -
|
||||
NodeMatrix[n2 + NODE_SIZE];
|
||||
|
||||
if (distance > 0) {
|
||||
factor =
|
||||
(coefficient *
|
||||
NodeMatrix[n1 + NODE_MASS] *
|
||||
NodeMatrix[n2 + NODE_MASS]) /
|
||||
distance /
|
||||
distance;
|
||||
|
||||
// Updating nodes' dx and dy
|
||||
NodeMatrix[n1 + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
||||
|
||||
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
||||
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
||||
} else if (distance < 0) {
|
||||
factor =
|
||||
100 *
|
||||
coefficient *
|
||||
NodeMatrix[n1 + NODE_MASS] *
|
||||
NodeMatrix[n2 + NODE_MASS];
|
||||
|
||||
// Updating nodes' dx and dy
|
||||
NodeMatrix[n1 + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
||||
|
||||
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
||||
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
||||
}
|
||||
} else {
|
||||
//-- Linear Repulsion
|
||||
distance = Math.sqrt(xDist * xDist + yDist * yDist);
|
||||
|
||||
if (distance > 0) {
|
||||
factor =
|
||||
(coefficient *
|
||||
NodeMatrix[n1 + NODE_MASS] *
|
||||
NodeMatrix[n2 + NODE_MASS]) /
|
||||
distance /
|
||||
distance;
|
||||
|
||||
// Updating nodes' dx and dy
|
||||
NodeMatrix[n1 + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
||||
|
||||
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
||||
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Gravity
|
||||
//------------
|
||||
g = options.gravity / options.scalingRatio;
|
||||
coefficient = options.scalingRatio;
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
factor = 0;
|
||||
|
||||
// Common to both methods
|
||||
xDist = NodeMatrix[n + NODE_X];
|
||||
yDist = NodeMatrix[n + NODE_Y];
|
||||
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 {
|
||||
//-- Linear Anti-collision Repulsion n
|
||||
if (distance > 0)
|
||||
factor = (coefficient * NodeMatrix[n + NODE_MASS] * g) / distance;
|
||||
}
|
||||
|
||||
// Updating node's dx and dy
|
||||
NodeMatrix[n + NODE_DX] -= xDist * factor;
|
||||
NodeMatrix[n + NODE_DY] -= yDist * factor;
|
||||
}
|
||||
|
||||
// 4) Attraction
|
||||
//---------------
|
||||
coefficient =
|
||||
1 * (options.outboundAttractionDistribution ? outboundAttCompensation : 1);
|
||||
|
||||
// TODO: simplify distance
|
||||
// TODO: coefficient is always used as -c --> optimize?
|
||||
for (e = 0; e < size; e += PPE) {
|
||||
n1 = EdgeMatrix[e + EDGE_SOURCE];
|
||||
n2 = EdgeMatrix[e + EDGE_TARGET];
|
||||
w = EdgeMatrix[e + EDGE_WEIGHT];
|
||||
|
||||
// Edge weight influence
|
||||
ewc = Math.pow(w, options.edgeWeightInfluence);
|
||||
|
||||
// Common measures
|
||||
xDist = NodeMatrix[n1 + NODE_X] - NodeMatrix[n2 + NODE_X];
|
||||
yDist = NodeMatrix[n1 + NODE_Y] - NodeMatrix[n2 + NODE_Y];
|
||||
|
||||
// Applying attraction to nodes
|
||||
if (adjustSizes === true) {
|
||||
distance =
|
||||
Math.sqrt(xDist * xDist + yDist * yDist) -
|
||||
NodeMatrix[n1 + NODE_SIZE] -
|
||||
NodeMatrix[n2 + NODE_SIZE];
|
||||
|
||||
if (options.linLogMode) {
|
||||
if (options.outboundAttractionDistribution) {
|
||||
//-- LinLog Degree Distributed Anti-collision Attraction
|
||||
if (distance > 0) {
|
||||
factor =
|
||||
(-coefficient * ewc * Math.log(1 + distance)) /
|
||||
distance /
|
||||
NodeMatrix[n1 + NODE_MASS];
|
||||
}
|
||||
} else {
|
||||
//-- LinLog Anti-collision Attraction
|
||||
if (distance > 0) {
|
||||
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (options.outboundAttractionDistribution) {
|
||||
//-- Linear Degree Distributed Anti-collision Attraction
|
||||
if (distance > 0) {
|
||||
factor = (-coefficient * ewc) / NodeMatrix[n1 + NODE_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 (options.linLogMode) {
|
||||
if (options.outboundAttractionDistribution) {
|
||||
//-- LinLog Degree Distributed Attraction
|
||||
if (distance > 0) {
|
||||
factor =
|
||||
(-coefficient * ewc * Math.log(1 + distance)) /
|
||||
distance /
|
||||
NodeMatrix[n1 + NODE_MASS];
|
||||
}
|
||||
} else {
|
||||
//-- LinLog Attraction
|
||||
if (distance > 0)
|
||||
factor = (-coefficient * ewc * Math.log(1 + distance)) / distance;
|
||||
}
|
||||
} 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 {
|
||||
//-- 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[n1 + NODE_DX] += xDist * factor;
|
||||
NodeMatrix[n1 + NODE_DY] += yDist * factor;
|
||||
|
||||
NodeMatrix[n2 + NODE_DX] -= xDist * factor;
|
||||
NodeMatrix[n2 + NODE_DY] -= yDist * factor;
|
||||
}
|
||||
}
|
||||
|
||||
// 5) Apply Forces
|
||||
//-----------------
|
||||
var force, swinging, traction, nodespeed, newX, newY;
|
||||
|
||||
// MATH: sqrt and square distances
|
||||
if (adjustSizes === true) {
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
if (NodeMatrix[n + NODE_FIXED] !== 1) {
|
||||
force = Math.sqrt(
|
||||
Math.pow(NodeMatrix[n + NODE_DX], 2) +
|
||||
Math.pow(NodeMatrix[n + NODE_DY], 2)
|
||||
);
|
||||
|
||||
if (force > MAX_FORCE) {
|
||||
NodeMatrix[n + NODE_DX] =
|
||||
(NodeMatrix[n + NODE_DX] * MAX_FORCE) / force;
|
||||
NodeMatrix[n + NODE_DY] =
|
||||
(NodeMatrix[n + NODE_DY] * MAX_FORCE) / force;
|
||||
}
|
||||
|
||||
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])
|
||||
);
|
||||
|
||||
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));
|
||||
|
||||
// Updating node's positon
|
||||
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);
|
||||
NodeMatrix[n + NODE_Y] = newY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < order; n += PPN) {
|
||||
if (NodeMatrix[n + NODE_FIXED] !== 1) {
|
||||
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])
|
||||
);
|
||||
|
||||
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));
|
||||
|
||||
// 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))
|
||||
)
|
||||
);
|
||||
|
||||
// Updating node's positon
|
||||
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);
|
||||
NodeMatrix[n + NODE_Y] = newY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We return the information about the layout (no need to return the matrices)
|
||||
return {};
|
||||
};
|
||||
Reference in New Issue
Block a user