mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
8.5 KiB
405 lines
8.5 KiB
var LGraphObject = require('./LGraphObject');
|
|
var Integer = require('./util/Integer');
|
|
var RectangleD = require('./util/RectangleD');
|
|
var LayoutConstants = require('./LayoutConstants');
|
|
var RandomSeed = require('./util/RandomSeed');
|
|
var PointD = require('./util/PointD');
|
|
|
|
function LNode(gm, loc, size, vNode) {
|
|
//Alternative constructor 1 : LNode(LGraphManager gm, Point loc, Dimension size, Object vNode)
|
|
if (size == null && vNode == null) {
|
|
vNode = loc;
|
|
}
|
|
|
|
LGraphObject.call(this, vNode);
|
|
|
|
//Alternative constructor 2 : LNode(Layout layout, Object vNode)
|
|
if (gm.graphManager != null)
|
|
gm = gm.graphManager;
|
|
|
|
this.estimatedSize = Integer.MIN_VALUE;
|
|
this.inclusionTreeDepth = Integer.MAX_VALUE;
|
|
this.vGraphObject = vNode;
|
|
this.edges = [];
|
|
this.graphManager = gm;
|
|
|
|
if (size != null && loc != null)
|
|
this.rect = new RectangleD(loc.x, loc.y, size.width, size.height);
|
|
else
|
|
this.rect = new RectangleD();
|
|
}
|
|
|
|
LNode.prototype = Object.create(LGraphObject.prototype);
|
|
for (var prop in LGraphObject) {
|
|
LNode[prop] = LGraphObject[prop];
|
|
}
|
|
|
|
LNode.prototype.getEdges = function ()
|
|
{
|
|
return this.edges;
|
|
};
|
|
|
|
LNode.prototype.getChild = function ()
|
|
{
|
|
return this.child;
|
|
};
|
|
|
|
LNode.prototype.getOwner = function ()
|
|
{
|
|
// if (this.owner != null) {
|
|
// if (!(this.owner == null || this.owner.getNodes().indexOf(this) > -1)) {
|
|
// throw "assert failed";
|
|
// }
|
|
// }
|
|
|
|
return this.owner;
|
|
};
|
|
|
|
LNode.prototype.getWidth = function ()
|
|
{
|
|
return this.rect.width;
|
|
};
|
|
|
|
LNode.prototype.setWidth = function (width)
|
|
{
|
|
this.rect.width = width;
|
|
};
|
|
|
|
LNode.prototype.getHeight = function ()
|
|
{
|
|
return this.rect.height;
|
|
};
|
|
|
|
LNode.prototype.setHeight = function (height)
|
|
{
|
|
this.rect.height = height;
|
|
};
|
|
|
|
LNode.prototype.getCenterX = function ()
|
|
{
|
|
return this.rect.x + this.rect.width / 2;
|
|
};
|
|
|
|
LNode.prototype.getCenterY = function ()
|
|
{
|
|
return this.rect.y + this.rect.height / 2;
|
|
};
|
|
|
|
LNode.prototype.getCenter = function ()
|
|
{
|
|
return new PointD(this.rect.x + this.rect.width / 2,
|
|
this.rect.y + this.rect.height / 2);
|
|
};
|
|
|
|
LNode.prototype.getLocation = function ()
|
|
{
|
|
return new PointD(this.rect.x, this.rect.y);
|
|
};
|
|
|
|
LNode.prototype.getRect = function ()
|
|
{
|
|
return this.rect;
|
|
};
|
|
|
|
LNode.prototype.getDiagonal = function ()
|
|
{
|
|
return Math.sqrt(this.rect.width * this.rect.width +
|
|
this.rect.height * this.rect.height);
|
|
};
|
|
|
|
/**
|
|
* This method returns half the diagonal length of this node.
|
|
*/
|
|
LNode.prototype.getHalfTheDiagonal = function () {
|
|
return Math.sqrt(this.rect.height * this.rect.height +
|
|
this.rect.width * this.rect.width) / 2;
|
|
};
|
|
|
|
LNode.prototype.setRect = function (upperLeft, dimension)
|
|
{
|
|
this.rect.x = upperLeft.x;
|
|
this.rect.y = upperLeft.y;
|
|
this.rect.width = dimension.width;
|
|
this.rect.height = dimension.height;
|
|
};
|
|
|
|
LNode.prototype.setCenter = function (cx, cy)
|
|
{
|
|
this.rect.x = cx - this.rect.width / 2;
|
|
this.rect.y = cy - this.rect.height / 2;
|
|
};
|
|
|
|
LNode.prototype.setLocation = function (x, y)
|
|
{
|
|
this.rect.x = x;
|
|
this.rect.y = y;
|
|
};
|
|
|
|
LNode.prototype.moveBy = function (dx, dy)
|
|
{
|
|
this.rect.x += dx;
|
|
this.rect.y += dy;
|
|
};
|
|
|
|
LNode.prototype.getEdgeListToNode = function (to)
|
|
{
|
|
var edgeList = [];
|
|
var edge;
|
|
var self = this;
|
|
|
|
self.edges.forEach(function(edge) {
|
|
|
|
if (edge.target == to)
|
|
{
|
|
if (edge.source != self)
|
|
throw "Incorrect edge source!";
|
|
|
|
edgeList.push(edge);
|
|
}
|
|
});
|
|
|
|
return edgeList;
|
|
};
|
|
|
|
LNode.prototype.getEdgesBetween = function (other)
|
|
{
|
|
var edgeList = [];
|
|
var edge;
|
|
|
|
var self = this;
|
|
self.edges.forEach(function(edge) {
|
|
|
|
if (!(edge.source == self || edge.target == self))
|
|
throw "Incorrect edge source and/or target";
|
|
|
|
if ((edge.target == other) || (edge.source == other))
|
|
{
|
|
edgeList.push(edge);
|
|
}
|
|
});
|
|
|
|
return edgeList;
|
|
};
|
|
|
|
LNode.prototype.getNeighborsList = function ()
|
|
{
|
|
var neighbors = new Set();
|
|
|
|
var self = this;
|
|
self.edges.forEach(function(edge) {
|
|
|
|
if (edge.source == self)
|
|
{
|
|
neighbors.add(edge.target);
|
|
}
|
|
else
|
|
{
|
|
if (edge.target != self) {
|
|
throw "Incorrect incidency!";
|
|
}
|
|
|
|
neighbors.add(edge.source);
|
|
}
|
|
});
|
|
|
|
return neighbors;
|
|
};
|
|
|
|
LNode.prototype.withChildren = function ()
|
|
{
|
|
var withNeighborsList = new Set();
|
|
var childNode;
|
|
var children;
|
|
|
|
withNeighborsList.add(this);
|
|
|
|
if (this.child != null)
|
|
{
|
|
var nodes = this.child.getNodes();
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
childNode = nodes[i];
|
|
children = childNode.withChildren();
|
|
children.forEach(function(node) {
|
|
withNeighborsList.add(node);
|
|
});
|
|
}
|
|
}
|
|
|
|
return withNeighborsList;
|
|
};
|
|
|
|
LNode.prototype.getNoOfChildren = function ()
|
|
{
|
|
var noOfChildren = 0;
|
|
var childNode;
|
|
|
|
if(this.child == null){
|
|
noOfChildren = 1;
|
|
}
|
|
else
|
|
{
|
|
var nodes = this.child.getNodes();
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
childNode = nodes[i];
|
|
|
|
noOfChildren += childNode.getNoOfChildren();
|
|
}
|
|
}
|
|
|
|
if(noOfChildren == 0){
|
|
noOfChildren = 1;
|
|
}
|
|
return noOfChildren;
|
|
};
|
|
|
|
LNode.prototype.getEstimatedSize = function () {
|
|
if (this.estimatedSize == Integer.MIN_VALUE) {
|
|
throw "assert failed";
|
|
}
|
|
return this.estimatedSize;
|
|
};
|
|
|
|
LNode.prototype.calcEstimatedSize = function () {
|
|
if (this.child == null)
|
|
{
|
|
return this.estimatedSize = (this.rect.width + this.rect.height) / 2;
|
|
}
|
|
else
|
|
{
|
|
this.estimatedSize = this.child.calcEstimatedSize();
|
|
this.rect.width = this.estimatedSize;
|
|
this.rect.height = this.estimatedSize;
|
|
|
|
return this.estimatedSize;
|
|
}
|
|
};
|
|
|
|
LNode.prototype.scatter = function () {
|
|
var randomCenterX;
|
|
var randomCenterY;
|
|
|
|
var minX = -LayoutConstants.INITIAL_WORLD_BOUNDARY;
|
|
var maxX = LayoutConstants.INITIAL_WORLD_BOUNDARY;
|
|
randomCenterX = LayoutConstants.WORLD_CENTER_X +
|
|
(RandomSeed.nextDouble() * (maxX - minX)) + minX;
|
|
|
|
var minY = -LayoutConstants.INITIAL_WORLD_BOUNDARY;
|
|
var maxY = LayoutConstants.INITIAL_WORLD_BOUNDARY;
|
|
randomCenterY = LayoutConstants.WORLD_CENTER_Y +
|
|
(RandomSeed.nextDouble() * (maxY - minY)) + minY;
|
|
|
|
this.rect.x = randomCenterX;
|
|
this.rect.y = randomCenterY
|
|
};
|
|
|
|
LNode.prototype.updateBounds = function () {
|
|
if (this.getChild() == null) {
|
|
throw "assert failed";
|
|
}
|
|
if (this.getChild().getNodes().length != 0)
|
|
{
|
|
// wrap the children nodes by re-arranging the boundaries
|
|
var childGraph = this.getChild();
|
|
childGraph.updateBounds(true);
|
|
|
|
this.rect.x = childGraph.getLeft();
|
|
this.rect.y = childGraph.getTop();
|
|
|
|
this.setWidth(childGraph.getRight() - childGraph.getLeft());
|
|
this.setHeight(childGraph.getBottom() - childGraph.getTop());
|
|
|
|
// Update compound bounds considering its label properties
|
|
if(LayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS){
|
|
|
|
var width = childGraph.getRight() - childGraph.getLeft();
|
|
var height = childGraph.getBottom() - childGraph.getTop();
|
|
|
|
if(this.labelWidth > width){
|
|
this.rect.x -= (this.labelWidth - width) / 2;
|
|
this.setWidth(this.labelWidth);
|
|
}
|
|
|
|
if(this.labelHeight > height){
|
|
if(this.labelPos == "center"){
|
|
this.rect.y -= (this.labelHeight - height) / 2;
|
|
}
|
|
else if(this.labelPos == "top"){
|
|
this.rect.y -= (this.labelHeight - height);
|
|
}
|
|
this.setHeight(this.labelHeight);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
LNode.prototype.getInclusionTreeDepth = function ()
|
|
{
|
|
if (this.inclusionTreeDepth == Integer.MAX_VALUE) {
|
|
throw "assert failed";
|
|
}
|
|
return this.inclusionTreeDepth;
|
|
};
|
|
|
|
LNode.prototype.transform = function (trans)
|
|
{
|
|
var left = this.rect.x;
|
|
|
|
if (left > LayoutConstants.WORLD_BOUNDARY)
|
|
{
|
|
left = LayoutConstants.WORLD_BOUNDARY;
|
|
}
|
|
else if (left < -LayoutConstants.WORLD_BOUNDARY)
|
|
{
|
|
left = -LayoutConstants.WORLD_BOUNDARY;
|
|
}
|
|
|
|
var top = this.rect.y;
|
|
|
|
if (top > LayoutConstants.WORLD_BOUNDARY)
|
|
{
|
|
top = LayoutConstants.WORLD_BOUNDARY;
|
|
}
|
|
else if (top < -LayoutConstants.WORLD_BOUNDARY)
|
|
{
|
|
top = -LayoutConstants.WORLD_BOUNDARY;
|
|
}
|
|
|
|
var leftTop = new PointD(left, top);
|
|
var vLeftTop = trans.inverseTransformPoint(leftTop);
|
|
|
|
this.setLocation(vLeftTop.x, vLeftTop.y);
|
|
};
|
|
|
|
LNode.prototype.getLeft = function ()
|
|
{
|
|
return this.rect.x;
|
|
};
|
|
|
|
LNode.prototype.getRight = function ()
|
|
{
|
|
return this.rect.x + this.rect.width;
|
|
};
|
|
|
|
LNode.prototype.getTop = function ()
|
|
{
|
|
return this.rect.y;
|
|
};
|
|
|
|
LNode.prototype.getBottom = function ()
|
|
{
|
|
return this.rect.y + this.rect.height;
|
|
};
|
|
|
|
LNode.prototype.getParent = function ()
|
|
{
|
|
if (this.owner == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return this.owner.getParent();
|
|
};
|
|
|
|
module.exports = LNode;
|
|
|