cose-base.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447
  1. (function webpackUniversalModuleDefinition(root, factory) {
  2. if(typeof exports === 'object' && typeof module === 'object')
  3. module.exports = factory(require("layout-base"));
  4. else if(typeof define === 'function' && define.amd)
  5. define(["layout-base"], factory);
  6. else if(typeof exports === 'object')
  7. exports["coseBase"] = factory(require("layout-base"));
  8. else
  9. root["coseBase"] = factory(root["layoutBase"]);
  10. })(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {
  11. return /******/ (function(modules) { // webpackBootstrap
  12. /******/ // The module cache
  13. /******/ var installedModules = {};
  14. /******/
  15. /******/ // The require function
  16. /******/ function __webpack_require__(moduleId) {
  17. /******/
  18. /******/ // Check if module is in cache
  19. /******/ if(installedModules[moduleId]) {
  20. /******/ return installedModules[moduleId].exports;
  21. /******/ }
  22. /******/ // Create a new module (and put it into the cache)
  23. /******/ var module = installedModules[moduleId] = {
  24. /******/ i: moduleId,
  25. /******/ l: false,
  26. /******/ exports: {}
  27. /******/ };
  28. /******/
  29. /******/ // Execute the module function
  30. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  31. /******/
  32. /******/ // Flag the module as loaded
  33. /******/ module.l = true;
  34. /******/
  35. /******/ // Return the exports of the module
  36. /******/ return module.exports;
  37. /******/ }
  38. /******/
  39. /******/
  40. /******/ // expose the modules object (__webpack_modules__)
  41. /******/ __webpack_require__.m = modules;
  42. /******/
  43. /******/ // expose the module cache
  44. /******/ __webpack_require__.c = installedModules;
  45. /******/
  46. /******/ // identity function for calling harmony imports with the correct context
  47. /******/ __webpack_require__.i = function(value) { return value; };
  48. /******/
  49. /******/ // define getter function for harmony exports
  50. /******/ __webpack_require__.d = function(exports, name, getter) {
  51. /******/ if(!__webpack_require__.o(exports, name)) {
  52. /******/ Object.defineProperty(exports, name, {
  53. /******/ configurable: false,
  54. /******/ enumerable: true,
  55. /******/ get: getter
  56. /******/ });
  57. /******/ }
  58. /******/ };
  59. /******/
  60. /******/ // getDefaultExport function for compatibility with non-harmony modules
  61. /******/ __webpack_require__.n = function(module) {
  62. /******/ var getter = module && module.__esModule ?
  63. /******/ function getDefault() { return module['default']; } :
  64. /******/ function getModuleExports() { return module; };
  65. /******/ __webpack_require__.d(getter, 'a', getter);
  66. /******/ return getter;
  67. /******/ };
  68. /******/
  69. /******/ // Object.prototype.hasOwnProperty.call
  70. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  71. /******/
  72. /******/ // __webpack_public_path__
  73. /******/ __webpack_require__.p = "";
  74. /******/
  75. /******/ // Load entry module and return exports
  76. /******/ return __webpack_require__(__webpack_require__.s = 7);
  77. /******/ })
  78. /************************************************************************/
  79. /******/ ([
  80. /* 0 */
  81. /***/ (function(module, exports) {
  82. module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
  83. /***/ }),
  84. /* 1 */
  85. /***/ (function(module, exports, __webpack_require__) {
  86. "use strict";
  87. var FDLayoutConstants = __webpack_require__(0).FDLayoutConstants;
  88. function CoSEConstants() {}
  89. //CoSEConstants inherits static props in FDLayoutConstants
  90. for (var prop in FDLayoutConstants) {
  91. CoSEConstants[prop] = FDLayoutConstants[prop];
  92. }
  93. CoSEConstants.DEFAULT_USE_MULTI_LEVEL_SCALING = false;
  94. CoSEConstants.DEFAULT_RADIAL_SEPARATION = FDLayoutConstants.DEFAULT_EDGE_LENGTH;
  95. CoSEConstants.DEFAULT_COMPONENT_SEPERATION = 60;
  96. CoSEConstants.TILE = true;
  97. CoSEConstants.TILING_PADDING_VERTICAL = 10;
  98. CoSEConstants.TILING_PADDING_HORIZONTAL = 10;
  99. CoSEConstants.TREE_REDUCTION_ON_INCREMENTAL = false; // make this true when cose is used incrementally as a part of other non-incremental layout
  100. module.exports = CoSEConstants;
  101. /***/ }),
  102. /* 2 */
  103. /***/ (function(module, exports, __webpack_require__) {
  104. "use strict";
  105. var FDLayoutEdge = __webpack_require__(0).FDLayoutEdge;
  106. function CoSEEdge(source, target, vEdge) {
  107. FDLayoutEdge.call(this, source, target, vEdge);
  108. }
  109. CoSEEdge.prototype = Object.create(FDLayoutEdge.prototype);
  110. for (var prop in FDLayoutEdge) {
  111. CoSEEdge[prop] = FDLayoutEdge[prop];
  112. }
  113. module.exports = CoSEEdge;
  114. /***/ }),
  115. /* 3 */
  116. /***/ (function(module, exports, __webpack_require__) {
  117. "use strict";
  118. var LGraph = __webpack_require__(0).LGraph;
  119. function CoSEGraph(parent, graphMgr, vGraph) {
  120. LGraph.call(this, parent, graphMgr, vGraph);
  121. }
  122. CoSEGraph.prototype = Object.create(LGraph.prototype);
  123. for (var prop in LGraph) {
  124. CoSEGraph[prop] = LGraph[prop];
  125. }
  126. module.exports = CoSEGraph;
  127. /***/ }),
  128. /* 4 */
  129. /***/ (function(module, exports, __webpack_require__) {
  130. "use strict";
  131. var LGraphManager = __webpack_require__(0).LGraphManager;
  132. function CoSEGraphManager(layout) {
  133. LGraphManager.call(this, layout);
  134. }
  135. CoSEGraphManager.prototype = Object.create(LGraphManager.prototype);
  136. for (var prop in LGraphManager) {
  137. CoSEGraphManager[prop] = LGraphManager[prop];
  138. }
  139. module.exports = CoSEGraphManager;
  140. /***/ }),
  141. /* 5 */
  142. /***/ (function(module, exports, __webpack_require__) {
  143. "use strict";
  144. var FDLayoutNode = __webpack_require__(0).FDLayoutNode;
  145. var IMath = __webpack_require__(0).IMath;
  146. function CoSENode(gm, loc, size, vNode) {
  147. FDLayoutNode.call(this, gm, loc, size, vNode);
  148. }
  149. CoSENode.prototype = Object.create(FDLayoutNode.prototype);
  150. for (var prop in FDLayoutNode) {
  151. CoSENode[prop] = FDLayoutNode[prop];
  152. }
  153. CoSENode.prototype.move = function () {
  154. var layout = this.graphManager.getLayout();
  155. this.displacementX = layout.coolingFactor * (this.springForceX + this.repulsionForceX + this.gravitationForceX) / this.noOfChildren;
  156. this.displacementY = layout.coolingFactor * (this.springForceY + this.repulsionForceY + this.gravitationForceY) / this.noOfChildren;
  157. if (Math.abs(this.displacementX) > layout.coolingFactor * layout.maxNodeDisplacement) {
  158. this.displacementX = layout.coolingFactor * layout.maxNodeDisplacement * IMath.sign(this.displacementX);
  159. }
  160. if (Math.abs(this.displacementY) > layout.coolingFactor * layout.maxNodeDisplacement) {
  161. this.displacementY = layout.coolingFactor * layout.maxNodeDisplacement * IMath.sign(this.displacementY);
  162. }
  163. // a simple node, just move it
  164. if (this.child == null) {
  165. this.moveBy(this.displacementX, this.displacementY);
  166. }
  167. // an empty compound node, again just move it
  168. else if (this.child.getNodes().length == 0) {
  169. this.moveBy(this.displacementX, this.displacementY);
  170. }
  171. // non-empty compound node, propogate movement to children as well
  172. else {
  173. this.propogateDisplacementToChildren(this.displacementX, this.displacementY);
  174. }
  175. layout.totalDisplacement += Math.abs(this.displacementX) + Math.abs(this.displacementY);
  176. this.springForceX = 0;
  177. this.springForceY = 0;
  178. this.repulsionForceX = 0;
  179. this.repulsionForceY = 0;
  180. this.gravitationForceX = 0;
  181. this.gravitationForceY = 0;
  182. this.displacementX = 0;
  183. this.displacementY = 0;
  184. };
  185. CoSENode.prototype.propogateDisplacementToChildren = function (dX, dY) {
  186. var nodes = this.getChild().getNodes();
  187. var node;
  188. for (var i = 0; i < nodes.length; i++) {
  189. node = nodes[i];
  190. if (node.getChild() == null) {
  191. node.moveBy(dX, dY);
  192. node.displacementX += dX;
  193. node.displacementY += dY;
  194. } else {
  195. node.propogateDisplacementToChildren(dX, dY);
  196. }
  197. }
  198. };
  199. CoSENode.prototype.setPred1 = function (pred1) {
  200. this.pred1 = pred1;
  201. };
  202. CoSENode.prototype.getPred1 = function () {
  203. return pred1;
  204. };
  205. CoSENode.prototype.getPred2 = function () {
  206. return pred2;
  207. };
  208. CoSENode.prototype.setNext = function (next) {
  209. this.next = next;
  210. };
  211. CoSENode.prototype.getNext = function () {
  212. return next;
  213. };
  214. CoSENode.prototype.setProcessed = function (processed) {
  215. this.processed = processed;
  216. };
  217. CoSENode.prototype.isProcessed = function () {
  218. return processed;
  219. };
  220. module.exports = CoSENode;
  221. /***/ }),
  222. /* 6 */
  223. /***/ (function(module, exports, __webpack_require__) {
  224. "use strict";
  225. var FDLayout = __webpack_require__(0).FDLayout;
  226. var CoSEGraphManager = __webpack_require__(4);
  227. var CoSEGraph = __webpack_require__(3);
  228. var CoSENode = __webpack_require__(5);
  229. var CoSEEdge = __webpack_require__(2);
  230. var CoSEConstants = __webpack_require__(1);
  231. var FDLayoutConstants = __webpack_require__(0).FDLayoutConstants;
  232. var LayoutConstants = __webpack_require__(0).LayoutConstants;
  233. var Point = __webpack_require__(0).Point;
  234. var PointD = __webpack_require__(0).PointD;
  235. var Layout = __webpack_require__(0).Layout;
  236. var Integer = __webpack_require__(0).Integer;
  237. var IGeometry = __webpack_require__(0).IGeometry;
  238. var LGraph = __webpack_require__(0).LGraph;
  239. var Transform = __webpack_require__(0).Transform;
  240. function CoSELayout() {
  241. FDLayout.call(this);
  242. this.toBeTiled = {}; // Memorize if a node is to be tiled or is tiled
  243. }
  244. CoSELayout.prototype = Object.create(FDLayout.prototype);
  245. for (var prop in FDLayout) {
  246. CoSELayout[prop] = FDLayout[prop];
  247. }
  248. CoSELayout.prototype.newGraphManager = function () {
  249. var gm = new CoSEGraphManager(this);
  250. this.graphManager = gm;
  251. return gm;
  252. };
  253. CoSELayout.prototype.newGraph = function (vGraph) {
  254. return new CoSEGraph(null, this.graphManager, vGraph);
  255. };
  256. CoSELayout.prototype.newNode = function (vNode) {
  257. return new CoSENode(this.graphManager, vNode);
  258. };
  259. CoSELayout.prototype.newEdge = function (vEdge) {
  260. return new CoSEEdge(null, null, vEdge);
  261. };
  262. CoSELayout.prototype.initParameters = function () {
  263. FDLayout.prototype.initParameters.call(this, arguments);
  264. if (!this.isSubLayout) {
  265. if (CoSEConstants.DEFAULT_EDGE_LENGTH < 10) {
  266. this.idealEdgeLength = 10;
  267. } else {
  268. this.idealEdgeLength = CoSEConstants.DEFAULT_EDGE_LENGTH;
  269. }
  270. this.useSmartIdealEdgeLengthCalculation = CoSEConstants.DEFAULT_USE_SMART_IDEAL_EDGE_LENGTH_CALCULATION;
  271. this.springConstant = FDLayoutConstants.DEFAULT_SPRING_STRENGTH;
  272. this.repulsionConstant = FDLayoutConstants.DEFAULT_REPULSION_STRENGTH;
  273. this.gravityConstant = FDLayoutConstants.DEFAULT_GRAVITY_STRENGTH;
  274. this.compoundGravityConstant = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_STRENGTH;
  275. this.gravityRangeFactor = FDLayoutConstants.DEFAULT_GRAVITY_RANGE_FACTOR;
  276. this.compoundGravityRangeFactor = FDLayoutConstants.DEFAULT_COMPOUND_GRAVITY_RANGE_FACTOR;
  277. // variables for tree reduction support
  278. this.prunedNodesAll = [];
  279. this.growTreeIterations = 0;
  280. this.afterGrowthIterations = 0;
  281. this.isTreeGrowing = false;
  282. this.isGrowthFinished = false;
  283. // variables for cooling
  284. this.coolingCycle = 0;
  285. this.maxCoolingCycle = this.maxIterations / FDLayoutConstants.CONVERGENCE_CHECK_PERIOD;
  286. this.finalTemperature = FDLayoutConstants.CONVERGENCE_CHECK_PERIOD / this.maxIterations;
  287. this.coolingAdjuster = 1;
  288. }
  289. };
  290. CoSELayout.prototype.layout = function () {
  291. var createBendsAsNeeded = LayoutConstants.DEFAULT_CREATE_BENDS_AS_NEEDED;
  292. if (createBendsAsNeeded) {
  293. this.createBendpoints();
  294. this.graphManager.resetAllEdges();
  295. }
  296. this.level = 0;
  297. return this.classicLayout();
  298. };
  299. CoSELayout.prototype.classicLayout = function () {
  300. this.nodesWithGravity = this.calculateNodesToApplyGravitationTo();
  301. this.graphManager.setAllNodesToApplyGravitation(this.nodesWithGravity);
  302. this.calcNoOfChildrenForAllNodes();
  303. this.graphManager.calcLowestCommonAncestors();
  304. this.graphManager.calcInclusionTreeDepths();
  305. this.graphManager.getRoot().calcEstimatedSize();
  306. this.calcIdealEdgeLengths();
  307. if (!this.incremental) {
  308. var forest = this.getFlatForest();
  309. // The graph associated with this layout is flat and a forest
  310. if (forest.length > 0) {
  311. this.positionNodesRadially(forest);
  312. }
  313. // The graph associated with this layout is not flat or a forest
  314. else {
  315. // Reduce the trees when incremental mode is not enabled and graph is not a forest
  316. this.reduceTrees();
  317. // Update nodes that gravity will be applied
  318. this.graphManager.resetAllNodesToApplyGravitation();
  319. var allNodes = new Set(this.getAllNodes());
  320. var intersection = this.nodesWithGravity.filter(function (x) {
  321. return allNodes.has(x);
  322. });
  323. this.graphManager.setAllNodesToApplyGravitation(intersection);
  324. this.positionNodesRandomly();
  325. }
  326. } else {
  327. if (CoSEConstants.TREE_REDUCTION_ON_INCREMENTAL) {
  328. // Reduce the trees in incremental mode if only this constant is set to true
  329. this.reduceTrees();
  330. // Update nodes that gravity will be applied
  331. this.graphManager.resetAllNodesToApplyGravitation();
  332. var allNodes = new Set(this.getAllNodes());
  333. var intersection = this.nodesWithGravity.filter(function (x) {
  334. return allNodes.has(x);
  335. });
  336. this.graphManager.setAllNodesToApplyGravitation(intersection);
  337. }
  338. }
  339. this.initSpringEmbedder();
  340. this.runSpringEmbedder();
  341. return true;
  342. };
  343. CoSELayout.prototype.tick = function () {
  344. this.totalIterations++;
  345. if (this.totalIterations === this.maxIterations && !this.isTreeGrowing && !this.isGrowthFinished) {
  346. if (this.prunedNodesAll.length > 0) {
  347. this.isTreeGrowing = true;
  348. } else {
  349. return true;
  350. }
  351. }
  352. if (this.totalIterations % FDLayoutConstants.CONVERGENCE_CHECK_PERIOD == 0 && !this.isTreeGrowing && !this.isGrowthFinished) {
  353. if (this.isConverged()) {
  354. if (this.prunedNodesAll.length > 0) {
  355. this.isTreeGrowing = true;
  356. } else {
  357. return true;
  358. }
  359. }
  360. this.coolingCycle++;
  361. if (this.layoutQuality == 0) {
  362. // quality - "draft"
  363. this.coolingAdjuster = this.coolingCycle;
  364. } else if (this.layoutQuality == 1) {
  365. // quality - "default"
  366. this.coolingAdjuster = this.coolingCycle / 3;
  367. }
  368. // cooling schedule is based on http://www.btluke.com/simanf1.html -> cooling schedule 3
  369. 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);
  370. this.animationPeriod = Math.ceil(this.initialAnimationPeriod * Math.sqrt(this.coolingFactor));
  371. }
  372. // Operations while tree is growing again
  373. if (this.isTreeGrowing) {
  374. if (this.growTreeIterations % 10 == 0) {
  375. if (this.prunedNodesAll.length > 0) {
  376. this.graphManager.updateBounds();
  377. this.updateGrid();
  378. this.growTree(this.prunedNodesAll);
  379. // Update nodes that gravity will be applied
  380. this.graphManager.resetAllNodesToApplyGravitation();
  381. var allNodes = new Set(this.getAllNodes());
  382. var intersection = this.nodesWithGravity.filter(function (x) {
  383. return allNodes.has(x);
  384. });
  385. this.graphManager.setAllNodesToApplyGravitation(intersection);
  386. this.graphManager.updateBounds();
  387. this.updateGrid();
  388. this.coolingFactor = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL;
  389. } else {
  390. this.isTreeGrowing = false;
  391. this.isGrowthFinished = true;
  392. }
  393. }
  394. this.growTreeIterations++;
  395. }
  396. // Operations after growth is finished
  397. if (this.isGrowthFinished) {
  398. if (this.isConverged()) {
  399. return true;
  400. }
  401. if (this.afterGrowthIterations % 10 == 0) {
  402. this.graphManager.updateBounds();
  403. this.updateGrid();
  404. }
  405. this.coolingFactor = FDLayoutConstants.DEFAULT_COOLING_FACTOR_INCREMENTAL * ((100 - this.afterGrowthIterations) / 100);
  406. this.afterGrowthIterations++;
  407. }
  408. var gridUpdateAllowed = !this.isTreeGrowing && !this.isGrowthFinished;
  409. var forceToNodeSurroundingUpdate = this.growTreeIterations % 10 == 1 && this.isTreeGrowing || this.afterGrowthIterations % 10 == 1 && this.isGrowthFinished;
  410. this.totalDisplacement = 0;
  411. this.graphManager.updateBounds();
  412. this.calcSpringForces();
  413. this.calcRepulsionForces(gridUpdateAllowed, forceToNodeSurroundingUpdate);
  414. this.calcGravitationalForces();
  415. this.moveNodes();
  416. this.animate();
  417. return false; // Layout is not ended yet return false
  418. };
  419. CoSELayout.prototype.getPositionsData = function () {
  420. var allNodes = this.graphManager.getAllNodes();
  421. var pData = {};
  422. for (var i = 0; i < allNodes.length; i++) {
  423. var rect = allNodes[i].rect;
  424. var id = allNodes[i].id;
  425. pData[id] = {
  426. id: id,
  427. x: rect.getCenterX(),
  428. y: rect.getCenterY(),
  429. w: rect.width,
  430. h: rect.height
  431. };
  432. }
  433. return pData;
  434. };
  435. CoSELayout.prototype.runSpringEmbedder = function () {
  436. this.initialAnimationPeriod = 25;
  437. this.animationPeriod = this.initialAnimationPeriod;
  438. var layoutEnded = false;
  439. // If aminate option is 'during' signal that layout is supposed to start iterating
  440. if (FDLayoutConstants.ANIMATE === 'during') {
  441. this.emit('layoutstarted');
  442. } else {
  443. // If aminate option is 'during' tick() function will be called on index.js
  444. while (!layoutEnded) {
  445. layoutEnded = this.tick();
  446. }
  447. this.graphManager.updateBounds();
  448. }
  449. };
  450. CoSELayout.prototype.calculateNodesToApplyGravitationTo = function () {
  451. var nodeList = [];
  452. var graph;
  453. var graphs = this.graphManager.getGraphs();
  454. var size = graphs.length;
  455. var i;
  456. for (i = 0; i < size; i++) {
  457. graph = graphs[i];
  458. graph.updateConnected();
  459. if (!graph.isConnected) {
  460. nodeList = nodeList.concat(graph.getNodes());
  461. }
  462. }
  463. return nodeList;
  464. };
  465. CoSELayout.prototype.createBendpoints = function () {
  466. var edges = [];
  467. edges = edges.concat(this.graphManager.getAllEdges());
  468. var visited = new Set();
  469. var i;
  470. for (i = 0; i < edges.length; i++) {
  471. var edge = edges[i];
  472. if (!visited.has(edge)) {
  473. var source = edge.getSource();
  474. var target = edge.getTarget();
  475. if (source == target) {
  476. edge.getBendpoints().push(new PointD());
  477. edge.getBendpoints().push(new PointD());
  478. this.createDummyNodesForBendpoints(edge);
  479. visited.add(edge);
  480. } else {
  481. var edgeList = [];
  482. edgeList = edgeList.concat(source.getEdgeListToNode(target));
  483. edgeList = edgeList.concat(target.getEdgeListToNode(source));
  484. if (!visited.has(edgeList[0])) {
  485. if (edgeList.length > 1) {
  486. var k;
  487. for (k = 0; k < edgeList.length; k++) {
  488. var multiEdge = edgeList[k];
  489. multiEdge.getBendpoints().push(new PointD());
  490. this.createDummyNodesForBendpoints(multiEdge);
  491. }
  492. }
  493. edgeList.forEach(function (edge) {
  494. visited.add(edge);
  495. });
  496. }
  497. }
  498. }
  499. if (visited.size == edges.length) {
  500. break;
  501. }
  502. }
  503. };
  504. CoSELayout.prototype.positionNodesRadially = function (forest) {
  505. // We tile the trees to a grid row by row; first tree starts at (0,0)
  506. var currentStartingPoint = new Point(0, 0);
  507. var numberOfColumns = Math.ceil(Math.sqrt(forest.length));
  508. var height = 0;
  509. var currentY = 0;
  510. var currentX = 0;
  511. var point = new PointD(0, 0);
  512. for (var i = 0; i < forest.length; i++) {
  513. if (i % numberOfColumns == 0) {
  514. // Start of a new row, make the x coordinate 0, increment the
  515. // y coordinate with the max height of the previous row
  516. currentX = 0;
  517. currentY = height;
  518. if (i != 0) {
  519. currentY += CoSEConstants.DEFAULT_COMPONENT_SEPERATION;
  520. }
  521. height = 0;
  522. }
  523. var tree = forest[i];
  524. // Find the center of the tree
  525. var centerNode = Layout.findCenterOfTree(tree);
  526. // Set the staring point of the next tree
  527. currentStartingPoint.x = currentX;
  528. currentStartingPoint.y = currentY;
  529. // Do a radial layout starting with the center
  530. point = CoSELayout.radialLayout(tree, centerNode, currentStartingPoint);
  531. if (point.y > height) {
  532. height = Math.floor(point.y);
  533. }
  534. currentX = Math.floor(point.x + CoSEConstants.DEFAULT_COMPONENT_SEPERATION);
  535. }
  536. this.transform(new PointD(LayoutConstants.WORLD_CENTER_X - point.x / 2, LayoutConstants.WORLD_CENTER_Y - point.y / 2));
  537. };
  538. CoSELayout.radialLayout = function (tree, centerNode, startingPoint) {
  539. var radialSep = Math.max(this.maxDiagonalInTree(tree), CoSEConstants.DEFAULT_RADIAL_SEPARATION);
  540. CoSELayout.branchRadialLayout(centerNode, null, 0, 359, 0, radialSep);
  541. var bounds = LGraph.calculateBounds(tree);
  542. var transform = new Transform();
  543. transform.setDeviceOrgX(bounds.getMinX());
  544. transform.setDeviceOrgY(bounds.getMinY());
  545. transform.setWorldOrgX(startingPoint.x);
  546. transform.setWorldOrgY(startingPoint.y);
  547. for (var i = 0; i < tree.length; i++) {
  548. var node = tree[i];
  549. node.transform(transform);
  550. }
  551. var bottomRight = new PointD(bounds.getMaxX(), bounds.getMaxY());
  552. return transform.inverseTransformPoint(bottomRight);
  553. };
  554. CoSELayout.branchRadialLayout = function (node, parentOfNode, startAngle, endAngle, distance, radialSeparation) {
  555. // First, position this node by finding its angle.
  556. var halfInterval = (endAngle - startAngle + 1) / 2;
  557. if (halfInterval < 0) {
  558. halfInterval += 180;
  559. }
  560. var nodeAngle = (halfInterval + startAngle) % 360;
  561. var teta = nodeAngle * IGeometry.TWO_PI / 360;
  562. // Make polar to java cordinate conversion.
  563. var cos_teta = Math.cos(teta);
  564. var x_ = distance * Math.cos(teta);
  565. var y_ = distance * Math.sin(teta);
  566. node.setCenter(x_, y_);
  567. // Traverse all neighbors of this node and recursively call this
  568. // function.
  569. var neighborEdges = [];
  570. neighborEdges = neighborEdges.concat(node.getEdges());
  571. var childCount = neighborEdges.length;
  572. if (parentOfNode != null) {
  573. childCount--;
  574. }
  575. var branchCount = 0;
  576. var incEdgesCount = neighborEdges.length;
  577. var startIndex;
  578. var edges = node.getEdgesBetween(parentOfNode);
  579. // If there are multiple edges, prune them until there remains only one
  580. // edge.
  581. while (edges.length > 1) {
  582. //neighborEdges.remove(edges.remove(0));
  583. var temp = edges[0];
  584. edges.splice(0, 1);
  585. var index = neighborEdges.indexOf(temp);
  586. if (index >= 0) {
  587. neighborEdges.splice(index, 1);
  588. }
  589. incEdgesCount--;
  590. childCount--;
  591. }
  592. if (parentOfNode != null) {
  593. //assert edges.length == 1;
  594. startIndex = (neighborEdges.indexOf(edges[0]) + 1) % incEdgesCount;
  595. } else {
  596. startIndex = 0;
  597. }
  598. var stepAngle = Math.abs(endAngle - startAngle) / childCount;
  599. for (var i = startIndex; branchCount != childCount; i = ++i % incEdgesCount) {
  600. var currentNeighbor = neighborEdges[i].getOtherEnd(node);
  601. // Don't back traverse to root node in current tree.
  602. if (currentNeighbor == parentOfNode) {
  603. continue;
  604. }
  605. var childStartAngle = (startAngle + branchCount * stepAngle) % 360;
  606. var childEndAngle = (childStartAngle + stepAngle) % 360;
  607. CoSELayout.branchRadialLayout(currentNeighbor, node, childStartAngle, childEndAngle, distance + radialSeparation, radialSeparation);
  608. branchCount++;
  609. }
  610. };
  611. CoSELayout.maxDiagonalInTree = function (tree) {
  612. var maxDiagonal = Integer.MIN_VALUE;
  613. for (var i = 0; i < tree.length; i++) {
  614. var node = tree[i];
  615. var diagonal = node.getDiagonal();
  616. if (diagonal > maxDiagonal) {
  617. maxDiagonal = diagonal;
  618. }
  619. }
  620. return maxDiagonal;
  621. };
  622. CoSELayout.prototype.calcRepulsionRange = function () {
  623. // formula is 2 x (level + 1) x idealEdgeLength
  624. return 2 * (this.level + 1) * this.idealEdgeLength;
  625. };
  626. // Tiling methods
  627. // 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
  628. CoSELayout.prototype.groupZeroDegreeMembers = function () {
  629. var self = this;
  630. // array of [parent_id x oneDegreeNode_id]
  631. var tempMemberGroups = {}; // A temporary map of parent node and its zero degree members
  632. this.memberGroups = {}; // A map of dummy parent node and its zero degree members whose parents are not to be tiled
  633. this.idToDummyNode = {}; // A map of id to dummy node
  634. var zeroDegree = []; // List of zero degree nodes whose parents are not to be tiled
  635. var allNodes = this.graphManager.getAllNodes();
  636. // Fill zero degree list
  637. for (var i = 0; i < allNodes.length; i++) {
  638. var node = allNodes[i];
  639. var parent = node.getParent();
  640. // If a node has zero degree and its parent is not to be tiled if exists add that node to zeroDegres list
  641. if (this.getNodeDegreeWithChildren(node) === 0 && (parent.id == undefined || !this.getToBeTiled(parent))) {
  642. zeroDegree.push(node);
  643. }
  644. }
  645. // Create a map of parent node and its zero degree members
  646. for (var i = 0; i < zeroDegree.length; i++) {
  647. var node = zeroDegree[i]; // Zero degree node itself
  648. var p_id = node.getParent().id; // Parent id
  649. if (typeof tempMemberGroups[p_id] === "undefined") tempMemberGroups[p_id] = [];
  650. tempMemberGroups[p_id] = tempMemberGroups[p_id].concat(node); // Push node to the list belongs to its parent in tempMemberGroups
  651. }
  652. // If there are at least two nodes at a level, create a dummy compound for them
  653. Object.keys(tempMemberGroups).forEach(function (p_id) {
  654. if (tempMemberGroups[p_id].length > 1) {
  655. var dummyCompoundId = "DummyCompound_" + p_id; // The id of dummy compound which will be created soon
  656. self.memberGroups[dummyCompoundId] = tempMemberGroups[p_id]; // Add dummy compound to memberGroups
  657. var parent = tempMemberGroups[p_id][0].getParent(); // The parent of zero degree nodes will be the parent of new dummy compound
  658. // Create a dummy compound with calculated id
  659. var dummyCompound = new CoSENode(self.graphManager);
  660. dummyCompound.id = dummyCompoundId;
  661. dummyCompound.paddingLeft = parent.paddingLeft || 0;
  662. dummyCompound.paddingRight = parent.paddingRight || 0;
  663. dummyCompound.paddingBottom = parent.paddingBottom || 0;
  664. dummyCompound.paddingTop = parent.paddingTop || 0;
  665. self.idToDummyNode[dummyCompoundId] = dummyCompound;
  666. var dummyParentGraph = self.getGraphManager().add(self.newGraph(), dummyCompound);
  667. var parentGraph = parent.getChild();
  668. // Add dummy compound to parent the graph
  669. parentGraph.add(dummyCompound);
  670. // For each zero degree node in this level remove it from its parent graph and add it to the graph of dummy parent
  671. for (var i = 0; i < tempMemberGroups[p_id].length; i++) {
  672. var node = tempMemberGroups[p_id][i];
  673. parentGraph.remove(node);
  674. dummyParentGraph.add(node);
  675. }
  676. }
  677. });
  678. };
  679. CoSELayout.prototype.clearCompounds = function () {
  680. var childGraphMap = {};
  681. var idToNode = {};
  682. // Get compound ordering by finding the inner one first
  683. this.performDFSOnCompounds();
  684. for (var i = 0; i < this.compoundOrder.length; i++) {
  685. idToNode[this.compoundOrder[i].id] = this.compoundOrder[i];
  686. childGraphMap[this.compoundOrder[i].id] = [].concat(this.compoundOrder[i].getChild().getNodes());
  687. // Remove children of compounds
  688. this.graphManager.remove(this.compoundOrder[i].getChild());
  689. this.compoundOrder[i].child = null;
  690. }
  691. this.graphManager.resetAllNodes();
  692. // Tile the removed children
  693. this.tileCompoundMembers(childGraphMap, idToNode);
  694. };
  695. CoSELayout.prototype.clearZeroDegreeMembers = function () {
  696. var self = this;
  697. var tiledZeroDegreePack = this.tiledZeroDegreePack = [];
  698. Object.keys(this.memberGroups).forEach(function (id) {
  699. var compoundNode = self.idToDummyNode[id]; // Get the dummy compound
  700. tiledZeroDegreePack[id] = self.tileNodes(self.memberGroups[id], compoundNode.paddingLeft + compoundNode.paddingRight);
  701. // Set the width and height of the dummy compound as calculated
  702. compoundNode.rect.width = tiledZeroDegreePack[id].width;
  703. compoundNode.rect.height = tiledZeroDegreePack[id].height;
  704. });
  705. };
  706. CoSELayout.prototype.repopulateCompounds = function () {
  707. for (var i = this.compoundOrder.length - 1; i >= 0; i--) {
  708. var lCompoundNode = this.compoundOrder[i];
  709. var id = lCompoundNode.id;
  710. var horizontalMargin = lCompoundNode.paddingLeft;
  711. var verticalMargin = lCompoundNode.paddingTop;
  712. this.adjustLocations(this.tiledMemberPack[id], lCompoundNode.rect.x, lCompoundNode.rect.y, horizontalMargin, verticalMargin);
  713. }
  714. };
  715. CoSELayout.prototype.repopulateZeroDegreeMembers = function () {
  716. var self = this;
  717. var tiledPack = this.tiledZeroDegreePack;
  718. Object.keys(tiledPack).forEach(function (id) {
  719. var compoundNode = self.idToDummyNode[id]; // Get the dummy compound by its id
  720. var horizontalMargin = compoundNode.paddingLeft;
  721. var verticalMargin = compoundNode.paddingTop;
  722. // Adjust the positions of nodes wrt its compound
  723. self.adjustLocations(tiledPack[id], compoundNode.rect.x, compoundNode.rect.y, horizontalMargin, verticalMargin);
  724. });
  725. };
  726. CoSELayout.prototype.getToBeTiled = function (node) {
  727. var id = node.id;
  728. //firstly check the previous results
  729. if (this.toBeTiled[id] != null) {
  730. return this.toBeTiled[id];
  731. }
  732. //only compound nodes are to be tiled
  733. var childGraph = node.getChild();
  734. if (childGraph == null) {
  735. this.toBeTiled[id] = false;
  736. return false;
  737. }
  738. var children = childGraph.getNodes(); // Get the children nodes
  739. //a compound node is not to be tiled if all of its compound children are not to be tiled
  740. for (var i = 0; i < children.length; i++) {
  741. var theChild = children[i];
  742. if (this.getNodeDegree(theChild) > 0) {
  743. this.toBeTiled[id] = false;
  744. return false;
  745. }
  746. //pass the children not having the compound structure
  747. if (theChild.getChild() == null) {
  748. this.toBeTiled[theChild.id] = false;
  749. continue;
  750. }
  751. if (!this.getToBeTiled(theChild)) {
  752. this.toBeTiled[id] = false;
  753. return false;
  754. }
  755. }
  756. this.toBeTiled[id] = true;
  757. return true;
  758. };
  759. // Get degree of a node depending of its edges and independent of its children
  760. CoSELayout.prototype.getNodeDegree = function (node) {
  761. var id = node.id;
  762. var edges = node.getEdges();
  763. var degree = 0;
  764. // For the edges connected
  765. for (var i = 0; i < edges.length; i++) {
  766. var edge = edges[i];
  767. if (edge.getSource().id !== edge.getTarget().id) {
  768. degree = degree + 1;
  769. }
  770. }
  771. return degree;
  772. };
  773. // Get degree of a node with its children
  774. CoSELayout.prototype.getNodeDegreeWithChildren = function (node) {
  775. var degree = this.getNodeDegree(node);
  776. if (node.getChild() == null) {
  777. return degree;
  778. }
  779. var children = node.getChild().getNodes();
  780. for (var i = 0; i < children.length; i++) {
  781. var child = children[i];
  782. degree += this.getNodeDegreeWithChildren(child);
  783. }
  784. return degree;
  785. };
  786. CoSELayout.prototype.performDFSOnCompounds = function () {
  787. this.compoundOrder = [];
  788. this.fillCompexOrderByDFS(this.graphManager.getRoot().getNodes());
  789. };
  790. CoSELayout.prototype.fillCompexOrderByDFS = function (children) {
  791. for (var i = 0; i < children.length; i++) {
  792. var child = children[i];
  793. if (child.getChild() != null) {
  794. this.fillCompexOrderByDFS(child.getChild().getNodes());
  795. }
  796. if (this.getToBeTiled(child)) {
  797. this.compoundOrder.push(child);
  798. }
  799. }
  800. };
  801. /**
  802. * This method places each zero degree member wrt given (x,y) coordinates (top left).
  803. */
  804. CoSELayout.prototype.adjustLocations = function (organization, x, y, compoundHorizontalMargin, compoundVerticalMargin) {
  805. x += compoundHorizontalMargin;
  806. y += compoundVerticalMargin;
  807. var left = x;
  808. for (var i = 0; i < organization.rows.length; i++) {
  809. var row = organization.rows[i];
  810. x = left;
  811. var maxHeight = 0;
  812. for (var j = 0; j < row.length; j++) {
  813. var lnode = row[j];
  814. lnode.rect.x = x; // + lnode.rect.width / 2;
  815. lnode.rect.y = y; // + lnode.rect.height / 2;
  816. x += lnode.rect.width + organization.horizontalPadding;
  817. if (lnode.rect.height > maxHeight) maxHeight = lnode.rect.height;
  818. }
  819. y += maxHeight + organization.verticalPadding;
  820. }
  821. };
  822. CoSELayout.prototype.tileCompoundMembers = function (childGraphMap, idToNode) {
  823. var self = this;
  824. this.tiledMemberPack = [];
  825. Object.keys(childGraphMap).forEach(function (id) {
  826. // Get the compound node
  827. var compoundNode = idToNode[id];
  828. self.tiledMemberPack[id] = self.tileNodes(childGraphMap[id], compoundNode.paddingLeft + compoundNode.paddingRight);
  829. compoundNode.rect.width = self.tiledMemberPack[id].width;
  830. compoundNode.rect.height = self.tiledMemberPack[id].height;
  831. });
  832. };
  833. CoSELayout.prototype.tileNodes = function (nodes, minWidth) {
  834. var verticalPadding = CoSEConstants.TILING_PADDING_VERTICAL;
  835. var horizontalPadding = CoSEConstants.TILING_PADDING_HORIZONTAL;
  836. var organization = {
  837. rows: [],
  838. rowWidth: [],
  839. rowHeight: [],
  840. width: 0,
  841. height: minWidth, // assume minHeight equals to minWidth
  842. verticalPadding: verticalPadding,
  843. horizontalPadding: horizontalPadding
  844. };
  845. // Sort the nodes in ascending order of their areas
  846. nodes.sort(function (n1, n2) {
  847. if (n1.rect.width * n1.rect.height > n2.rect.width * n2.rect.height) return -1;
  848. if (n1.rect.width * n1.rect.height < n2.rect.width * n2.rect.height) return 1;
  849. return 0;
  850. });
  851. // Create the organization -> tile members
  852. for (var i = 0; i < nodes.length; i++) {
  853. var lNode = nodes[i];
  854. if (organization.rows.length == 0) {
  855. this.insertNodeToRow(organization, lNode, 0, minWidth);
  856. } else if (this.canAddHorizontal(organization, lNode.rect.width, lNode.rect.height)) {
  857. this.insertNodeToRow(organization, lNode, this.getShortestRowIndex(organization), minWidth);
  858. } else {
  859. this.insertNodeToRow(organization, lNode, organization.rows.length, minWidth);
  860. }
  861. this.shiftToLastRow(organization);
  862. }
  863. return organization;
  864. };
  865. CoSELayout.prototype.insertNodeToRow = function (organization, node, rowIndex, minWidth) {
  866. var minCompoundSize = minWidth;
  867. // Add new row if needed
  868. if (rowIndex == organization.rows.length) {
  869. var secondDimension = [];
  870. organization.rows.push(secondDimension);
  871. organization.rowWidth.push(minCompoundSize);
  872. organization.rowHeight.push(0);
  873. }
  874. // Update row width
  875. var w = organization.rowWidth[rowIndex] + node.rect.width;
  876. if (organization.rows[rowIndex].length > 0) {
  877. w += organization.horizontalPadding;
  878. }
  879. organization.rowWidth[rowIndex] = w;
  880. // Update compound width
  881. if (organization.width < w) {
  882. organization.width = w;
  883. }
  884. // Update height
  885. var h = node.rect.height;
  886. if (rowIndex > 0) h += organization.verticalPadding;
  887. var extraHeight = 0;
  888. if (h > organization.rowHeight[rowIndex]) {
  889. extraHeight = organization.rowHeight[rowIndex];
  890. organization.rowHeight[rowIndex] = h;
  891. extraHeight = organization.rowHeight[rowIndex] - extraHeight;
  892. }
  893. organization.height += extraHeight;
  894. // Insert node
  895. organization.rows[rowIndex].push(node);
  896. };
  897. //Scans the rows of an organization and returns the one with the min width
  898. CoSELayout.prototype.getShortestRowIndex = function (organization) {
  899. var r = -1;
  900. var min = Number.MAX_VALUE;
  901. for (var i = 0; i < organization.rows.length; i++) {
  902. if (organization.rowWidth[i] < min) {
  903. r = i;
  904. min = organization.rowWidth[i];
  905. }
  906. }
  907. return r;
  908. };
  909. //Scans the rows of an organization and returns the one with the max width
  910. CoSELayout.prototype.getLongestRowIndex = function (organization) {
  911. var r = -1;
  912. var max = Number.MIN_VALUE;
  913. for (var i = 0; i < organization.rows.length; i++) {
  914. if (organization.rowWidth[i] > max) {
  915. r = i;
  916. max = organization.rowWidth[i];
  917. }
  918. }
  919. return r;
  920. };
  921. /**
  922. * This method checks whether adding extra width to the organization violates
  923. * the aspect ratio(1) or not.
  924. */
  925. CoSELayout.prototype.canAddHorizontal = function (organization, extraWidth, extraHeight) {
  926. var sri = this.getShortestRowIndex(organization);
  927. if (sri < 0) {
  928. return true;
  929. }
  930. var min = organization.rowWidth[sri];
  931. if (min + organization.horizontalPadding + extraWidth <= organization.width) return true;
  932. var hDiff = 0;
  933. // Adding to an existing row
  934. if (organization.rowHeight[sri] < extraHeight) {
  935. if (sri > 0) hDiff = extraHeight + organization.verticalPadding - organization.rowHeight[sri];
  936. }
  937. var add_to_row_ratio;
  938. if (organization.width - min >= extraWidth + organization.horizontalPadding) {
  939. add_to_row_ratio = (organization.height + hDiff) / (min + extraWidth + organization.horizontalPadding);
  940. } else {
  941. add_to_row_ratio = (organization.height + hDiff) / organization.width;
  942. }
  943. // Adding a new row for this node
  944. hDiff = extraHeight + organization.verticalPadding;
  945. var add_new_row_ratio;
  946. if (organization.width < extraWidth) {
  947. add_new_row_ratio = (organization.height + hDiff) / extraWidth;
  948. } else {
  949. add_new_row_ratio = (organization.height + hDiff) / organization.width;
  950. }
  951. if (add_new_row_ratio < 1) add_new_row_ratio = 1 / add_new_row_ratio;
  952. if (add_to_row_ratio < 1) add_to_row_ratio = 1 / add_to_row_ratio;
  953. return add_to_row_ratio < add_new_row_ratio;
  954. };
  955. //If moving the last node from the longest row and adding it to the last
  956. //row makes the bounding box smaller, do it.
  957. CoSELayout.prototype.shiftToLastRow = function (organization) {
  958. var longest = this.getLongestRowIndex(organization);
  959. var last = organization.rowWidth.length - 1;
  960. var row = organization.rows[longest];
  961. var node = row[row.length - 1];
  962. var diff = node.width + organization.horizontalPadding;
  963. // Check if there is enough space on the last row
  964. if (organization.width - organization.rowWidth[last] > diff && longest != last) {
  965. // Remove the last element of the longest row
  966. row.splice(-1, 1);
  967. // Push it to the last row
  968. organization.rows[last].push(node);
  969. organization.rowWidth[longest] = organization.rowWidth[longest] - diff;
  970. organization.rowWidth[last] = organization.rowWidth[last] + diff;
  971. organization.width = organization.rowWidth[instance.getLongestRowIndex(organization)];
  972. // Update heights of the organization
  973. var maxHeight = Number.MIN_VALUE;
  974. for (var i = 0; i < row.length; i++) {
  975. if (row[i].height > maxHeight) maxHeight = row[i].height;
  976. }
  977. if (longest > 0) maxHeight += organization.verticalPadding;
  978. var prevTotal = organization.rowHeight[longest] + organization.rowHeight[last];
  979. organization.rowHeight[longest] = maxHeight;
  980. if (organization.rowHeight[last] < node.height + organization.verticalPadding) organization.rowHeight[last] = node.height + organization.verticalPadding;
  981. var finalTotal = organization.rowHeight[longest] + organization.rowHeight[last];
  982. organization.height += finalTotal - prevTotal;
  983. this.shiftToLastRow(organization);
  984. }
  985. };
  986. CoSELayout.prototype.tilingPreLayout = function () {
  987. if (CoSEConstants.TILE) {
  988. // Find zero degree nodes and create a compound for each level
  989. this.groupZeroDegreeMembers();
  990. // Tile and clear children of each compound
  991. this.clearCompounds();
  992. // Separately tile and clear zero degree nodes for each level
  993. this.clearZeroDegreeMembers();
  994. }
  995. };
  996. CoSELayout.prototype.tilingPostLayout = function () {
  997. if (CoSEConstants.TILE) {
  998. this.repopulateZeroDegreeMembers();
  999. this.repopulateCompounds();
  1000. }
  1001. };
  1002. // -----------------------------------------------------------------------------
  1003. // Section: Tree Reduction methods
  1004. // -----------------------------------------------------------------------------
  1005. // Reduce trees
  1006. CoSELayout.prototype.reduceTrees = function () {
  1007. var prunedNodesAll = [];
  1008. var containsLeaf = true;
  1009. var node;
  1010. while (containsLeaf) {
  1011. var allNodes = this.graphManager.getAllNodes();
  1012. var prunedNodesInStepTemp = [];
  1013. containsLeaf = false;
  1014. for (var i = 0; i < allNodes.length; i++) {
  1015. node = allNodes[i];
  1016. if (node.getEdges().length == 1 && !node.getEdges()[0].isInterGraph && node.getChild() == null) {
  1017. prunedNodesInStepTemp.push([node, node.getEdges()[0], node.getOwner()]);
  1018. containsLeaf = true;
  1019. }
  1020. }
  1021. if (containsLeaf == true) {
  1022. var prunedNodesInStep = [];
  1023. for (var j = 0; j < prunedNodesInStepTemp.length; j++) {
  1024. if (prunedNodesInStepTemp[j][0].getEdges().length == 1) {
  1025. prunedNodesInStep.push(prunedNodesInStepTemp[j]);
  1026. prunedNodesInStepTemp[j][0].getOwner().remove(prunedNodesInStepTemp[j][0]);
  1027. }
  1028. }
  1029. prunedNodesAll.push(prunedNodesInStep);
  1030. this.graphManager.resetAllNodes();
  1031. this.graphManager.resetAllEdges();
  1032. }
  1033. }
  1034. this.prunedNodesAll = prunedNodesAll;
  1035. };
  1036. // Grow tree one step
  1037. CoSELayout.prototype.growTree = function (prunedNodesAll) {
  1038. var lengthOfPrunedNodesInStep = prunedNodesAll.length;
  1039. var prunedNodesInStep = prunedNodesAll[lengthOfPrunedNodesInStep - 1];
  1040. var nodeData;
  1041. for (var i = 0; i < prunedNodesInStep.length; i++) {
  1042. nodeData = prunedNodesInStep[i];
  1043. this.findPlaceforPrunedNode(nodeData);
  1044. nodeData[2].add(nodeData[0]);
  1045. nodeData[2].add(nodeData[1], nodeData[1].source, nodeData[1].target);
  1046. }
  1047. prunedNodesAll.splice(prunedNodesAll.length - 1, 1);
  1048. this.graphManager.resetAllNodes();
  1049. this.graphManager.resetAllEdges();
  1050. };
  1051. // Find an appropriate position to replace pruned node, this method can be improved
  1052. CoSELayout.prototype.findPlaceforPrunedNode = function (nodeData) {
  1053. var gridForPrunedNode;
  1054. var nodeToConnect;
  1055. var prunedNode = nodeData[0];
  1056. if (prunedNode == nodeData[1].source) {
  1057. nodeToConnect = nodeData[1].target;
  1058. } else {
  1059. nodeToConnect = nodeData[1].source;
  1060. }
  1061. var startGridX = nodeToConnect.startX;
  1062. var finishGridX = nodeToConnect.finishX;
  1063. var startGridY = nodeToConnect.startY;
  1064. var finishGridY = nodeToConnect.finishY;
  1065. var upNodeCount = 0;
  1066. var downNodeCount = 0;
  1067. var rightNodeCount = 0;
  1068. var leftNodeCount = 0;
  1069. var controlRegions = [upNodeCount, rightNodeCount, downNodeCount, leftNodeCount];
  1070. if (startGridY > 0) {
  1071. for (var i = startGridX; i <= finishGridX; i++) {
  1072. controlRegions[0] += this.grid[i][startGridY - 1].length + this.grid[i][startGridY].length - 1;
  1073. }
  1074. }
  1075. if (finishGridX < this.grid.length - 1) {
  1076. for (var i = startGridY; i <= finishGridY; i++) {
  1077. controlRegions[1] += this.grid[finishGridX + 1][i].length + this.grid[finishGridX][i].length - 1;
  1078. }
  1079. }
  1080. if (finishGridY < this.grid[0].length - 1) {
  1081. for (var i = startGridX; i <= finishGridX; i++) {
  1082. controlRegions[2] += this.grid[i][finishGridY + 1].length + this.grid[i][finishGridY].length - 1;
  1083. }
  1084. }
  1085. if (startGridX > 0) {
  1086. for (var i = startGridY; i <= finishGridY; i++) {
  1087. controlRegions[3] += this.grid[startGridX - 1][i].length + this.grid[startGridX][i].length - 1;
  1088. }
  1089. }
  1090. var min = Integer.MAX_VALUE;
  1091. var minCount;
  1092. var minIndex;
  1093. for (var j = 0; j < controlRegions.length; j++) {
  1094. if (controlRegions[j] < min) {
  1095. min = controlRegions[j];
  1096. minCount = 1;
  1097. minIndex = j;
  1098. } else if (controlRegions[j] == min) {
  1099. minCount++;
  1100. }
  1101. }
  1102. if (minCount == 3 && min == 0) {
  1103. if (controlRegions[0] == 0 && controlRegions[1] == 0 && controlRegions[2] == 0) {
  1104. gridForPrunedNode = 1;
  1105. } else if (controlRegions[0] == 0 && controlRegions[1] == 0 && controlRegions[3] == 0) {
  1106. gridForPrunedNode = 0;
  1107. } else if (controlRegions[0] == 0 && controlRegions[2] == 0 && controlRegions[3] == 0) {
  1108. gridForPrunedNode = 3;
  1109. } else if (controlRegions[1] == 0 && controlRegions[2] == 0 && controlRegions[3] == 0) {
  1110. gridForPrunedNode = 2;
  1111. }
  1112. } else if (minCount == 2 && min == 0) {
  1113. var random = Math.floor(Math.random() * 2);
  1114. if (controlRegions[0] == 0 && controlRegions[1] == 0) {
  1115. ;
  1116. if (random == 0) {
  1117. gridForPrunedNode = 0;
  1118. } else {
  1119. gridForPrunedNode = 1;
  1120. }
  1121. } else if (controlRegions[0] == 0 && controlRegions[2] == 0) {
  1122. if (random == 0) {
  1123. gridForPrunedNode = 0;
  1124. } else {
  1125. gridForPrunedNode = 2;
  1126. }
  1127. } else if (controlRegions[0] == 0 && controlRegions[3] == 0) {
  1128. if (random == 0) {
  1129. gridForPrunedNode = 0;
  1130. } else {
  1131. gridForPrunedNode = 3;
  1132. }
  1133. } else if (controlRegions[1] == 0 && controlRegions[2] == 0) {
  1134. if (random == 0) {
  1135. gridForPrunedNode = 1;
  1136. } else {
  1137. gridForPrunedNode = 2;
  1138. }
  1139. } else if (controlRegions[1] == 0 && controlRegions[3] == 0) {
  1140. if (random == 0) {
  1141. gridForPrunedNode = 1;
  1142. } else {
  1143. gridForPrunedNode = 3;
  1144. }
  1145. } else {
  1146. if (random == 0) {
  1147. gridForPrunedNode = 2;
  1148. } else {
  1149. gridForPrunedNode = 3;
  1150. }
  1151. }
  1152. } else if (minCount == 4 && min == 0) {
  1153. var random = Math.floor(Math.random() * 4);
  1154. gridForPrunedNode = random;
  1155. } else {
  1156. gridForPrunedNode = minIndex;
  1157. }
  1158. if (gridForPrunedNode == 0) {
  1159. prunedNode.setCenter(nodeToConnect.getCenterX(), nodeToConnect.getCenterY() - nodeToConnect.getHeight() / 2 - FDLayoutConstants.DEFAULT_EDGE_LENGTH - prunedNode.getHeight() / 2);
  1160. } else if (gridForPrunedNode == 1) {
  1161. prunedNode.setCenter(nodeToConnect.getCenterX() + nodeToConnect.getWidth() / 2 + FDLayoutConstants.DEFAULT_EDGE_LENGTH + prunedNode.getWidth() / 2, nodeToConnect.getCenterY());
  1162. } else if (gridForPrunedNode == 2) {
  1163. prunedNode.setCenter(nodeToConnect.getCenterX(), nodeToConnect.getCenterY() + nodeToConnect.getHeight() / 2 + FDLayoutConstants.DEFAULT_EDGE_LENGTH + prunedNode.getHeight() / 2);
  1164. } else {
  1165. prunedNode.setCenter(nodeToConnect.getCenterX() - nodeToConnect.getWidth() / 2 - FDLayoutConstants.DEFAULT_EDGE_LENGTH - prunedNode.getWidth() / 2, nodeToConnect.getCenterY());
  1166. }
  1167. };
  1168. module.exports = CoSELayout;
  1169. /***/ }),
  1170. /* 7 */
  1171. /***/ (function(module, exports, __webpack_require__) {
  1172. "use strict";
  1173. var coseBase = {};
  1174. coseBase.layoutBase = __webpack_require__(0);
  1175. coseBase.CoSEConstants = __webpack_require__(1);
  1176. coseBase.CoSEEdge = __webpack_require__(2);
  1177. coseBase.CoSEGraph = __webpack_require__(3);
  1178. coseBase.CoSEGraphManager = __webpack_require__(4);
  1179. coseBase.CoSELayout = __webpack_require__(6);
  1180. coseBase.CoSENode = __webpack_require__(5);
  1181. module.exports = coseBase;
  1182. /***/ })
  1183. /******/ ]);
  1184. });