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; } // set source and target newEdge.source = sourceNode; newEdge.target = targetNode; // set as intra-graph edge newEdge.isInterGraph = false; // add to graph edge list this.getEdges().push(newEdge); // add to incidency lists 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!"; } // remove incident edges first (make a copy to do it safely) 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); } } // now the node itself 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; } } // Do we have any nodes in this graph? 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; // Apply the margins and return the result return new Point(this.left, this.top); }; LGraph.prototype.updateBounds = function (recursive) { // calculate bounds 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(); // Traverse all neighbors of this node neighborEdges = currentNode.getEdges(); var size = neighborEdges.length; for (var i = 0; i < size; i++) { var neighborEdge = neighborEdges[i]; currentNeighbor = neighborEdge.getOtherEndInGraph(currentNode, this); // Add unvisited neighbors to the list to visit 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;