|
var LGraphObject = require('./LGraphObject');
|
|
var Integer = require('./util/Integer');
|
|
var LayoutConstants = require('./LayoutConstants');
|
|
var LGraphManager = require('./LGraphManager');
|
|
var LNode = require('./LNode');
|
|
var LEdge = require('./LEdge');
|
|
var RectangleD = require('./util/RectangleD');
|
|
var Point = require('./util/Point');
|
|
var LinkedList = require('./util/LinkedList');
|
|
|
|
function LGraph(parent, obj2, vGraph) {
|
|
LGraphObject.call(this, vGraph);
|
|
this.estimatedSize = Integer.MIN_VALUE;
|
|
this.margin = LayoutConstants.DEFAULT_GRAPH_MARGIN;
|
|
this.edges = [];
|
|
this.nodes = [];
|
|
this.isConnected = false;
|
|
this.parent = parent;
|
|
|
|
if (obj2 != null && obj2 instanceof LGraphManager) {
|
|
this.graphManager = obj2;
|
|
}
|
|
else if (obj2 != null && obj2 instanceof Layout) {
|
|
this.graphManager = obj2.graphManager;
|
|
}
|
|
}
|
|
|
|
LGraph.prototype = Object.create(LGraphObject.prototype);
|
|
for (var prop in LGraphObject) {
|
|
LGraph[prop] = LGraphObject[prop];
|
|
}
|
|
|
|
LGraph.prototype.getNodes = function () {
|
|
return this.nodes;
|
|
};
|
|
|
|
LGraph.prototype.getEdges = function () {
|
|
return this.edges;
|
|
};
|
|
|
|
LGraph.prototype.getGraphManager = function ()
|
|
{
|
|
return this.graphManager;
|
|
};
|
|
|
|
LGraph.prototype.getParent = function ()
|
|
{
|
|
return this.parent;
|
|
};
|
|
|
|
LGraph.prototype.getLeft = function ()
|
|
{
|
|
return this.left;
|
|
};
|
|
|
|
LGraph.prototype.getRight = function ()
|
|
{
|
|
return this.right;
|
|
};
|
|
|
|
LGraph.prototype.getTop = function ()
|
|
{
|
|
return this.top;
|
|
};
|
|
|
|
LGraph.prototype.getBottom = function ()
|
|
{
|
|
return this.bottom;
|
|
};
|
|
|
|
LGraph.prototype.isConnected = function ()
|
|
{
|
|
return this.isConnected;
|
|
};
|
|
|
|
LGraph.prototype.add = function (obj1, sourceNode, targetNode) {
|
|
if (sourceNode == null && targetNode == null) {
|
|
var newNode = obj1;
|
|
if (this.graphManager == null) {
|
|
throw "Graph has no graph mgr!";
|
|
}
|
|
if (this.getNodes().indexOf(newNode) > -1) {
|
|
throw "Node already in graph!";
|
|
}
|
|
newNode.owner = this;
|
|
this.getNodes().push(newNode);
|
|
|
|
return newNode;
|
|
}
|
|
else {
|
|
var newEdge = obj1;
|
|
if (!(this.getNodes().indexOf(sourceNode) > -1 && (this.getNodes().indexOf(targetNode)) > -1)) {
|
|
throw "Source or target not in graph!";
|
|
}
|
|
|
|
if (!(sourceNode.owner == targetNode.owner && sourceNode.owner == this)) {
|
|
throw "Both owners must be this graph!";
|
|
}
|
|
|
|
if (sourceNode.owner != targetNode.owner)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
|
|
newEdge.source = sourceNode;
|
|
newEdge.target = targetNode;
|
|
|
|
|
|
newEdge.isInterGraph = false;
|
|
|
|
|
|
this.getEdges().push(newEdge);
|
|
|
|
|
|
sourceNode.edges.push(newEdge);
|
|
|
|
if (targetNode != sourceNode)
|
|
{
|
|
targetNode.edges.push(newEdge);
|
|
}
|
|
|
|
return newEdge;
|
|
}
|
|
};
|
|
|
|
LGraph.prototype.remove = function (obj) {
|
|
var node = obj;
|
|
if (obj instanceof LNode) {
|
|
if (node == null) {
|
|
throw "Node is null!";
|
|
}
|
|
if (!(node.owner != null && node.owner == this)) {
|
|
throw "Owner graph is invalid!";
|
|
}
|
|
if (this.graphManager == null) {
|
|
throw "Owner graph manager is invalid!";
|
|
}
|
|
|
|
var edgesToBeRemoved = node.edges.slice();
|
|
var edge;
|
|
var s = edgesToBeRemoved.length;
|
|
for (var i = 0; i < s; i++)
|
|
{
|
|
edge = edgesToBeRemoved[i];
|
|
|
|
if (edge.isInterGraph)
|
|
{
|
|
this.graphManager.remove(edge);
|
|
}
|
|
else
|
|
{
|
|
edge.source.owner.remove(edge);
|
|
}
|
|
}
|
|
|
|
|
|
var index = this.nodes.indexOf(node);
|
|
if (index == -1) {
|
|
throw "Node not in owner node list!";
|
|
}
|
|
|
|
this.nodes.splice(index, 1);
|
|
}
|
|
else if (obj instanceof LEdge) {
|
|
var edge = obj;
|
|
if (edge == null) {
|
|
throw "Edge is null!";
|
|
}
|
|
if (!(edge.source != null && edge.target != null)) {
|
|
throw "Source and/or target is null!";
|
|
}
|
|
if (!(edge.source.owner != null && edge.target.owner != null &&
|
|
edge.source.owner == this && edge.target.owner == this)) {
|
|
throw "Source and/or target owner is invalid!";
|
|
}
|
|
|
|
var sourceIndex = edge.source.edges.indexOf(edge);
|
|
var targetIndex = edge.target.edges.indexOf(edge);
|
|
if (!(sourceIndex > -1 && targetIndex > -1)) {
|
|
throw "Source and/or target doesn't know this edge!";
|
|
}
|
|
|
|
edge.source.edges.splice(sourceIndex, 1);
|
|
|
|
if (edge.target != edge.source)
|
|
{
|
|
edge.target.edges.splice(targetIndex, 1);
|
|
}
|
|
|
|
var index = edge.source.owner.getEdges().indexOf(edge);
|
|
if (index == -1) {
|
|
throw "Not in owner's edge list!";
|
|
}
|
|
|
|
edge.source.owner.getEdges().splice(index, 1);
|
|
}
|
|
};
|
|
|
|
LGraph.prototype.updateLeftTop = function ()
|
|
{
|
|
var top = Integer.MAX_VALUE;
|
|
var left = Integer.MAX_VALUE;
|
|
var nodeTop;
|
|
var nodeLeft;
|
|
var margin;
|
|
|
|
var nodes = this.getNodes();
|
|
var s = nodes.length;
|
|
|
|
for (var i = 0; i < s; i++)
|
|
{
|
|
var lNode = nodes[i];
|
|
nodeTop = lNode.getTop();
|
|
nodeLeft = lNode.getLeft();
|
|
|
|
if (top > nodeTop)
|
|
{
|
|
top = nodeTop;
|
|
}
|
|
|
|
if (left > nodeLeft)
|
|
{
|
|
left = nodeLeft;
|
|
}
|
|
}
|
|
|
|
|
|
if (top == Integer.MAX_VALUE)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(nodes[0].getParent().paddingLeft != undefined){
|
|
margin = nodes[0].getParent().paddingLeft;
|
|
}
|
|
else{
|
|
margin = this.margin;
|
|
}
|
|
|
|
this.left = left - margin;
|
|
this.top = top - margin;
|
|
|
|
|
|
return new Point(this.left, this.top);
|
|
};
|
|
|
|
LGraph.prototype.updateBounds = function (recursive)
|
|
{
|
|
|
|
var left = Integer.MAX_VALUE;
|
|
var right = -Integer.MAX_VALUE;
|
|
var top = Integer.MAX_VALUE;
|
|
var bottom = -Integer.MAX_VALUE;
|
|
var nodeLeft;
|
|
var nodeRight;
|
|
var nodeTop;
|
|
var nodeBottom;
|
|
var margin;
|
|
|
|
var nodes = this.nodes;
|
|
var s = nodes.length;
|
|
for (var i = 0; i < s; i++)
|
|
{
|
|
var lNode = nodes[i];
|
|
|
|
if (recursive && lNode.child != null)
|
|
{
|
|
lNode.updateBounds();
|
|
}
|
|
nodeLeft = lNode.getLeft();
|
|
nodeRight = lNode.getRight();
|
|
nodeTop = lNode.getTop();
|
|
nodeBottom = lNode.getBottom();
|
|
|
|
if (left > nodeLeft)
|
|
{
|
|
left = nodeLeft;
|
|
}
|
|
|
|
if (right < nodeRight)
|
|
{
|
|
right = nodeRight;
|
|
}
|
|
|
|
if (top > nodeTop)
|
|
{
|
|
top = nodeTop;
|
|
}
|
|
|
|
if (bottom < nodeBottom)
|
|
{
|
|
bottom = nodeBottom;
|
|
}
|
|
}
|
|
|
|
var boundingRect = new RectangleD(left, top, right - left, bottom - top);
|
|
if (left == Integer.MAX_VALUE)
|
|
{
|
|
this.left = this.parent.getLeft();
|
|
this.right = this.parent.getRight();
|
|
this.top = this.parent.getTop();
|
|
this.bottom = this.parent.getBottom();
|
|
}
|
|
|
|
if(nodes[0].getParent().paddingLeft != undefined){
|
|
margin = nodes[0].getParent().paddingLeft;
|
|
}
|
|
else{
|
|
margin = this.margin;
|
|
}
|
|
|
|
this.left = boundingRect.x - margin;
|
|
this.right = boundingRect.x + boundingRect.width + margin;
|
|
this.top = boundingRect.y - margin;
|
|
this.bottom = boundingRect.y + boundingRect.height + margin;
|
|
};
|
|
|
|
LGraph.calculateBounds = function (nodes)
|
|
{
|
|
var left = Integer.MAX_VALUE;
|
|
var right = -Integer.MAX_VALUE;
|
|
var top = Integer.MAX_VALUE;
|
|
var bottom = -Integer.MAX_VALUE;
|
|
var nodeLeft;
|
|
var nodeRight;
|
|
var nodeTop;
|
|
var nodeBottom;
|
|
|
|
var s = nodes.length;
|
|
|
|
for (var i = 0; i < s; i++)
|
|
{
|
|
var lNode = nodes[i];
|
|
nodeLeft = lNode.getLeft();
|
|
nodeRight = lNode.getRight();
|
|
nodeTop = lNode.getTop();
|
|
nodeBottom = lNode.getBottom();
|
|
|
|
if (left > nodeLeft)
|
|
{
|
|
left = nodeLeft;
|
|
}
|
|
|
|
if (right < nodeRight)
|
|
{
|
|
right = nodeRight;
|
|
}
|
|
|
|
if (top > nodeTop)
|
|
{
|
|
top = nodeTop;
|
|
}
|
|
|
|
if (bottom < nodeBottom)
|
|
{
|
|
bottom = nodeBottom;
|
|
}
|
|
}
|
|
|
|
var boundingRect = new RectangleD(left, top, right - left, bottom - top);
|
|
|
|
return boundingRect;
|
|
};
|
|
|
|
LGraph.prototype.getInclusionTreeDepth = function ()
|
|
{
|
|
if (this == this.graphManager.getRoot())
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return this.parent.getInclusionTreeDepth();
|
|
}
|
|
};
|
|
|
|
LGraph.prototype.getEstimatedSize = function ()
|
|
{
|
|
if (this.estimatedSize == Integer.MIN_VALUE) {
|
|
throw "assert failed";
|
|
}
|
|
return this.estimatedSize;
|
|
};
|
|
|
|
LGraph.prototype.calcEstimatedSize = function ()
|
|
{
|
|
var size = 0;
|
|
var nodes = this.nodes;
|
|
var s = nodes.length;
|
|
|
|
for (var i = 0; i < s; i++)
|
|
{
|
|
var lNode = nodes[i];
|
|
size += lNode.calcEstimatedSize();
|
|
}
|
|
|
|
if (size == 0)
|
|
{
|
|
this.estimatedSize = LayoutConstants.EMPTY_COMPOUND_NODE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
this.estimatedSize = size / Math.sqrt(this.nodes.length);
|
|
}
|
|
|
|
return this.estimatedSize;
|
|
};
|
|
|
|
LGraph.prototype.updateConnected = function ()
|
|
{
|
|
var self = this;
|
|
if (this.nodes.length == 0)
|
|
{
|
|
this.isConnected = true;
|
|
return;
|
|
}
|
|
|
|
var queue = new LinkedList();
|
|
var visited = new Set();
|
|
var currentNode = this.nodes[0];
|
|
var neighborEdges;
|
|
var currentNeighbor;
|
|
var childrenOfNode = currentNode.withChildren();
|
|
childrenOfNode.forEach(function(node) {
|
|
queue.push(node);
|
|
visited.add(node);
|
|
});
|
|
|
|
while (queue.length !== 0)
|
|
{
|
|
currentNode = queue.shift();
|
|
|
|
|
|
neighborEdges = currentNode.getEdges();
|
|
var size = neighborEdges.length;
|
|
for (var i = 0; i < size; i++)
|
|
{
|
|
var neighborEdge = neighborEdges[i];
|
|
currentNeighbor =
|
|
neighborEdge.getOtherEndInGraph(currentNode, this);
|
|
|
|
|
|
if (currentNeighbor != null &&
|
|
!visited.has(currentNeighbor))
|
|
{
|
|
var childrenOfNeighbor = currentNeighbor.withChildren();
|
|
|
|
childrenOfNeighbor.forEach(function(node) {
|
|
queue.push(node);
|
|
visited.add(node);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
this.isConnected = false;
|
|
|
|
if (visited.size >= this.nodes.length)
|
|
{
|
|
var noOfVisitedInThisGraph = 0;
|
|
|
|
visited.forEach(function(visitedNode) {
|
|
if (visitedNode.owner == self)
|
|
{
|
|
noOfVisitedInThisGraph++;
|
|
}
|
|
});
|
|
|
|
if (noOfVisitedInThisGraph == this.nodes.length)
|
|
{
|
|
this.isConnected = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = LGraph;
|
|
|