|
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 );
|
|
|
|
|
|
this.layoutQuality = LayoutConstants.QUALITY;
|
|
|
|
this.createBendsAsNeeded =
|
|
LayoutConstants.DEFAULT_CREATE_BENDS_AS_NEEDED;
|
|
|
|
this.incremental = LayoutConstants.DEFAULT_INCREMENTAL;
|
|
|
|
this.animationOnLayout =
|
|
LayoutConstants.DEFAULT_ANIMATION_ON_LAYOUT;
|
|
|
|
this.animationDuringLayout = LayoutConstants.DEFAULT_ANIMATION_DURING_LAYOUT;
|
|
|
|
this.animationPeriod = LayoutConstants.DEFAULT_ANIMATION_PERIOD;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.uniformLeafNodeSizes =
|
|
LayoutConstants.DEFAULT_UNIFORM_LEAF_NODE_SIZES;
|
|
|
|
|
|
|
|
|
|
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') {
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
if (isLayoutSuccessfull)
|
|
{
|
|
if (!this.isSubLayout)
|
|
{
|
|
this.doPostLayout();
|
|
}
|
|
}
|
|
|
|
if (this.tilingPostLayout) {
|
|
this.tilingPostLayout();
|
|
}
|
|
|
|
this.isLayoutFinished = true;
|
|
|
|
return isLayoutSuccessfull;
|
|
};
|
|
|
|
|
|
|
|
|
|
Layout.prototype.doPostLayout = function ()
|
|
{
|
|
|
|
|
|
if(!this.incremental){
|
|
this.transform();
|
|
}
|
|
this.update();
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Layout.prototype.update2 = function () {
|
|
|
|
if (this.createBendsAsNeeded)
|
|
{
|
|
this.createBendpointsFromDummyNodes();
|
|
|
|
|
|
this.graphManager.resetAllEdges();
|
|
}
|
|
|
|
|
|
|
|
if (!this.isRemoteUse)
|
|
{
|
|
|
|
var edge;
|
|
var allEdges = this.graphManager.getAllEdges();
|
|
for (var i = 0; i < allEdges.length; i++)
|
|
{
|
|
edge = allEdges[i];
|
|
|
|
}
|
|
|
|
|
|
var node;
|
|
var nodes = this.graphManager.getRoot().getNodes();
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
node = nodes[i];
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
|
|
var nodes = node.getChild().getNodes();
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
update(nodes[i]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.vGraphObject != null)
|
|
{
|
|
|
|
var vNode = node.vGraphObject;
|
|
|
|
|
|
vNode.update(node);
|
|
}
|
|
}
|
|
else if (obj instanceof LEdge) {
|
|
var edge = obj;
|
|
|
|
|
|
|
|
|
|
if (edge.vGraphObject != null)
|
|
{
|
|
|
|
var vEdge = edge.vGraphObject;
|
|
|
|
|
|
vEdge.update(edge);
|
|
}
|
|
}
|
|
else if (obj instanceof LGraph) {
|
|
var graph = obj;
|
|
|
|
|
|
|
|
|
|
if (graph.vGraphObject != null)
|
|
{
|
|
|
|
var vGraph = graph.vGraphObject;
|
|
|
|
|
|
vGraph.update(graph);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Layout.prototype.getFlatForest = function ()
|
|
{
|
|
var flatForest = [];
|
|
var isForest = true;
|
|
|
|
|
|
|
|
var allNodes = this.graphManager.getRoot().getNodes();
|
|
|
|
|
|
var isFlat = true;
|
|
|
|
for (var i = 0; i < allNodes.length; i++)
|
|
{
|
|
if (allNodes[i].getChild() != null)
|
|
{
|
|
isFlat = false;
|
|
}
|
|
}
|
|
|
|
|
|
if (!isFlat)
|
|
{
|
|
return flatForest;
|
|
}
|
|
|
|
|
|
|
|
var visited = new Set();
|
|
var toBeVisited = [];
|
|
var parents = new Map();
|
|
var unProcessedNodes = [];
|
|
|
|
unProcessedNodes = unProcessedNodes.concat(allNodes);
|
|
|
|
|
|
|
|
|
|
|
|
while (unProcessedNodes.length > 0 && isForest)
|
|
{
|
|
toBeVisited.push(unProcessedNodes[0]);
|
|
|
|
|
|
|
|
while (toBeVisited.length > 0 && isForest)
|
|
{
|
|
|
|
var currentNode = toBeVisited[0];
|
|
toBeVisited.splice(0, 1);
|
|
visited.add(currentNode);
|
|
|
|
|
|
var neighborEdges = currentNode.getEdges();
|
|
|
|
for (var i = 0; i < neighborEdges.length; i++)
|
|
{
|
|
var currentNeighbor =
|
|
neighborEdges[i].getOtherEnd(currentNode);
|
|
|
|
|
|
if (parents.get(currentNode) != currentNeighbor)
|
|
{
|
|
|
|
if (!visited.has(currentNeighbor))
|
|
{
|
|
toBeVisited.push(currentNeighbor);
|
|
parents.set(currentNeighbor, currentNode);
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
{
|
|
isForest = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (!isForest)
|
|
{
|
|
flatForest = [];
|
|
}
|
|
|
|
|
|
|
|
else
|
|
{
|
|
var temp = [...visited];
|
|
flatForest.push(temp);
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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++)
|
|
{
|
|
|
|
var dummyNode = this.newNode(null);
|
|
dummyNode.setRect(new Point(0, 0), new Dimension(1, 1));
|
|
|
|
graph.add(dummyNode);
|
|
|
|
|
|
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);
|
|
|
|
|
|
if (edge.isInterGraph())
|
|
{
|
|
this.graphManager.remove(edge);
|
|
}
|
|
|
|
else
|
|
{
|
|
graph.remove(edge);
|
|
}
|
|
|
|
return dummyNodes;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
var ebp = lEdge.bendpoints.get(i);
|
|
ebp.x = p.x;
|
|
ebp.y = p.y;
|
|
|
|
|
|
|
|
dummyNode.getOwner().remove(dummyNode);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Layout.prototype.setGraphManager = function (gm)
|
|
{
|
|
this.graphManager = gm;
|
|
};
|
|
|
|
module.exports = Layout;
|
|
|