完成世界书、骰子、apiconfig页面处理
This commit is contained in:
25
frontend/node_modules/cytoscape-fcose/bower.json
generated
vendored
Normal file
25
frontend/node_modules/cytoscape-fcose/bower.json
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "cytoscape-fcose",
|
||||
"description": "The fCoSE layout for Cytoscape.js by Bilkent with fast compound node placement",
|
||||
"main": "cytoscape-fcose.js",
|
||||
"dependencies": {
|
||||
"cytoscape": "^3.2.0",
|
||||
"cose-base": "^1.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/iVis-at-Bilkent/cytoscape.js-fcose.git"
|
||||
},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"keywords": [
|
||||
"cytoscape",
|
||||
"cytoscape-extension"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
1482
frontend/node_modules/cytoscape-fcose/demo/demo-constraint-control.js
generated
vendored
Normal file
1482
frontend/node_modules/cytoscape-fcose/demo/demo-constraint-control.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
249
frontend/node_modules/cytoscape-fcose/demo/samples/unix_constraints.js
generated
vendored
Normal file
249
frontend/node_modules/cytoscape-fcose/demo/samples/unix_constraints.js
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
unix_constraints = {
|
||||
"relativePlacementConstraint": [
|
||||
{
|
||||
"top": "nwtN_4451c335-4668-4f8d-9f71-274aa2e6ab06",
|
||||
"bottom": "nwtN_8d99e7af-1b2f-4605-a381-ce3127b6199a",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_4451c335-4668-4f8d-9f71-274aa2e6ab06",
|
||||
"bottom": "nwtN_37220ed2-0cbe-4d1f-9cac-07dfb11d6eda",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8d99e7af-1b2f-4605-a381-ce3127b6199a",
|
||||
"bottom": "nwtN_020b84b2-717f-4465-b716-7520e5c9c470",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8d99e7af-1b2f-4605-a381-ce3127b6199a",
|
||||
"bottom": "nwtN_c1880331-8411-443e-9218-87e78c057b22",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8d99e7af-1b2f-4605-a381-ce3127b6199a",
|
||||
"bottom": "nwtN_3f3b6234-e449-44bb-9ba8-059d1f843c98",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8d99e7af-1b2f-4605-a381-ce3127b6199a",
|
||||
"bottom": "nwtN_d7bbe746-9028-4268-a642-70544788d687",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8d99e7af-1b2f-4605-a381-ce3127b6199a",
|
||||
"bottom": "nwtN_5bb0a938-c7ee-42d8-85c8-17bdc1ff0ade",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_37220ed2-0cbe-4d1f-9cac-07dfb11d6eda",
|
||||
"bottom": "nwtN_8227939a-5dc0-433e-8c4d-987ec44a1f8f",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_37220ed2-0cbe-4d1f-9cac-07dfb11d6eda",
|
||||
"bottom": "nwtN_3f7d61be-a016-4383-9b8d-8c1db26192f2",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_020b84b2-717f-4465-b716-7520e5c9c470",
|
||||
"bottom": "nwtN_166d8364-fd55-499e-9098-0a48db6615c1",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_c1880331-8411-443e-9218-87e78c057b22",
|
||||
"bottom": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_c1880331-8411-443e-9218-87e78c057b22",
|
||||
"bottom": "nwtN_db35fc70-2b89-4857-acb2-9dd545e61447",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_c1880331-8411-443e-9218-87e78c057b22",
|
||||
"bottom": "nwtN_9dfde51e-a410-4e3b-a472-9061ae4fae7a",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8227939a-5dc0-433e-8c4d-987ec44a1f8f",
|
||||
"bottom": "nwtN_9dfde51e-a410-4e3b-a472-9061ae4fae7a",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_3f7d61be-a016-4383-9b8d-8c1db26192f2",
|
||||
"bottom": "nwtN_a186f2a4-524e-4a84-95c0-66d41dbda586",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_3f7d61be-a016-4383-9b8d-8c1db26192f2",
|
||||
"bottom": "nwtN_26c73172-708d-45cc-8ba5-f29671089767",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"bottom": "nwtN_a2bfb9bd-8b13-43a5-87a5-d88b0137bd78",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"bottom": "nwtN_2d9c1f24-1482-46fb-a0f4-f963ba454710",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"bottom": "nwtN_15fb1e74-6b08-4914-95ac-ee4ecb1b7f17",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"bottom": "nwtN_485d980b-dc42-48ca-9ada-3c1b754cc2ef",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"bottom": "nwtN_bce869d1-7c58-4489-81aa-e87e9418ab70",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_9dfde51e-a410-4e3b-a472-9061ae4fae7a",
|
||||
"bottom": "nwtN_db35fc70-2b89-4857-acb2-9dd545e61447",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_a186f2a4-524e-4a84-95c0-66d41dbda586",
|
||||
"bottom": "nwtN_2cf0c5aa-67eb-4394-85e3-dc84f4b1cd52",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_26c73172-708d-45cc-8ba5-f29671089767",
|
||||
"bottom": "nwtN_2b6ef682-26c7-48d7-9fd8-09fa76c7b718",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_a2bfb9bd-8b13-43a5-87a5-d88b0137bd78",
|
||||
"bottom": "nwtN_be8097dc-4a84-4d3f-ade9-e80221f86dd6",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_d53ea35c-4609-4051-917a-8a183e5976cf",
|
||||
"bottom": "nwtN_c261ca93-6060-49c1-8286-2a98900c3b9e",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_db35fc70-2b89-4857-acb2-9dd545e61447",
|
||||
"bottom": "nwtN_a68571c0-2669-4894-aa53-332555f625a2",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_eed8fc62-a21d-4677-85f2-f1244e3abf00",
|
||||
"bottom": "nwtN_2f175f07-154a-4873-98cc-bc4e4d6d13a1",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_eed8fc62-a21d-4677-85f2-f1244e3abf00",
|
||||
"bottom": "nwtN_a68571c0-2669-4894-aa53-332555f625a2",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_eed8fc62-a21d-4677-85f2-f1244e3abf00",
|
||||
"bottom": "nwtN_e55d29c7-89bc-4aa0-b599-3637e141f17f",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_c261ca93-6060-49c1-8286-2a98900c3b9e",
|
||||
"bottom": "nwtN_02a50d8b-3939-492a-ac5b-1f356cd514f6",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_2f175f07-154a-4873-98cc-bc4e4d6d13a1",
|
||||
"bottom": "nwtN_a68571c0-2669-4894-aa53-332555f625a2",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_166d8364-fd55-499e-9098-0a48db6615c1",
|
||||
"bottom": "nwtN_205680d2-dc98-4faf-baf4-21e27e8585e4",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_02a50d8b-3939-492a-ac5b-1f356cd514f6",
|
||||
"bottom": "nwtN_205680d2-dc98-4faf-baf4-21e27e8585e4",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_02a50d8b-3939-492a-ac5b-1f356cd514f6",
|
||||
"bottom": "nwtN_75318a95-e179-40fc-b6e1-e01fcd66cd8c",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_02a50d8b-3939-492a-ac5b-1f356cd514f6",
|
||||
"bottom": "nwtN_bce869d1-7c58-4489-81aa-e87e9418ab70",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_a68571c0-2669-4894-aa53-332555f625a2",
|
||||
"bottom": "nwtN_8ed84db0-02da-43c6-847c-6b33f657f52c",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_205680d2-dc98-4faf-baf4-21e27e8585e4",
|
||||
"bottom": "nwtN_be8097dc-4a84-4d3f-ade9-e80221f86dd6",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_205680d2-dc98-4faf-baf4-21e27e8585e4",
|
||||
"bottom": "nwtN_8489a816-b7af-452f-a722-04d06226341c",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_75318a95-e179-40fc-b6e1-e01fcd66cd8c",
|
||||
"bottom": "nwtN_d4bf8470-416d-4387-a563-3c9cccf55079",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_75318a95-e179-40fc-b6e1-e01fcd66cd8c",
|
||||
"bottom": "nwtN_e48d9732-8ee0-4714-8b62-8de8238ec9c2",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_bce869d1-7c58-4489-81aa-e87e9418ab70",
|
||||
"bottom": "nwtN_103a9105-ef50-47a4-b501-840a431fcbdd",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_8ed84db0-02da-43c6-847c-6b33f657f52c",
|
||||
"bottom": "nwtN_6385f491-0d09-4d53-a34c-781a861e8b42",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_6385f491-0d09-4d53-a34c-781a861e8b42",
|
||||
"bottom": "nwtN_88e20782-9221-4b12-9b62-6a6c7116a70f",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_167a075d-9d93-49d0-b5be-446efebec06c",
|
||||
"bottom": "nwtN_be8097dc-4a84-4d3f-ade9-e80221f86dd6",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_485d980b-dc42-48ca-9ada-3c1b754cc2ef",
|
||||
"bottom": "nwtN_d53ea35c-4609-4051-917a-8a183e5976cf",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_2cf0c5aa-67eb-4394-85e3-dc84f4b1cd52",
|
||||
"bottom": "nwtN_db35fc70-2b89-4857-acb2-9dd545e61447",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_cc15f875-f4dd-4df8-a09e-a4f2e6c8a3f5",
|
||||
"bottom": "nwtN_db35fc70-2b89-4857-acb2-9dd545e61447",
|
||||
"gap": 76.25
|
||||
},
|
||||
{
|
||||
"top": "nwtN_2b6ef682-26c7-48d7-9fd8-09fa76c7b718",
|
||||
"bottom": "nwtN_eed8fc62-a21d-4677-85f2-f1244e3abf00",
|
||||
"gap": 76.25
|
||||
}
|
||||
]
|
||||
}
|
||||
3
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/.babelrc
generated
vendored
Normal file
3
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/.babelrc
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["env"]
|
||||
}
|
||||
23
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/bower.json
generated
vendored
Normal file
23
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/bower.json
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "cose-base",
|
||||
"description": "Core module for compound spring embedder based layout styles",
|
||||
"main": "cose-base.js",
|
||||
"dependencies": {
|
||||
"layout-base": "^1.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/iVis-at-Bilkent/cose-base.git"
|
||||
},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"keywords": [
|
||||
"layout"
|
||||
],
|
||||
"license": "MIT"
|
||||
}
|
||||
12
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/src/CoSEGraphManager.js
generated
vendored
Normal file
12
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/src/CoSEGraphManager.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
var LGraphManager = require('layout-base').LGraphManager;
|
||||
|
||||
function CoSEGraphManager(layout) {
|
||||
LGraphManager.call(this, layout);
|
||||
}
|
||||
|
||||
CoSEGraphManager.prototype = Object.create(LGraphManager.prototype);
|
||||
for (var prop in LGraphManager) {
|
||||
CoSEGraphManager[prop] = LGraphManager[prop];
|
||||
}
|
||||
|
||||
module.exports = CoSEGraphManager;
|
||||
848
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/src/ConstraintHandler.js
generated
vendored
Normal file
848
frontend/node_modules/cytoscape-fcose/node_modules/cose-base/src/ConstraintHandler.js
generated
vendored
Normal file
@@ -0,0 +1,848 @@
|
||||
var CoSEConstants = require('./CoSEConstants');
|
||||
var LinkedList = require('layout-base').LinkedList;
|
||||
var Matrix = require('layout-base').Matrix;
|
||||
var SVD = require('layout-base').SVD;
|
||||
|
||||
function ConstraintHandler() {
|
||||
}
|
||||
|
||||
ConstraintHandler.handleConstraints = function (layout)
|
||||
{
|
||||
// let layout = this.graphManager.getLayout();
|
||||
|
||||
// get constraints from layout
|
||||
let constraints = {};
|
||||
constraints.fixedNodeConstraint = layout.constraints.fixedNodeConstraint;
|
||||
constraints.alignmentConstraint = layout.constraints.alignmentConstraint;
|
||||
constraints.relativePlacementConstraint = layout.constraints.relativePlacementConstraint;
|
||||
|
||||
let idToNodeMap = new Map();
|
||||
let nodeIndexes = new Map();
|
||||
let xCoords = [];
|
||||
let yCoords = [];
|
||||
|
||||
let allNodes = layout.getAllNodes();
|
||||
let index = 0;
|
||||
// fill index map and coordinates
|
||||
for (let i = 0; i < allNodes.length; i++) {
|
||||
let node = allNodes[i];
|
||||
if (node.getChild() == null) {
|
||||
nodeIndexes.set(node.id, index++);
|
||||
xCoords.push(node.getCenterX());
|
||||
yCoords.push(node.getCenterY());
|
||||
idToNodeMap.set(node.id, node);
|
||||
}
|
||||
}
|
||||
|
||||
// if there exists relative placement constraint without gap value, set it to default
|
||||
if (constraints.relativePlacementConstraint) {
|
||||
constraints.relativePlacementConstraint.forEach(function(constraint) {
|
||||
if (!constraint.gap && constraint.gap != 0) {
|
||||
if (constraint.left) {
|
||||
constraint.gap = CoSEConstants.DEFAULT_EDGE_LENGTH + idToNodeMap.get(constraint.left).getWidth()/2 + idToNodeMap.get(constraint.right).getWidth()/2;
|
||||
}
|
||||
else {
|
||||
constraint.gap = CoSEConstants.DEFAULT_EDGE_LENGTH + idToNodeMap.get(constraint.top).getHeight()/2 + idToNodeMap.get(constraint.bottom).getHeight()/2;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* auxiliary functions */
|
||||
|
||||
// calculate difference between two position objects
|
||||
let calculatePositionDiff = function(pos1, pos2) {
|
||||
return {x: pos1.x - pos2.x, y: pos1.y - pos2.y};
|
||||
};
|
||||
|
||||
// calculate average position of the nodes
|
||||
let calculateAvgPosition = function(nodeIdSet) {
|
||||
let xPosSum = 0;
|
||||
let yPosSum = 0;
|
||||
nodeIdSet.forEach(function(nodeId) {
|
||||
xPosSum += xCoords[nodeIndexes.get(nodeId)];
|
||||
yPosSum += yCoords[nodeIndexes.get(nodeId)];
|
||||
});
|
||||
|
||||
return {x: xPosSum / nodeIdSet.size, y: yPosSum / nodeIdSet.size};
|
||||
};
|
||||
|
||||
// find an appropriate positioning for the nodes in a given graph according to relative placement constraints
|
||||
// this function also takes the fixed nodes and alignment constraints into account
|
||||
// graph: dag to be evaluated, direction: "horizontal" or "vertical",
|
||||
// fixedNodes: set of fixed nodes to consider during evaluation, dummyPositions: appropriate coordinates of the dummy nodes
|
||||
let findAppropriatePositionForRelativePlacement = function(graph, direction, fixedNodes, dummyPositions, componentSources) {
|
||||
|
||||
// find union of two sets
|
||||
function setUnion(setA, setB) {
|
||||
let union = new Set(setA);
|
||||
for (let elem of setB) {
|
||||
union.add(elem);
|
||||
}
|
||||
return union;
|
||||
}
|
||||
|
||||
// find indegree count for each node
|
||||
let inDegrees = new Map();
|
||||
|
||||
graph.forEach(function(value, key) {
|
||||
inDegrees.set(key, 0);
|
||||
});
|
||||
graph.forEach(function(value, key) {
|
||||
value.forEach(function(adjacent) {
|
||||
inDegrees.set(adjacent.id, inDegrees.get(adjacent.id) + 1);
|
||||
});
|
||||
});
|
||||
|
||||
let positionMap = new Map(); // keeps the position for each node
|
||||
let pastMap = new Map(); // keeps the predecessors(past) of a node
|
||||
let queue = new LinkedList();
|
||||
inDegrees.forEach(function(value, key) {
|
||||
if (value == 0) {
|
||||
queue.push(key);
|
||||
if (!fixedNodes) {
|
||||
if (direction == "horizontal") {
|
||||
positionMap.set(key, nodeIndexes.has(key) ? xCoords[nodeIndexes.get(key)] : dummyPositions.get(key));
|
||||
}
|
||||
else {
|
||||
positionMap.set(key, nodeIndexes.has(key) ? yCoords[nodeIndexes.get(key)] : dummyPositions.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
positionMap.set(key, Number.NEGATIVE_INFINITY);
|
||||
}
|
||||
if (fixedNodes) {
|
||||
pastMap.set(key, new Set([key]));
|
||||
}
|
||||
});
|
||||
|
||||
// align sources of each component in enforcement phase
|
||||
if (fixedNodes) {
|
||||
componentSources.forEach(function(component) {
|
||||
let fixedIds = [];
|
||||
component.forEach(function(nodeId) {
|
||||
if (fixedNodes.has(nodeId)) {
|
||||
fixedIds.push(nodeId);
|
||||
}
|
||||
});
|
||||
if (fixedIds.length > 0) {
|
||||
let position = 0;
|
||||
fixedIds.forEach(function(fixedId) {
|
||||
if (direction == "horizontal") {
|
||||
positionMap.set(fixedId, nodeIndexes.has(fixedId) ? xCoords[nodeIndexes.get(fixedId)] : dummyPositions.get(fixedId));
|
||||
position += positionMap.get(fixedId);
|
||||
}
|
||||
else {
|
||||
positionMap.set(fixedId, nodeIndexes.has(fixedId) ? yCoords[nodeIndexes.get(fixedId)] : dummyPositions.get(fixedId));
|
||||
position += positionMap.get(fixedId);
|
||||
}
|
||||
});
|
||||
position = position / fixedIds.length;
|
||||
component.forEach(function(nodeId) {
|
||||
if (!fixedNodes.has(nodeId)) {
|
||||
positionMap.set(nodeId, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
let position = 0;
|
||||
component.forEach(function(nodeId) {
|
||||
if (direction == "horizontal") {
|
||||
position += nodeIndexes.has(nodeId) ? xCoords[nodeIndexes.get(nodeId)] : dummyPositions.get(nodeId);
|
||||
}
|
||||
else {
|
||||
position += nodeIndexes.has(nodeId) ? yCoords[nodeIndexes.get(nodeId)] : dummyPositions.get(nodeId);
|
||||
}
|
||||
});
|
||||
position = position / component.length;
|
||||
component.forEach(function(nodeId) {
|
||||
positionMap.set(nodeId, position);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// calculate positions of the nodes
|
||||
while (queue.length != 0) {
|
||||
let currentNode = queue.shift();
|
||||
let neighbors = graph.get(currentNode);
|
||||
neighbors.forEach(function(neighbor) {
|
||||
if (positionMap.get(neighbor.id) < (positionMap.get(currentNode) + neighbor.gap)) {
|
||||
if (fixedNodes && fixedNodes.has(neighbor.id)) {
|
||||
let fixedPosition;
|
||||
if (direction == "horizontal") {
|
||||
fixedPosition = nodeIndexes.has(neighbor.id) ? xCoords[nodeIndexes.get(neighbor.id)] : dummyPositions.get(neighbor.id);
|
||||
}
|
||||
else {
|
||||
fixedPosition = nodeIndexes.has(neighbor.id) ? yCoords[nodeIndexes.get(neighbor.id)] : dummyPositions.get(neighbor.id);
|
||||
}
|
||||
positionMap.set(neighbor.id, fixedPosition); // TODO: may do unnecessary work
|
||||
if (fixedPosition < (positionMap.get(currentNode) + neighbor.gap)) {
|
||||
let diff = (positionMap.get(currentNode) + neighbor.gap) - fixedPosition;
|
||||
pastMap.get(currentNode).forEach(function(nodeId) {
|
||||
positionMap.set(nodeId, positionMap.get(nodeId) - diff);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
positionMap.set(neighbor.id, positionMap.get(currentNode) + neighbor.gap);
|
||||
}
|
||||
}
|
||||
inDegrees.set(neighbor.id, inDegrees.get(neighbor.id) - 1);
|
||||
if (inDegrees.get(neighbor.id) == 0) {
|
||||
queue.push(neighbor.id);
|
||||
}
|
||||
if (fixedNodes) {
|
||||
pastMap.set(neighbor.id, setUnion(pastMap.get(currentNode), pastMap.get(neighbor.id)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// readjust position of the nodes after enforcement
|
||||
if (fixedNodes) {
|
||||
// find indegree count for each node
|
||||
let sinkNodes = new Set();
|
||||
|
||||
graph.forEach(function(value, key) {
|
||||
if (value.length == 0) {
|
||||
sinkNodes.add(key);
|
||||
}
|
||||
});
|
||||
|
||||
let components = [];
|
||||
pastMap.forEach(function(value, key) {
|
||||
if (sinkNodes.has(key)) {
|
||||
let isFixedComponent = false;
|
||||
for (let nodeId of value) {
|
||||
if (fixedNodes.has(nodeId)) {
|
||||
isFixedComponent = true;
|
||||
}
|
||||
}
|
||||
if (!isFixedComponent) {
|
||||
let isExist = false;
|
||||
let existAt;
|
||||
components.forEach(function(component, index) {
|
||||
if (component.has([...value][0])) {
|
||||
isExist = true;
|
||||
existAt = index;
|
||||
}
|
||||
});
|
||||
if (!isExist) {
|
||||
components.push(new Set(value));
|
||||
}
|
||||
else {
|
||||
value.forEach(function(ele) {
|
||||
components[existAt].add(ele);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
components.forEach(function(component, index) {
|
||||
let minBefore = Number.POSITIVE_INFINITY;
|
||||
let minAfter = Number.POSITIVE_INFINITY;
|
||||
let maxBefore = Number.NEGATIVE_INFINITY;
|
||||
let maxAfter = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let nodeId of component) {
|
||||
let posBefore;
|
||||
if (direction == "horizontal") {
|
||||
posBefore = nodeIndexes.has(nodeId) ? xCoords[nodeIndexes.get(nodeId)] : dummyPositions.get(nodeId);
|
||||
}
|
||||
else {
|
||||
posBefore = nodeIndexes.has(nodeId) ? yCoords[nodeIndexes.get(nodeId)] : dummyPositions.get(nodeId);
|
||||
}
|
||||
let posAfter = positionMap.get(nodeId);
|
||||
if (posBefore < minBefore) {
|
||||
minBefore = posBefore;
|
||||
}
|
||||
if (posBefore > maxBefore) {
|
||||
maxBefore = posBefore;
|
||||
}
|
||||
if (posAfter < minAfter) {
|
||||
minAfter = posAfter;
|
||||
}
|
||||
if (posAfter > maxAfter) {
|
||||
maxAfter = posAfter;
|
||||
}
|
||||
}
|
||||
let diff = (minBefore + maxBefore) / 2 - (minAfter + maxAfter) / 2;
|
||||
|
||||
for (let nodeId of component) {
|
||||
positionMap.set(nodeId, positionMap.get(nodeId) + diff);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return positionMap;
|
||||
};
|
||||
|
||||
// find transformation based on rel. placement constraints if there are both alignment and rel. placement constraints
|
||||
// or if there are only rel. placement contraints where the largest component isn't sufficiently large
|
||||
let applyReflectionForRelativePlacement = function (relativePlacementConstraints) {
|
||||
// variables to count votes
|
||||
let reflectOnY = 0, notReflectOnY = 0;
|
||||
let reflectOnX = 0, notReflectOnX = 0;
|
||||
|
||||
relativePlacementConstraints.forEach(function(constraint) {
|
||||
if (constraint.left) {
|
||||
(xCoords[nodeIndexes.get(constraint.left)] - xCoords[nodeIndexes.get(constraint.right)] >= 0) ? reflectOnY++ : notReflectOnY++;
|
||||
}
|
||||
else {
|
||||
(yCoords[nodeIndexes.get(constraint.top)] - yCoords[nodeIndexes.get(constraint.bottom)] >= 0) ? reflectOnX++ : notReflectOnX++;
|
||||
}
|
||||
});
|
||||
|
||||
if (reflectOnY > notReflectOnY && reflectOnX > notReflectOnX) {
|
||||
for (let i = 0; i < nodeIndexes.size; i++) {
|
||||
xCoords[i] = -1 * xCoords[i];
|
||||
yCoords[i] = -1 * yCoords[i];
|
||||
}
|
||||
}
|
||||
else if (reflectOnY > notReflectOnY) {
|
||||
for (let i = 0; i < nodeIndexes.size; i++) {
|
||||
xCoords[i] = -1 * xCoords[i];
|
||||
}
|
||||
}
|
||||
else if (reflectOnX > notReflectOnX) {
|
||||
for (let i = 0; i < nodeIndexes.size; i++) {
|
||||
yCoords[i] = -1 * yCoords[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// find weakly connected components in undirected graph
|
||||
let findComponents = function(graph) {
|
||||
// find weakly connected components in dag
|
||||
let components = [];
|
||||
let queue = new LinkedList();
|
||||
let visited = new Set();
|
||||
let count = 0;
|
||||
|
||||
graph.forEach(function(value, key) {
|
||||
if (!visited.has(key)) {
|
||||
components[count] = [];
|
||||
let currentNode = key;
|
||||
queue.push(currentNode);
|
||||
visited.add(currentNode);
|
||||
components[count].push(currentNode);
|
||||
|
||||
while (queue.length != 0) {
|
||||
currentNode = queue.shift();
|
||||
let neighbors = graph.get(currentNode);
|
||||
neighbors.forEach(function(neighbor) {
|
||||
if (!visited.has(neighbor.id)) {
|
||||
queue.push(neighbor.id);
|
||||
visited.add(neighbor.id);
|
||||
components[count].push(neighbor.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
count++;
|
||||
}
|
||||
});
|
||||
return components;
|
||||
};
|
||||
|
||||
// return undirected version of given dag
|
||||
let dagToUndirected = function(dag) {
|
||||
let undirected = new Map();
|
||||
|
||||
dag.forEach(function(value, key) {
|
||||
undirected.set(key, []);
|
||||
});
|
||||
|
||||
dag.forEach(function(value, key) {
|
||||
value.forEach(function(adjacent) {
|
||||
undirected.get(key).push(adjacent);
|
||||
undirected.get(adjacent.id).push({id: key, gap: adjacent.gap, direction: adjacent.direction});
|
||||
});
|
||||
});
|
||||
|
||||
return undirected;
|
||||
};
|
||||
|
||||
// return reversed (directions inverted) version of given dag
|
||||
let dagToReversed = function(dag) {
|
||||
let reversed = new Map();
|
||||
|
||||
dag.forEach(function(value, key) {
|
||||
reversed.set(key, []);
|
||||
});
|
||||
|
||||
dag.forEach(function(value, key) {
|
||||
value.forEach(function(adjacent) {
|
||||
reversed.get(adjacent.id).push({id: key, gap: adjacent.gap, direction: adjacent.direction});
|
||||
});
|
||||
});
|
||||
|
||||
return reversed;
|
||||
};
|
||||
|
||||
/**** apply transformation to the initial draft layout to better align with constrained nodes ****/
|
||||
// solve the Orthogonal Procrustean Problem to rotate and/or reflect initial draft layout
|
||||
// here we follow the solution in Chapter 20.2 of Borg, I. & Groenen, P. (2005) Modern Multidimensional Scaling: Theory and Applications
|
||||
|
||||
/* construct source and target configurations */
|
||||
|
||||
let targetMatrix = []; // A - target configuration
|
||||
let sourceMatrix = []; // B - source configuration
|
||||
let standardTransformation = false; // false for no transformation, true for standart (Procrustes) transformation (rotation and/or reflection)
|
||||
let reflectionType = false; // false/true for reflection check, 'reflectOnX', 'reflectOnY' or 'reflectOnBoth' for reflection type if necessary
|
||||
let fixedNodes = new Set();
|
||||
let dag = new Map(); // adjacency list to keep directed acyclic graph (dag) that consists of relative placement constraints
|
||||
let dagUndirected = new Map(); // undirected version of the dag
|
||||
let components = []; // weakly connected components
|
||||
|
||||
// fill fixedNodes collection to use later
|
||||
if (constraints.fixedNodeConstraint) {
|
||||
constraints.fixedNodeConstraint.forEach(function(nodeData) {
|
||||
fixedNodes.add(nodeData.nodeId);
|
||||
});
|
||||
}
|
||||
|
||||
// construct dag from relative placement constraints
|
||||
if (constraints.relativePlacementConstraint) {
|
||||
// construct both directed and undirected version of the dag
|
||||
constraints.relativePlacementConstraint.forEach(function(constraint) {
|
||||
if (constraint.left) {
|
||||
if (dag.has(constraint.left)) {
|
||||
dag.get(constraint.left).push({id: constraint.right, gap: constraint.gap, direction: "horizontal"});
|
||||
}
|
||||
else {
|
||||
dag.set(constraint.left, [{id: constraint.right, gap: constraint.gap, direction: "horizontal"}]);
|
||||
}
|
||||
if (!dag.has(constraint.right)) {
|
||||
dag.set(constraint.right, []);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (dag.has(constraint.top)) {
|
||||
dag.get(constraint.top).push({id: constraint.bottom, gap: constraint.gap, direction: "vertical"});
|
||||
}
|
||||
else {
|
||||
dag.set(constraint.top, [{id: constraint.bottom, gap: constraint.gap, direction: "vertical"}]);
|
||||
}
|
||||
if (!dag.has(constraint.bottom)) {
|
||||
dag.set(constraint.bottom, []);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dagUndirected = dagToUndirected(dag);
|
||||
components = findComponents(dagUndirected);
|
||||
}
|
||||
|
||||
if (CoSEConstants.TRANSFORM_ON_CONSTRAINT_HANDLING) {
|
||||
// first check fixed node constraint
|
||||
if (constraints.fixedNodeConstraint && constraints.fixedNodeConstraint.length > 1) {
|
||||
constraints.fixedNodeConstraint.forEach(function(nodeData, i) {
|
||||
targetMatrix[i] = [nodeData.position.x, nodeData.position.y];
|
||||
sourceMatrix[i] = [xCoords[nodeIndexes.get(nodeData.nodeId)], yCoords[nodeIndexes.get(nodeData.nodeId)]];
|
||||
});
|
||||
standardTransformation = true;
|
||||
}
|
||||
else if (constraints.alignmentConstraint) { // then check alignment constraint
|
||||
let count = 0;
|
||||
if (constraints.alignmentConstraint.vertical) {
|
||||
let verticalAlign = constraints.alignmentConstraint.vertical;
|
||||
for (let i = 0; i < verticalAlign.length; i++) {
|
||||
let alignmentSet = new Set();
|
||||
verticalAlign[i].forEach(function(nodeId) {
|
||||
alignmentSet.add(nodeId);
|
||||
});
|
||||
let intersection = new Set([...alignmentSet].filter(x => fixedNodes.has(x)));
|
||||
let xPos;
|
||||
if (intersection.size > 0)
|
||||
xPos = xCoords[nodeIndexes.get(intersection.values().next().value)];
|
||||
else
|
||||
xPos = calculateAvgPosition(alignmentSet).x;
|
||||
|
||||
verticalAlign[i].forEach(function(nodeId) {
|
||||
targetMatrix[count] = [xPos, yCoords[nodeIndexes.get(nodeId)]];
|
||||
sourceMatrix[count] = [xCoords[nodeIndexes.get(nodeId)], yCoords[nodeIndexes.get(nodeId)]];
|
||||
count++;
|
||||
});
|
||||
}
|
||||
standardTransformation = true;
|
||||
}
|
||||
if (constraints.alignmentConstraint.horizontal) {
|
||||
let horizontalAlign = constraints.alignmentConstraint.horizontal;
|
||||
for (let i = 0; i < horizontalAlign.length; i++) {
|
||||
let alignmentSet = new Set();
|
||||
horizontalAlign[i].forEach(function(nodeId) {
|
||||
alignmentSet.add(nodeId);
|
||||
});
|
||||
let intersection = new Set([...alignmentSet].filter(x => fixedNodes.has(x)));
|
||||
let yPos;
|
||||
if (intersection.size > 0)
|
||||
yPos = xCoords[nodeIndexes.get(intersection.values().next().value)];
|
||||
else
|
||||
yPos = calculateAvgPosition(alignmentSet).y;
|
||||
|
||||
horizontalAlign[i].forEach(function(nodeId) {
|
||||
targetMatrix[count] = [xCoords[nodeIndexes.get(nodeId)], yPos];
|
||||
sourceMatrix[count] = [xCoords[nodeIndexes.get(nodeId)], yCoords[nodeIndexes.get(nodeId)]];
|
||||
count++;
|
||||
});
|
||||
}
|
||||
standardTransformation = true;
|
||||
}
|
||||
if (constraints.relativePlacementConstraint) {
|
||||
reflectionType = true;
|
||||
}
|
||||
}
|
||||
else if (constraints.relativePlacementConstraint) { // finally check relative placement constraint
|
||||
// find largest component in dag
|
||||
let largestComponentSize = 0;
|
||||
let largestComponentIndex = 0;
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
if (components[i].length > largestComponentSize) {
|
||||
largestComponentSize = components[i].length;
|
||||
largestComponentIndex = i;
|
||||
}
|
||||
}
|
||||
// if largest component isn't dominant, then take the votes for reflection
|
||||
if (largestComponentSize < (dagUndirected.size / 2)) {
|
||||
applyReflectionForRelativePlacement(constraints.relativePlacementConstraint);
|
||||
standardTransformation = false;
|
||||
reflectionType = false;
|
||||
}
|
||||
else { // use largest component for transformation
|
||||
// construct horizontal and vertical subgraphs in the largest component
|
||||
let subGraphOnHorizontal = new Map();
|
||||
let subGraphOnVertical = new Map();
|
||||
let constraintsInlargestComponent = [];
|
||||
|
||||
components[largestComponentIndex].forEach(function(nodeId) {
|
||||
dag.get(nodeId).forEach(function(adjacent) {
|
||||
if (adjacent.direction == "horizontal") {
|
||||
if (subGraphOnHorizontal.has(nodeId)) {
|
||||
subGraphOnHorizontal.get(nodeId).push(adjacent);
|
||||
}
|
||||
else {
|
||||
subGraphOnHorizontal.set(nodeId, [adjacent]);
|
||||
}
|
||||
if (!subGraphOnHorizontal.has(adjacent.id)) {
|
||||
subGraphOnHorizontal.set(adjacent.id, []);
|
||||
}
|
||||
constraintsInlargestComponent.push({left: nodeId, right: adjacent.id});
|
||||
}
|
||||
else {
|
||||
if (subGraphOnVertical.has(nodeId)) {
|
||||
subGraphOnVertical.get(nodeId).push(adjacent);
|
||||
}
|
||||
else {
|
||||
subGraphOnVertical.set(nodeId, [adjacent]);
|
||||
}
|
||||
if (!subGraphOnVertical.has(adjacent.id)) {
|
||||
subGraphOnVertical.set(adjacent.id, []);
|
||||
}
|
||||
constraintsInlargestComponent.push({top: nodeId, bottom: adjacent.id});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
applyReflectionForRelativePlacement(constraintsInlargestComponent);
|
||||
reflectionType = false;
|
||||
|
||||
// calculate appropriate positioning for subgraphs
|
||||
let positionMapHorizontal = findAppropriatePositionForRelativePlacement(subGraphOnHorizontal, "horizontal");
|
||||
let positionMapVertical = findAppropriatePositionForRelativePlacement(subGraphOnVertical, "vertical");
|
||||
|
||||
// construct source and target configuration
|
||||
components[largestComponentIndex].forEach(function(nodeId, i) {
|
||||
sourceMatrix[i] = [xCoords[nodeIndexes.get(nodeId)], yCoords[nodeIndexes.get(nodeId)]];
|
||||
targetMatrix[i] = [];
|
||||
if (positionMapHorizontal.has(nodeId)) {
|
||||
targetMatrix[i][0] = positionMapHorizontal.get(nodeId);
|
||||
}
|
||||
else {
|
||||
targetMatrix[i][0] = xCoords[nodeIndexes.get(nodeId)];
|
||||
}
|
||||
if (positionMapVertical.has(nodeId)) {
|
||||
targetMatrix[i][1] = positionMapVertical.get(nodeId);
|
||||
}
|
||||
else {
|
||||
targetMatrix[i][1] = yCoords[nodeIndexes.get(nodeId)];
|
||||
}
|
||||
});
|
||||
|
||||
standardTransformation = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if transformation is required, then calculate and apply transformation matrix
|
||||
if (standardTransformation) {
|
||||
/* calculate transformation matrix */
|
||||
let transformationMatrix;
|
||||
let targetMatrixTranspose = Matrix.transpose(targetMatrix); // A'
|
||||
let sourceMatrixTranspose = Matrix.transpose(sourceMatrix); // B'
|
||||
|
||||
// centralize transpose matrices
|
||||
for (let i = 0; i < targetMatrixTranspose.length; i++) {
|
||||
targetMatrixTranspose[i] = Matrix.multGamma(targetMatrixTranspose[i]);
|
||||
sourceMatrixTranspose[i] = Matrix.multGamma(sourceMatrixTranspose[i]);
|
||||
}
|
||||
|
||||
// do actual calculation for transformation matrix
|
||||
let tempMatrix = Matrix.multMat(targetMatrixTranspose, Matrix.transpose(sourceMatrixTranspose)); // tempMatrix = A'B
|
||||
let SVDResult = SVD.svd(tempMatrix); // SVD(A'B) = USV', svd function returns U, S and V
|
||||
transformationMatrix = Matrix.multMat(SVDResult.V, Matrix.transpose(SVDResult.U)); // transformationMatrix = T = VU'
|
||||
|
||||
/* apply found transformation matrix to obtain final draft layout */
|
||||
for (let i = 0; i < nodeIndexes.size; i++) {
|
||||
let temp1 = [xCoords[i], yCoords[i]];
|
||||
let temp2 = [transformationMatrix[0][0], transformationMatrix[1][0]];
|
||||
let temp3 = [transformationMatrix[0][1], transformationMatrix[1][1]];
|
||||
xCoords[i] = Matrix.dotProduct(temp1, temp2);
|
||||
yCoords[i] = Matrix.dotProduct(temp1, temp3);
|
||||
}
|
||||
|
||||
// applied only both alignment and rel. placement constraints exist
|
||||
if (reflectionType) {
|
||||
applyReflectionForRelativePlacement(constraints.relativePlacementConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CoSEConstants.ENFORCE_CONSTRAINTS) {
|
||||
/**** enforce constraints on the transformed draft layout ****/
|
||||
|
||||
/* first enforce fixed node constraint */
|
||||
|
||||
if (constraints.fixedNodeConstraint && constraints.fixedNodeConstraint.length > 0) {
|
||||
let translationAmount = { x: 0, y: 0 };
|
||||
constraints.fixedNodeConstraint.forEach(function(nodeData, i) {
|
||||
let posInTheory = {x: xCoords[nodeIndexes.get(nodeData.nodeId)], y: yCoords[nodeIndexes.get(nodeData.nodeId)]};
|
||||
let posDesired = nodeData.position;
|
||||
let posDiff = calculatePositionDiff(posDesired, posInTheory);
|
||||
translationAmount.x += posDiff.x;
|
||||
translationAmount.y += posDiff.y;
|
||||
});
|
||||
translationAmount.x /= constraints.fixedNodeConstraint.length;
|
||||
translationAmount.y /= constraints.fixedNodeConstraint.length;
|
||||
|
||||
xCoords.forEach(function(value, i) {
|
||||
xCoords[i] += translationAmount.x;
|
||||
});
|
||||
|
||||
yCoords.forEach(function(value, i) {
|
||||
yCoords[i] += translationAmount.y;
|
||||
});
|
||||
|
||||
constraints.fixedNodeConstraint.forEach(function(nodeData) {
|
||||
xCoords[nodeIndexes.get(nodeData.nodeId)] = nodeData.position.x;
|
||||
yCoords[nodeIndexes.get(nodeData.nodeId)] = nodeData.position.y;
|
||||
});
|
||||
}
|
||||
|
||||
/* then enforce alignment constraint */
|
||||
|
||||
if (constraints.alignmentConstraint) {
|
||||
if (constraints.alignmentConstraint.vertical) {
|
||||
let xAlign = constraints.alignmentConstraint.vertical;
|
||||
for (let i = 0; i < xAlign.length; i++) {
|
||||
let alignmentSet = new Set();
|
||||
xAlign[i].forEach(function(nodeId) {
|
||||
alignmentSet.add(nodeId);
|
||||
});
|
||||
let intersection = new Set([...alignmentSet].filter(x => fixedNodes.has(x)));
|
||||
let xPos;
|
||||
if (intersection.size > 0)
|
||||
xPos = xCoords[nodeIndexes.get(intersection.values().next().value)];
|
||||
else
|
||||
xPos = calculateAvgPosition(alignmentSet).x;
|
||||
|
||||
alignmentSet.forEach(function(nodeId) {
|
||||
if (!fixedNodes.has(nodeId))
|
||||
xCoords[nodeIndexes.get(nodeId)] = xPos;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (constraints.alignmentConstraint.horizontal) {
|
||||
let yAlign = constraints.alignmentConstraint.horizontal;
|
||||
for (let i = 0; i < yAlign.length; i++) {
|
||||
let alignmentSet = new Set();
|
||||
yAlign[i].forEach(function(nodeId) {
|
||||
alignmentSet.add(nodeId);
|
||||
});
|
||||
let intersection = new Set([...alignmentSet].filter(x => fixedNodes.has(x)));
|
||||
let yPos;
|
||||
if (intersection.size > 0)
|
||||
yPos = yCoords[nodeIndexes.get(intersection.values().next().value)];
|
||||
else
|
||||
yPos = calculateAvgPosition(alignmentSet).y;
|
||||
|
||||
alignmentSet.forEach(function(nodeId) {
|
||||
if (!fixedNodes.has(nodeId))
|
||||
yCoords[nodeIndexes.get(nodeId)] = yPos;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* finally enforce relative placement constraint */
|
||||
|
||||
if (constraints.relativePlacementConstraint) {
|
||||
let nodeToDummyForVerticalAlignment = new Map();
|
||||
let nodeToDummyForHorizontalAlignment = new Map();
|
||||
let dummyToNodeForVerticalAlignment = new Map();
|
||||
let dummyToNodeForHorizontalAlignment = new Map();
|
||||
let dummyPositionsForVerticalAlignment = new Map();
|
||||
let dummyPositionsForHorizontalAlignment = new Map();
|
||||
let fixedNodesOnHorizontal = new Set();
|
||||
let fixedNodesOnVertical = new Set();
|
||||
|
||||
// fill maps and sets
|
||||
fixedNodes.forEach(function(nodeId) {
|
||||
fixedNodesOnHorizontal.add(nodeId);
|
||||
fixedNodesOnVertical.add(nodeId);
|
||||
});
|
||||
|
||||
if (constraints.alignmentConstraint) {
|
||||
if (constraints.alignmentConstraint.vertical) {
|
||||
let verticalAlignment = constraints.alignmentConstraint.vertical;
|
||||
for (let i = 0; i < verticalAlignment.length; i++) {
|
||||
dummyToNodeForVerticalAlignment.set("dummy" + i, []);
|
||||
verticalAlignment[i].forEach(function(nodeId) {
|
||||
nodeToDummyForVerticalAlignment.set(nodeId, "dummy" + i);
|
||||
dummyToNodeForVerticalAlignment.get("dummy" + i).push(nodeId);
|
||||
if (fixedNodes.has(nodeId)) {
|
||||
fixedNodesOnHorizontal.add("dummy" + i);
|
||||
}
|
||||
});
|
||||
dummyPositionsForVerticalAlignment.set("dummy" + i, xCoords[nodeIndexes.get(verticalAlignment[i][0])]);
|
||||
}
|
||||
}
|
||||
if (constraints.alignmentConstraint.horizontal) {
|
||||
let horizontalAlignment = constraints.alignmentConstraint.horizontal;
|
||||
for (let i = 0; i < horizontalAlignment.length; i++) {
|
||||
dummyToNodeForHorizontalAlignment.set("dummy" + i, []);
|
||||
horizontalAlignment[i].forEach(function(nodeId) {
|
||||
nodeToDummyForHorizontalAlignment.set(nodeId, "dummy" + i);
|
||||
dummyToNodeForHorizontalAlignment.get("dummy" + i).push(nodeId);
|
||||
if (fixedNodes.has(nodeId)) {
|
||||
fixedNodesOnVertical.add("dummy" + i);
|
||||
}
|
||||
});
|
||||
dummyPositionsForHorizontalAlignment.set("dummy" + i, yCoords[nodeIndexes.get(horizontalAlignment[i][0])]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// construct horizontal and vertical dags (subgraphs) from overall dag
|
||||
let dagOnHorizontal = new Map();
|
||||
let dagOnVertical = new Map();
|
||||
|
||||
for (let nodeId of dag.keys()) {
|
||||
dag.get(nodeId).forEach(function(adjacent) {
|
||||
let sourceId;
|
||||
let targetNode;
|
||||
if (adjacent["direction"] == "horizontal") {
|
||||
sourceId = nodeToDummyForVerticalAlignment.get(nodeId) ? nodeToDummyForVerticalAlignment.get(nodeId) : nodeId;
|
||||
if (nodeToDummyForVerticalAlignment.get(adjacent.id)) {
|
||||
targetNode = {id: nodeToDummyForVerticalAlignment.get(adjacent.id), gap: adjacent.gap, direction: adjacent.direction};
|
||||
}
|
||||
else {
|
||||
targetNode = adjacent;
|
||||
}
|
||||
if (dagOnHorizontal.has(sourceId)) {
|
||||
dagOnHorizontal.get(sourceId).push(targetNode);
|
||||
}
|
||||
else {
|
||||
dagOnHorizontal.set(sourceId, [targetNode]);
|
||||
}
|
||||
if (!dagOnHorizontal.has(targetNode.id)) {
|
||||
dagOnHorizontal.set(targetNode.id, []);
|
||||
}
|
||||
}
|
||||
else {
|
||||
sourceId = nodeToDummyForHorizontalAlignment.get(nodeId) ? nodeToDummyForHorizontalAlignment.get(nodeId) : nodeId;
|
||||
if (nodeToDummyForHorizontalAlignment.get(adjacent.id)) {
|
||||
targetNode = {id: nodeToDummyForHorizontalAlignment.get(adjacent.id), gap: adjacent.gap, direction: adjacent.direction};
|
||||
}
|
||||
else {
|
||||
targetNode = adjacent;
|
||||
}
|
||||
if (dagOnVertical.has(sourceId)) {
|
||||
dagOnVertical.get(sourceId).push(targetNode);
|
||||
}
|
||||
else {
|
||||
dagOnVertical.set(sourceId, [targetNode]);
|
||||
}
|
||||
if (!dagOnVertical.has(targetNode.id)) {
|
||||
dagOnVertical.set(targetNode.id, []);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// find source nodes of each component in horizontal and vertical dags
|
||||
let undirectedOnHorizontal = dagToUndirected(dagOnHorizontal);
|
||||
let undirectedOnVertical = dagToUndirected(dagOnVertical);
|
||||
let componentsOnHorizontal = findComponents(undirectedOnHorizontal);
|
||||
let componentsOnVertical = findComponents(undirectedOnVertical);
|
||||
let reversedDagOnHorizontal = dagToReversed(dagOnHorizontal);
|
||||
let reversedDagOnVertical = dagToReversed(dagOnVertical);
|
||||
let componentSourcesOnHorizontal = [];
|
||||
let componentSourcesOnVertical = [];
|
||||
|
||||
componentsOnHorizontal.forEach(function(component, index) {
|
||||
componentSourcesOnHorizontal[index] = [];
|
||||
component.forEach(function(nodeId) {
|
||||
if (reversedDagOnHorizontal.get(nodeId).length == 0) {
|
||||
componentSourcesOnHorizontal[index].push(nodeId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
componentsOnVertical.forEach(function(component, index) {
|
||||
componentSourcesOnVertical[index] = [];
|
||||
component.forEach(function(nodeId) {
|
||||
if (reversedDagOnVertical.get(nodeId).length == 0) {
|
||||
componentSourcesOnVertical[index].push(nodeId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// calculate appropriate positioning for subgraphs
|
||||
let positionMapHorizontal = findAppropriatePositionForRelativePlacement(dagOnHorizontal, "horizontal", fixedNodesOnHorizontal, dummyPositionsForVerticalAlignment, componentSourcesOnHorizontal);
|
||||
let positionMapVertical = findAppropriatePositionForRelativePlacement(dagOnVertical, "vertical", fixedNodesOnVertical, dummyPositionsForHorizontalAlignment, componentSourcesOnVertical);
|
||||
|
||||
// update positions of the nodes based on relative placement constraints
|
||||
for (let key of positionMapHorizontal.keys()) {
|
||||
if (dummyToNodeForVerticalAlignment.get(key)) {
|
||||
dummyToNodeForVerticalAlignment.get(key).forEach(function(nodeId) {
|
||||
xCoords[nodeIndexes.get(nodeId)] = positionMapHorizontal.get(key);
|
||||
});
|
||||
}
|
||||
else {
|
||||
xCoords[nodeIndexes.get(key)] = positionMapHorizontal.get(key);
|
||||
}
|
||||
}
|
||||
for (let key of positionMapVertical.keys()) {
|
||||
if (dummyToNodeForHorizontalAlignment.get(key)) {
|
||||
dummyToNodeForHorizontalAlignment.get(key).forEach(function(nodeId) {
|
||||
yCoords[nodeIndexes.get(nodeId)] = positionMapVertical.get(key);
|
||||
});
|
||||
}
|
||||
else {
|
||||
yCoords[nodeIndexes.get(key)] = positionMapVertical.get(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assign new coordinates to nodes after constraint handling
|
||||
for (let i = 0; i < allNodes.length; i++) {
|
||||
let node = allNodes[i];
|
||||
if (node.getChild() == null) {
|
||||
node.setCenter(xCoords[nodeIndexes.get(node.id)], yCoords[nodeIndexes.get(node.id)]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ConstraintHandler;
|
||||
20
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/README.md
generated
vendored
Normal file
20
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/README.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
layout-base
|
||||
================================================================================
|
||||
|
||||
## Description
|
||||
|
||||
This repository implements a basic layout model and some utilities for Cytoscape.js layout extensions.
|
||||
|
||||
## Usage instructions
|
||||
|
||||
Add `layout-base` as a dependecy to your layout extension.
|
||||
|
||||
`require()` in the extension to reach functionality:
|
||||
|
||||
* `var Integer = require(layout-base).Integer`,
|
||||
* `var Layout = require(layout-base).Layout`,
|
||||
* `...`
|
||||
|
||||
For a usage example, see [cose-base](https://github.com/iVis-at-Bilkent/cose-base) or [avsdf-base](https://github.com/iVis-at-Bilkent/avsdf-base).
|
||||
|
||||

|
||||
5230
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/layout-base.js
generated
vendored
Normal file
5230
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/layout-base.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
153
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/LEdge.js
generated
vendored
Normal file
153
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/LEdge.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
var LGraphObject = require('./LGraphObject');
|
||||
var IGeometry = require('./util/IGeometry');
|
||||
var IMath = require('./util/IMath');
|
||||
|
||||
function LEdge(source, target, vEdge) {
|
||||
LGraphObject.call(this, vEdge);
|
||||
|
||||
this.isOverlapingSourceAndTarget = false;
|
||||
this.vGraphObject = vEdge;
|
||||
this.bendpoints = [];
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
LEdge.prototype = Object.create(LGraphObject.prototype);
|
||||
|
||||
for (var prop in LGraphObject) {
|
||||
LEdge[prop] = LGraphObject[prop];
|
||||
}
|
||||
|
||||
LEdge.prototype.getSource = function ()
|
||||
{
|
||||
return this.source;
|
||||
};
|
||||
|
||||
LEdge.prototype.getTarget = function ()
|
||||
{
|
||||
return this.target;
|
||||
};
|
||||
|
||||
LEdge.prototype.isInterGraph = function ()
|
||||
{
|
||||
return this.isInterGraph;
|
||||
};
|
||||
|
||||
LEdge.prototype.getLength = function ()
|
||||
{
|
||||
return this.length;
|
||||
};
|
||||
|
||||
LEdge.prototype.isOverlapingSourceAndTarget = function ()
|
||||
{
|
||||
return this.isOverlapingSourceAndTarget;
|
||||
};
|
||||
|
||||
LEdge.prototype.getBendpoints = function ()
|
||||
{
|
||||
return this.bendpoints;
|
||||
};
|
||||
|
||||
LEdge.prototype.getLca = function ()
|
||||
{
|
||||
return this.lca;
|
||||
};
|
||||
|
||||
LEdge.prototype.getSourceInLca = function ()
|
||||
{
|
||||
return this.sourceInLca;
|
||||
};
|
||||
|
||||
LEdge.prototype.getTargetInLca = function ()
|
||||
{
|
||||
return this.targetInLca;
|
||||
};
|
||||
|
||||
LEdge.prototype.getOtherEnd = function (node)
|
||||
{
|
||||
if (this.source === node)
|
||||
{
|
||||
return this.target;
|
||||
}
|
||||
else if (this.target === node)
|
||||
{
|
||||
return this.source;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Node is not incident with this edge";
|
||||
}
|
||||
}
|
||||
|
||||
LEdge.prototype.getOtherEndInGraph = function (node, graph)
|
||||
{
|
||||
var otherEnd = this.getOtherEnd(node);
|
||||
var root = graph.getGraphManager().getRoot();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (otherEnd.getOwner() == graph)
|
||||
{
|
||||
return otherEnd;
|
||||
}
|
||||
|
||||
if (otherEnd.getOwner() == root)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
otherEnd = otherEnd.getOwner().getParent();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
LEdge.prototype.updateLength = function ()
|
||||
{
|
||||
var clipPointCoordinates = new Array(4);
|
||||
|
||||
this.isOverlapingSourceAndTarget =
|
||||
IGeometry.getIntersection(this.target.getRect(),
|
||||
this.source.getRect(),
|
||||
clipPointCoordinates);
|
||||
|
||||
if (!this.isOverlapingSourceAndTarget)
|
||||
{
|
||||
this.lengthX = clipPointCoordinates[0] - clipPointCoordinates[2];
|
||||
this.lengthY = clipPointCoordinates[1] - clipPointCoordinates[3];
|
||||
|
||||
if (Math.abs(this.lengthX) < 1.0)
|
||||
{
|
||||
this.lengthX = IMath.sign(this.lengthX);
|
||||
}
|
||||
|
||||
if (Math.abs(this.lengthY) < 1.0)
|
||||
{
|
||||
this.lengthY = IMath.sign(this.lengthY);
|
||||
}
|
||||
|
||||
this.length = Math.sqrt(
|
||||
this.lengthX * this.lengthX + this.lengthY * this.lengthY);
|
||||
}
|
||||
};
|
||||
|
||||
LEdge.prototype.updateLengthSimple = function ()
|
||||
{
|
||||
this.lengthX = this.target.getCenterX() - this.source.getCenterX();
|
||||
this.lengthY = this.target.getCenterY() - this.source.getCenterY();
|
||||
|
||||
if (Math.abs(this.lengthX) < 1.0)
|
||||
{
|
||||
this.lengthX = IMath.sign(this.lengthX);
|
||||
}
|
||||
|
||||
if (Math.abs(this.lengthY) < 1.0)
|
||||
{
|
||||
this.lengthY = IMath.sign(this.lengthY);
|
||||
}
|
||||
|
||||
this.length = Math.sqrt(
|
||||
this.lengthX * this.lengthX + this.lengthY * this.lengthY);
|
||||
}
|
||||
|
||||
module.exports = LEdge;
|
||||
672
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/Layout.js
generated
vendored
Normal file
672
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/Layout.js
generated
vendored
Normal file
@@ -0,0 +1,672 @@
|
||||
var LayoutConstants = require('./LayoutConstants');
|
||||
var LGraphManager = require('./LGraphManager');
|
||||
var LNode = require('./LNode');
|
||||
var LEdge = require('./LEdge');
|
||||
var LGraph = require('./LGraph');
|
||||
var PointD = require('./util/PointD');
|
||||
var Transform = require('./util/Transform');
|
||||
var Emitter = require('./util/Emitter');
|
||||
|
||||
function Layout(isRemoteUse) {
|
||||
Emitter.call( this );
|
||||
|
||||
//Layout Quality: 0:draft, 1:default, 2:proof
|
||||
this.layoutQuality = LayoutConstants.QUALITY;
|
||||
//Whether layout should create bendpoints as needed or not
|
||||
this.createBendsAsNeeded =
|
||||
LayoutConstants.DEFAULT_CREATE_BENDS_AS_NEEDED;
|
||||
//Whether layout should be incremental or not
|
||||
this.incremental = LayoutConstants.DEFAULT_INCREMENTAL;
|
||||
//Whether we animate from before to after layout node positions
|
||||
this.animationOnLayout =
|
||||
LayoutConstants.DEFAULT_ANIMATION_ON_LAYOUT;
|
||||
//Whether we animate the layout process or not
|
||||
this.animationDuringLayout = LayoutConstants.DEFAULT_ANIMATION_DURING_LAYOUT;
|
||||
//Number iterations that should be done between two successive animations
|
||||
this.animationPeriod = LayoutConstants.DEFAULT_ANIMATION_PERIOD;
|
||||
/**
|
||||
* Whether or not leaf nodes (non-compound nodes) are of uniform sizes. When
|
||||
* they are, both spring and repulsion forces between two leaf nodes can be
|
||||
* calculated without the expensive clipping point calculations, resulting
|
||||
* in major speed-up.
|
||||
*/
|
||||
this.uniformLeafNodeSizes =
|
||||
LayoutConstants.DEFAULT_UNIFORM_LEAF_NODE_SIZES;
|
||||
/**
|
||||
* This is used for creation of bendpoints by using dummy nodes and edges.
|
||||
* Maps an LEdge to its dummy bendpoint path.
|
||||
*/
|
||||
this.edgeToDummyNodes = new Map();
|
||||
this.graphManager = new LGraphManager(this);
|
||||
this.isLayoutFinished = false;
|
||||
this.isSubLayout = false;
|
||||
this.isRemoteUse = false;
|
||||
|
||||
if (isRemoteUse != null) {
|
||||
this.isRemoteUse = isRemoteUse;
|
||||
}
|
||||
}
|
||||
|
||||
Layout.RANDOM_SEED = 1;
|
||||
|
||||
Layout.prototype = Object.create( Emitter.prototype );
|
||||
|
||||
Layout.prototype.getGraphManager = function () {
|
||||
return this.graphManager;
|
||||
};
|
||||
|
||||
Layout.prototype.getAllNodes = function () {
|
||||
return this.graphManager.getAllNodes();
|
||||
};
|
||||
|
||||
Layout.prototype.getAllEdges = function () {
|
||||
return this.graphManager.getAllEdges();
|
||||
};
|
||||
|
||||
Layout.prototype.getAllNodesToApplyGravitation = function () {
|
||||
return this.graphManager.getAllNodesToApplyGravitation();
|
||||
};
|
||||
|
||||
Layout.prototype.newGraphManager = function () {
|
||||
var gm = new LGraphManager(this);
|
||||
this.graphManager = gm;
|
||||
return gm;
|
||||
};
|
||||
|
||||
Layout.prototype.newGraph = function (vGraph)
|
||||
{
|
||||
return new LGraph(null, this.graphManager, vGraph);
|
||||
};
|
||||
|
||||
Layout.prototype.newNode = function (vNode)
|
||||
{
|
||||
return new LNode(this.graphManager, vNode);
|
||||
};
|
||||
|
||||
Layout.prototype.newEdge = function (vEdge)
|
||||
{
|
||||
return new LEdge(null, null, vEdge);
|
||||
};
|
||||
|
||||
Layout.prototype.checkLayoutSuccess = function() {
|
||||
return (this.graphManager.getRoot() == null)
|
||||
|| this.graphManager.getRoot().getNodes().length == 0
|
||||
|| this.graphManager.includesInvalidEdge();
|
||||
};
|
||||
|
||||
Layout.prototype.runLayout = function ()
|
||||
{
|
||||
this.isLayoutFinished = false;
|
||||
|
||||
if (this.tilingPreLayout) {
|
||||
this.tilingPreLayout();
|
||||
}
|
||||
|
||||
this.initParameters();
|
||||
var isLayoutSuccessfull;
|
||||
|
||||
if (this.checkLayoutSuccess())
|
||||
{
|
||||
isLayoutSuccessfull = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isLayoutSuccessfull = this.layout();
|
||||
}
|
||||
|
||||
if (LayoutConstants.ANIMATE === 'during') {
|
||||
// If this is a 'during' layout animation. Layout is not finished yet.
|
||||
// We need to perform these in index.js when layout is really finished.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isLayoutSuccessfull)
|
||||
{
|
||||
if (!this.isSubLayout)
|
||||
{
|
||||
this.doPostLayout();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tilingPostLayout) {
|
||||
this.tilingPostLayout();
|
||||
}
|
||||
|
||||
this.isLayoutFinished = true;
|
||||
|
||||
return isLayoutSuccessfull;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method performs the operations required after layout.
|
||||
*/
|
||||
Layout.prototype.doPostLayout = function ()
|
||||
{
|
||||
//assert !isSubLayout : "Should not be called on sub-layout!";
|
||||
// Propagate geometric changes to v-level objects
|
||||
if(!this.incremental){
|
||||
this.transform();
|
||||
}
|
||||
this.update();
|
||||
};
|
||||
|
||||
/**
|
||||
* This method updates the geometry of the target graph according to
|
||||
* calculated layout.
|
||||
*/
|
||||
Layout.prototype.update2 = function () {
|
||||
// update bend points
|
||||
if (this.createBendsAsNeeded)
|
||||
{
|
||||
this.createBendpointsFromDummyNodes();
|
||||
|
||||
// reset all edges, since the topology has changed
|
||||
this.graphManager.resetAllEdges();
|
||||
}
|
||||
|
||||
// perform edge, node and root updates if layout is not called
|
||||
// remotely
|
||||
if (!this.isRemoteUse)
|
||||
{
|
||||
// update all edges
|
||||
var edge;
|
||||
var allEdges = this.graphManager.getAllEdges();
|
||||
for (var i = 0; i < allEdges.length; i++)
|
||||
{
|
||||
edge = allEdges[i];
|
||||
// this.update(edge);
|
||||
}
|
||||
|
||||
// recursively update nodes
|
||||
var node;
|
||||
var nodes = this.graphManager.getRoot().getNodes();
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
{
|
||||
node = nodes[i];
|
||||
// this.update(node);
|
||||
}
|
||||
|
||||
// update root graph
|
||||
this.update(this.graphManager.getRoot());
|
||||
}
|
||||
};
|
||||
|
||||
Layout.prototype.update = function (obj) {
|
||||
if (obj == null) {
|
||||
this.update2();
|
||||
}
|
||||
else if (obj instanceof LNode) {
|
||||
var node = obj;
|
||||
if (node.getChild() != null)
|
||||
{
|
||||
// since node is compound, recursively update child nodes
|
||||
var nodes = node.getChild().getNodes();
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
{
|
||||
update(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// if the l-level node is associated with a v-level graph object,
|
||||
// then it is assumed that the v-level node implements the
|
||||
// interface Updatable.
|
||||
if (node.vGraphObject != null)
|
||||
{
|
||||
// cast to Updatable without any type check
|
||||
var vNode = node.vGraphObject;
|
||||
|
||||
// call the update method of the interface
|
||||
vNode.update(node);
|
||||
}
|
||||
}
|
||||
else if (obj instanceof LEdge) {
|
||||
var edge = obj;
|
||||
// if the l-level edge is associated with a v-level graph object,
|
||||
// then it is assumed that the v-level edge implements the
|
||||
// interface Updatable.
|
||||
|
||||
if (edge.vGraphObject != null)
|
||||
{
|
||||
// cast to Updatable without any type check
|
||||
var vEdge = edge.vGraphObject;
|
||||
|
||||
// call the update method of the interface
|
||||
vEdge.update(edge);
|
||||
}
|
||||
}
|
||||
else if (obj instanceof LGraph) {
|
||||
var graph = obj;
|
||||
// if the l-level graph is associated with a v-level graph object,
|
||||
// then it is assumed that the v-level object implements the
|
||||
// interface Updatable.
|
||||
|
||||
if (graph.vGraphObject != null)
|
||||
{
|
||||
// cast to Updatable without any type check
|
||||
var vGraph = graph.vGraphObject;
|
||||
|
||||
// call the update method of the interface
|
||||
vGraph.update(graph);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is used to set all layout parameters to default values
|
||||
* determined at compile time.
|
||||
*/
|
||||
Layout.prototype.initParameters = function () {
|
||||
if (!this.isSubLayout)
|
||||
{
|
||||
this.layoutQuality = LayoutConstants.QUALITY;
|
||||
this.animationDuringLayout = LayoutConstants.DEFAULT_ANIMATION_DURING_LAYOUT;
|
||||
this.animationPeriod = LayoutConstants.DEFAULT_ANIMATION_PERIOD;
|
||||
this.animationOnLayout = LayoutConstants.DEFAULT_ANIMATION_ON_LAYOUT;
|
||||
this.incremental = LayoutConstants.DEFAULT_INCREMENTAL;
|
||||
this.createBendsAsNeeded = LayoutConstants.DEFAULT_CREATE_BENDS_AS_NEEDED;
|
||||
this.uniformLeafNodeSizes = LayoutConstants.DEFAULT_UNIFORM_LEAF_NODE_SIZES;
|
||||
}
|
||||
|
||||
if (this.animationDuringLayout)
|
||||
{
|
||||
this.animationOnLayout = false;
|
||||
}
|
||||
};
|
||||
|
||||
Layout.prototype.transform = function (newLeftTop) {
|
||||
if (newLeftTop == undefined) {
|
||||
this.transform(new PointD(0, 0));
|
||||
}
|
||||
else {
|
||||
// create a transformation object (from Eclipse to layout). When an
|
||||
// inverse transform is applied, we get upper-left coordinate of the
|
||||
// drawing or the root graph at given input coordinate (some margins
|
||||
// already included in calculation of left-top).
|
||||
|
||||
var trans = new Transform();
|
||||
var leftTop = this.graphManager.getRoot().updateLeftTop();
|
||||
|
||||
if (leftTop != null)
|
||||
{
|
||||
trans.setWorldOrgX(newLeftTop.x);
|
||||
trans.setWorldOrgY(newLeftTop.y);
|
||||
|
||||
trans.setDeviceOrgX(leftTop.x);
|
||||
trans.setDeviceOrgY(leftTop.y);
|
||||
|
||||
var nodes = this.getAllNodes();
|
||||
var node;
|
||||
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
{
|
||||
node = nodes[i];
|
||||
node.transform(trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Layout.prototype.positionNodesRandomly = function (graph) {
|
||||
|
||||
if (graph == undefined) {
|
||||
//assert !this.incremental;
|
||||
this.positionNodesRandomly(this.getGraphManager().getRoot());
|
||||
this.getGraphManager().getRoot().updateBounds(true);
|
||||
}
|
||||
else {
|
||||
var lNode;
|
||||
var childGraph;
|
||||
|
||||
var nodes = graph.getNodes();
|
||||
for (var i = 0; i < nodes.length; i++)
|
||||
{
|
||||
lNode = nodes[i];
|
||||
childGraph = lNode.getChild();
|
||||
|
||||
if (childGraph == null)
|
||||
{
|
||||
lNode.scatter();
|
||||
}
|
||||
else if (childGraph.getNodes().length == 0)
|
||||
{
|
||||
lNode.scatter();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.positionNodesRandomly(childGraph);
|
||||
lNode.updateBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method returns a list of trees where each tree is represented as a
|
||||
* list of l-nodes. The method returns a list of size 0 when:
|
||||
* - The graph is not flat or
|
||||
* - One of the component(s) of the graph is not a tree.
|
||||
*/
|
||||
Layout.prototype.getFlatForest = function ()
|
||||
{
|
||||
var flatForest = [];
|
||||
var isForest = true;
|
||||
|
||||
// Quick reference for all nodes in the graph manager associated with
|
||||
// this layout. The list should not be changed.
|
||||
var allNodes = this.graphManager.getRoot().getNodes();
|
||||
|
||||
// First be sure that the graph is flat
|
||||
var isFlat = true;
|
||||
|
||||
for (var i = 0; i < allNodes.length; i++)
|
||||
{
|
||||
if (allNodes[i].getChild() != null)
|
||||
{
|
||||
isFlat = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return empty forest if the graph is not flat.
|
||||
if (!isFlat)
|
||||
{
|
||||
return flatForest;
|
||||
}
|
||||
|
||||
// Run BFS for each component of the graph.
|
||||
|
||||
var visited = new Set();
|
||||
var toBeVisited = [];
|
||||
var parents = new Map();
|
||||
var unProcessedNodes = [];
|
||||
|
||||
unProcessedNodes = unProcessedNodes.concat(allNodes);
|
||||
|
||||
// Each iteration of this loop finds a component of the graph and
|
||||
// decides whether it is a tree or not. If it is a tree, adds it to the
|
||||
// forest and continued with the next component.
|
||||
|
||||
while (unProcessedNodes.length > 0 && isForest)
|
||||
{
|
||||
toBeVisited.push(unProcessedNodes[0]);
|
||||
|
||||
// Start the BFS. Each iteration of this loop visits a node in a
|
||||
// BFS manner.
|
||||
while (toBeVisited.length > 0 && isForest)
|
||||
{
|
||||
//pool operation
|
||||
var currentNode = toBeVisited[0];
|
||||
toBeVisited.splice(0, 1);
|
||||
visited.add(currentNode);
|
||||
|
||||
// Traverse all neighbors of this node
|
||||
var neighborEdges = currentNode.getEdges();
|
||||
|
||||
for (var i = 0; i < neighborEdges.length; i++)
|
||||
{
|
||||
var currentNeighbor =
|
||||
neighborEdges[i].getOtherEnd(currentNode);
|
||||
|
||||
// If BFS is not growing from this neighbor.
|
||||
if (parents.get(currentNode) != currentNeighbor)
|
||||
{
|
||||
// We haven't previously visited this neighbor.
|
||||
if (!visited.has(currentNeighbor))
|
||||
{
|
||||
toBeVisited.push(currentNeighbor);
|
||||
parents.set(currentNeighbor, currentNode);
|
||||
}
|
||||
// Since we have previously visited this neighbor and
|
||||
// this neighbor is not parent of currentNode, given
|
||||
// graph contains a component that is not tree, hence
|
||||
// it is not a forest.
|
||||
else
|
||||
{
|
||||
isForest = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The graph contains a component that is not a tree. Empty
|
||||
// previously found trees. The method will end.
|
||||
if (!isForest)
|
||||
{
|
||||
flatForest = [];
|
||||
}
|
||||
// Save currently visited nodes as a tree in our forest. Reset
|
||||
// visited and parents lists. Continue with the next component of
|
||||
// the graph, if any.
|
||||
else
|
||||
{
|
||||
var temp = [...visited];
|
||||
flatForest.push(temp);
|
||||
//flatForest = flatForest.concat(temp);
|
||||
//unProcessedNodes.removeAll(visited);
|
||||
for (var i = 0; i < temp.length; i++) {
|
||||
var value = temp[i];
|
||||
var index = unProcessedNodes.indexOf(value);
|
||||
if (index > -1) {
|
||||
unProcessedNodes.splice(index, 1);
|
||||
}
|
||||
}
|
||||
visited = new Set();
|
||||
parents = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
return flatForest;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method creates dummy nodes (an l-level node with minimal dimensions)
|
||||
* for the given edge (one per bendpoint). The existing l-level structure
|
||||
* is updated accordingly.
|
||||
*/
|
||||
Layout.prototype.createDummyNodesForBendpoints = function (edge)
|
||||
{
|
||||
var dummyNodes = [];
|
||||
var prev = edge.source;
|
||||
|
||||
var graph = this.graphManager.calcLowestCommonAncestor(edge.source, edge.target);
|
||||
|
||||
for (var i = 0; i < edge.bendpoints.length; i++)
|
||||
{
|
||||
// create new dummy node
|
||||
var dummyNode = this.newNode(null);
|
||||
dummyNode.setRect(new Point(0, 0), new Dimension(1, 1));
|
||||
|
||||
graph.add(dummyNode);
|
||||
|
||||
// create new dummy edge between prev and dummy node
|
||||
var dummyEdge = this.newEdge(null);
|
||||
this.graphManager.add(dummyEdge, prev, dummyNode);
|
||||
|
||||
dummyNodes.add(dummyNode);
|
||||
prev = dummyNode;
|
||||
}
|
||||
|
||||
var dummyEdge = this.newEdge(null);
|
||||
this.graphManager.add(dummyEdge, prev, edge.target);
|
||||
|
||||
this.edgeToDummyNodes.set(edge, dummyNodes);
|
||||
|
||||
// remove real edge from graph manager if it is inter-graph
|
||||
if (edge.isInterGraph())
|
||||
{
|
||||
this.graphManager.remove(edge);
|
||||
}
|
||||
// else, remove the edge from the current graph
|
||||
else
|
||||
{
|
||||
graph.remove(edge);
|
||||
}
|
||||
|
||||
return dummyNodes;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method creates bendpoints for edges from the dummy nodes
|
||||
* at l-level.
|
||||
*/
|
||||
Layout.prototype.createBendpointsFromDummyNodes = function ()
|
||||
{
|
||||
var edges = [];
|
||||
edges = edges.concat(this.graphManager.getAllEdges());
|
||||
edges = [...this.edgeToDummyNodes.keys()].concat(edges);
|
||||
|
||||
for (var k = 0; k < edges.length; k++)
|
||||
{
|
||||
var lEdge = edges[k];
|
||||
|
||||
if (lEdge.bendpoints.length > 0)
|
||||
{
|
||||
var path = this.edgeToDummyNodes.get(lEdge);
|
||||
|
||||
for (var i = 0; i < path.length; i++)
|
||||
{
|
||||
var dummyNode = path[i];
|
||||
var p = new PointD(dummyNode.getCenterX(),
|
||||
dummyNode.getCenterY());
|
||||
|
||||
// update bendpoint's location according to dummy node
|
||||
var ebp = lEdge.bendpoints.get(i);
|
||||
ebp.x = p.x;
|
||||
ebp.y = p.y;
|
||||
|
||||
// remove the dummy node, dummy edges incident with this
|
||||
// dummy node is also removed (within the remove method)
|
||||
dummyNode.getOwner().remove(dummyNode);
|
||||
}
|
||||
|
||||
// add the real edge to graph
|
||||
this.graphManager.add(lEdge, lEdge.source, lEdge.target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Layout.transform = function (sliderValue, defaultValue, minDiv, maxMul) {
|
||||
if (minDiv != undefined && maxMul != undefined) {
|
||||
var value = defaultValue;
|
||||
|
||||
if (sliderValue <= 50)
|
||||
{
|
||||
var minValue = defaultValue / minDiv;
|
||||
value -= ((defaultValue - minValue) / 50) * (50 - sliderValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var maxValue = defaultValue * maxMul;
|
||||
value += ((maxValue - defaultValue) / 50) * (sliderValue - 50);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
var a, b;
|
||||
|
||||
if (sliderValue <= 50)
|
||||
{
|
||||
a = 9.0 * defaultValue / 500.0;
|
||||
b = defaultValue / 10.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = 9.0 * defaultValue / 50.0;
|
||||
b = -8 * defaultValue;
|
||||
}
|
||||
|
||||
return (a * sliderValue + b);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method finds and returns the center of the given nodes, assuming
|
||||
* that the given nodes form a tree in themselves.
|
||||
*/
|
||||
Layout.findCenterOfTree = function (nodes)
|
||||
{
|
||||
var list = [];
|
||||
list = list.concat(nodes);
|
||||
|
||||
var removedNodes = [];
|
||||
var remainingDegrees = new Map();
|
||||
var foundCenter = false;
|
||||
var centerNode = null;
|
||||
|
||||
if (list.length == 1 || list.length == 2)
|
||||
{
|
||||
foundCenter = true;
|
||||
centerNode = list[0];
|
||||
}
|
||||
|
||||
for (var i = 0; i < list.length; i++)
|
||||
{
|
||||
var node = list[i];
|
||||
var degree = node.getNeighborsList().size;
|
||||
remainingDegrees.set(node, node.getNeighborsList().size);
|
||||
|
||||
if (degree == 1)
|
||||
{
|
||||
removedNodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
var tempList = [];
|
||||
tempList = tempList.concat(removedNodes);
|
||||
|
||||
while (!foundCenter)
|
||||
{
|
||||
var tempList2 = [];
|
||||
tempList2 = tempList2.concat(tempList);
|
||||
tempList = [];
|
||||
|
||||
for (var i = 0; i < list.length; i++)
|
||||
{
|
||||
var node = list[i];
|
||||
|
||||
var index = list.indexOf(node);
|
||||
if (index >= 0) {
|
||||
list.splice(index, 1);
|
||||
}
|
||||
|
||||
var neighbours = node.getNeighborsList();
|
||||
|
||||
neighbours.forEach(function(neighbour) {
|
||||
if (removedNodes.indexOf(neighbour) < 0)
|
||||
{
|
||||
var otherDegree = remainingDegrees.get(neighbour);
|
||||
var newDegree = otherDegree - 1;
|
||||
|
||||
if (newDegree == 1)
|
||||
{
|
||||
tempList.push(neighbour);
|
||||
}
|
||||
|
||||
remainingDegrees.set(neighbour, newDegree);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removedNodes = removedNodes.concat(tempList);
|
||||
|
||||
if (list.length == 1 || list.length == 2)
|
||||
{
|
||||
foundCenter = true;
|
||||
centerNode = list[0];
|
||||
}
|
||||
}
|
||||
|
||||
return centerNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* During the coarsening process, this layout may be referenced by two graph managers
|
||||
* this setter function grants access to change the currently being used graph manager
|
||||
*/
|
||||
Layout.prototype.setGraphManager = function (gm)
|
||||
{
|
||||
this.graphManager = gm;
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
529
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/fd/FDLayout.js
generated
vendored
Normal file
529
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/fd/FDLayout.js
generated
vendored
Normal file
@@ -0,0 +1,529 @@
|
||||
var Layout = require('../Layout');
|
||||
var FDLayoutConstants = require('./FDLayoutConstants');
|
||||
var LayoutConstants = require('../LayoutConstants');
|
||||
var IGeometry = require('../util/IGeometry');
|
||||
var IMath = require('../util/IMath');
|
||||
|
||||
function FDLayout() {
|
||||
Layout.call(this);
|
||||
|
||||
this.useSmartIdealEdgeLengthCalculation = FDLayoutConstants.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION;
|
||||
this.gravityConstant = FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH;
|
||||
this.compoundGravityConstant = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH;
|
||||
this.gravityRangeFactor = FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR;
|
||||
this.compoundGravityRangeFactor = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR;
|
||||
this.displacementThresholdPerNode = (3.0 * FDLayoutConstants.DEFAULT_EDGE_LENGTH) / 100;
|
||||
this.coolingFactor = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL;
|
||||
this.initialCoolingFactor = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL;
|
||||
this.totalDisplacement = 0.0;
|
||||
this.oldTotalDisplacement = 0.0;
|
||||
this.maxIterations = FDLayoutConstants.MAX_ITERATIONS;
|
||||
}
|
||||
|
||||
FDLayout.prototype = Object.create(Layout.prototype);
|
||||
|
||||
for (var prop in Layout) {
|
||||
FDLayout[prop] = Layout[prop];
|
||||
}
|
||||
|
||||
FDLayout.prototype.initParameters = function () {
|
||||
Layout.prototype.initParameters.call(this, arguments);
|
||||
|
||||
this.totalIterations = 0;
|
||||
this.notAnimatedIterations = 0;
|
||||
|
||||
this.useFRGridVariant = FDLayoutConstants.DEFAULT_USE_SMART_REPULSION_RANGE_CALCULATION;
|
||||
|
||||
this.grid = [];
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcIdealEdgeLengths = function () {
|
||||
var edge;
|
||||
var originalIdealLength;
|
||||
var lcaDepth;
|
||||
var source;
|
||||
var target;
|
||||
var sizeOfSourceInLca;
|
||||
var sizeOfTargetInLca;
|
||||
|
||||
var allEdges = this.getGraphManager().getAllEdges();
|
||||
for (var i = 0; i < allEdges.length; i++)
|
||||
{
|
||||
edge = allEdges[i];
|
||||
|
||||
originalIdealLength = edge.idealLength;
|
||||
|
||||
if (edge.isInterGraph)
|
||||
{
|
||||
source = edge.getSource();
|
||||
target = edge.getTarget();
|
||||
|
||||
sizeOfSourceInLca = edge.getSourceInLca().getEstimatedSize();
|
||||
sizeOfTargetInLca = edge.getTargetInLca().getEstimatedSize();
|
||||
|
||||
if (this.useSmartIdealEdgeLengthCalculation)
|
||||
{
|
||||
edge.idealLength += sizeOfSourceInLca + sizeOfTargetInLca -
|
||||
2 * LayoutConstants.SIMPLE_NODE_SIZE;
|
||||
}
|
||||
|
||||
lcaDepth = edge.getLca().getInclusionTreeDepth();
|
||||
|
||||
edge.idealLength += originalIdealLength *
|
||||
FDLayoutConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR *
|
||||
(source.getInclusionTreeDepth() +
|
||||
target.getInclusionTreeDepth() - 2 * lcaDepth);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.initSpringEmbedder = function () {
|
||||
|
||||
var s = this.getAllNodes().length;
|
||||
if (this.incremental) {
|
||||
if(s > FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT){
|
||||
this.coolingFactor = Math.max(this.coolingFactor*FDLayoutConstants.COOLING_ADAPTATION_FACTOR, this.coolingFactor -
|
||||
(s-FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT)/(FDLayoutConstants.ADAPTATION_UPPER_NODE_LIMIT-FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT)*this.coolingFactor*(1-FDLayoutConstants.COOLING_ADAPTATION_FACTOR));
|
||||
}
|
||||
this.maxNodeDisplacement = FDLayoutConstants.MAX_NODE_DISPLACEMENT_INCREMENTAL;
|
||||
}
|
||||
else {
|
||||
if(s > FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT){
|
||||
this.coolingFactor = Math.max(FDLayoutConstants.COOLING_ADAPTATION_FACTOR, 1.0 -
|
||||
(s-FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT)/(FDLayoutConstants.ADAPTATION_UPPER_NODE_LIMIT-FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT)*(1-FDLayoutConstants.COOLING_ADAPTATION_FACTOR));
|
||||
}
|
||||
else {
|
||||
this.coolingFactor = 1.0;
|
||||
}
|
||||
this.initialCoolingFactor = this.coolingFactor;
|
||||
this.maxNodeDisplacement = FDLayoutConstants.MAX_NODE_DISPLACEMENT;
|
||||
}
|
||||
|
||||
this.maxIterations =
|
||||
Math.max(this.getAllNodes().length * 5, this.maxIterations);
|
||||
|
||||
// Reassign this attribute by using new constant value
|
||||
this.displacementThresholdPerNode = (3.0 * FDLayoutConstants.DEFAULT_EDGE_LENGTH) / 100;
|
||||
this.totalDisplacementThreshold = this.displacementThresholdPerNode * this.getAllNodes().length;
|
||||
|
||||
this.repulsionRange = this.calcRepulsionRange();
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcSpringForces = function () {
|
||||
var lEdges = this.getAllEdges();
|
||||
var edge;
|
||||
|
||||
for (var i = 0; i < lEdges.length; i++)
|
||||
{
|
||||
edge = lEdges[i];
|
||||
|
||||
this.calcSpringForce(edge, edge.idealLength);
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcRepulsionForces = function (gridUpdateAllowed = true, forceToNodeSurroundingUpdate = false) {
|
||||
var i, j;
|
||||
var nodeA, nodeB;
|
||||
var lNodes = this.getAllNodes();
|
||||
var processedNodeSet;
|
||||
|
||||
if (this.useFRGridVariant)
|
||||
{
|
||||
if ((this.totalIterations % FDLayoutConstants.GRID_CALCULATION_CHECK_PERIOD == 1 && gridUpdateAllowed))
|
||||
{
|
||||
this.updateGrid();
|
||||
}
|
||||
|
||||
processedNodeSet = new Set();
|
||||
|
||||
// calculate repulsion forces between each nodes and its surrounding
|
||||
for (i = 0; i < lNodes.length; i++)
|
||||
{
|
||||
nodeA = lNodes[i];
|
||||
this.calculateRepulsionForceOfANode(nodeA, processedNodeSet, gridUpdateAllowed, forceToNodeSurroundingUpdate);
|
||||
processedNodeSet.add(nodeA);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < lNodes.length; i++)
|
||||
{
|
||||
nodeA = lNodes[i];
|
||||
|
||||
for (j = i + 1; j < lNodes.length; j++)
|
||||
{
|
||||
nodeB = lNodes[j];
|
||||
|
||||
// If both nodes are not members of the same graph, skip.
|
||||
if (nodeA.getOwner() != nodeB.getOwner())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.calcRepulsionForce(nodeA, nodeB);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcGravitationalForces = function () {
|
||||
var node;
|
||||
var lNodes = this.getAllNodesToApplyGravitation();
|
||||
|
||||
for (var i = 0; i < lNodes.length; i++)
|
||||
{
|
||||
node = lNodes[i];
|
||||
this.calcGravitationalForce(node);
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.moveNodes = function () {
|
||||
var lNodes = this.getAllNodes();
|
||||
var node;
|
||||
|
||||
for (var i = 0; i < lNodes.length; i++)
|
||||
{
|
||||
node = lNodes[i];
|
||||
node.move();
|
||||
}
|
||||
}
|
||||
|
||||
FDLayout.prototype.calcSpringForce = function (edge, idealLength) {
|
||||
var sourceNode = edge.getSource();
|
||||
var targetNode = edge.getTarget();
|
||||
|
||||
var length;
|
||||
var springForce;
|
||||
var springForceX;
|
||||
var springForceY;
|
||||
|
||||
// Update edge length
|
||||
if (this.uniformLeafNodeSizes &&
|
||||
sourceNode.getChild() == null && targetNode.getChild() == null)
|
||||
{
|
||||
edge.updateLengthSimple();
|
||||
}
|
||||
else
|
||||
{
|
||||
edge.updateLength();
|
||||
|
||||
if (edge.isOverlapingSourceAndTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
length = edge.getLength();
|
||||
|
||||
if(length == 0)
|
||||
return;
|
||||
|
||||
// Calculate spring forces
|
||||
springForce = edge.edgeElasticity * (length - idealLength);
|
||||
|
||||
// Project force onto x and y axes
|
||||
springForceX = springForce * (edge.lengthX / length);
|
||||
springForceY = springForce * (edge.lengthY / length);
|
||||
|
||||
// Apply forces on the end nodes
|
||||
sourceNode.springForceX += springForceX;
|
||||
sourceNode.springForceY += springForceY;
|
||||
targetNode.springForceX -= springForceX;
|
||||
targetNode.springForceY -= springForceY;
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcRepulsionForce = function (nodeA, nodeB) {
|
||||
var rectA = nodeA.getRect();
|
||||
var rectB = nodeB.getRect();
|
||||
var overlapAmount = new Array(2);
|
||||
var clipPoints = new Array(4);
|
||||
var distanceX;
|
||||
var distanceY;
|
||||
var distanceSquared;
|
||||
var distance;
|
||||
var repulsionForce;
|
||||
var repulsionForceX;
|
||||
var repulsionForceY;
|
||||
|
||||
if (rectA.intersects(rectB))// two nodes overlap
|
||||
{
|
||||
// calculate separation amount in x and y directions
|
||||
IGeometry.calcSeparationAmount(rectA,
|
||||
rectB,
|
||||
overlapAmount,
|
||||
FDLayoutConstants.DEFAULT_EDGE_LENGTH / 2.0);
|
||||
|
||||
repulsionForceX = 2 * overlapAmount[0];
|
||||
repulsionForceY = 2 * overlapAmount[1];
|
||||
|
||||
var childrenConstant = nodeA.noOfChildren * nodeB.noOfChildren / (nodeA.noOfChildren + nodeB.noOfChildren);
|
||||
|
||||
// Apply forces on the two nodes
|
||||
nodeA.repulsionForceX -= childrenConstant * repulsionForceX;
|
||||
nodeA.repulsionForceY -= childrenConstant * repulsionForceY;
|
||||
nodeB.repulsionForceX += childrenConstant * repulsionForceX;
|
||||
nodeB.repulsionForceY += childrenConstant * repulsionForceY;
|
||||
}
|
||||
else// no overlap
|
||||
{
|
||||
// calculate distance
|
||||
|
||||
if (this.uniformLeafNodeSizes &&
|
||||
nodeA.getChild() == null && nodeB.getChild() == null)// simply base repulsion on distance of node centers
|
||||
{
|
||||
distanceX = rectB.getCenterX() - rectA.getCenterX();
|
||||
distanceY = rectB.getCenterY() - rectA.getCenterY();
|
||||
}
|
||||
else// use clipping points
|
||||
{
|
||||
IGeometry.getIntersection(rectA, rectB, clipPoints);
|
||||
|
||||
distanceX = clipPoints[2] - clipPoints[0];
|
||||
distanceY = clipPoints[3] - clipPoints[1];
|
||||
}
|
||||
|
||||
// No repulsion range. FR grid variant should take care of this.
|
||||
if (Math.abs(distanceX) < FDLayoutConstants.MIN_REPULSION_DIST)
|
||||
{
|
||||
distanceX = IMath.sign(distanceX) *
|
||||
FDLayoutConstants.MIN_REPULSION_DIST;
|
||||
}
|
||||
|
||||
if (Math.abs(distanceY) < FDLayoutConstants.MIN_REPULSION_DIST)
|
||||
{
|
||||
distanceY = IMath.sign(distanceY) *
|
||||
FDLayoutConstants.MIN_REPULSION_DIST;
|
||||
}
|
||||
|
||||
distanceSquared = distanceX * distanceX + distanceY * distanceY;
|
||||
distance = Math.sqrt(distanceSquared);
|
||||
|
||||
// Here we use half of the nodes' repulsion values for backward compatibility
|
||||
repulsionForce = (nodeA.nodeRepulsion / 2 + nodeB.nodeRepulsion / 2) * nodeA.noOfChildren * nodeB.noOfChildren / distanceSquared;
|
||||
|
||||
// Project force onto x and y axes
|
||||
repulsionForceX = repulsionForce * distanceX / distance;
|
||||
repulsionForceY = repulsionForce * distanceY / distance;
|
||||
|
||||
// Apply forces on the two nodes
|
||||
nodeA.repulsionForceX -= repulsionForceX;
|
||||
nodeA.repulsionForceY -= repulsionForceY;
|
||||
nodeB.repulsionForceX += repulsionForceX;
|
||||
nodeB.repulsionForceY += repulsionForceY;
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcGravitationalForce = function (node) {
|
||||
var ownerGraph;
|
||||
var ownerCenterX;
|
||||
var ownerCenterY;
|
||||
var distanceX;
|
||||
var distanceY;
|
||||
var absDistanceX;
|
||||
var absDistanceY;
|
||||
var estimatedSize;
|
||||
ownerGraph = node.getOwner();
|
||||
|
||||
ownerCenterX = (ownerGraph.getRight() + ownerGraph.getLeft()) / 2;
|
||||
ownerCenterY = (ownerGraph.getTop() + ownerGraph.getBottom()) / 2;
|
||||
distanceX = node.getCenterX() - ownerCenterX;
|
||||
distanceY = node.getCenterY() - ownerCenterY;
|
||||
absDistanceX = Math.abs(distanceX) + node.getWidth() / 2;
|
||||
absDistanceY = Math.abs(distanceY) + node.getHeight() / 2;
|
||||
|
||||
if (node.getOwner() == this.graphManager.getRoot())// in the root graph
|
||||
{
|
||||
estimatedSize = ownerGraph.getEstimatedSize() * this.gravityRangeFactor;
|
||||
|
||||
if (absDistanceX > estimatedSize || absDistanceY > estimatedSize)
|
||||
{
|
||||
node.gravitationForceX = -this.gravityConstant * distanceX;
|
||||
node.gravitationForceY = -this.gravityConstant * distanceY;
|
||||
}
|
||||
}
|
||||
else// inside a compound
|
||||
{
|
||||
estimatedSize = ownerGraph.getEstimatedSize() * this.compoundGravityRangeFactor;
|
||||
|
||||
if (absDistanceX > estimatedSize || absDistanceY > estimatedSize)
|
||||
{
|
||||
node.gravitationForceX = -this.gravityConstant * distanceX *
|
||||
this.compoundGravityConstant;
|
||||
node.gravitationForceY = -this.gravityConstant * distanceY *
|
||||
this.compoundGravityConstant;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.isConverged = function () {
|
||||
var converged;
|
||||
var oscilating = false;
|
||||
|
||||
if (this.totalIterations > this.maxIterations / 3)
|
||||
{
|
||||
oscilating =
|
||||
Math.abs(this.totalDisplacement - this.oldTotalDisplacement) < 2;
|
||||
}
|
||||
|
||||
converged = this.totalDisplacement < this.totalDisplacementThreshold;
|
||||
|
||||
this.oldTotalDisplacement = this.totalDisplacement;
|
||||
|
||||
return converged || oscilating;
|
||||
};
|
||||
|
||||
FDLayout.prototype.animate = function () {
|
||||
if (this.animationDuringLayout && !this.isSubLayout)
|
||||
{
|
||||
if (this.notAnimatedIterations == this.animationPeriod)
|
||||
{
|
||||
this.update();
|
||||
this.notAnimatedIterations = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.notAnimatedIterations++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//This method calculates the number of children (weight) for all nodes
|
||||
FDLayout.prototype.calcNoOfChildrenForAllNodes = function ()
|
||||
{
|
||||
var node;
|
||||
var allNodes = this.graphManager.getAllNodes();
|
||||
|
||||
for(var i = 0; i < allNodes.length; i++)
|
||||
{
|
||||
node = allNodes[i];
|
||||
node.noOfChildren = node.getNoOfChildren();
|
||||
}
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Section: FR-Grid Variant Repulsion Force Calculation
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
FDLayout.prototype.calcGrid = function (graph){
|
||||
|
||||
var sizeX = 0;
|
||||
var sizeY = 0;
|
||||
|
||||
sizeX = parseInt(Math.ceil((graph.getRight() - graph.getLeft()) / this.repulsionRange));
|
||||
sizeY = parseInt(Math.ceil((graph.getBottom() - graph.getTop()) / this.repulsionRange));
|
||||
|
||||
var grid = new Array(sizeX);
|
||||
|
||||
for(var i = 0; i < sizeX; i++){
|
||||
grid[i] = new Array(sizeY);
|
||||
}
|
||||
|
||||
for(var i = 0; i < sizeX; i++){
|
||||
for(var j = 0; j < sizeY; j++){
|
||||
grid[i][j] = new Array();
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
};
|
||||
|
||||
FDLayout.prototype.addNodeToGrid = function (v, left, top){
|
||||
|
||||
var startX = 0;
|
||||
var finishX = 0;
|
||||
var startY = 0;
|
||||
var finishY = 0;
|
||||
|
||||
startX = parseInt(Math.floor((v.getRect().x - left) / this.repulsionRange));
|
||||
finishX = parseInt(Math.floor((v.getRect().width + v.getRect().x - left) / this.repulsionRange));
|
||||
startY = parseInt(Math.floor((v.getRect().y - top) / this.repulsionRange));
|
||||
finishY = parseInt(Math.floor((v.getRect().height + v.getRect().y - top) / this.repulsionRange));
|
||||
|
||||
for (var i = startX; i <= finishX; i++)
|
||||
{
|
||||
for (var j = startY; j <= finishY; j++)
|
||||
{
|
||||
this.grid[i][j].push(v);
|
||||
v.setGridCoordinates(startX, finishX, startY, finishY);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FDLayout.prototype.updateGrid = function() {
|
||||
var i;
|
||||
var nodeA;
|
||||
var lNodes = this.getAllNodes();
|
||||
|
||||
this.grid = this.calcGrid(this.graphManager.getRoot());
|
||||
|
||||
// put all nodes to proper grid cells
|
||||
for (i = 0; i < lNodes.length; i++)
|
||||
{
|
||||
nodeA = lNodes[i];
|
||||
this.addNodeToGrid(nodeA, this.graphManager.getRoot().getLeft(), this.graphManager.getRoot().getTop());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
FDLayout.prototype.calculateRepulsionForceOfANode = function (nodeA, processedNodeSet, gridUpdateAllowed, forceToNodeSurroundingUpdate){
|
||||
|
||||
if ((this.totalIterations % FDLayoutConstants.GRID_CALCULATION_CHECK_PERIOD == 1 && gridUpdateAllowed) || forceToNodeSurroundingUpdate)
|
||||
{
|
||||
var surrounding = new Set();
|
||||
nodeA.surrounding = new Array();
|
||||
var nodeB;
|
||||
var grid = this.grid;
|
||||
|
||||
for (var i = (nodeA.startX - 1); i < (nodeA.finishX + 2); i++)
|
||||
{
|
||||
for (var j = (nodeA.startY - 1); j < (nodeA.finishY + 2); j++)
|
||||
{
|
||||
if (!((i < 0) || (j < 0) || (i >= grid.length) || (j >= grid[0].length)))
|
||||
{
|
||||
for (var k = 0; k < grid[i][j].length; k++) {
|
||||
nodeB = grid[i][j][k];
|
||||
|
||||
// If both nodes are not members of the same graph,
|
||||
// or both nodes are the same, skip.
|
||||
if ((nodeA.getOwner() != nodeB.getOwner()) || (nodeA == nodeB))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if the repulsion force between
|
||||
// nodeA and nodeB has already been calculated
|
||||
if (!processedNodeSet.has(nodeB) && !surrounding.has(nodeB))
|
||||
{
|
||||
var distanceX = Math.abs(nodeA.getCenterX()-nodeB.getCenterX()) -
|
||||
((nodeA.getWidth()/2) + (nodeB.getWidth()/2));
|
||||
var distanceY = Math.abs(nodeA.getCenterY()-nodeB.getCenterY()) -
|
||||
((nodeA.getHeight()/2) + (nodeB.getHeight()/2));
|
||||
|
||||
// if the distance between nodeA and nodeB
|
||||
// is less then calculation range
|
||||
if ((distanceX <= this.repulsionRange) && (distanceY <= this.repulsionRange))
|
||||
{
|
||||
//then add nodeB to surrounding of nodeA
|
||||
surrounding.add(nodeB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nodeA.surrounding = [...surrounding];
|
||||
|
||||
}
|
||||
for (i = 0; i < nodeA.surrounding.length; i++)
|
||||
{
|
||||
this.calcRepulsionForce(nodeA, nodeA.surrounding[i]);
|
||||
}
|
||||
};
|
||||
|
||||
FDLayout.prototype.calcRepulsionRange = function () {
|
||||
return 0.0;
|
||||
};
|
||||
|
||||
module.exports = FDLayout;
|
||||
47
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/fd/FDLayoutNode.js
generated
vendored
Normal file
47
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/fd/FDLayoutNode.js
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
var LNode = require('../LNode');
|
||||
var FDLayoutConstants = require('./FDLayoutConstants');
|
||||
|
||||
function FDLayoutNode(gm, loc, size, vNode) {
|
||||
// alternative constructor is handled inside LNode
|
||||
LNode.call(this, gm, loc, size, vNode);
|
||||
|
||||
// Repulsion value of this node
|
||||
this.nodeRepulsion = FDLayoutConstants.DEFAULT_REPULSION_STRENGTH;
|
||||
|
||||
//Spring, repulsion and gravitational forces acting on this node
|
||||
this.springForceX = 0;
|
||||
this.springForceY = 0;
|
||||
this.repulsionForceX = 0;
|
||||
this.repulsionForceY = 0;
|
||||
this.gravitationForceX = 0;
|
||||
this.gravitationForceY = 0;
|
||||
//Amount by which this node is to be moved in this iteration
|
||||
this.displacementX = 0;
|
||||
this.displacementY = 0;
|
||||
|
||||
//Start and finish grid coordinates that this node is fallen into
|
||||
this.startX = 0;
|
||||
this.finishX = 0;
|
||||
this.startY = 0;
|
||||
this.finishY = 0;
|
||||
|
||||
//Geometric neighbors of this node
|
||||
this.surrounding = [];
|
||||
}
|
||||
|
||||
FDLayoutNode.prototype = Object.create(LNode.prototype);
|
||||
|
||||
for (var prop in LNode) {
|
||||
FDLayoutNode[prop] = LNode[prop];
|
||||
}
|
||||
|
||||
FDLayoutNode.prototype.setGridCoordinates = function (_startX, _finishX, _startY, _finishY)
|
||||
{
|
||||
this.startX = _startX;
|
||||
this.finishX = _finishX;
|
||||
this.startY = _startY;
|
||||
this.finishY = _finishY;
|
||||
|
||||
};
|
||||
|
||||
module.exports = FDLayoutNode;
|
||||
34
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/Emitter.js
generated
vendored
Normal file
34
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/Emitter.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
function Emitter(){
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
var p = Emitter.prototype;
|
||||
|
||||
p.addListener = function( event, callback ){
|
||||
this.listeners.push({
|
||||
event: event,
|
||||
callback: callback
|
||||
});
|
||||
};
|
||||
|
||||
p.removeListener = function( event, callback ){
|
||||
for( var i = this.listeners.length; i >= 0; i-- ){
|
||||
var l = this.listeners[i];
|
||||
|
||||
if( l.event === event && l.callback === callback ){
|
||||
this.listeners.splice( i, 1 );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
p.emit = function( event, data ){
|
||||
for( var i = 0; i < this.listeners.length; i++ ){
|
||||
var l = this.listeners[i];
|
||||
|
||||
if( event === l.event ){
|
||||
l.callback( data );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Emitter;
|
||||
30
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/HashMap.js
generated
vendored
Normal file
30
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/HashMap.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
var UniqueIDGeneretor = require('./UniqueIDGeneretor');
|
||||
|
||||
function HashMap() {
|
||||
this.map = {};
|
||||
this.keys = [];
|
||||
}
|
||||
|
||||
HashMap.prototype.put = function (key, value) {
|
||||
var theId = UniqueIDGeneretor.createID(key);
|
||||
if (!this.contains(theId)) {
|
||||
this.map[theId] = value;
|
||||
this.keys.push(key);
|
||||
}
|
||||
};
|
||||
|
||||
HashMap.prototype.contains = function (key) {
|
||||
var theId = UniqueIDGeneretor.createID(key);
|
||||
return this.map[key] != null;
|
||||
};
|
||||
|
||||
HashMap.prototype.get = function (key) {
|
||||
var theId = UniqueIDGeneretor.createID(key);
|
||||
return this.map[theId];
|
||||
};
|
||||
|
||||
HashMap.prototype.keySet = function () {
|
||||
return this.keys;
|
||||
};
|
||||
|
||||
module.exports = HashMap;
|
||||
55
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/HashSet.js
generated
vendored
Normal file
55
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/HashSet.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
var UniqueIDGeneretor = require('./UniqueIDGeneretor');
|
||||
|
||||
function HashSet() {
|
||||
this.set = {};
|
||||
}
|
||||
;
|
||||
|
||||
HashSet.prototype.add = function (obj) {
|
||||
var theId = UniqueIDGeneretor.createID(obj);
|
||||
if (!this.contains(theId))
|
||||
this.set[theId] = obj;
|
||||
};
|
||||
|
||||
HashSet.prototype.remove = function (obj) {
|
||||
delete this.set[UniqueIDGeneretor.createID(obj)];
|
||||
};
|
||||
|
||||
HashSet.prototype.clear = function () {
|
||||
this.set = {};
|
||||
};
|
||||
|
||||
HashSet.prototype.contains = function (obj) {
|
||||
return this.set[UniqueIDGeneretor.createID(obj)] == obj;
|
||||
};
|
||||
|
||||
HashSet.prototype.isEmpty = function () {
|
||||
return this.size() === 0;
|
||||
};
|
||||
|
||||
HashSet.prototype.size = function () {
|
||||
return Object.keys(this.set).length;
|
||||
};
|
||||
|
||||
//concats this.set to the given list
|
||||
HashSet.prototype.addAllTo = function (list) {
|
||||
var keys = Object.keys(this.set);
|
||||
var length = keys.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
list.push(this.set[keys[i]]);
|
||||
}
|
||||
};
|
||||
|
||||
HashSet.prototype.size = function () {
|
||||
return Object.keys(this.set).length;
|
||||
};
|
||||
|
||||
HashSet.prototype.addAll = function (list) {
|
||||
var s = list.length;
|
||||
for (var i = 0; i < s; i++) {
|
||||
var v = list[i];
|
||||
this.add(v);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = HashSet;
|
||||
621
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/IGeometry.js
generated
vendored
Normal file
621
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/IGeometry.js
generated
vendored
Normal file
@@ -0,0 +1,621 @@
|
||||
/**
|
||||
* This class maintains a list of static geometry related utility methods.
|
||||
*
|
||||
*
|
||||
* Copyright: i-Vis Research Group, Bilkent University, 2007 - present
|
||||
*/
|
||||
|
||||
const Point = require('./Point');
|
||||
|
||||
function IGeometry() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates *half* the amount in x and y directions of the two
|
||||
* input rectangles needed to separate them keeping their respective
|
||||
* positioning, and returns the result in the input array. An input
|
||||
* separation buffer added to the amount in both directions. We assume that
|
||||
* the two rectangles do intersect.
|
||||
*/
|
||||
IGeometry.calcSeparationAmount = function (rectA, rectB, overlapAmount, separationBuffer)
|
||||
{
|
||||
if (!rectA.intersects(rectB)) {
|
||||
throw "assert failed";
|
||||
}
|
||||
|
||||
let directions = new Array(2);
|
||||
|
||||
this.decideDirectionsForOverlappingNodes(rectA, rectB, directions);
|
||||
|
||||
overlapAmount[0] = Math.min(rectA.getRight(), rectB.getRight()) -
|
||||
Math.max(rectA.x, rectB.x);
|
||||
overlapAmount[1] = Math.min(rectA.getBottom(), rectB.getBottom()) -
|
||||
Math.max(rectA.y, rectB.y);
|
||||
|
||||
// update the overlapping amounts for the following cases:
|
||||
if ((rectA.getX() <= rectB.getX()) && (rectA.getRight() >= rectB.getRight()))
|
||||
{
|
||||
/* Case x.1:
|
||||
*
|
||||
* rectA
|
||||
* | |
|
||||
* | _________ |
|
||||
* | | | |
|
||||
* |________|_______|______|
|
||||
* | |
|
||||
* | |
|
||||
* rectB
|
||||
*/
|
||||
overlapAmount[0] += Math.min((rectB.getX() - rectA.getX()),
|
||||
(rectA.getRight() - rectB.getRight()));
|
||||
}
|
||||
else if ((rectB.getX() <= rectA.getX()) && (rectB.getRight() >= rectA.getRight()))
|
||||
{
|
||||
/* Case x.2:
|
||||
*
|
||||
* rectB
|
||||
* | |
|
||||
* | _________ |
|
||||
* | | | |
|
||||
* |________|_______|______|
|
||||
* | |
|
||||
* | |
|
||||
* rectA
|
||||
*/
|
||||
overlapAmount[0] += Math.min((rectA.getX() - rectB.getX()),
|
||||
(rectB.getRight() - rectA.getRight()));
|
||||
}
|
||||
if ((rectA.getY() <= rectB.getY()) && (rectA.getBottom() >= rectB.getBottom()))
|
||||
{
|
||||
/* Case y.1:
|
||||
* ________ rectA
|
||||
* |
|
||||
* |
|
||||
* ______|____ rectB
|
||||
* | |
|
||||
* | |
|
||||
* ______|____|
|
||||
* |
|
||||
* |
|
||||
* |________
|
||||
*
|
||||
*/
|
||||
overlapAmount[1] += Math.min((rectB.getY() - rectA.getY()),
|
||||
(rectA.getBottom() - rectB.getBottom()));
|
||||
}
|
||||
else if ((rectB.getY() <= rectA.getY()) && (rectB.getBottom() >= rectA.getBottom()))
|
||||
{
|
||||
/* Case y.2:
|
||||
* ________ rectB
|
||||
* |
|
||||
* |
|
||||
* ______|____ rectA
|
||||
* | |
|
||||
* | |
|
||||
* ______|____|
|
||||
* |
|
||||
* |
|
||||
* |________
|
||||
*
|
||||
*/
|
||||
overlapAmount[1] += Math.min((rectA.getY() - rectB.getY()),
|
||||
(rectB.getBottom() - rectA.getBottom()));
|
||||
}
|
||||
|
||||
// find slope of the line passes two centers
|
||||
let slope = Math.abs((rectB.getCenterY() - rectA.getCenterY()) /
|
||||
(rectB.getCenterX() - rectA.getCenterX()));
|
||||
// if centers are overlapped
|
||||
if ((rectB.getCenterY() === rectA.getCenterY()) &&
|
||||
(rectB.getCenterX() === rectA.getCenterX()))
|
||||
{
|
||||
// assume the slope is 1 (45 degree)
|
||||
slope = 1.0;
|
||||
}
|
||||
|
||||
let moveByY = slope * overlapAmount[0];
|
||||
let moveByX = overlapAmount[1] / slope;
|
||||
if (overlapAmount[0] < moveByX)
|
||||
{
|
||||
moveByX = overlapAmount[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
moveByY = overlapAmount[1];
|
||||
}
|
||||
// return half the amount so that if each rectangle is moved by these
|
||||
// amounts in opposite directions, overlap will be resolved
|
||||
overlapAmount[0] = -1 * directions[0] * ((moveByX / 2) + separationBuffer);
|
||||
overlapAmount[1] = -1 * directions[1] * ((moveByY / 2) + separationBuffer);
|
||||
};
|
||||
|
||||
/**
|
||||
* This method decides the separation direction of overlapping nodes
|
||||
*
|
||||
* if directions[0] = -1, then rectA goes left
|
||||
* if directions[0] = 1, then rectA goes right
|
||||
* if directions[1] = -1, then rectA goes up
|
||||
* if directions[1] = 1, then rectA goes down
|
||||
*/
|
||||
IGeometry.decideDirectionsForOverlappingNodes = function (rectA, rectB, directions)
|
||||
{
|
||||
if (rectA.getCenterX() < rectB.getCenterX())
|
||||
{
|
||||
directions[0] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
directions[0] = 1;
|
||||
}
|
||||
|
||||
if (rectA.getCenterY() < rectB.getCenterY())
|
||||
{
|
||||
directions[1] = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
directions[1] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method calculates the intersection (clipping) points of the two
|
||||
* input rectangles with line segment defined by the centers of these two
|
||||
* rectangles. The clipping points are saved in the input double array and
|
||||
* whether or not the two rectangles overlap is returned.
|
||||
*/
|
||||
IGeometry.getIntersection2 = function(rectA, rectB, result)
|
||||
{
|
||||
//result[0-1] will contain clipPoint of rectA, result[2-3] will contain clipPoint of rectB
|
||||
let p1x = rectA.getCenterX();
|
||||
let p1y = rectA.getCenterY();
|
||||
let p2x = rectB.getCenterX();
|
||||
let p2y = rectB.getCenterY();
|
||||
|
||||
//if two rectangles intersect, then clipping points are centers
|
||||
if (rectA.intersects(rectB))
|
||||
{
|
||||
result[0] = p1x;
|
||||
result[1] = p1y;
|
||||
result[2] = p2x;
|
||||
result[3] = p2y;
|
||||
return true;
|
||||
}
|
||||
//variables for rectA
|
||||
let topLeftAx = rectA.getX();
|
||||
let topLeftAy = rectA.getY();
|
||||
let topRightAx = rectA.getRight();
|
||||
let bottomLeftAx = rectA.getX();
|
||||
let bottomLeftAy = rectA.getBottom();
|
||||
let bottomRightAx = rectA.getRight();
|
||||
let halfWidthA = rectA.getWidthHalf();
|
||||
let halfHeightA = rectA.getHeightHalf();
|
||||
//variables for rectB
|
||||
let topLeftBx = rectB.getX();
|
||||
let topLeftBy = rectB.getY();
|
||||
let topRightBx = rectB.getRight();
|
||||
let bottomLeftBx = rectB.getX();
|
||||
let bottomLeftBy = rectB.getBottom();
|
||||
let bottomRightBx = rectB.getRight();
|
||||
let halfWidthB = rectB.getWidthHalf();
|
||||
let halfHeightB = rectB.getHeightHalf();
|
||||
|
||||
//flag whether clipping points are found
|
||||
let clipPointAFound = false;
|
||||
let clipPointBFound = false;
|
||||
|
||||
// line is vertical
|
||||
if (p1x === p2x)
|
||||
{
|
||||
if (p1y > p2y)
|
||||
{
|
||||
result[0] = p1x;
|
||||
result[1] = topLeftAy;
|
||||
result[2] = p2x;
|
||||
result[3] = bottomLeftBy;
|
||||
return false;
|
||||
}
|
||||
else if (p1y < p2y)
|
||||
{
|
||||
result[0] = p1x;
|
||||
result[1] = bottomLeftAy;
|
||||
result[2] = p2x;
|
||||
result[3] = topLeftBy;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//not line, return null;
|
||||
}
|
||||
}
|
||||
// line is horizontal
|
||||
else if (p1y === p2y)
|
||||
{
|
||||
if (p1x > p2x)
|
||||
{
|
||||
result[0] = topLeftAx;
|
||||
result[1] = p1y;
|
||||
result[2] = topRightBx;
|
||||
result[3] = p2y;
|
||||
return false;
|
||||
}
|
||||
else if (p1x < p2x)
|
||||
{
|
||||
result[0] = topRightAx;
|
||||
result[1] = p1y;
|
||||
result[2] = topLeftBx;
|
||||
result[3] = p2y;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//not valid line, return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//slopes of rectA's and rectB's diagonals
|
||||
let slopeA = rectA.height / rectA.width;
|
||||
let slopeB = rectB.height / rectB.width;
|
||||
|
||||
//slope of line between center of rectA and center of rectB
|
||||
let slopePrime = (p2y - p1y) / (p2x - p1x);
|
||||
let cardinalDirectionA;
|
||||
let cardinalDirectionB;
|
||||
let tempPointAx;
|
||||
let tempPointAy;
|
||||
let tempPointBx;
|
||||
let tempPointBy;
|
||||
|
||||
//determine whether clipping point is the corner of nodeA
|
||||
if ((-slopeA) === slopePrime)
|
||||
{
|
||||
if (p1x > p2x)
|
||||
{
|
||||
result[0] = bottomLeftAx;
|
||||
result[1] = bottomLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = topRightAx;
|
||||
result[1] = topLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
}
|
||||
else if (slopeA === slopePrime)
|
||||
{
|
||||
if (p1x > p2x)
|
||||
{
|
||||
result[0] = topLeftAx;
|
||||
result[1] = topLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[0] = bottomRightAx;
|
||||
result[1] = bottomLeftAy;
|
||||
clipPointAFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//determine whether clipping point is the corner of nodeB
|
||||
if ((-slopeB) === slopePrime)
|
||||
{
|
||||
if (p2x > p1x)
|
||||
{
|
||||
result[2] = bottomLeftBx;
|
||||
result[3] = bottomLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[2] = topRightBx;
|
||||
result[3] = topLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
}
|
||||
else if (slopeB === slopePrime)
|
||||
{
|
||||
if (p2x > p1x)
|
||||
{
|
||||
result[2] = topLeftBx;
|
||||
result[3] = topLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[2] = bottomRightBx;
|
||||
result[3] = bottomLeftBy;
|
||||
clipPointBFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
//if both clipping points are corners
|
||||
if (clipPointAFound && clipPointBFound)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//determine Cardinal Direction of rectangles
|
||||
if (p1x > p2x)
|
||||
{
|
||||
if (p1y > p2y)
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(slopeA, slopePrime, 4);
|
||||
cardinalDirectionB = this.getCardinalDirection(slopeB, slopePrime, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(-slopeA, slopePrime, 3);
|
||||
cardinalDirectionB = this.getCardinalDirection(-slopeB, slopePrime, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p1y > p2y)
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(-slopeA, slopePrime, 1);
|
||||
cardinalDirectionB = this.getCardinalDirection(-slopeB, slopePrime, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardinalDirectionA = this.getCardinalDirection(slopeA, slopePrime, 2);
|
||||
cardinalDirectionB = this.getCardinalDirection(slopeB, slopePrime, 4);
|
||||
}
|
||||
}
|
||||
//calculate clipping Point if it is not found before
|
||||
if (!clipPointAFound)
|
||||
{
|
||||
switch (cardinalDirectionA)
|
||||
{
|
||||
case 1:
|
||||
tempPointAy = topLeftAy;
|
||||
tempPointAx = p1x + (-halfHeightA) / slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
case 2:
|
||||
tempPointAx = bottomRightAx;
|
||||
tempPointAy = p1y + halfWidthA * slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
case 3:
|
||||
tempPointAy = bottomLeftAy;
|
||||
tempPointAx = p1x + halfHeightA / slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
case 4:
|
||||
tempPointAx = bottomLeftAx;
|
||||
tempPointAy = p1y + (-halfWidthA) * slopePrime;
|
||||
result[0] = tempPointAx;
|
||||
result[1] = tempPointAy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!clipPointBFound)
|
||||
{
|
||||
switch (cardinalDirectionB)
|
||||
{
|
||||
case 1:
|
||||
tempPointBy = topLeftBy;
|
||||
tempPointBx = p2x + (-halfHeightB) / slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
case 2:
|
||||
tempPointBx = bottomRightBx;
|
||||
tempPointBy = p2y + halfWidthB * slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
case 3:
|
||||
tempPointBy = bottomLeftBy;
|
||||
tempPointBx = p2x + halfHeightB / slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
case 4:
|
||||
tempPointBx = bottomLeftBx;
|
||||
tempPointBy = p2y + (-halfWidthB) * slopePrime;
|
||||
result[2] = tempPointBx;
|
||||
result[3] = tempPointBy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method returns in which cardinal direction does input point stays
|
||||
* 1: North
|
||||
* 2: East
|
||||
* 3: South
|
||||
* 4: West
|
||||
*/
|
||||
IGeometry.getCardinalDirection = function (slope, slopePrime, line)
|
||||
{
|
||||
if (slope > slopePrime)
|
||||
{
|
||||
return line;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1 + line % 4;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method calculates the intersection of the two lines defined by
|
||||
* point pairs (s1,s2) and (f1,f2).
|
||||
*/
|
||||
IGeometry.getIntersection = function(s1, s2, f1, f2)
|
||||
{
|
||||
if (f2 == null) {
|
||||
return this.getIntersection2(s1, s2, f1);
|
||||
}
|
||||
|
||||
let x1 = s1.x;
|
||||
let y1 = s1.y;
|
||||
let x2 = s2.x;
|
||||
let y2 = s2.y;
|
||||
let x3 = f1.x;
|
||||
let y3 = f1.y;
|
||||
let x4 = f2.x;
|
||||
let y4 = f2.y;
|
||||
let x, y; // intersection point
|
||||
let a1, a2, b1, b2, c1, c2; // coefficients of line eqns.
|
||||
let denom;
|
||||
|
||||
a1 = y2 - y1;
|
||||
b1 = x1 - x2;
|
||||
c1 = x2 * y1 - x1 * y2; // { a1*x + b1*y + c1 = 0 is line 1 }
|
||||
|
||||
a2 = y4 - y3;
|
||||
b2 = x3 - x4;
|
||||
c2 = x4 * y3 - x3 * y4; // { a2*x + b2*y + c2 = 0 is line 2 }
|
||||
|
||||
denom = a1 * b2 - a2 * b1;
|
||||
|
||||
if (denom === 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
x = (b1 * c2 - b2 * c1) / denom;
|
||||
y = (a2 * c1 - a1 * c2) / denom;
|
||||
|
||||
return new Point(x, y);
|
||||
};
|
||||
|
||||
/**
|
||||
* This method finds and returns the angle of the vector from the + x-axis
|
||||
* in clockwise direction (compatible w/ Java coordinate system!).
|
||||
*/
|
||||
IGeometry.angleOfVector = function(Cx, Cy, Nx, Ny)
|
||||
{
|
||||
let C_angle;
|
||||
|
||||
if (Cx !== Nx)
|
||||
{
|
||||
C_angle = Math.atan((Ny - Cy) / (Nx - Cx));
|
||||
|
||||
if (Nx < Cx)
|
||||
{
|
||||
C_angle += Math.PI;
|
||||
}
|
||||
else if (Ny < Cy)
|
||||
{
|
||||
C_angle += this.TWO_PI;
|
||||
}
|
||||
}
|
||||
else if (Ny < Cy)
|
||||
{
|
||||
C_angle = this.ONE_AND_HALF_PI; // 270 degrees
|
||||
}
|
||||
else
|
||||
{
|
||||
C_angle = this.HALF_PI; // 90 degrees
|
||||
}
|
||||
|
||||
return C_angle;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This method checks whether the given two line segments (one with point
|
||||
* p1 and p2, the other with point p3 and p4) intersect at a point other
|
||||
* than these points.
|
||||
*/
|
||||
IGeometry.doIntersect = function(p1, p2, p3, p4){
|
||||
let a = p1.x;
|
||||
let b = p1.y;
|
||||
let c = p2.x;
|
||||
let d = p2.y;
|
||||
let p = p3.x;
|
||||
let q = p3.y;
|
||||
let r = p4.x;
|
||||
let s = p4.y;
|
||||
let det = (c - a) * (s - q) - (r - p) * (d - b);
|
||||
|
||||
if (det === 0) {
|
||||
return false;
|
||||
} else {
|
||||
let lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
|
||||
let gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
|
||||
return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This method checks and calculates the intersection of
|
||||
* a line segment and a circle.
|
||||
*/
|
||||
IGeometry.findCircleLineIntersections = function(Ex, Ey, Lx, Ly, Cx, Cy, r) {
|
||||
|
||||
// E is the starting point of the ray,
|
||||
// L is the end point of the ray,
|
||||
// C is the center of sphere you're testing against
|
||||
// r is the radius of that sphere
|
||||
|
||||
// Compute:
|
||||
// d = L - E ( Direction vector of ray, from start to end )
|
||||
// f = E - C ( Vector from center sphere to ray start )
|
||||
|
||||
// Then the intersection is found by..
|
||||
// P = E + t * d
|
||||
// This is a parametric equation:
|
||||
// Px = Ex + tdx
|
||||
// Py = Ey + tdy
|
||||
|
||||
// get a, b, c values
|
||||
let a = (Lx-Ex)*(Lx-Ex) + (Ly-Ey)*(Ly-Ey);
|
||||
let b = 2*((Ex-Cx)*(Lx-Ex)+(Ey-Cy)*(Ly-Ey)) ;
|
||||
let c = (Ex-Cx)*(Ex-Cx)+(Ey-Cy)*(Ey-Cy) - r*r ;
|
||||
|
||||
// get discriminant
|
||||
var disc = b*b - 4 * a * c;
|
||||
if (disc >= 0) {
|
||||
// insert into quadratic formula
|
||||
let t1 = (-b + Math.sqrt(b*b - 4 * a * c)) / (2 * a);
|
||||
let t2 = (-b - Math.sqrt(b*b - 4 * a * c)) / (2 * a);
|
||||
let intersections = null;
|
||||
if( t1 >= 0 && t1 <= 1 )
|
||||
{
|
||||
// t1 is the intersection, and it's closer than t2
|
||||
// (since t1 uses -b - discriminant)
|
||||
// Impale, Poke
|
||||
return [t1];
|
||||
}
|
||||
|
||||
// here t1 didn't intersect so we are either started
|
||||
// inside the sphere or completely past it
|
||||
if( t2 >= 0 && t2 <= 1 )
|
||||
{
|
||||
// ExitWound
|
||||
return [t2] ;
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Section: Class Constants
|
||||
// -----------------------------------------------------------------------------
|
||||
/**
|
||||
* Some useful pre-calculated constants
|
||||
*/
|
||||
IGeometry.HALF_PI = 0.5 * Math.PI;
|
||||
IGeometry.ONE_AND_HALF_PI = 1.5 * Math.PI;
|
||||
IGeometry.TWO_PI = 2.0 * Math.PI;
|
||||
IGeometry.THREE_PI = 3.0 * Math.PI;
|
||||
|
||||
|
||||
module.exports = IGeometry;
|
||||
30
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/IMath.js
generated
vendored
Normal file
30
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/IMath.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
function IMath() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the sign of the input value.
|
||||
*/
|
||||
IMath.sign = function (value) {
|
||||
if (value > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
IMath.floor = function (value) {
|
||||
return value < 0 ? Math.ceil(value) : Math.floor(value);
|
||||
};
|
||||
|
||||
IMath.ceil = function (value) {
|
||||
return value < 0 ? Math.floor(value) : Math.ceil(value);
|
||||
};
|
||||
|
||||
module.exports = IMath;
|
||||
7
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/Integer.js
generated
vendored
Normal file
7
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/Integer.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
function Integer() {
|
||||
}
|
||||
|
||||
Integer.MAX_VALUE = 2147483647;
|
||||
Integer.MIN_VALUE = -2147483648;
|
||||
|
||||
module.exports = Integer;
|
||||
639
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/SVD.js
generated
vendored
Normal file
639
frontend/node_modules/cytoscape-fcose/node_modules/layout-base/src/util/SVD.js
generated
vendored
Normal file
@@ -0,0 +1,639 @@
|
||||
// Singular Value Decomposition implementation
|
||||
function SVD() {
|
||||
};
|
||||
|
||||
/* Below singular value decomposition (svd) code including hypot function is adopted from https://github.com/dragonfly-ai/JamaJS
|
||||
Some changes are applied to make the code compatible with the fcose code and to make it independent from Jama.
|
||||
Input matrix is changed to a 2D array instead of Jama matrix. Matrix dimensions are taken according to 2D array instead of using Jama functions.
|
||||
An object that includes singular value components is created for return.
|
||||
The types of input parameters of the hypot function are removed.
|
||||
let is used instead of var for the variable initialization.
|
||||
*/
|
||||
/*
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
SVD.svd = function (A) {
|
||||
this.U = null;
|
||||
this.V = null;
|
||||
this.s = null;
|
||||
this.m = 0;
|
||||
this.n = 0;
|
||||
this.m = A.length;
|
||||
this.n = A[0].length;
|
||||
let nu = Math.min(this.m, this.n);
|
||||
this.s = (function (s) {
|
||||
let a = [];
|
||||
while (s-- > 0)
|
||||
a.push(0);
|
||||
return a;
|
||||
})(Math.min(this.m + 1, this.n));
|
||||
this.U = (function (dims) {
|
||||
let allocate = function (dims) {
|
||||
if (dims.length == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
let array = [];
|
||||
for (let i = 0; i < dims[0]; i++) {
|
||||
array.push(allocate(dims.slice(1)));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
};
|
||||
return allocate(dims);
|
||||
})([this.m, nu]);
|
||||
this.V = (function (dims) {
|
||||
let allocate = function (dims) {
|
||||
if (dims.length == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
let array = [];
|
||||
for (let i = 0; i < dims[0]; i++) {
|
||||
array.push(allocate(dims.slice(1)));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
};
|
||||
return allocate(dims);
|
||||
})([this.n, this.n]);
|
||||
let e = (function (s) {
|
||||
let a = [];
|
||||
while (s-- > 0)
|
||||
a.push(0);
|
||||
return a;
|
||||
})(this.n);
|
||||
let work = (function (s) {
|
||||
let a = [];
|
||||
while (s-- > 0)
|
||||
a.push(0);
|
||||
return a;
|
||||
})(this.m);
|
||||
let wantu = true;
|
||||
let wantv = true;
|
||||
let nct = Math.min(this.m - 1, this.n);
|
||||
let nrt = Math.max(0, Math.min(this.n - 2, this.m));
|
||||
for (let k = 0; k < Math.max(nct, nrt); k++) {
|
||||
if (k < nct) {
|
||||
this.s[k] = 0;
|
||||
for (let i = k; i < this.m; i++) {
|
||||
this.s[k] = SVD.hypot(this.s[k], A[i][k]);
|
||||
}
|
||||
;
|
||||
if (this.s[k] !== 0.0) {
|
||||
if (A[k][k] < 0.0) {
|
||||
this.s[k] = -this.s[k];
|
||||
}
|
||||
for (let i = k; i < this.m; i++) {
|
||||
A[i][k] /= this.s[k];
|
||||
}
|
||||
;
|
||||
A[k][k] += 1.0;
|
||||
}
|
||||
this.s[k] = -this.s[k];
|
||||
}
|
||||
for (let j = k + 1; j < this.n; j++) {
|
||||
if ((function (lhs, rhs) {
|
||||
return lhs && rhs;
|
||||
})((k < nct), (this.s[k] !== 0.0))) {
|
||||
let t = 0;
|
||||
for (let i = k; i < this.m; i++) {
|
||||
t += A[i][k] * A[i][j];
|
||||
}
|
||||
;
|
||||
t = -t / A[k][k];
|
||||
for (let i = k; i < this.m; i++) {
|
||||
A[i][j] += t * A[i][k];
|
||||
}
|
||||
;
|
||||
}
|
||||
e[j] = A[k][j];
|
||||
}
|
||||
;
|
||||
if ((function (lhs, rhs) {
|
||||
return lhs && rhs;
|
||||
})(wantu, (k < nct))) {
|
||||
for (let i = k; i < this.m; i++) {
|
||||
this.U[i][k] = A[i][k];
|
||||
}
|
||||
;
|
||||
}
|
||||
if (k < nrt) {
|
||||
e[k] = 0;
|
||||
for (let i = k + 1; i < this.n; i++) {
|
||||
e[k] = SVD.hypot(e[k], e[i]);
|
||||
}
|
||||
;
|
||||
if (e[k] !== 0.0) {
|
||||
if (e[k + 1] < 0.0) {
|
||||
e[k] = -e[k];
|
||||
}
|
||||
for (let i = k + 1; i < this.n; i++) {
|
||||
e[i] /= e[k];
|
||||
}
|
||||
;
|
||||
e[k + 1] += 1.0;
|
||||
}
|
||||
e[k] = -e[k];
|
||||
if ((function (lhs, rhs) {
|
||||
return lhs && rhs;
|
||||
})((k + 1 < this.m), (e[k] !== 0.0))) {
|
||||
for (let i = k + 1; i < this.m; i++) {
|
||||
work[i] = 0.0;
|
||||
}
|
||||
;
|
||||
for (let j = k + 1; j < this.n; j++) {
|
||||
for (let i = k + 1; i < this.m; i++) {
|
||||
work[i] += e[j] * A[i][j];
|
||||
}
|
||||
;
|
||||
}
|
||||
;
|
||||
for (let j = k + 1; j < this.n; j++) {
|
||||
let t = -e[j] / e[k + 1];
|
||||
for (let i = k + 1; i < this.m; i++) {
|
||||
A[i][j] += t * work[i];
|
||||
}
|
||||
;
|
||||
}
|
||||
;
|
||||
}
|
||||
if (wantv) {
|
||||
for (let i = k + 1; i < this.n; i++) {
|
||||
this.V[i][k] = e[i];
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
let p = Math.min(this.n, this.m + 1);
|
||||
if (nct < this.n) {
|
||||
this.s[nct] = A[nct][nct];
|
||||
}
|
||||
if (this.m < p) {
|
||||
this.s[p - 1] = 0.0;
|
||||
}
|
||||
if (nrt + 1 < p) {
|
||||
e[nrt] = A[nrt][p - 1];
|
||||
}
|
||||
e[p - 1] = 0.0;
|
||||
if (wantu) {
|
||||
for (let j = nct; j < nu; j++) {
|
||||
for (let i = 0; i < this.m; i++) {
|
||||
this.U[i][j] = 0.0;
|
||||
}
|
||||
;
|
||||
this.U[j][j] = 1.0;
|
||||
};
|
||||
for (let k = nct - 1; k >= 0; k--) {
|
||||
if (this.s[k] !== 0.0) {
|
||||
for (let j = k + 1; j < nu; j++) {
|
||||
let t = 0;
|
||||
for (let i = k; i < this.m; i++) {
|
||||
t += this.U[i][k] * this.U[i][j];
|
||||
};
|
||||
t = -t / this.U[k][k];
|
||||
for (let i = k; i < this.m; i++) {
|
||||
this.U[i][j] += t * this.U[i][k];
|
||||
};
|
||||
};
|
||||
for (let i = k; i < this.m; i++) {
|
||||
this.U[i][k] = -this.U[i][k];
|
||||
};
|
||||
this.U[k][k] = 1.0 + this.U[k][k];
|
||||
for (let i = 0; i < k - 1; i++) {
|
||||
this.U[i][k] = 0.0;
|
||||
};
|
||||
} else {
|
||||
for (let i = 0; i < this.m; i++) {
|
||||
this.U[i][k] = 0.0;
|
||||
};
|
||||
this.U[k][k] = 1.0;
|
||||
}
|
||||
};
|
||||
}
|
||||
if (wantv) {
|
||||
for (let k = this.n - 1; k >= 0; k--) {
|
||||
if ((function (lhs, rhs) {
|
||||
return lhs && rhs;
|
||||
})((k < nrt), (e[k] !== 0.0))) {
|
||||
for (let j = k + 1; j < nu; j++) {
|
||||
let t = 0;
|
||||
for (let i = k + 1; i < this.n; i++) {
|
||||
t += this.V[i][k] * this.V[i][j];
|
||||
};
|
||||
t = -t / this.V[k + 1][k];
|
||||
for (let i = k + 1; i < this.n; i++) {
|
||||
this.V[i][j] += t * this.V[i][k];
|
||||
};
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < this.n; i++) {
|
||||
this.V[i][k] = 0.0;
|
||||
};
|
||||
this.V[k][k] = 1.0;
|
||||
};
|
||||
}
|
||||
let pp = p - 1;
|
||||
let iter = 0;
|
||||
let eps = Math.pow(2.0, -52.0);
|
||||
let tiny = Math.pow(2.0, -966.0);
|
||||
while ((p > 0)) {
|
||||
let k = void 0;
|
||||
let kase = void 0;
|
||||
for (k = p - 2; k >= -1; k--) {
|
||||
if (k === -1) {
|
||||
break;
|
||||
}
|
||||
if (Math.abs(e[k]) <= tiny + eps * (Math.abs(this.s[k]) + Math.abs(this.s[k + 1]))) {
|
||||
e[k] = 0.0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
if (k === p - 2) {
|
||||
kase = 4;
|
||||
} else {
|
||||
let ks = void 0;
|
||||
for (ks = p - 1; ks >= k; ks--) {
|
||||
if (ks === k) {
|
||||
break;
|
||||
}
|
||||
let t = (ks !== p ? Math.abs(e[ks]) : 0.0) + (ks !== k + 1 ? Math.abs(e[ks - 1]) : 0.0);
|
||||
if (Math.abs(this.s[ks]) <= tiny + eps * t) {
|
||||
this.s[ks] = 0.0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
if (ks === k) {
|
||||
kase = 3;
|
||||
} else if (ks === p - 1) {
|
||||
kase = 1;
|
||||
} else {
|
||||
kase = 2;
|
||||
k = ks;
|
||||
}
|
||||
}
|
||||
k++;
|
||||
switch ((kase)) {
|
||||
case 1:
|
||||
{
|
||||
let f = e[p - 2];
|
||||
e[p - 2] = 0.0;
|
||||
for (let j = p - 2; j >= k; j--) {
|
||||
let t = SVD.hypot(this.s[j], f);
|
||||
let cs = this.s[j] / t;
|
||||
let sn = f / t;
|
||||
this.s[j] = t;
|
||||
if (j !== k) {
|
||||
f = -sn * e[j - 1];
|
||||
e[j - 1] = cs * e[j - 1];
|
||||
}
|
||||
if (wantv) {
|
||||
for (let i = 0; i < this.n; i++) {
|
||||
t = cs * this.V[i][j] + sn * this.V[i][p - 1];
|
||||
this.V[i][p - 1] = -sn * this.V[i][j] + cs * this.V[i][p - 1];
|
||||
this.V[i][j] = t;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
let f = e[k - 1];
|
||||
e[k - 1] = 0.0;
|
||||
for (let j = k; j < p; j++) {
|
||||
let t = SVD.hypot(this.s[j], f);
|
||||
let cs = this.s[j] / t;
|
||||
let sn = f / t;
|
||||
this.s[j] = t;
|
||||
f = -sn * e[j];
|
||||
e[j] = cs * e[j];
|
||||
if (wantu) {
|
||||
for (let i = 0; i < this.m; i++) {
|
||||
t = cs * this.U[i][j] + sn * this.U[i][k - 1];
|
||||
this.U[i][k - 1] = -sn * this.U[i][j] + cs * this.U[i][k - 1];
|
||||
this.U[i][j] = t;
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
let scale = Math.max(Math.max(Math.max(Math.max(Math.abs(this.s[p - 1]), Math.abs(this.s[p - 2])), Math.abs(e[p - 2])), Math.abs(this.s[k])), Math.abs(e[k]));
|
||||
let sp = this.s[p - 1] / scale;
|
||||
let spm1 = this.s[p - 2] / scale;
|
||||
let epm1 = e[p - 2] / scale;
|
||||
let sk = this.s[k] / scale;
|
||||
let ek = e[k] / scale;
|
||||
let b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
|
||||
let c = (sp * epm1) * (sp * epm1);
|
||||
let shift = 0.0;
|
||||
if ((function (lhs, rhs) {
|
||||
return lhs || rhs;
|
||||
})((b !== 0.0), (c !== 0.0))) {
|
||||
shift = Math.sqrt(b * b + c);
|
||||
if (b < 0.0) {
|
||||
shift = -shift;
|
||||
}
|
||||
shift = c / (b + shift);
|
||||
}
|
||||
let f = (sk + sp) * (sk - sp) + shift;
|
||||
let g = sk * ek;
|
||||
for (let j = k; j < p - 1; j++) {
|
||||
let t = SVD.hypot(f, g);
|
||||
let cs = f / t;
|
||||
let sn = g / t;
|
||||
if (j !== k) {
|
||||
e[j - 1] = t;
|
||||
}
|
||||
f = cs * this.s[j] + sn * e[j];
|
||||
e[j] = cs * e[j] - sn * this.s[j];
|
||||
g = sn * this.s[j + 1];
|
||||
this.s[j + 1] = cs * this.s[j + 1];
|
||||
if (wantv) {
|
||||
for (let i = 0; i < this.n; i++) {
|
||||
t = cs * this.V[i][j] + sn * this.V[i][j + 1];
|
||||
this.V[i][j + 1] = -sn * this.V[i][j] + cs * this.V[i][j + 1];
|
||||
this.V[i][j] = t;
|
||||
};
|
||||
}
|
||||
t = SVD.hypot(f, g);
|
||||
cs = f / t;
|
||||
sn = g / t;
|
||||
this.s[j] = t;
|
||||
f = cs * e[j] + sn * this.s[j + 1];
|
||||
this.s[j + 1] = -sn * e[j] + cs * this.s[j + 1];
|
||||
g = sn * e[j + 1];
|
||||
e[j + 1] = cs * e[j + 1];
|
||||
if (wantu && (j < this.m - 1)) {
|
||||
for (let i = 0; i < this.m; i++) {
|
||||
t = cs * this.U[i][j] + sn * this.U[i][j + 1];
|
||||
this.U[i][j + 1] = -sn * this.U[i][j] + cs * this.U[i][j + 1];
|
||||
this.U[i][j] = t;
|
||||
};
|
||||
}
|
||||
};
|
||||
e[p - 2] = f;
|
||||
iter = iter + 1;
|
||||
};
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
if (this.s[k] <= 0.0) {
|
||||
this.s[k] = (this.s[k] < 0.0 ? -this.s[k] : 0.0);
|
||||
if (wantv) {
|
||||
for (let i = 0; i <= pp; i++) {
|
||||
this.V[i][k] = -this.V[i][k];
|
||||
};
|
||||
}
|
||||
}
|
||||
while ((k < pp)) {
|
||||
if (this.s[k] >= this.s[k + 1]) {
|
||||
break;
|
||||
}
|
||||
let t = this.s[k];
|
||||
this.s[k] = this.s[k + 1];
|
||||
this.s[k + 1] = t;
|
||||
if (wantv && (k < this.n - 1)) {
|
||||
for (let i = 0; i < this.n; i++) {
|
||||
t = this.V[i][k + 1];
|
||||
this.V[i][k + 1] = this.V[i][k];
|
||||
this.V[i][k] = t;
|
||||
};
|
||||
}
|
||||
if (wantu && (k < this.m - 1)) {
|
||||
for (let i = 0; i < this.m; i++) {
|
||||
t = this.U[i][k + 1];
|
||||
this.U[i][k + 1] = this.U[i][k];
|
||||
this.U[i][k] = t;
|
||||
};
|
||||
}
|
||||
k++;
|
||||
};
|
||||
iter = 0;
|
||||
p--;
|
||||
};
|
||||
break;
|
||||
}
|
||||
};
|
||||
let result = {U: this.U, V: this.V, S: this.s};
|
||||
return result;
|
||||
};
|
||||
|
||||
// sqrt(a^2 + b^2) without under/overflow.
|
||||
SVD.hypot = function(a, b) {
|
||||
let r;
|
||||
if (Math.abs(a) > Math.abs(b)) {
|
||||
r = b/a;
|
||||
r = Math.abs(a)*Math.sqrt(1+r*r);
|
||||
} else if (b != 0) {
|
||||
r = a/b;
|
||||
r = Math.abs(b)*Math.sqrt(1+r*r);
|
||||
} else {
|
||||
r = 0.0;
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
module.exports = SVD;
|
||||
|
||||
9
frontend/node_modules/cytoscape-fcose/src/assign.js
generated
vendored
Normal file
9
frontend/node_modules/cytoscape-fcose/src/assign.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Simple, internal Object.assign() polyfill for options objects etc.
|
||||
|
||||
module.exports = Object.assign != null ? Object.assign.bind( Object ) : function( tgt, ...srcs ){
|
||||
srcs.forEach( src => {
|
||||
Object.keys( src ).forEach( k => tgt[k] = src[k] );
|
||||
} );
|
||||
|
||||
return tgt;
|
||||
};
|
||||
269
frontend/node_modules/cytoscape-fcose/src/fcose/auxiliary.js
generated
vendored
Normal file
269
frontend/node_modules/cytoscape-fcose/src/fcose/auxiliary.js
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Auxiliary functions
|
||||
*/
|
||||
|
||||
const LinkedList = require('cose-base').layoutBase.LinkedList;
|
||||
|
||||
let auxiliary = {};
|
||||
|
||||
// get the top most nodes
|
||||
auxiliary.getTopMostNodes = function(nodes) {
|
||||
let nodesMap = {};
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
nodesMap[nodes[i].id()] = true;
|
||||
}
|
||||
let roots = nodes.filter(function (ele, i) {
|
||||
if(typeof ele === "number") {
|
||||
ele = i;
|
||||
}
|
||||
let parent = ele.parent()[0];
|
||||
while(parent != null){
|
||||
if(nodesMap[parent.id()]){
|
||||
return false;
|
||||
}
|
||||
parent = parent.parent()[0];
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return roots;
|
||||
};
|
||||
|
||||
// find disconnected components and create dummy nodes that connect them
|
||||
auxiliary.connectComponents = function(cy, eles, topMostNodes, dummyNodes){
|
||||
let queue = new LinkedList();
|
||||
let visited = new Set();
|
||||
let visitedTopMostNodes = [];
|
||||
let currentNeighbor;
|
||||
let minDegreeNode;
|
||||
let minDegree;
|
||||
|
||||
let isConnected = false;
|
||||
let count = 1;
|
||||
let nodesConnectedToDummy = [];
|
||||
let components = [];
|
||||
|
||||
do{
|
||||
let cmpt = cy.collection();
|
||||
components.push(cmpt);
|
||||
|
||||
let currentNode = topMostNodes[0];
|
||||
let childrenOfCurrentNode = cy.collection();
|
||||
childrenOfCurrentNode.merge(currentNode).merge(currentNode.descendants().intersection(eles));
|
||||
visitedTopMostNodes.push(currentNode);
|
||||
|
||||
childrenOfCurrentNode.forEach(function(node) {
|
||||
queue.push(node);
|
||||
visited.add(node);
|
||||
cmpt.merge(node);
|
||||
});
|
||||
|
||||
while(queue.length != 0){
|
||||
currentNode = queue.shift();
|
||||
|
||||
// Traverse all neighbors of this node
|
||||
let neighborNodes = cy.collection();
|
||||
currentNode.neighborhood().nodes().forEach(function(node){
|
||||
if(eles.intersection(currentNode.edgesWith(node)).length > 0){
|
||||
neighborNodes.merge(node);
|
||||
}
|
||||
});
|
||||
|
||||
for(let i = 0; i < neighborNodes.length; i++){
|
||||
let neighborNode = neighborNodes[i];
|
||||
currentNeighbor = topMostNodes.intersection(neighborNode.union(neighborNode.ancestors()));
|
||||
if(currentNeighbor != null && !visited.has(currentNeighbor[0])){
|
||||
let childrenOfNeighbor = currentNeighbor.union(currentNeighbor.descendants());
|
||||
|
||||
childrenOfNeighbor.forEach(function(node){
|
||||
queue.push(node);
|
||||
visited.add(node);
|
||||
cmpt.merge(node);
|
||||
if(topMostNodes.has(node)){
|
||||
visitedTopMostNodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmpt.forEach(node => {
|
||||
eles.intersection(node.connectedEdges()).forEach(e => { // connectedEdges() usually cached
|
||||
if( cmpt.has(e.source()) && cmpt.has(e.target()) ){ // has() is cheap
|
||||
cmpt.merge(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if(visitedTopMostNodes.length == topMostNodes.length){
|
||||
isConnected = true;
|
||||
}
|
||||
|
||||
if(!isConnected || (isConnected && count > 1)){
|
||||
minDegreeNode = visitedTopMostNodes[0];
|
||||
minDegree = minDegreeNode.connectedEdges().length;
|
||||
visitedTopMostNodes.forEach(function(node){
|
||||
if(node.connectedEdges().length < minDegree){
|
||||
minDegree = node.connectedEdges().length;
|
||||
minDegreeNode = node;
|
||||
}
|
||||
});
|
||||
nodesConnectedToDummy.push(minDegreeNode.id());
|
||||
// TO DO: Check efficiency of this part
|
||||
let temp = cy.collection();
|
||||
temp.merge(visitedTopMostNodes[0]);
|
||||
visitedTopMostNodes.forEach(function(node){
|
||||
temp.merge(node);
|
||||
});
|
||||
visitedTopMostNodes = [];
|
||||
topMostNodes = topMostNodes.difference(temp);
|
||||
count++;
|
||||
}
|
||||
|
||||
}
|
||||
while(!isConnected);
|
||||
|
||||
if(dummyNodes){
|
||||
if(nodesConnectedToDummy.length > 0 ){
|
||||
dummyNodes.set('dummy'+(dummyNodes.size+1), nodesConnectedToDummy);
|
||||
}
|
||||
}
|
||||
return components;
|
||||
};
|
||||
|
||||
// relocates componentResult to originalCenter if there is no fixedNodeConstraint
|
||||
auxiliary.relocateComponent = function(originalCenter, componentResult, options) {
|
||||
if (!options.fixedNodeConstraint) {
|
||||
let minXCoord = Number.POSITIVE_INFINITY;
|
||||
let maxXCoord = Number.NEGATIVE_INFINITY;
|
||||
let minYCoord = Number.POSITIVE_INFINITY;
|
||||
let maxYCoord = Number.NEGATIVE_INFINITY;
|
||||
if (options.quality == "draft") {
|
||||
// calculate current bounding box
|
||||
for (let [key, value] of componentResult.nodeIndexes) {
|
||||
let cyNode = options.cy.getElementById(key);
|
||||
if (cyNode) {
|
||||
let nodeBB = cyNode.boundingBox();
|
||||
let leftX = componentResult.xCoords[value] - nodeBB.w / 2;
|
||||
let rightX = componentResult.xCoords[value] + nodeBB.w / 2;
|
||||
let topY = componentResult.yCoords[value] - nodeBB.h / 2;
|
||||
let bottomY = componentResult.yCoords[value] + nodeBB.h / 2;
|
||||
|
||||
if (leftX < minXCoord)
|
||||
minXCoord = leftX;
|
||||
if (rightX > maxXCoord)
|
||||
maxXCoord = rightX;
|
||||
if (topY < minYCoord)
|
||||
minYCoord = topY;
|
||||
if (bottomY > maxYCoord)
|
||||
maxYCoord = bottomY;
|
||||
}
|
||||
}
|
||||
// find difference between current and original center
|
||||
let diffOnX = originalCenter.x - (maxXCoord + minXCoord) / 2;
|
||||
let diffOnY = originalCenter.y - (maxYCoord + minYCoord) / 2;
|
||||
// move component to original center
|
||||
componentResult.xCoords = componentResult.xCoords.map(x => x + diffOnX);
|
||||
componentResult.yCoords = componentResult.yCoords.map(y => y + diffOnY);
|
||||
}
|
||||
else {
|
||||
// calculate current bounding box
|
||||
Object.keys(componentResult).forEach(function (item) {
|
||||
let node = componentResult[item];
|
||||
let leftX = node.getRect().x;
|
||||
let rightX = node.getRect().x + node.getRect().width;
|
||||
let topY = node.getRect().y;
|
||||
let bottomY = node.getRect().y + node.getRect().height;
|
||||
|
||||
if (leftX < minXCoord)
|
||||
minXCoord = leftX;
|
||||
if (rightX > maxXCoord)
|
||||
maxXCoord = rightX;
|
||||
if (topY < minYCoord)
|
||||
minYCoord = topY;
|
||||
if (bottomY > maxYCoord)
|
||||
maxYCoord = bottomY;
|
||||
});
|
||||
// find difference between current and original center
|
||||
let diffOnX = originalCenter.x - (maxXCoord + minXCoord) / 2;
|
||||
let diffOnY = originalCenter.y - (maxYCoord + minYCoord) / 2;
|
||||
// move component to original center
|
||||
Object.keys(componentResult).forEach(function (item) {
|
||||
let node = componentResult[item];
|
||||
node.setCenter(node.getCenterX() + diffOnX, node.getCenterY() + diffOnY);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auxiliary.calcBoundingBox = function(parentNode, xCoords, yCoords, nodeIndexes){
|
||||
// calculate bounds
|
||||
let left = Number.MAX_SAFE_INTEGER;
|
||||
let right = Number.MIN_SAFE_INTEGER;
|
||||
let top = Number.MAX_SAFE_INTEGER;
|
||||
let bottom = Number.MIN_SAFE_INTEGER;
|
||||
let nodeLeft;
|
||||
let nodeRight;
|
||||
let nodeTop;
|
||||
let nodeBottom;
|
||||
|
||||
let nodes = parentNode.descendants().not(":parent");
|
||||
let s = nodes.length;
|
||||
for (let i = 0; i < s; i++)
|
||||
{
|
||||
let node = nodes[i];
|
||||
|
||||
nodeLeft = xCoords[nodeIndexes.get(node.id())] - node.width()/2;
|
||||
nodeRight = xCoords[nodeIndexes.get(node.id())] + node.width()/2;
|
||||
nodeTop = yCoords[nodeIndexes.get(node.id())] - node.height()/2;
|
||||
nodeBottom = yCoords[nodeIndexes.get(node.id())] + node.height()/2;
|
||||
|
||||
if (left > nodeLeft)
|
||||
{
|
||||
left = nodeLeft;
|
||||
}
|
||||
|
||||
if (right < nodeRight)
|
||||
{
|
||||
right = nodeRight;
|
||||
}
|
||||
|
||||
if (top > nodeTop)
|
||||
{
|
||||
top = nodeTop;
|
||||
}
|
||||
|
||||
if (bottom < nodeBottom)
|
||||
{
|
||||
bottom = nodeBottom;
|
||||
}
|
||||
}
|
||||
|
||||
let boundingBox = {};
|
||||
boundingBox.topLeftX = left;
|
||||
boundingBox.topLeftY = top;
|
||||
boundingBox.width = right - left;
|
||||
boundingBox.height = bottom - top;
|
||||
return boundingBox;
|
||||
};
|
||||
|
||||
// This function finds and returns parent nodes whose all children are hidden
|
||||
auxiliary.calcParentsWithoutChildren = function(cy, eles){
|
||||
let parentsWithoutChildren = cy.collection();
|
||||
eles.nodes(':parent').forEach((parent) => {
|
||||
let check = false;
|
||||
parent.children().forEach((child) => {
|
||||
if(child.css('display') != 'none') {
|
||||
check = true;
|
||||
}
|
||||
});
|
||||
if(!check) {
|
||||
parentsWithoutChildren.merge(parent);
|
||||
}
|
||||
});
|
||||
|
||||
return parentsWithoutChildren;
|
||||
}
|
||||
|
||||
module.exports = auxiliary;
|
||||
Reference in New Issue
Block a user