DuyTa's picture
Upload folder using huggingface_hub
bc20498 verified
raw
history blame
46.1 kB
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("layout-base"));
else if(typeof define === 'function' && define.amd)
define(["layout-base"], factory);
else if(typeof exports === 'object')
exports["coseBase"] = factory(require("layout-base"));
else
root["coseBase"] = factory(root["layoutBase"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {
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 = 7);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var FDLayoutConstants = __webpack_require__(0).FDLayoutConstants;
function CoSEConstants() {}
//CoSEConstants inherits static props in FDLayoutConstants
for (var prop in FDLayoutConstants) {
CoSEConstants[prop] = FDLayoutConstants[prop];
}
CoSEConstants.DEFAULT_USE_MULTI_LEVEL_SCALING = false;
CoSEConstants.DEFAULT_RADIAL_SEPARATION = FDLayoutConstants.DEFAULT_EDGE_LENGTH;
CoSEConstants.DEFAULT_COMPONENT_SEPERATION = 60;
CoSEConstants.TILE = true;
CoSEConstants.TILING_PADDING_VERTICAL = 10;
CoSEConstants.TILING_PADDING_HORIZONTAL = 10;
CoSEConstants.TREE_REDUCTION_ON_INCREMENTAL = false; // make this true when cose is used incrementally as a part of other non-incremental layout
module.exports = CoSEConstants;
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var FDLayoutEdge = __webpack_require__(0).FDLayoutEdge;
function CoSEEdge(source, target, vEdge) {
FDLayoutEdge.call(this, source, target, vEdge);
}
CoSEEdge.prototype = Object.create(FDLayoutEdge.prototype);
for (var prop in FDLayoutEdge) {
CoSEEdge[prop] = FDLayoutEdge[prop];
}
module.exports = CoSEEdge;
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LGraph = __webpack_require__(0).LGraph;
function CoSEGraph(parent, graphMgr, vGraph) {
LGraph.call(this, parent, graphMgr, vGraph);
}
CoSEGraph.prototype = Object.create(LGraph.prototype);
for (var prop in LGraph) {
CoSEGraph[prop] = LGraph[prop];
}
module.exports = CoSEGraph;
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var LGraphManager = __webpack_require__(0).LGraphManager;
function CoSEGraphManager(layout) {
LGraphManager.call(this, layout);
}
CoSEGraphManager.prototype = Object.create(LGraphManager.prototype);
for (var prop in LGraphManager) {
CoSEGraphManager[prop] = LGraphManager[prop];
}
module.exports = CoSEGraphManager;
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var FDLayoutNode = __webpack_require__(0).FDLayoutNode;
var IMath = __webpack_require__(0).IMath;
function CoSENode(gm, loc, size, vNode) {
FDLayoutNode.call(this, gm, loc, size, vNode);
}
CoSENode.prototype = Object.create(FDLayoutNode.prototype);
for (var prop in FDLayoutNode) {
CoSENode[prop] = FDLayoutNode[prop];
}
CoSENode.prototype.move = function () {
var layout = this.graphManager.getLayout();
this.displacementX = layout.coolingFactor * (this.springForceX + this.repulsionForceX + this.gravitationForceX) / this.noOfChildren;
this.displacementY = layout.coolingFactor * (this.springForceY + this.repulsionForceY + this.gravitationForceY) / this.noOfChildren;
if (Math.abs(this.displacementX) > layout.coolingFactor * layout.maxNodeDisplacement) {
this.displacementX = layout.coolingFactor * layout.maxNodeDisplacement * IMath.sign(this.displacementX);
}
if (Math.abs(this.displacementY) > layout.coolingFactor * layout.maxNodeDisplacement) {
this.displacementY = layout.coolingFactor * layout.maxNodeDisplacement * IMath.sign(this.displacementY);
}
// a simple node, just move it
if (this.child == null) {
this.moveBy(this.displacementX, this.displacementY);
}
// an empty compound node, again just move it
else if (this.child.getNodes().length == 0) {
this.moveBy(this.displacementX, this.displacementY);
}
// non-empty compound node, propogate movement to children as well
else {
this.propogateDisplacementToChildren(this.displacementX, this.displacementY);
}
layout.totalDisplacement += Math.abs(this.displacementX) + Math.abs(this.displacementY);
this.springForceX = 0;
this.springForceY = 0;
this.repulsionForceX = 0;
this.repulsionForceY = 0;
this.gravitationForceX = 0;
this.gravitationForceY = 0;
this.displacementX = 0;
this.displacementY = 0;
};
CoSENode.prototype.propogateDisplacementToChildren = function (dX, dY) {
var nodes = this.getChild().getNodes();
var node;
for (var i = 0; i < nodes.length; i++) {
node = nodes[i];
if (node.getChild() == null) {
node.moveBy(dX, dY);
node.displacementX += dX;
node.displacementY += dY;
} else {
node.propogateDisplacementToChildren(dX, dY);
}
}
};
CoSENode.prototype.setPred1 = function (pred1) {
this.pred1 = pred1;
};
CoSENode.prototype.getPred1 = function () {
return pred1;
};
CoSENode.prototype.getPred2 = function () {
return pred2;
};
CoSENode.prototype.setNext = function (next) {
this.next = next;
};
CoSENode.prototype.getNext = function () {
return next;
};
CoSENode.prototype.setProcessed = function (processed) {
this.processed = processed;
};
CoSENode.prototype.isProcessed = function () {
return processed;
};
module.exports = CoSENode;
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var FDLayout = __webpack_require__(0).FDLayout;
var CoSEGraphManager = __webpack_require__(4);
var CoSEGraph = __webpack_require__(3);
var CoSENode = __webpack_require__(5);
var CoSEEdge = __webpack_require__(2);
var CoSEConstants = __webpack_require__(1);
var FDLayoutConstants = __webpack_require__(0).FDLayoutConstants;
var LayoutConstants = __webpack_require__(0).LayoutConstants;
var Point = __webpack_require__(0).Point;
var PointD = __webpack_require__(0).PointD;
var Layout = __webpack_require__(0).Layout;
var Integer = __webpack_require__(0).Integer;
var IGeometry = __webpack_require__(0).IGeometry;
var LGraph = __webpack_require__(0).LGraph;
var Transform = __webpack_require__(0).Transform;
function CoSELayout() {
FDLayout.call(this);
this.toBeTiled = {}; // Memorize if a node is to be tiled or is tiled
}
CoSELayout.prototype = Object.create(FDLayout.prototype);
for (var prop in FDLayout) {
CoSELayout[prop] = FDLayout[prop];
}
CoSELayout.prototype.newGraphManager = function () {
var gm = new CoSEGraphManager(this);
this.graphManager = gm;
return gm;
};
CoSELayout.prototype.newGraph = function (vGraph) {
return new CoSEGraph(null, this.graphManager, vGraph);
};
CoSELayout.prototype.newNode = function (vNode) {
return new CoSENode(this.graphManager, vNode);
};
CoSELayout.prototype.newEdge = function (vEdge) {
return new CoSEEdge(null, null, vEdge);
};
CoSELayout.prototype.initParameters = function () {
FDLayout.prototype.initParameters.call(this, arguments);
if (!this.isSubLayout) {
if (CoSEConstants.DEFAULT_EDGE_LENGTH < 10) {
this.idealEdgeLength = 10;
} else {
this.idealEdgeLength = CoSEConstants.DEFAULT_EDGE_LENGTH;
}
this.useSmartIdealEdgeLengthCalculation = CoSEConstants.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION;
this.springConstant = FDLayoutConstants.DEFAULT_SPRING_STRENGTH;
this.repulsionConstant = FDLayoutConstants.DEFAULT_REPULSION_STRENGTH;
this.gravityConstant = FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH;
this.compoundGravityConstant = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH;
this.gravityRangeFactor = FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR;
this.compoundGravityRangeFactor = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR;
// variables for tree reduction support
this.prunedNodesAll = [];
this.growTreeIterations = 0;
this.afterGrowthIterations = 0;
this.isTreeGrowing = false;
this.isGrowthFinished = false;
// variables for cooling
this.coolingCycle = 0;
this.maxCoolingCycle = this.maxIterations / FDLayoutConstants.CONVERGENCE_CHECK_PERIOD;
this.finalTemperature = FDLayoutConstants.CONVERGENCE_CHECK_PERIOD / this.maxIterations;
this.coolingAdjuster = 1;
}
};
CoSELayout.prototype.layout = function () {
var createBendsAsNeeded = LayoutConstants.DEFAULT_CREATE_BENDS_AS_NEEDED;
if (createBendsAsNeeded) {
this.createBendpoints();
this.graphManager.resetAllEdges();
}
this.level = 0;
return this.classicLayout();
};
CoSELayout.prototype.classicLayout = function () {
this.nodesWithGravity = this.calculateNodesToApplyGravitationTo();
this.graphManager.setAllNodesToApplyGravitation(this.nodesWithGravity);
this.calcNoOfChildrenForAllNodes();
this.graphManager.calcLowestCommonAncestors();
this.graphManager.calcInclusionTreeDepths();
this.graphManager.getRoot().calcEstimatedSize();
this.calcIdealEdgeLengths();
if (!this.incremental) {
var forest = this.getFlatForest();
// The graph associated with this layout is flat and a forest
if (forest.length > 0) {
this.positionNodesRadially(forest);
}
// The graph associated with this layout is not flat or a forest
else {
// Reduce the trees when incremental mode is not enabled and graph is not a forest
this.reduceTrees();
// Update nodes that gravity will be applied
this.graphManager.resetAllNodesToApplyGravitation();
var allNodes = new Set(this.getAllNodes());
var intersection = this.nodesWithGravity.filter(function (x) {
return allNodes.has(x);
});
this.graphManager.setAllNodesToApplyGravitation(intersection);
this.positionNodesRandomly();
}
} else {
if (CoSEConstants.TREE_REDUCTION_ON_INCREMENTAL) {
// Reduce the trees in incremental mode if only this constant is set to true
this.reduceTrees();
// Update nodes that gravity will be applied
this.graphManager.resetAllNodesToApplyGravitation();
var allNodes = new Set(this.getAllNodes());
var intersection = this.nodesWithGravity.filter(function (x) {
return allNodes.has(x);
});
this.graphManager.setAllNodesToApplyGravitation(intersection);
}
}
this.initSpringEmbedder();
this.runSpringEmbedder();
return true;
};
CoSELayout.prototype.tick = function () {
this.totalIterations++;
if (this.totalIterations === this.maxIterations && !this.isTreeGrowing && !this.isGrowthFinished) {
if (this.prunedNodesAll.length > 0) {
this.isTreeGrowing = true;
} else {
return true;
}
}
if (this.totalIterations % FDLayoutConstants.CONVERGENCE_CHECK_PERIOD == 0 && !this.isTreeGrowing && !this.isGrowthFinished) {
if (this.isConverged()) {
if (this.prunedNodesAll.length > 0) {
this.isTreeGrowing = true;
} else {
return true;
}
}
this.coolingCycle++;
if (this.layoutQuality == 0) {
// quality - "draft"
this.coolingAdjuster = this.coolingCycle;
} else if (this.layoutQuality == 1) {
// quality - "default"
this.coolingAdjuster = this.coolingCycle / 3;
}
// cooling schedule is based on http://www.btluke.com/simanf1.html -> cooling schedule 3
this.coolingFactor = Math.max(this.initialCoolingFactor - Math.pow(this.coolingCycle, Math.log(100 * (this.initialCoolingFactor - this.finalTemperature)) / Math.log(this.maxCoolingCycle)) / 100 * this.coolingAdjuster, this.finalTemperature);
this.animationPeriod = Math.ceil(this.initialAnimationPeriod * Math.sqrt(this.coolingFactor));
}
// Operations while tree is growing again
if (this.isTreeGrowing) {
if (this.growTreeIterations % 10 == 0) {
if (this.prunedNodesAll.length > 0) {
this.graphManager.updateBounds();
this.updateGrid();
this.growTree(this.prunedNodesAll);
// Update nodes that gravity will be applied
this.graphManager.resetAllNodesToApplyGravitation();
var allNodes = new Set(this.getAllNodes());
var intersection = this.nodesWithGravity.filter(function (x) {
return allNodes.has(x);
});
this.graphManager.setAllNodesToApplyGravitation(intersection);
this.graphManager.updateBounds();
this.updateGrid();
this.coolingFactor = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL;
} else {
this.isTreeGrowing = false;
this.isGrowthFinished = true;
}
}
this.growTreeIterations++;
}
// Operations after growth is finished
if (this.isGrowthFinished) {
if (this.isConverged()) {
return true;
}
if (this.afterGrowthIterations % 10 == 0) {
this.graphManager.updateBounds();
this.updateGrid();
}
this.coolingFactor = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL * ((100 - this.afterGrowthIterations) / 100);
this.afterGrowthIterations++;
}
var gridUpdateAllowed = !this.isTreeGrowing && !this.isGrowthFinished;
var forceToNodeSurroundingUpdate = this.growTreeIterations % 10 == 1 && this.isTreeGrowing || this.afterGrowthIterations % 10 == 1 && this.isGrowthFinished;
this.totalDisplacement = 0;
this.graphManager.updateBounds();
this.calcSpringForces();
this.calcRepulsionForces(gridUpdateAllowed, forceToNodeSurroundingUpdate);
this.calcGravitationalForces();
this.moveNodes();
this.animate();
return false; // Layout is not ended yet return false
};
CoSELayout.prototype.getPositionsData = function () {
var allNodes = this.graphManager.getAllNodes();
var pData = {};
for (var i = 0; i < allNodes.length; i++) {
var rect = allNodes[i].rect;
var id = allNodes[i].id;
pData[id] = {
id: id,
x: rect.getCenterX(),
y: rect.getCenterY(),
w: rect.width,
h: rect.height
};
}
return pData;
};
CoSELayout.prototype.runSpringEmbedder = function () {
this.initialAnimationPeriod = 25;
this.animationPeriod = this.initialAnimationPeriod;
var layoutEnded = false;
// If aminate option is 'during' signal that layout is supposed to start iterating
if (FDLayoutConstants.ANIMATE === 'during') {
this.emit('layoutstarted');
} else {
// If aminate option is 'during' tick() function will be called on index.js
while (!layoutEnded) {
layoutEnded = this.tick();
}
this.graphManager.updateBounds();
}
};
CoSELayout.prototype.calculateNodesToApplyGravitationTo = function () {
var nodeList = [];
var graph;
var graphs = this.graphManager.getGraphs();
var size = graphs.length;
var i;
for (i = 0; i < size; i++) {
graph = graphs[i];
graph.updateConnected();
if (!graph.isConnected) {
nodeList = nodeList.concat(graph.getNodes());
}
}
return nodeList;
};
CoSELayout.prototype.createBendpoints = function () {
var edges = [];
edges = edges.concat(this.graphManager.getAllEdges());
var visited = new Set();
var i;
for (i = 0; i < edges.length; i++) {
var edge = edges[i];
if (!visited.has(edge)) {
var source = edge.getSource();
var target = edge.getTarget();
if (source == target) {
edge.getBendpoints().push(new PointD());
edge.getBendpoints().push(new PointD());
this.createDummyNodesForBendpoints(edge);
visited.add(edge);
} else {
var edgeList = [];
edgeList = edgeList.concat(source.getEdgeListToNode(target));
edgeList = edgeList.concat(target.getEdgeListToNode(source));
if (!visited.has(edgeList[0])) {
if (edgeList.length > 1) {
var k;
for (k = 0; k < edgeList.length; k++) {
var multiEdge = edgeList[k];
multiEdge.getBendpoints().push(new PointD());
this.createDummyNodesForBendpoints(multiEdge);
}
}
edgeList.forEach(function (edge) {
visited.add(edge);
});
}
}
}
if (visited.size == edges.length) {
break;
}
}
};
CoSELayout.prototype.positionNodesRadially = function (forest) {
// We tile the trees to a grid row by row; first tree starts at (0,0)
var currentStartingPoint = new Point(0, 0);
var numberOfColumns = Math.ceil(Math.sqrt(forest.length));
var height = 0;
var currentY = 0;
var currentX = 0;
var point = new PointD(0, 0);
for (var i = 0; i < forest.length; i++) {
if (i % numberOfColumns == 0) {
// Start of a new row, make the x coordinate 0, increment the
// y coordinate with the max height of the previous row
currentX = 0;
currentY = height;
if (i != 0) {
currentY += CoSEConstants.DEFAULT_COMPONENT_SEPERATION;
}
height = 0;
}
var tree = forest[i];
// Find the center of the tree
var centerNode = Layout.findCenterOfTree(tree);
// Set the staring point of the next tree
currentStartingPoint.x = currentX;
currentStartingPoint.y = currentY;
// Do a radial layout starting with the center
point = CoSELayout.radialLayout(tree, centerNode, currentStartingPoint);
if (point.y > height) {
height = Math.floor(point.y);
}
currentX = Math.floor(point.x + CoSEConstants.DEFAULT_COMPONENT_SEPERATION);
}
this.transform(new PointD(LayoutConstants.WORLD_CENTER_X - point.x / 2, LayoutConstants.WORLD_CENTER_Y - point.y / 2));
};
CoSELayout.radialLayout = function (tree, centerNode, startingPoint) {
var radialSep = Math.max(this.maxDiagonalInTree(tree), CoSEConstants.DEFAULT_RADIAL_SEPARATION);
CoSELayout.branchRadialLayout(centerNode, null, 0, 359, 0, radialSep);
var bounds = LGraph.calculateBounds(tree);
var transform = new Transform();
transform.setDeviceOrgX(bounds.getMinX());
transform.setDeviceOrgY(bounds.getMinY());
transform.setWorldOrgX(startingPoint.x);
transform.setWorldOrgY(startingPoint.y);
for (var i = 0; i < tree.length; i++) {
var node = tree[i];
node.transform(transform);
}
var bottomRight = new PointD(bounds.getMaxX(), bounds.getMaxY());
return transform.inverseTransformPoint(bottomRight);
};
CoSELayout.branchRadialLayout = function (node, parentOfNode, startAngle, endAngle, distance, radialSeparation) {
// First, position this node by finding its angle.
var halfInterval = (endAngle - startAngle + 1) / 2;
if (halfInterval < 0) {
halfInterval += 180;
}
var nodeAngle = (halfInterval + startAngle) % 360;
var teta = nodeAngle * IGeometry.TWO_PI / 360;
// Make polar to java cordinate conversion.
var cos_teta = Math.cos(teta);
var x_ = distance * Math.cos(teta);
var y_ = distance * Math.sin(teta);
node.setCenter(x_, y_);
// Traverse all neighbors of this node and recursively call this
// function.
var neighborEdges = [];
neighborEdges = neighborEdges.concat(node.getEdges());
var childCount = neighborEdges.length;
if (parentOfNode != null) {
childCount--;
}
var branchCount = 0;
var incEdgesCount = neighborEdges.length;
var startIndex;
var edges = node.getEdgesBetween(parentOfNode);
// If there are multiple edges, prune them until there remains only one
// edge.
while (edges.length > 1) {
//neighborEdges.remove(edges.remove(0));
var temp = edges[0];
edges.splice(0, 1);
var index = neighborEdges.indexOf(temp);
if (index >= 0) {
neighborEdges.splice(index, 1);
}
incEdgesCount--;
childCount--;
}
if (parentOfNode != null) {
//assert edges.length == 1;
startIndex = (neighborEdges.indexOf(edges[0]) + 1) % incEdgesCount;
} else {
startIndex = 0;
}
var stepAngle = Math.abs(endAngle - startAngle) / childCount;
for (var i = startIndex; branchCount != childCount; i = ++i % incEdgesCount) {
var currentNeighbor = neighborEdges[i].getOtherEnd(node);
// Don't back traverse to root node in current tree.
if (currentNeighbor == parentOfNode) {
continue;
}
var childStartAngle = (startAngle + branchCount * stepAngle) % 360;
var childEndAngle = (childStartAngle + stepAngle) % 360;
CoSELayout.branchRadialLayout(currentNeighbor, node, childStartAngle, childEndAngle, distance + radialSeparation, radialSeparation);
branchCount++;
}
};
CoSELayout.maxDiagonalInTree = function (tree) {
var maxDiagonal = Integer.MIN_VALUE;
for (var i = 0; i < tree.length; i++) {
var node = tree[i];
var diagonal = node.getDiagonal();
if (diagonal > maxDiagonal) {
maxDiagonal = diagonal;
}
}
return maxDiagonal;
};
CoSELayout.prototype.calcRepulsionRange = function () {
// formula is 2 x (level + 1) x idealEdgeLength
return 2 * (this.level + 1) * this.idealEdgeLength;
};
// Tiling methods
// Group zero degree members whose parents are not to be tiled, create dummy parents where needed and fill memberGroups by their dummp parent id's
CoSELayout.prototype.groupZeroDegreeMembers = function () {
var self = this;
// array of [parent_id x oneDegreeNode_id]
var tempMemberGroups = {}; // A temporary map of parent node and its zero degree members
this.memberGroups = {}; // A map of dummy parent node and its zero degree members whose parents are not to be tiled
this.idToDummyNode = {}; // A map of id to dummy node
var zeroDegree = []; // List of zero degree nodes whose parents are not to be tiled
var allNodes = this.graphManager.getAllNodes();
// Fill zero degree list
for (var i = 0; i < allNodes.length; i++) {
var node = allNodes[i];
var parent = node.getParent();
// If a node has zero degree and its parent is not to be tiled if exists add that node to zeroDegres list
if (this.getNodeDegreeWithChildren(node) === 0 && (parent.id == undefined || !this.getToBeTiled(parent))) {
zeroDegree.push(node);
}
}
// Create a map of parent node and its zero degree members
for (var i = 0; i < zeroDegree.length; i++) {
var node = zeroDegree[i]; // Zero degree node itself
var p_id = node.getParent().id; // Parent id
if (typeof tempMemberGroups[p_id] === "undefined") tempMemberGroups[p_id] = [];
tempMemberGroups[p_id] = tempMemberGroups[p_id].concat(node); // Push node to the list belongs to its parent in tempMemberGroups
}
// If there are at least two nodes at a level, create a dummy compound for them
Object.keys(tempMemberGroups).forEach(function (p_id) {
if (tempMemberGroups[p_id].length > 1) {
var dummyCompoundId = "DummyCompound_" + p_id; // The id of dummy compound which will be created soon
self.memberGroups[dummyCompoundId] = tempMemberGroups[p_id]; // Add dummy compound to memberGroups
var parent = tempMemberGroups[p_id][0].getParent(); // The parent of zero degree nodes will be the parent of new dummy compound
// Create a dummy compound with calculated id
var dummyCompound = new CoSENode(self.graphManager);
dummyCompound.id = dummyCompoundId;
dummyCompound.paddingLeft = parent.paddingLeft || 0;
dummyCompound.paddingRight = parent.paddingRight || 0;
dummyCompound.paddingBottom = parent.paddingBottom || 0;
dummyCompound.paddingTop = parent.paddingTop || 0;
self.idToDummyNode[dummyCompoundId] = dummyCompound;
var dummyParentGraph = self.getGraphManager().add(self.newGraph(), dummyCompound);
var parentGraph = parent.getChild();
// Add dummy compound to parent the graph
parentGraph.add(dummyCompound);
// For each zero degree node in this level remove it from its parent graph and add it to the graph of dummy parent
for (var i = 0; i < tempMemberGroups[p_id].length; i++) {
var node = tempMemberGroups[p_id][i];
parentGraph.remove(node);
dummyParentGraph.add(node);
}
}
});
};
CoSELayout.prototype.clearCompounds = function () {
var childGraphMap = {};
var idToNode = {};
// Get compound ordering by finding the inner one first
this.performDFSOnCompounds();
for (var i = 0; i < this.compoundOrder.length; i++) {
idToNode[this.compoundOrder[i].id] = this.compoundOrder[i];
childGraphMap[this.compoundOrder[i].id] = [].concat(this.compoundOrder[i].getChild().getNodes());
// Remove children of compounds
this.graphManager.remove(this.compoundOrder[i].getChild());
this.compoundOrder[i].child = null;
}
this.graphManager.resetAllNodes();
// Tile the removed children
this.tileCompoundMembers(childGraphMap, idToNode);
};
CoSELayout.prototype.clearZeroDegreeMembers = function () {
var self = this;
var tiledZeroDegreePack = this.tiledZeroDegreePack = [];
Object.keys(this.memberGroups).forEach(function (id) {
var compoundNode = self.idToDummyNode[id]; // Get the dummy compound
tiledZeroDegreePack[id] = self.tileNodes(self.memberGroups[id], compoundNode.paddingLeft + compoundNode.paddingRight);
// Set the width and height of the dummy compound as calculated
compoundNode.rect.width = tiledZeroDegreePack[id].width;
compoundNode.rect.height = tiledZeroDegreePack[id].height;
});
};
CoSELayout.prototype.repopulateCompounds = function () {
for (var i = this.compoundOrder.length - 1; i >= 0; i--) {
var lCompoundNode = this.compoundOrder[i];
var id = lCompoundNode.id;
var horizontalMargin = lCompoundNode.paddingLeft;
var verticalMargin = lCompoundNode.paddingTop;
this.adjustLocations(this.tiledMemberPack[id], lCompoundNode.rect.x, lCompoundNode.rect.y, horizontalMargin, verticalMargin);
}
};
CoSELayout.prototype.repopulateZeroDegreeMembers = function () {
var self = this;
var tiledPack = this.tiledZeroDegreePack;
Object.keys(tiledPack).forEach(function (id) {
var compoundNode = self.idToDummyNode[id]; // Get the dummy compound by its id
var horizontalMargin = compoundNode.paddingLeft;
var verticalMargin = compoundNode.paddingTop;
// Adjust the positions of nodes wrt its compound
self.adjustLocations(tiledPack[id], compoundNode.rect.x, compoundNode.rect.y, horizontalMargin, verticalMargin);
});
};
CoSELayout.prototype.getToBeTiled = function (node) {
var id = node.id;
//firstly check the previous results
if (this.toBeTiled[id] != null) {
return this.toBeTiled[id];
}
//only compound nodes are to be tiled
var childGraph = node.getChild();
if (childGraph == null) {
this.toBeTiled[id] = false;
return false;
}
var children = childGraph.getNodes(); // Get the children nodes
//a compound node is not to be tiled if all of its compound children are not to be tiled
for (var i = 0; i < children.length; i++) {
var theChild = children[i];
if (this.getNodeDegree(theChild) > 0) {
this.toBeTiled[id] = false;
return false;
}
//pass the children not having the compound structure
if (theChild.getChild() == null) {
this.toBeTiled[theChild.id] = false;
continue;
}
if (!this.getToBeTiled(theChild)) {
this.toBeTiled[id] = false;
return false;
}
}
this.toBeTiled[id] = true;
return true;
};
// Get degree of a node depending of its edges and independent of its children
CoSELayout.prototype.getNodeDegree = function (node) {
var id = node.id;
var edges = node.getEdges();
var degree = 0;
// For the edges connected
for (var i = 0; i < edges.length; i++) {
var edge = edges[i];
if (edge.getSource().id !== edge.getTarget().id) {
degree = degree + 1;
}
}
return degree;
};
// Get degree of a node with its children
CoSELayout.prototype.getNodeDegreeWithChildren = function (node) {
var degree = this.getNodeDegree(node);
if (node.getChild() == null) {
return degree;
}
var children = node.getChild().getNodes();
for (var i = 0; i < children.length; i++) {
var child = children[i];
degree += this.getNodeDegreeWithChildren(child);
}
return degree;
};
CoSELayout.prototype.performDFSOnCompounds = function () {
this.compoundOrder = [];
this.fillCompexOrderByDFS(this.graphManager.getRoot().getNodes());
};
CoSELayout.prototype.fillCompexOrderByDFS = function (children) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.getChild() != null) {
this.fillCompexOrderByDFS(child.getChild().getNodes());
}
if (this.getToBeTiled(child)) {
this.compoundOrder.push(child);
}
}
};
/**
* This method places each zero degree member wrt given (x,y) coordinates (top left).
*/
CoSELayout.prototype.adjustLocations = function (organization, x, y, compoundHorizontalMargin, compoundVerticalMargin) {
x += compoundHorizontalMargin;
y += compoundVerticalMargin;
var left = x;
for (var i = 0; i < organization.rows.length; i++) {
var row = organization.rows[i];
x = left;
var maxHeight = 0;
for (var j = 0; j < row.length; j++) {
var lnode = row[j];
lnode.rect.x = x; // + lnode.rect.width / 2;
lnode.rect.y = y; // + lnode.rect.height / 2;
x += lnode.rect.width + organization.horizontalPadding;
if (lnode.rect.height > maxHeight) maxHeight = lnode.rect.height;
}
y += maxHeight + organization.verticalPadding;
}
};
CoSELayout.prototype.tileCompoundMembers = function (childGraphMap, idToNode) {
var self = this;
this.tiledMemberPack = [];
Object.keys(childGraphMap).forEach(function (id) {
// Get the compound node
var compoundNode = idToNode[id];
self.tiledMemberPack[id] = self.tileNodes(childGraphMap[id], compoundNode.paddingLeft + compoundNode.paddingRight);
compoundNode.rect.width = self.tiledMemberPack[id].width;
compoundNode.rect.height = self.tiledMemberPack[id].height;
});
};
CoSELayout.prototype.tileNodes = function (nodes, minWidth) {
var verticalPadding = CoSEConstants.TILING_PADDING_VERTICAL;
var horizontalPadding = CoSEConstants.TILING_PADDING_HORIZONTAL;
var organization = {
rows: [],
rowWidth: [],
rowHeight: [],
width: 0,
height: minWidth, // assume minHeight equals to minWidth
verticalPadding: verticalPadding,
horizontalPadding: horizontalPadding
};
// Sort the nodes in ascending order of their areas
nodes.sort(function (n1, n2) {
if (n1.rect.width * n1.rect.height > n2.rect.width * n2.rect.height) return -1;
if (n1.rect.width * n1.rect.height < n2.rect.width * n2.rect.height) return 1;
return 0;
});
// Create the organization -> tile members
for (var i = 0; i < nodes.length; i++) {
var lNode = nodes[i];
if (organization.rows.length == 0) {
this.insertNodeToRow(organization, lNode, 0, minWidth);
} else if (this.canAddHorizontal(organization, lNode.rect.width, lNode.rect.height)) {
this.insertNodeToRow(organization, lNode, this.getShortestRowIndex(organization), minWidth);
} else {
this.insertNodeToRow(organization, lNode, organization.rows.length, minWidth);
}
this.shiftToLastRow(organization);
}
return organization;
};
CoSELayout.prototype.insertNodeToRow = function (organization, node, rowIndex, minWidth) {
var minCompoundSize = minWidth;
// Add new row if needed
if (rowIndex == organization.rows.length) {
var secondDimension = [];
organization.rows.push(secondDimension);
organization.rowWidth.push(minCompoundSize);
organization.rowHeight.push(0);
}
// Update row width
var w = organization.rowWidth[rowIndex] + node.rect.width;
if (organization.rows[rowIndex].length > 0) {
w += organization.horizontalPadding;
}
organization.rowWidth[rowIndex] = w;
// Update compound width
if (organization.width < w) {
organization.width = w;
}
// Update height
var h = node.rect.height;
if (rowIndex > 0) h += organization.verticalPadding;
var extraHeight = 0;
if (h > organization.rowHeight[rowIndex]) {
extraHeight = organization.rowHeight[rowIndex];
organization.rowHeight[rowIndex] = h;
extraHeight = organization.rowHeight[rowIndex] - extraHeight;
}
organization.height += extraHeight;
// Insert node
organization.rows[rowIndex].push(node);
};
//Scans the rows of an organization and returns the one with the min width
CoSELayout.prototype.getShortestRowIndex = function (organization) {
var r = -1;
var min = Number.MAX_VALUE;
for (var i = 0; i < organization.rows.length; i++) {
if (organization.rowWidth[i] < min) {
r = i;
min = organization.rowWidth[i];
}
}
return r;
};
//Scans the rows of an organization and returns the one with the max width
CoSELayout.prototype.getLongestRowIndex = function (organization) {
var r = -1;
var max = Number.MIN_VALUE;
for (var i = 0; i < organization.rows.length; i++) {
if (organization.rowWidth[i] > max) {
r = i;
max = organization.rowWidth[i];
}
}
return r;
};
/**
* This method checks whether adding extra width to the organization violates
* the aspect ratio(1) or not.
*/
CoSELayout.prototype.canAddHorizontal = function (organization, extraWidth, extraHeight) {
var sri = this.getShortestRowIndex(organization);
if (sri < 0) {
return true;
}
var min = organization.rowWidth[sri];
if (min + organization.horizontalPadding + extraWidth <= organization.width) return true;
var hDiff = 0;
// Adding to an existing row
if (organization.rowHeight[sri] < extraHeight) {
if (sri > 0) hDiff = extraHeight + organization.verticalPadding - organization.rowHeight[sri];
}
var add_to_row_ratio;
if (organization.width - min >= extraWidth + organization.horizontalPadding) {
add_to_row_ratio = (organization.height + hDiff) / (min + extraWidth + organization.horizontalPadding);
} else {
add_to_row_ratio = (organization.height + hDiff) / organization.width;
}
// Adding a new row for this node
hDiff = extraHeight + organization.verticalPadding;
var add_new_row_ratio;
if (organization.width < extraWidth) {
add_new_row_ratio = (organization.height + hDiff) / extraWidth;
} else {
add_new_row_ratio = (organization.height + hDiff) / organization.width;
}
if (add_new_row_ratio < 1) add_new_row_ratio = 1 / add_new_row_ratio;
if (add_to_row_ratio < 1) add_to_row_ratio = 1 / add_to_row_ratio;
return add_to_row_ratio < add_new_row_ratio;
};
//If moving the last node from the longest row and adding it to the last
//row makes the bounding box smaller, do it.
CoSELayout.prototype.shiftToLastRow = function (organization) {
var longest = this.getLongestRowIndex(organization);
var last = organization.rowWidth.length - 1;
var row = organization.rows[longest];
var node = row[row.length - 1];
var diff = node.width + organization.horizontalPadding;
// Check if there is enough space on the last row
if (organization.width - organization.rowWidth[last] > diff && longest != last) {
// Remove the last element of the longest row
row.splice(-1, 1);
// Push it to the last row
organization.rows[last].push(node);
organization.rowWidth[longest] = organization.rowWidth[longest] - diff;
organization.rowWidth[last] = organization.rowWidth[last] + diff;
organization.width = organization.rowWidth[instance.getLongestRowIndex(organization)];
// Update heights of the organization
var maxHeight = Number.MIN_VALUE;
for (var i = 0; i < row.length; i++) {
if (row[i].height > maxHeight) maxHeight = row[i].height;
}
if (longest > 0) maxHeight += organization.verticalPadding;
var prevTotal = organization.rowHeight[longest] + organization.rowHeight[last];
organization.rowHeight[longest] = maxHeight;
if (organization.rowHeight[last] < node.height + organization.verticalPadding) organization.rowHeight[last] = node.height + organization.verticalPadding;
var finalTotal = organization.rowHeight[longest] + organization.rowHeight[last];
organization.height += finalTotal - prevTotal;
this.shiftToLastRow(organization);
}
};
CoSELayout.prototype.tilingPreLayout = function () {
if (CoSEConstants.TILE) {
// Find zero degree nodes and create a compound for each level
this.groupZeroDegreeMembers();
// Tile and clear children of each compound
this.clearCompounds();
// Separately tile and clear zero degree nodes for each level
this.clearZeroDegreeMembers();
}
};
CoSELayout.prototype.tilingPostLayout = function () {
if (CoSEConstants.TILE) {
this.repopulateZeroDegreeMembers();
this.repopulateCompounds();
}
};
// -----------------------------------------------------------------------------
// Section: Tree Reduction methods
// -----------------------------------------------------------------------------
// Reduce trees
CoSELayout.prototype.reduceTrees = function () {
var prunedNodesAll = [];
var containsLeaf = true;
var node;
while (containsLeaf) {
var allNodes = this.graphManager.getAllNodes();
var prunedNodesInStepTemp = [];
containsLeaf = false;
for (var i = 0; i < allNodes.length; i++) {
node = allNodes[i];
if (node.getEdges().length == 1 && !node.getEdges()[0].isInterGraph && node.getChild() == null) {
prunedNodesInStepTemp.push([node, node.getEdges()[0], node.getOwner()]);
containsLeaf = true;
}
}
if (containsLeaf == true) {
var prunedNodesInStep = [];
for (var j = 0; j < prunedNodesInStepTemp.length; j++) {
if (prunedNodesInStepTemp[j][0].getEdges().length == 1) {
prunedNodesInStep.push(prunedNodesInStepTemp[j]);
prunedNodesInStepTemp[j][0].getOwner().remove(prunedNodesInStepTemp[j][0]);
}
}
prunedNodesAll.push(prunedNodesInStep);
this.graphManager.resetAllNodes();
this.graphManager.resetAllEdges();
}
}
this.prunedNodesAll = prunedNodesAll;
};
// Grow tree one step
CoSELayout.prototype.growTree = function (prunedNodesAll) {
var lengthOfPrunedNodesInStep = prunedNodesAll.length;
var prunedNodesInStep = prunedNodesAll[lengthOfPrunedNodesInStep - 1];
var nodeData;
for (var i = 0; i < prunedNodesInStep.length; i++) {
nodeData = prunedNodesInStep[i];
this.findPlaceforPrunedNode(nodeData);
nodeData[2].add(nodeData[0]);
nodeData[2].add(nodeData[1], nodeData[1].source, nodeData[1].target);
}
prunedNodesAll.splice(prunedNodesAll.length - 1, 1);
this.graphManager.resetAllNodes();
this.graphManager.resetAllEdges();
};
// Find an appropriate position to replace pruned node, this method can be improved
CoSELayout.prototype.findPlaceforPrunedNode = function (nodeData) {
var gridForPrunedNode;
var nodeToConnect;
var prunedNode = nodeData[0];
if (prunedNode == nodeData[1].source) {
nodeToConnect = nodeData[1].target;
} else {
nodeToConnect = nodeData[1].source;
}
var startGridX = nodeToConnect.startX;
var finishGridX = nodeToConnect.finishX;
var startGridY = nodeToConnect.startY;
var finishGridY = nodeToConnect.finishY;
var upNodeCount = 0;
var downNodeCount = 0;
var rightNodeCount = 0;
var leftNodeCount = 0;
var controlRegions = [upNodeCount, rightNodeCount, downNodeCount, leftNodeCount];
if (startGridY > 0) {
for (var i = startGridX; i <= finishGridX; i++) {
controlRegions[0] += this.grid[i][startGridY - 1].length + this.grid[i][startGridY].length - 1;
}
}
if (finishGridX < this.grid.length - 1) {
for (var i = startGridY; i <= finishGridY; i++) {
controlRegions[1] += this.grid[finishGridX + 1][i].length + this.grid[finishGridX][i].length - 1;
}
}
if (finishGridY < this.grid[0].length - 1) {
for (var i = startGridX; i <= finishGridX; i++) {
controlRegions[2] += this.grid[i][finishGridY + 1].length + this.grid[i][finishGridY].length - 1;
}
}
if (startGridX > 0) {
for (var i = startGridY; i <= finishGridY; i++) {
controlRegions[3] += this.grid[startGridX - 1][i].length + this.grid[startGridX][i].length - 1;
}
}
var min = Integer.MAX_VALUE;
var minCount;
var minIndex;
for (var j = 0; j < controlRegions.length; j++) {
if (controlRegions[j] < min) {
min = controlRegions[j];
minCount = 1;
minIndex = j;
} else if (controlRegions[j] == min) {
minCount++;
}
}
if (minCount == 3 && min == 0) {
if (controlRegions[0] == 0 && controlRegions[1] == 0 && controlRegions[2] == 0) {
gridForPrunedNode = 1;
} else if (controlRegions[0] == 0 && controlRegions[1] == 0 && controlRegions[3] == 0) {
gridForPrunedNode = 0;
} else if (controlRegions[0] == 0 && controlRegions[2] == 0 && controlRegions[3] == 0) {
gridForPrunedNode = 3;
} else if (controlRegions[1] == 0 && controlRegions[2] == 0 && controlRegions[3] == 0) {
gridForPrunedNode = 2;
}
} else if (minCount == 2 && min == 0) {
var random = Math.floor(Math.random() * 2);
if (controlRegions[0] == 0 && controlRegions[1] == 0) {
;
if (random == 0) {
gridForPrunedNode = 0;
} else {
gridForPrunedNode = 1;
}
} else if (controlRegions[0] == 0 && controlRegions[2] == 0) {
if (random == 0) {
gridForPrunedNode = 0;
} else {
gridForPrunedNode = 2;
}
} else if (controlRegions[0] == 0 && controlRegions[3] == 0) {
if (random == 0) {
gridForPrunedNode = 0;
} else {
gridForPrunedNode = 3;
}
} else if (controlRegions[1] == 0 && controlRegions[2] == 0) {
if (random == 0) {
gridForPrunedNode = 1;
} else {
gridForPrunedNode = 2;
}
} else if (controlRegions[1] == 0 && controlRegions[3] == 0) {
if (random == 0) {
gridForPrunedNode = 1;
} else {
gridForPrunedNode = 3;
}
} else {
if (random == 0) {
gridForPrunedNode = 2;
} else {
gridForPrunedNode = 3;
}
}
} else if (minCount == 4 && min == 0) {
var random = Math.floor(Math.random() * 4);
gridForPrunedNode = random;
} else {
gridForPrunedNode = minIndex;
}
if (gridForPrunedNode == 0) {
prunedNode.setCenter(nodeToConnect.getCenterX(), nodeToConnect.getCenterY() - nodeToConnect.getHeight() / 2 - FDLayoutConstants.DEFAULT_EDGE_LENGTH - prunedNode.getHeight() / 2);
} else if (gridForPrunedNode == 1) {
prunedNode.setCenter(nodeToConnect.getCenterX() + nodeToConnect.getWidth() / 2 + FDLayoutConstants.DEFAULT_EDGE_LENGTH + prunedNode.getWidth() / 2, nodeToConnect.getCenterY());
} else if (gridForPrunedNode == 2) {
prunedNode.setCenter(nodeToConnect.getCenterX(), nodeToConnect.getCenterY() + nodeToConnect.getHeight() / 2 + FDLayoutConstants.DEFAULT_EDGE_LENGTH + prunedNode.getHeight() / 2);
} else {
prunedNode.setCenter(nodeToConnect.getCenterX() - nodeToConnect.getWidth() / 2 - FDLayoutConstants.DEFAULT_EDGE_LENGTH - prunedNode.getWidth() / 2, nodeToConnect.getCenterY());
}
};
module.exports = CoSELayout;
/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var coseBase = {};
coseBase.layoutBase = __webpack_require__(0);
coseBase.CoSEConstants = __webpack_require__(1);
coseBase.CoSEEdge = __webpack_require__(2);
coseBase.CoSEGraph = __webpack_require__(3);
coseBase.CoSEGraphManager = __webpack_require__(4);
coseBase.CoSELayout = __webpack_require__(6);
coseBase.CoSENode = __webpack_require__(5);
module.exports = coseBase;
/***/ })
/******/ ]);
});