layout-base
Version:
Basic layout model and some utilities for Cytoscape.js layout extensions
1,916 lines (1,566 loc) • 148 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["layoutBase"] = factory();
else
root["layoutBase"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 28);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function LayoutConstants() {}
/**
* Layout Quality: 0:draft, 1:default, 2:proof
*/
LayoutConstants.QUALITY = 1;
/**
* Default parameters
*/
LayoutConstants.DEFAULT_CREATE_BENDS_AS_NEEDED = false;
LayoutConstants.DEFAULT_INCREMENTAL = false;
LayoutConstants.DEFAULT_ANIMATION_ON_LAYOUT = true;
LayoutConstants.DEFAULT_ANIMATION_DURING_LAYOUT = false;
LayoutConstants.DEFAULT_ANIMATION_PERIOD = 50;
LayoutConstants.DEFAULT_UNIFORM_LEAF_NODE_SIZES = false;
// -----------------------------------------------------------------------------
// Section: General other constants
// -----------------------------------------------------------------------------
/*
* Margins of a graph to be applied on bouding rectangle of its contents. We
* assume margins on all four sides to be uniform.
*/
LayoutConstants.DEFAULT_GRAPH_MARGIN = 15;
/*
* Whether to consider labels in node dimensions or not
*/
LayoutConstants.NODE_DIMENSIONS_INCLUDE_LABELS = false;
/*
* Default dimension of a non-compound node.
*/
LayoutConstants.SIMPLE_NODE_SIZE = 40;
/*
* Default dimension of a non-compound node.
*/
LayoutConstants.SIMPLE_NODE_HALF_SIZE = LayoutConstants.SIMPLE_NODE_SIZE / 2;
/*
* Empty compound node size. When a compound node is empty, its both
* dimensions should be of this value.
*/
LayoutConstants.EMPTY_COMPOUND_NODE_SIZE = 40;
/*
* Minimum length that an edge should take during layout
*/
LayoutConstants.MIN_EDGE_LENGTH = 1;
/*
* World boundaries that layout operates on
*/
LayoutConstants.WORLD_BOUNDARY = 1000000;
/*
* World boundaries that random positioning can be performed with
*/
LayoutConstants.INITIAL_WORLD_BOUNDARY = LayoutConstants.WORLD_BOUNDARY / 1000;
/*
* Coordinates of the world center
*/
LayoutConstants.WORLD_CENTER_X = 1200;
LayoutConstants.WORLD_CENTER_Y = 900;
module.exports = LayoutConstants;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LGraphObject = __webpack_require__(2);
var IGeometry = __webpack_require__(8);
var IMath = __webpack_require__(9);
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;
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function LGraphObject(vGraphObject) {
this.vGraphObject = vGraphObject;
}
module.exports = LGraphObject;
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LGraphObject = __webpack_require__(2);
var Integer = __webpack_require__(10);
var RectangleD = __webpack_require__(13);
var LayoutConstants = __webpack_require__(0);
var RandomSeed = __webpack_require__(16);
var PointD = __webpack_require__(5);
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) {
if (this.labelPosHorizontal == "left") {
this.rect.x -= this.labelWidth;
this.setWidth(width + this.labelWidth);
} else if (this.labelPosHorizontal == "center" && this.labelWidth > width) {
this.rect.x -= (this.labelWidth - width) / 2;
this.setWidth(this.labelWidth);
} else if (this.labelPosHorizontal == "right") {
this.setWidth(width + this.labelWidth);
}
}
if (this.labelHeight) {
if (this.labelPosVertical == "top") {
this.rect.y -= this.labelHeight;
this.setHeight(height + this.labelHeight);
} else if (this.labelPosVertical == "center" && this.labelHeight > height) {
this.rect.y -= (this.labelHeight - height) / 2;
this.setHeight(this.labelHeight);
} else if (this.labelPosVertical == "bottom") {
this.setHeight(height + 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;
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LayoutConstants = __webpack_require__(0);
function FDLayoutConstants() {}
//FDLayoutConstants inherits static props in LayoutConstants
for (var prop in LayoutConstants) {
FDLayoutConstants[prop] = LayoutConstants[prop];
}
FDLayoutConstants.MAX_ITERATIONS = 2500;
FDLayoutConstants.DEFAULT_EDGE_LENGTH = 50;
FDLayoutConstants.DEFAULT_SPRING_STRENGTH = 0.45;
FDLayoutConstants.DEFAULT_REPULSION_STRENGTH = 4500.0;
FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH = 0.4;
FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH = 1.0;
FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR = 3.8;
FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR = 1.5;
FDLayoutConstants.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION = true;
FDLayoutConstants.DEFAULT_USE_SMART_REPULSION_RANGE_CALCULATION = true;
FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL = 0.3;
FDLayoutConstants.COOLING_ADAPTATION_FACTOR = 0.33;
FDLayoutConstants.ADAPTATION_LOWER_NODE_LIMIT = 1000;
FDLayoutConstants.ADAPTATION_UPPER_NODE_LIMIT = 5000;
FDLayoutConstants.MAX_NODE_DISPLACEMENT_INCREMENTAL = 100.0;
FDLayoutConstants.MAX_NODE_DISPLACEMENT = FDLayoutConstants.MAX_NODE_DISPLACEMENT_INCREMENTAL * 3;
FDLayoutConstants.MIN_REPULSION_DIST = FDLayoutConstants.DEFAULT_EDGE_LENGTH / 10.0;
FDLayoutConstants.CONVERGENCE_CHECK_PERIOD = 100;
FDLayoutConstants.PER_LEVEL_IDEAL_EDGE_LENGTH_FACTOR = 0.1;
FDLayoutConstants.MIN_EDGE_LENGTH = 1;
FDLayoutConstants.GRID_CALCULATION_CHECK_PERIOD = 10;
module.exports = FDLayoutConstants;
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
function PointD(x, y) {
if (x == null && y == null) {
this.x = 0;
this.y = 0;
} else {
this.x = x;
this.y = y;
}
}
PointD.prototype.getX = function () {
return this.x;
};
PointD.prototype.getY = function () {
return this.y;
};
PointD.prototype.setX = function (x) {
this.x = x;
};
PointD.prototype.setY = function (y) {
this.y = y;
};
PointD.prototype.getDifference = function (pt) {
return new DimensionD(this.x - pt.x, this.y - pt.y);
};
PointD.prototype.getCopy = function () {
return new PointD(this.x, this.y);
};
PointD.prototype.translate = function (dim) {
this.x += dim.width;
this.y += dim.height;
return this;
};
module.exports = PointD;
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LGraphObject = __webpack_require__(2);
var Integer = __webpack_require__(10);
var LayoutConstants = __webpack_require__(0);
var LGraphManager = __webpack_require__(7);
var LNode = __webpack_require__(3);
var LEdge = __webpack_require__(1);
var RectangleD = __webpack_require__(13);
var Point = __webpack_require__(12);
var LinkedList = __webpack_require__(11);
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;
/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LGraph;
var LEdge = __webpack_require__(1);
function LGraphManager(layout) {
LGraph = __webpack_require__(6); // It may be better to initilize this out of this function but it gives an error (Right-hand side of 'instanceof' is not callable) now.
this.layout = layout;
this.graphs = [];
this.edges = [];
}
LGraphManager.prototype.addRoot = function () {
var ngraph = this.layout.newGraph();
var nnode = this.layout.newNode(null);
var root = this.add(ngraph, nnode);
this.setRootGraph(root);
return this.rootGraph;
};
LGraphManager.prototype.add = function (newGraph, parentNode, newEdge, sourceNode, targetNode) {
//there are just 2 parameters are passed then it adds an LGraph else it adds an LEdge
if (newEdge == null && sourceNode == null && targetNode == null) {
if (newGraph == null) {
throw "Graph is null!";
}
if (parentNode == null) {
throw "Parent node is null!";
}
if (this.graphs.indexOf(newGraph) > -1) {
throw "Graph already in this graph mgr!";
}
this.graphs.push(newGraph);
if (newGraph.parent != null) {
throw "Already has a parent!";
}
if (parentNode.child != null) {
throw "Already has a child!";
}
newGraph.parent = parentNode;
parentNode.child = newGraph;
return newGraph;
} else {
//change the order of the parameters
targetNode = newEdge;
sourceNode = parentNode;
newEdge = newGraph;
var sourceGraph = sourceNode.getOwner();
var targetGraph = targetNode.getOwner();
if (!(sourceGraph != null && sourceGraph.getGraphManager() == this)) {
throw "Source not in this graph mgr!";
}
if (!(targetGraph != null && targetGraph.getGraphManager() == this)) {
throw "Target not in this graph mgr!";
}
if (sourceGraph == targetGraph) {
newEdge.isInterGraph = false;
return sourceGraph.add(newEdge, sourceNode, targetNode);
} else {
newEdge.isInterGraph = true;
// set source and target
newEdge.source = sourceNode;
newEdge.target = targetNode;
// add edge to inter-graph edge list
if (this.edges.indexOf(newEdge) > -1) {
throw "Edge already in inter-graph edge list!";
}
this.edges.push(newEdge);
// add edge to source and target incidency lists
if (!(newEdge.source != null && newEdge.target != null)) {
throw "Edge source and/or target is null!";
}
if (!(newEdge.source.edges.indexOf(newEdge) == -1 && newEdge.target.edges.indexOf(newEdge) == -1)) {
throw "Edge already in source and/or target incidency list!";
}
newEdge.source.edges.push(newEdge);
newEdge.target.edges.push(newEdge);
return newEdge;
}
}
};
LGraphManager.prototype.remove = function (lObj) {
if (lObj instanceof LGraph) {
var graph = lObj;
if (graph.getGraphManager() != this) {
throw "Graph not in this graph mgr";
}
if (!(graph == this.rootGraph || graph.parent != null && graph.parent.graphManager == this)) {
throw "Invalid parent node!";
}
// first the edges (make a copy to do it safely)
var edgesToBeRemoved = [];
edgesToBeRemoved = edgesToBeRemoved.concat(graph.getEdges());
var edge;
var s = edgesToBeRemoved.length;
for (var i = 0; i < s; i++) {
edge = edgesToBeRemoved[i];
graph.remove(edge);
}
// then the nodes (make a copy to do it safely)
var nodesToBeRemoved = [];
nodesToBeRemoved = nodesToBeRemoved.concat(graph.getNodes());
var node;
s = nodesToBeRemoved.length;
for (var i = 0; i < s; i++) {
node = nodesToBeRemoved[i];
graph.remove(node);
}
// check if graph is the root
if (graph == this.rootGraph) {
this.setRootGraph(null);
}
// now remove the graph itself
var index = this.graphs.indexOf(graph);
this.graphs.splice(index, 1);
// also reset the parent of the graph
graph.parent = null;
} else if (lObj instanceof LEdge) {
edge = lObj;
if (edge == null) {
throw "Edge is null!";
}
if (!edge.isInterGraph) {
throw "Not an inter-graph edge!";
}
if (!(edge.source != null && edge.target != null)) {
throw "Source and/or target is null!";
}
// remove edge from source and target nodes' incidency lists
if (!(edge.source.edges.indexOf(edge) != -1 && edge.target.edges.indexOf(edge) != -1)) {
throw "Source and/or target doesn't know this edge!";
}
var index = edge.source.edges.indexOf(edge);
edge.source.edges.splice(index, 1);
index = edge.target.edges.indexOf(edge);
edge.target.edges.splice(index, 1);
// remove edge from owner graph manager's inter-graph edge list
if (!(edge.source.owner != null && edge.source.owner.getGraphManager() != null)) {
throw "Edge owner graph or owner graph manager is null!";
}
if (edge.source.owner.getGraphManager().edges.indexOf(edge) == -1) {
throw "Not in owner graph manager's edge list!";
}
var index = edge.source.owner.getGraphManager().edges.indexOf(edge);
edge.source.owner.getGraphManager().edges.splice(index, 1);
}
};
LGraphManager.prototype.updateBounds = function () {
this.rootGraph.updateBounds(true);
};
LGraphManager.prototype.getGraphs = function () {
return this.graphs;
};
LGraphManager.prototype.getAllNodes = function () {
if (this.allNodes == null) {
var nodeList = [];
var graphs = this.getGraphs();
var s = graphs.length;
for (var i = 0; i < s; i++) {
nodeList = nodeList.concat(graphs[i].getNodes());
}
this.allNodes = nodeList;
}
return this.allNodes;
};
LGraphManager.prototype.resetAllNodes = function () {
this.allNodes = null;
};
LGraphManager.prototype.resetAllEdges = function () {
this.allEdges = null;
};
LGraphManager.prototype.resetAllNodesToApplyGravitation = function () {
this.allNodesToApplyGravitation = null;
};
LGraphManager.prototype.getAllEdges = function () {
if (this.allEdges == null) {
var edgeList = [];
var graphs = this.getGraphs();
var s = graphs.length;
for (var i = 0; i < graphs.length; i++) {
edgeList = edgeList.concat(graphs[i].getEdges());
}
edgeList = edgeList.concat(this.edges);
this.allEdges = edgeList;
}
return this.allEdges;
};
LGraphManager.prototype.getAllNodesToApplyGravitation = function () {
return this.allNodesToApplyGravitation;
};
LGraphManager.prototype.setAllNodesToApplyGravitation = function (nodeList) {
if (this.allNodesToApplyGravitation != null) {
throw "assert failed";
}
this.allNodesToApplyGravitation = nodeList;
};
LGraphManager.prototype.getRoot = function () {
return this.rootGraph;
};
LGraphManager.prototype.setRootGraph = function (graph) {
if (graph.getGraphManager() != this) {
throw "Root not in this graph mgr!";
}
this.rootGraph = graph;
// root graph must have a root node associated with it for convenience
if (graph.parent == null) {
graph.parent = this.layout.newNode("Root node");
}
};
LGraphManager.prototype.getLayout = function () {
return this.layout;
};
LGraphManager.prototype.isOneAncestorOfOther = function (firstNode, secondNode) {
if (!(firstNode != null && secondNode != null)) {
throw "assert failed";
}
if (firstNode == secondNode) {
return true;
}
// Is second node an ancestor of the first one?
var ownerGraph = firstNode.getOwner();
var parentNode;
do {
parentNode = ownerGraph.getParent();
if (parentNode == null) {
break;
}
if (parentNode == secondNode) {
return true;
}
ownerGraph = parentNode.getOwner();
if (ownerGraph == null) {
break;
}
} while (true);
// Is first node an ancestor of the second one?
ownerGraph = secondNode.getOwner();
do {
parentNode = ownerGraph.getParent();
if (parentNode == null) {
break;
}
if (parentNode == firstNode) {
return true;
}
ownerGraph = parentNode.getOwner();
if (ownerGraph == null) {
break;
}
} while (true);
return false;
};
LGraphManager.prototype.calcLowestCommonAncestors = function () {
var edge;
var sourceNode;
var targetNode;
var sourceAncestorGraph;
var targetAncestorGraph;
var edges = this.getAllEdges();
var s = edges.length;
for (var i = 0; i < s; i++) {
edge = edges[i];
sourceNode = edge.source;
targetNode = edge.target;
edge.lca = null;
edge.sourceInLca = sourceNode;
edge.targetInLca = targetNode;
if (sourceNode == targetNode) {
edge.lca = sourceNode.getOwner();
continue;
}
sourceAncestorGraph = sourceNode.getOwner();
while (edge.lca == null) {
edge.targetInLca = targetNode;
targetAncestorGraph = targetNode.getOwner();
while (edge.lca == null) {
if (targetAncestorGraph == sourceAncestorGraph) {
edge.lca = targetAncestorGraph;
break;
}
if (targetAncestorGraph == this.rootGraph) {
break;
}
if (edge.lca != null) {
throw "assert failed";
}
edge.targetInLca = targetAncestorGraph.getParent();
targetAncestorGraph = edge.targetInLca.getOwner();
}
if (sourceAncestorGraph == this.rootGraph) {
break;
}
if (edge.lca == null) {
edge.sourceInLca = sourceAncestorGraph.getParent();
sourceAncestorGraph = edge.sourceInLca.getOwner();
}
}
if (edge.lca == null) {
throw "assert failed";
}
}
};
LGraphManager.prototype.calcLowestCommonAncestor = function (firstNode, secondNode) {
if (firstNode == secondNode) {
return firstNode.getOwner();
}
var firstOwnerGraph = firstNode.getOwner();
do {
if (firstOwnerGraph == null) {
break;
}
var secondOwnerGraph = secondNode.getOwner();
do {
if (secondOwnerGraph == null) {
break;
}
if (secondOwnerGraph == firstOwnerGraph) {
return secondOwnerGraph;
}
secondOwnerGraph = secondOwnerGraph.getParent().getOwner();
} while (true);
firstOwnerGraph = firstOwnerGraph.getParent().getOwner();
} while (true);
return firstOwnerGraph;
};
LGraphManager.prototype.calcInclusionTreeDepths = function (graph, depth) {
if (graph == null && depth == null) {
graph = this.rootGraph;
depth = 1;
}
var node;
var nodes = graph.getNodes();
var s = nodes.length;
for (var i = 0; i < s; i++) {
node = nodes[i];
node.inclusionTreeDepth = depth;
if (node.child != null) {
this.calcInclusionTreeDepths(node.child, depth + 1);
}
}
};
LGraphManager.prototype.includesInvalidEdge = function () {
var edge;
var edgesToRemove = [];
var s = this.edges.length;
for (var i = 0; i < s; i++) {
edge = this.edges[i];
if (this.isOneAncestorOfOther(edge.source, edge.target)) {
edgesToRemove.push(edge);
}
}
// Remove invalid edges from graph manager
for (var i = 0; i < edgesToRemove.length; i++) {
this.remove(edgesToRemove[i]);
}
// Invalid edges are cleared, so return false
return false;
};
module.exports = LGraphManager;
/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/**
* This class maintains a list of static geometry related utility methods.
*
*
* Copyright: i-Vis Research Group, Bilkent University, 2007 - present
*/
var Point = __webpack_require__(12);
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";
}
var 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
var 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;
}
var moveByY = slope * overlapAmount[0];
var 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
var p1x = rectA.getCenterX();
var p1y = rectA.getCenterY();
var p2x = rectB.getCenterX();
var 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
var topLeftAx = rectA.getX();
var topLeftAy = rectA.getY();
var topRightAx = rectA.getRight();
var bottomLeftAx = rectA.getX();
var bottomLeftAy = rectA.getBottom();
var bottomRightAx = rectA.getRight();
var halfWidthA = rectA.getWidthHalf();
var halfHeightA = rectA.getHeightHalf();
//variables for rectB
var topLeftBx = rectB.getX();
var topLeftBy = rectB.getY();
var topRightBx = rectB.getRight();
var bottomLeftBx = rectB.getX();
var bottomLeftBy = rectB.getBottom();
var bottomRightBx = rectB.getRight();
var halfWidthB = rectB.getWidthHalf();
var halfHeightB = rectB.getHeightHalf();
//flag whether clipping points are found
var clipPointAFound = false;
var 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
var slopeA = rectA.height / rectA.width;
var slopeB = rectB.height / rectB.width;
//slope of line between center of rectA and center of rectB
var slopePrime = (p2y - p1y) / (p2x - p1x);
var cardinalDirectionA = void 0;
var cardinalDirectionB = void 0;
var tempPointAx = void 0;
var tempPointAy = void 0;
var tempPointBx = void 0;
var tempPointBy = void 0;
//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.get